feat(invite): 新增邀请功能及二维码扫码登录支持
- 添加邀请记录、统计、来源统计、小程序码等数据模型 - 实现小程序码生成、邀请关系绑定、邀请场景处理等接口 - 新增扫码登录相关接口,支持生成二维码、检查状态、确认登录等操作 - 实现二维码内容解析和设备信息获取工具函数 - 添加礼品卡核销相关接口和解密工具函数 - 集成环境配置管理,支持开发、生产、测试环境切换 - 在过期时间页面集成登录二维码和核销二维码处理逻辑 - 添加邀请参数解析工具函数,支持从小程序启动参数中提取邀请信息
This commit is contained in:
323
src/hooks/useShopInfo.ts
Normal file
323
src/hooks/useShopInfo.ts
Normal file
@@ -0,0 +1,323 @@
|
||||
import {useState, useEffect, useCallback} from 'react';
|
||||
import Taro from '@tarojs/taro';
|
||||
import {AppInfo} from '@/api/cms/cmsWebsite/model';
|
||||
import {getShopInfo} from '@/api/layout';
|
||||
|
||||
// 本地存储键名
|
||||
const SHOP_INFO_STORAGE_KEY = 'shop_info';
|
||||
const SHOP_INFO_CACHE_TIME_KEY = 'shop_info_cache_time';
|
||||
|
||||
// 缓存有效期(毫秒)- 默认30分钟
|
||||
const CACHE_DURATION = 30 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* 商店信息Hook
|
||||
* 提供商店信息的获取、缓存和管理功能
|
||||
*/
|
||||
export const useShopInfo = () => {
|
||||
const [shopInfo, setShopInfo] = useState<AppInfo | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// 从本地存储加载商店信息
|
||||
const loadShopInfoFromStorage = useCallback(() => {
|
||||
try {
|
||||
const cachedData = Taro.getStorageSync(SHOP_INFO_STORAGE_KEY);
|
||||
const cacheTime = Taro.getStorageSync(SHOP_INFO_CACHE_TIME_KEY);
|
||||
|
||||
if (cachedData && cacheTime) {
|
||||
const now = Date.now();
|
||||
const timeDiff = now - cacheTime;
|
||||
|
||||
// 检查缓存是否过期
|
||||
if (timeDiff < CACHE_DURATION) {
|
||||
const shopData = typeof cachedData === 'string' ? JSON.parse(cachedData) : cachedData;
|
||||
setShopInfo(shopData);
|
||||
setLoading(false);
|
||||
return true; // 返回true表示使用了缓存
|
||||
} else {
|
||||
// 缓存过期,清除旧数据
|
||||
Taro.removeStorageSync(SHOP_INFO_STORAGE_KEY);
|
||||
Taro.removeStorageSync(SHOP_INFO_CACHE_TIME_KEY);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载商店信息缓存失败:', error);
|
||||
}
|
||||
return false; // 返回false表示没有使用缓存
|
||||
}, []);
|
||||
|
||||
// 保存商店信息到本地存储
|
||||
const saveShopInfoToStorage = useCallback((data: AppInfo) => {
|
||||
try {
|
||||
Taro.setStorageSync(SHOP_INFO_STORAGE_KEY, data);
|
||||
Taro.setStorageSync(SHOP_INFO_CACHE_TIME_KEY, Date.now());
|
||||
} catch (error) {
|
||||
console.error('保存商店信息缓存失败:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 从服务器获取商店信息
|
||||
const fetchShopInfo = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const data = await getShopInfo();
|
||||
setShopInfo(data);
|
||||
|
||||
// 保存到本地存储
|
||||
saveShopInfoToStorage(data);
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error('获取商店信息失败:', error);
|
||||
setError(errorMessage);
|
||||
|
||||
// 如果网络请求失败,尝试使用缓存数据(即使过期)
|
||||
const cachedData = Taro.getStorageSync(SHOP_INFO_STORAGE_KEY);
|
||||
if (cachedData) {
|
||||
const shopData = typeof cachedData === 'string' ? JSON.parse(cachedData) : cachedData;
|
||||
setShopInfo(shopData);
|
||||
console.warn('网络请求失败,使用缓存数据');
|
||||
}
|
||||
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [saveShopInfoToStorage]);
|
||||
|
||||
// 刷新商店信息
|
||||
const refreshShopInfo = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const data = await getShopInfo();
|
||||
setShopInfo(data);
|
||||
|
||||
// 保存到本地存储
|
||||
saveShopInfoToStorage(data);
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error('刷新商店信息失败:', error);
|
||||
setError(errorMessage);
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [saveShopInfoToStorage]);
|
||||
|
||||
// 清除缓存
|
||||
const clearCache = useCallback(() => {
|
||||
try {
|
||||
Taro.removeStorageSync(SHOP_INFO_STORAGE_KEY);
|
||||
Taro.removeStorageSync(SHOP_INFO_CACHE_TIME_KEY);
|
||||
setShopInfo(null);
|
||||
setError(null);
|
||||
} catch (error) {
|
||||
console.error('清除商店信息缓存失败:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 获取应用名称
|
||||
const getAppName = useCallback(() => {
|
||||
return shopInfo?.appName || '商城';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取网站名称(兼容旧方法名)
|
||||
const getWebsiteName = useCallback(() => {
|
||||
return shopInfo?.appName || '商城';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取应用Logo
|
||||
const getAppLogo = useCallback(() => {
|
||||
return shopInfo?.logo || shopInfo?.icon || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取网站Logo(兼容旧方法名)
|
||||
const getWebsiteLogo = useCallback(() => {
|
||||
return shopInfo?.logo || shopInfo?.icon || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取应用图标
|
||||
const getAppIcon = useCallback(() => {
|
||||
return shopInfo?.icon || shopInfo?.logo || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取深色模式Logo(AppInfo中无此字段,使用普通Logo)
|
||||
const getDarkLogo = useCallback(() => {
|
||||
return shopInfo?.logo || shopInfo?.icon || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取应用域名
|
||||
const getDomain = useCallback(() => {
|
||||
return shopInfo?.domain || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取应用描述
|
||||
const getDescription = useCallback(() => {
|
||||
return shopInfo?.description || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取应用关键词
|
||||
const getKeywords = useCallback(() => {
|
||||
return shopInfo?.keywords || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取应用标题
|
||||
const getTitle = useCallback(() => {
|
||||
return shopInfo?.title || shopInfo?.appName || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取小程序二维码
|
||||
const getMpQrCode = useCallback(() => {
|
||||
return shopInfo?.mpQrCode || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取联系电话(AppInfo中无此字段,从config中获取)
|
||||
const getPhone = useCallback(() => {
|
||||
return (shopInfo?.config as any)?.phone || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取邮箱(AppInfo中无此字段,从config中获取)
|
||||
const getEmail = useCallback(() => {
|
||||
return (shopInfo?.config as any)?.email || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取地址(AppInfo中无此字段,从config中获取)
|
||||
const getAddress = useCallback(() => {
|
||||
return (shopInfo?.config as any)?.address || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取ICP备案号(AppInfo中无此字段,从config中获取)
|
||||
const getIcpNo = useCallback(() => {
|
||||
return (shopInfo?.config as any)?.icpNo || '';
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取应用状态
|
||||
const getStatus = useCallback(() => {
|
||||
return {
|
||||
running: shopInfo?.running || 0,
|
||||
statusText: shopInfo?.statusText || '',
|
||||
statusIcon: shopInfo?.statusIcon || '',
|
||||
expired: shopInfo?.expired || false,
|
||||
expiredDays: shopInfo?.expiredDays || 0,
|
||||
soon: shopInfo?.soon || 0
|
||||
};
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取应用配置
|
||||
const getConfig = useCallback(() => {
|
||||
return shopInfo?.config || {};
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取应用设置
|
||||
const getSetting = useCallback(() => {
|
||||
return shopInfo?.setting || {};
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取服务器时间
|
||||
const getServerTime = useCallback(() => {
|
||||
return shopInfo?.serverTime || {};
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取导航菜单
|
||||
const getNavigation = useCallback(() => {
|
||||
return {
|
||||
topNavs: shopInfo?.topNavs || [],
|
||||
bottomNavs: shopInfo?.bottomNavs || []
|
||||
};
|
||||
}, [shopInfo]);
|
||||
|
||||
// 检查是否支持搜索(从config中获取)
|
||||
const isSearchEnabled = useCallback(() => {
|
||||
return (shopInfo?.config as any)?.search === true;
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取应用版本信息
|
||||
const getVersionInfo = useCallback(() => {
|
||||
return {
|
||||
version: shopInfo?.version || 10,
|
||||
expirationTime: shopInfo?.expirationTime || '',
|
||||
expired: shopInfo?.expired || false,
|
||||
expiredDays: shopInfo?.expiredDays || 0,
|
||||
soon: shopInfo?.soon || 0
|
||||
};
|
||||
}, [shopInfo]);
|
||||
|
||||
// 检查应用是否过期
|
||||
const isExpired = useCallback(() => {
|
||||
return shopInfo?.expired === true;
|
||||
}, [shopInfo]);
|
||||
|
||||
// 获取过期天数
|
||||
const getExpiredDays = useCallback(() => {
|
||||
return shopInfo?.expiredDays || 0;
|
||||
}, [shopInfo]);
|
||||
|
||||
// 检查是否即将过期
|
||||
const isSoonExpired = useCallback(() => {
|
||||
return (shopInfo?.soon || 0) > 0;
|
||||
}, [shopInfo]);
|
||||
|
||||
// 初始化时加载商店信息
|
||||
useEffect(() => {
|
||||
const initShopInfo = async () => {
|
||||
// 先尝试从缓存加载
|
||||
const hasCache = loadShopInfoFromStorage();
|
||||
|
||||
// 如果没有缓存或需要刷新,则从服务器获取
|
||||
if (!hasCache) {
|
||||
await fetchShopInfo();
|
||||
}
|
||||
};
|
||||
|
||||
initShopInfo();
|
||||
}, []); // 空依赖数组,只在组件挂载时执行一次
|
||||
|
||||
return {
|
||||
// 状态
|
||||
shopInfo,
|
||||
loading,
|
||||
error,
|
||||
|
||||
// 方法
|
||||
fetchShopInfo,
|
||||
refreshShopInfo,
|
||||
clearCache,
|
||||
|
||||
// 新的工具方法(基于AppInfo字段)
|
||||
getAppName,
|
||||
getAppLogo,
|
||||
getAppIcon,
|
||||
getDescription,
|
||||
getKeywords,
|
||||
getTitle,
|
||||
getMpQrCode,
|
||||
getDomain,
|
||||
getConfig,
|
||||
getSetting,
|
||||
getServerTime,
|
||||
getNavigation,
|
||||
getStatus,
|
||||
getVersionInfo,
|
||||
isExpired,
|
||||
getExpiredDays,
|
||||
isSoonExpired,
|
||||
|
||||
// 兼容旧方法名
|
||||
getWebsiteName,
|
||||
getWebsiteLogo,
|
||||
getDarkLogo,
|
||||
getPhone,
|
||||
getEmail,
|
||||
getAddress,
|
||||
getIcpNo,
|
||||
isSearchEnabled
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user