Files

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