460 lines
14 KiB
Plaintext
460 lines
14 KiB
Plaintext
import {
|
|
IAppState, type ISystemConfig, type ILaunchData,
|
|
type IBaseData, type IRecommendData,
|
|
IUnread
|
|
} from '@/types/app.uts'
|
|
|
|
import userState, { isLoggedIn } from './user.uts'
|
|
import Api from '@/common/api-service.uts'
|
|
import messageService from '@/services/websocket/message-service.uts'
|
|
import httpApi from '@/utlis/http-api.uts'
|
|
|
|
const initialAppState : IAppState = {
|
|
baseURL: '',
|
|
timeout: 15000,
|
|
enableCache: true,
|
|
cacheExpire: 5 * 60 * 1000,
|
|
enableLog: true,
|
|
systemConf: null,
|
|
launchData: null,
|
|
baseData: null,
|
|
recommendData: null,
|
|
unread: null,
|
|
initialized: false,
|
|
// 时间信息
|
|
startTime: 0,
|
|
endTime: 0,
|
|
lastInitTime: 0,
|
|
// 网络状态
|
|
networkType: 'none',
|
|
isConnected: false
|
|
}
|
|
|
|
const appState = reactive<IAppState>(initialAppState)
|
|
|
|
|
|
/**
|
|
* 初始化应用
|
|
* 注意:系统配置/启动数据/基础数据每次启动都需加载(提供 socketUrl、功能开关、cdn 等),
|
|
* 不再依赖 isInitialUse 闸门,避免配置永远不加载。
|
|
*/
|
|
export const initApp = async function () : Promise<boolean> {
|
|
try {
|
|
appState.startTime = Date.now()
|
|
startNetworkListen()
|
|
|
|
// 1. 系统配置(socketUrl / 功能开关 / cdn 等)
|
|
await loadSystemConfig()
|
|
// 2. 启动数据(广告/轮播/快捷入口/新手引导)
|
|
await loadLaunchData()
|
|
// 3. 基础数据(城市/标签/礼物/等级等)
|
|
await loadBaseData()
|
|
|
|
// 已登录:连接实时消息通道并加载用户态数据
|
|
if (isLoggedIn.value) {
|
|
connectWebSocket()
|
|
await loadRecommendData()
|
|
await syncUnreadCounts()
|
|
}
|
|
|
|
appState.initialized = true
|
|
appState.endTime = Date.now()
|
|
appState.lastInitTime = Date.now()
|
|
return true
|
|
} catch (error : any) {
|
|
console.error('应用初始化失败:', error)
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 已登录时按需连接 WebSocket 实时消息通道
|
|
*/
|
|
export const connectWebSocket = function () : void {
|
|
messageService.connectIfNeeded(appState.systemConf?.socketUrl ?? null)
|
|
}
|
|
|
|
|
|
export const startNetworkListen = async function () {
|
|
uni.onNetworkStatusChange((result) => {
|
|
console.log('网络状态变化:', result)
|
|
appState.networkType = result.networkType //网络类型
|
|
appState.isConnected = result.isConnected //是否已连接
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 加载系统配置
|
|
*/
|
|
export const loadSystemConfig = async function () : Promise<void> {
|
|
try {
|
|
const data = await Api.app.appConf()
|
|
const result = data.parse<ISystemConfig>()
|
|
if (result != null) {
|
|
appState.systemConf = result
|
|
setCacheData('system_config', data)
|
|
} else {
|
|
appState.systemConf = getAppDefaultConf()
|
|
}
|
|
} catch (error) {
|
|
console.warn('加载系统配置失败,使用缓存或默认配置', error)
|
|
const cachedConfig = getCachedSystemConfig('system_config')
|
|
appState.systemConf = cachedConfig != null ? cachedConfig : getAppDefaultConf()
|
|
}
|
|
// 将后端下发的 apiUrl 同步为 HTTP 客户端基础域名(接口 URL 已含 /wxchat/api 前缀)
|
|
applyApiBaseURL(appState.systemConf?.apiUrl)
|
|
}
|
|
|
|
/**
|
|
* 设置 HTTP 客户端基础域名(去除末尾斜杠,避免拼接出 //wxchat)
|
|
*/
|
|
export const applyApiBaseURL = function (apiUrl : string | null | undefined) : void {
|
|
if (apiUrl == null || apiUrl.length == 0) {
|
|
return
|
|
}
|
|
const base = apiUrl.replace(/\/+$/, '')
|
|
if (base.length > 0) {
|
|
httpApi.setBaseURL(base)
|
|
console.log('[app] HTTP baseURL 已设置为:', base)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 加载启动数据
|
|
*/
|
|
const loadLaunchData = async function () : Promise<void> {
|
|
try {
|
|
const data = await Api.app.launchData()
|
|
const result = data.parse<ILaunchData>()
|
|
if (result != null) {
|
|
appState.launchData = result
|
|
setCacheData('launch_data', data)
|
|
} else {
|
|
appState.launchData = getDefaultLaunchData()
|
|
}
|
|
} catch (error) {
|
|
console.warn('加载启动数据失败,使用默认启动数据 :', error)
|
|
const cachedData = getCachedLaunchData('launch_data')
|
|
appState.launchData = cachedData != null ? cachedData : getDefaultLaunchData()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 加载基础数据
|
|
*/
|
|
const loadBaseData = async function () : Promise<void> {
|
|
try {
|
|
const data = await Api.app.baseData()
|
|
const result = data.parse<IBaseData>()
|
|
if (result != null) {
|
|
appState.baseData = result
|
|
setCacheData('base_data', data)
|
|
console.log('基础数据加载成功')
|
|
} else {
|
|
appState.baseData = getDefaultBaseData()
|
|
console.log('基础数据为空,使用默认基础数据')
|
|
}
|
|
} catch (error) {
|
|
console.warn('加载基础数据失败,使用缓存或默认基础数据', error)
|
|
const cachedData = getCachedBaseData('base_data')
|
|
appState.baseData = cachedData != null ? cachedData : getDefaultBaseData()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 加载推荐数据
|
|
*/
|
|
const loadRecommendData = async function () : Promise<void> {
|
|
if (isLoggedIn.value == false) {
|
|
appState.recommendData = null
|
|
console.log('当前未登录,跳过推荐数据加载')
|
|
return
|
|
}
|
|
try {
|
|
const data = await Api.recommend.index()
|
|
const result = data.parse<IRecommendData>()
|
|
if (result != null) {
|
|
appState.recommendData = result
|
|
setCacheData('recommend_data', data)
|
|
console.log('推荐数据加载成功')
|
|
}
|
|
} catch (error) {
|
|
console.warn('加载推荐数据失败,尝试使用缓存数据', error)
|
|
const cachedData = getCachedRecommendData('recommend_data')
|
|
if (cachedData != null) {
|
|
appState.recommendData = cachedData
|
|
console.log('使用缓存的推荐数据')
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 同步未读消息
|
|
*/
|
|
const syncUnreadCounts = async function () : Promise<void> {
|
|
try {
|
|
const data = await Api.message.unread()
|
|
const result = data.parse<IUnread>()
|
|
if (result != null) {
|
|
appState.unread = result
|
|
console.log('未读消息同步成功')
|
|
}
|
|
} catch (error) {
|
|
console.warn('同步未读消息失败:', error)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* 获取缓存数据
|
|
*/
|
|
export const getCachedData = function (key : string) : UTSJSONObject | null {
|
|
try {
|
|
const cached = uni.getStorageSync(key) as string | null
|
|
const cachedTime = uni.getStorageSync(`${key}_time`) as string | null
|
|
if (cached == null || cachedTime == null) {
|
|
return null
|
|
}
|
|
const cacheAge = Date.now() - parseInt(cachedTime)
|
|
if (cacheAge < appState.cacheExpire) {
|
|
const parsed = JSON.parse(cached) as UTSJSONObject
|
|
return parsed
|
|
}
|
|
return null
|
|
} catch (error) {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export const getCachedSystemConfig = function (key : string) : ISystemConfig | null {
|
|
const cached = getCachedData(key)
|
|
if (cached == null) {
|
|
return null
|
|
}
|
|
return cached.parse<ISystemConfig>()
|
|
}
|
|
|
|
export const getCachedLaunchData = function (key : string) : ILaunchData | null {
|
|
const cached = getCachedData(key)
|
|
if (cached == null) {
|
|
return null
|
|
}
|
|
return cached.parse<ILaunchData>()
|
|
}
|
|
|
|
export const getCachedBaseData = function (key : string) : IBaseData | null {
|
|
const cached = getCachedData(key)
|
|
if (cached == null) {
|
|
return null
|
|
}
|
|
return cached.parse<IBaseData>()
|
|
}
|
|
|
|
export const getCachedRecommendData = function (key : string) : IRecommendData | null {
|
|
const cached = getCachedData(key)
|
|
if (cached == null) {
|
|
return null
|
|
}
|
|
return cached.parse<IRecommendData>()
|
|
}
|
|
|
|
/**
|
|
* 缓存数据
|
|
*/
|
|
export const setCacheData = function (key : string, data : any) : void {
|
|
try {
|
|
uni.setStorageSync(key, JSON.stringify(data))
|
|
uni.setStorageSync(`${key}_time`, Date.now().toString())
|
|
} catch (error) {
|
|
console.error('缓存数据失败:', error)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取默认加载数据
|
|
*/
|
|
export const getDefaultLaunchData = () : ILaunchData{
|
|
const launchData : ILaunchData = {
|
|
splashAds: [],
|
|
banners: [
|
|
{
|
|
id: 1,
|
|
image: 'https://example.com/banner1.jpg',
|
|
title: '欢迎使用',
|
|
link: '',
|
|
type: 'page',
|
|
order: 1
|
|
}
|
|
],
|
|
// announcements: [],
|
|
activities: [],
|
|
quickActions: [
|
|
{
|
|
id: 1,
|
|
name: '匹配',
|
|
icon: 'heart',
|
|
color: '#FF6B6B',
|
|
route: '/pages/match/match',
|
|
visible: true
|
|
},
|
|
{
|
|
id: 2,
|
|
name: '聊天',
|
|
icon: 'message',
|
|
color: '#007AFF',
|
|
route: '/pages/chat/chat',
|
|
visible: true
|
|
},
|
|
{
|
|
id: 3,
|
|
name: '动态',
|
|
icon: 'image',
|
|
color: '#34C759',
|
|
route: '/pages/moment/moment',
|
|
visible: true
|
|
},
|
|
{
|
|
id: 4,
|
|
name: '房间',
|
|
icon: 'mic',
|
|
color: '#5856D6',
|
|
route: '/pages/room/room',
|
|
visible: true
|
|
}
|
|
],
|
|
newbieGuide: {
|
|
enabled: true,
|
|
steps: [
|
|
{
|
|
id: 1,
|
|
title: '欢迎来到心遇',
|
|
content: '遇见美好,遇见你',
|
|
image: 'https://example.com/guide1.png'
|
|
},
|
|
{
|
|
id: 2,
|
|
title: '智能匹配',
|
|
content: '根据你的兴趣和喜好,智能推荐合适的伙伴',
|
|
image: 'https://example.com/guide2.png'
|
|
},
|
|
{
|
|
id: 3,
|
|
title: '实时聊天',
|
|
content: '文字、语音、视频,多种方式畅聊无阻',
|
|
image: 'https://example.com/guide3.png'
|
|
}
|
|
]
|
|
}
|
|
}
|
|
return launchData
|
|
}
|
|
|
|
/**
|
|
* 获取默认系统配置
|
|
*/
|
|
export const getAppDefaultConf = () : ISystemConfig {
|
|
const defaultConf : ISystemConfig = {
|
|
|
|
dailyMatchLimit: 50,
|
|
dailyMessageLimit: 100,
|
|
maxFriends: 500,
|
|
maxMomentImages: 9,
|
|
maxChatImageSize: 10 * 1024 * 1024, // 10MB
|
|
maxVideoDuration: 300, // 5分钟
|
|
maxVoiceDuration: 60 // 1分钟
|
|
|
|
enableAd: false,
|
|
splashAdId: '',
|
|
bannerAdId: '',
|
|
interstitialAdId: '',
|
|
rewardAdId: '',
|
|
adFrequency: 3
|
|
|
|
uploadUrl: 'https://upload.example.com',
|
|
cdnUrl: 'https://cdn.example.com',
|
|
socketUrl: 'wss://socket.example.com',
|
|
apiVersion: 'v1'
|
|
|
|
wechatAppId: '',
|
|
qqAppId: '',
|
|
appleServiceId: '',
|
|
pushService: '',
|
|
mapService: 'amap'
|
|
|
|
minIosVersion: '11.0',
|
|
minAndroidVersion: '5.0',
|
|
supportedPlatforms: ['ios', 'android', 'web']
|
|
|
|
}
|
|
return defaultConf
|
|
}
|
|
|
|
/**
|
|
* 获取默认基础数据
|
|
*/
|
|
export const getDefaultBaseData = () : IBaseData {
|
|
const baseData : IBaseData = {
|
|
cities: [
|
|
{ id: 1, name: '北京', code: '110000', parentId: 0, level: 1, pinyin: 'beijing', hot: true },
|
|
{ id: 2, name: '上海', code: '310000', parentId: 0, level: 1, pinyin: 'shanghai', hot: true },
|
|
{ id: 3, name: '广州', code: '440100', parentId: 0, level: 1, pinyin: 'guangzhou', hot: true },
|
|
{ id: 4, name: '深圳', code: '440300', parentId: 0, level: 1, pinyin: 'shenzhen', hot: true },
|
|
{ id: 5, name: '成都', code: '510100', parentId: 0, level: 1, pinyin: 'chengdu', hot: true }
|
|
],
|
|
tags: [
|
|
{ id: 1, name: '旅行', type: 'hobby', hot: true, count: 1000 },
|
|
{ id: 2, name: '美食', type: 'hobby', hot: true, count: 1500 },
|
|
{ id: 3, name: '音乐', type: 'hobby', hot: false, count: 800 },
|
|
{ id: 4, name: '运动', type: 'hobby', hot: true, count: 1200 },
|
|
{ id: 5, name: '游戏', type: 'hobby', hot: true, count: 2000 }
|
|
],
|
|
gifts: [
|
|
{ id: 1, name: '玫瑰花', description: '代表爱意', image: '🌹', price: 10, coinType: 'coin', category: 'love', isHot: true, isNew: false, isVipOnly: false, isLimited: false, sortOrder: 1 },
|
|
{ id: 2, name: '棒棒糖', description: '甜甜蜜蜜', image: '🍭', price: 20, coinType: 'coin', category: 'sweet', isHot: true, isNew: false, isVipOnly: false, isLimited: false, sortOrder: 2 },
|
|
{ id: 3, name: '爱心', description: '表达心意', image: '❤️', price: 50, coinType: 'coin', category: 'love', isHot: true, isNew: true, isVipOnly: false, isLimited: false, sortOrder: 3 },
|
|
{ id: 4, name: '钻戒', description: '永恒的爱', image: '💍', price: 999, coinType: 'diamond', category: 'luxury', isHot: false, isNew: false, isVipOnly: true, isLimited: false, sortOrder: 4 }
|
|
],
|
|
levels: [
|
|
{ level: 1, minExp: 0, maxExp: 100, name: '新手', icon: '🌟', color: '#999999', privileges: ['基础功能'] },
|
|
{ level: 2, minExp: 100, maxExp: 300, name: '入门', icon: '🌟', color: '#666666', privileges: ['每日匹配+5'] },
|
|
{ level: 3, minExp: 300, maxExp: 600, name: '活跃', icon: '🌟', color: '#FF9500', privileges: ['去广告', '高级筛选'] },
|
|
{ level: 4, minExp: 600, maxExp: 1000, name: '资深', icon: '🌟', color: '#FF3B30', privileges: ['VIP标识', '优先展示'] },
|
|
{ level: 5, minExp: 1000, maxExp: 2000, name: '大神', icon: '🌟', color: '#5856D6', privileges: ['专属客服', '特权标识'] }
|
|
],
|
|
vipPackages: [
|
|
{ id: 1, name: '月卡', duration: 30, durationUnit: 'day', price: 30, benefits: ['去广告', '每日匹配+20', '聊天背景+5'], isHot: true, isRecommend: true },
|
|
{ id: 2, name: '季卡', duration: 90, durationUnit: 'day', price: 80, originalPrice: 90, benefits: ['月卡所有特权', '专属标识', '优先匹配'], isHot: false, isRecommend: true },
|
|
{ id: 3, name: '年卡', duration: 365, durationUnit: 'day', price: 288, originalPrice: 360, benefits: ['季卡所有特权', '专属客服', '无限匹配'], isHot: true, isRecommend: false }
|
|
],
|
|
rechargePackages: [
|
|
{ id: 1, name: '小试牛刀', coinAmount: 60, diamondAmount: 6, price: 6, currency: 'CNY', discount: 0, isHot: false, isRecommend: false },
|
|
{ id: 2, name: '日常所需', coinAmount: 300, diamondAmount: 30, price: 30, currency: 'CNY', discount: 0, isHot: true, isRecommend: true },
|
|
{ id: 3, name: '土豪专享', coinAmount: 980, diamondAmount: 98, price: 98, currency: 'CNY', discount: 10, isHot: true, isRecommend: false },
|
|
{ id: 4, name: '至尊VIP', coinAmount: 2980, diamondAmount: 298, price: 298, currency: 'CNY', discount: 20, isHot: false, isRecommend: true }
|
|
],
|
|
verifications: [
|
|
{ id: 1, name: '实名认证', description: '提高信任度', icon: '✅', price: 0, benefits: ['认证标识', '提高匹配率'] },
|
|
{ id: 2, name: '学历认证', description: '展示教育背景', icon: '🎓', price: 9.9, benefits: ['学历标识', '优先展示'] },
|
|
{ id: 3, name: '职业认证', description: '验证职业信息', icon: '💼', price: 9.9, benefits: ['职业标识', '增加可信度'] }
|
|
],
|
|
chatBackgrounds: [
|
|
{ id: 1, name: '默认背景', image: 'https://example.com/bg1.jpg', price: 0, isFree: true },
|
|
{ id: 2, name: '星空', image: 'https://example.com/bg2.jpg', price: 10, isFree: false },
|
|
{ id: 3, name: '海洋', image: 'https://example.com/bg3.jpg', price: 10, isFree: false },
|
|
{ id: 4, name: '森林', image: 'https://example.com/bg4.jpg', price: 20, isFree: false }
|
|
],
|
|
emojis: [
|
|
{ id: 1, name: '微笑', emoji: '😊', category: '表情' },
|
|
{ id: 2, name: '爱心', emoji: '❤️', category: '表情' },
|
|
{ id: 3, name: '笑哭', emoji: '😂', category: '表情' },
|
|
{ id: 4, name: '点赞', emoji: '👍', category: '手势' },
|
|
{ id: 5, name: '加油', emoji: '💪', category: '手势' }
|
|
]
|
|
}
|
|
return baseData
|
|
}
|
|
|
|
|
|
export { appState }
|
|
export default appState |