import { currentUser, genealogies, treeMembers, familyFeeds, familyContent, notifications, joinApplications } from '@/data/mock.js' import { hasRemoteConfig, isMockMode, runtimeConfig } from '@/utils/config.js' import { session } from '@/utils/session.js' const successCodes = [0, 200] export const unwrapResponse = (response) => { if (!response || typeof response !== 'object' || Array.isArray(response)) return response if (!Object.prototype.hasOwnProperty.call(response, 'code')) return response if (!successCodes.includes(Number(response.code))) { throw new Error(response.msg || '请求未成功') } return Object.prototype.hasOwnProperty.call(response, 'data') ? response.data : response } const request = (options, { authenticated = true } = {}) => new Promise((resolve, reject) => { const token = session.getToken() uni.request({ ...options, url: `${runtimeConfig.baseUrl}${options.url}`, header: { clientid: runtimeConfig.clientId, ...(authenticated && token ? { Authorization: `Bearer ${token}` } : {}), ...(options.header || {}) }, success: ({ data, statusCode }) => { if (statusCode < 200 || statusCode >= 300) { reject(new Error(data?.msg || `请求失败(${statusCode})`)) return } try { resolve(unwrapResponse(data)) } catch (error) { reject(error) } }, fail: (error) => reject(new Error(error.errMsg || '网络连接失败')) }) }) const authPayload = (payload) => ({ clientId: runtimeConfig.clientId, tenantId: runtimeConfig.tenantId, ...payload }) const saveLogin = (loginResult) => { const token = loginResult?.token || loginResult?.accessToken || loginResult?.tokenValue if (!token) throw new Error('登录响应未包含会话令牌') session.saveToken(token) return loginResult } const toTreeNode = (person, index = 0) => { const generation = Number(person.generationNo || person.generation || 1) return { ...person, id: person.id || person.personId, name: person.personName || person.name || '未命名族人', relation: person.relation || (generation === 1 ? '始祖' : '族人'), generation, years: person.years || [person.birthDate, person.deathDate].filter(Boolean).join('—') || '生卒待补', branch: person.branch || '主支', x: person.x ?? (20 + (index % 4) * 20), y: person.y ?? (generation * 31 - 24) } } export const appApi = { async sendSmsCode({ phone, validToken = '' }) { if (isMockMode()) return { success: true } return request({ url: '/genealogy/app/auth/sms/code', method: 'POST', data: authPayload({ grantType: 'sms', sceneCode: 'APP_LOGIN', phone, validToken }) }, { authenticated: false }) }, async loginWithPassword({ phone, passwordHash }) { if (isMockMode()) { session.saveToken('mock-session-token') return { token: 'mock-session-token', userId: currentUser.id } } const result = await request({ url: '/genealogy/app/auth/login', method: 'POST', data: authPayload({ grantType: 'password', phone, password: passwordHash }) }, { authenticated: false }) return saveLogin(result) }, async loginWithSms({ phone, smsCode }) { if (isMockMode()) { session.saveToken('mock-session-token') return { token: 'mock-session-token', userId: currentUser.id } } const result = await request({ url: '/genealogy/app/auth/login/sms', method: 'POST', data: authPayload({ grantType: 'sms', phone, smsCode }) }, { authenticated: false }) return saveLogin(result) }, async getProfile() { return hasRemoteConfig() ? request({ url: '/genealogy/app/auth/profile' }) : currentUser }, async getMyGenealogies() { return hasRemoteConfig() ? request({ url: '/genealogy/app/genealogies/mine' }) : genealogies }, async createGenealogy(payload) { if (hasRemoteConfig()) return request({ url: '/genealogy/app/genealogies', method: 'POST', data: payload }) const created = { ...payload, id: Date.now(), memberCount: 0, activeCount: 0, motto: '敦亲睦族,敬祖传家。', visibility: '仅成员可见' } genealogies.unshift(created) return created }, async getGenealogy(genealogyId) { return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}` }) : genealogies.find((item) => String(item.id) === String(genealogyId)) }, async getOverview(genealogyId) { return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/overview` }) : await this.getGenealogy(genealogyId) }, async getTree(genealogyId) { if (hasRemoteConfig()) { const tree = await request({ url: `/genealogy/app/genealogies/${genealogyId}/lineage/tree` }) return (tree || []).map(toTreeNode) } return treeMembers.filter((item) => String(item.genealogyId || 1001) === String(genealogyId)).map(toTreeNode) }, async getPerson(genealogyId, personId) { if (hasRemoteConfig()) { const person = await request({ url: `/genealogy/app/genealogies/${genealogyId}/lineage/persons/${personId}` }) return toTreeNode(person) } const person = treeMembers.find((item) => String(item.id) === String(personId) && String(item.genealogyId || 1001) === String(genealogyId)) return person ? toTreeNode(person) : null }, async createPerson(genealogyId, payload) { if (hasRemoteConfig()) { const person = await request({ url: `/genealogy/app/genealogies/${genealogyId}/lineage/persons`, method: 'POST', data: payload }) return toTreeNode(person) } const person = toTreeNode({ ...payload, id: Date.now(), genealogyId, relation: Number(payload.generationNo) === 1 ? '始祖' : '族人', x: 50, y: 7 }) treeMembers.push(person) const genealogy = genealogies.find((item) => String(item.id) === String(genealogyId)) if (genealogy) { genealogy.memberCount = (genealogy.memberCount || 0) + 1 genealogy.activeCount = (genealogy.activeCount || 0) + 1 } return person }, async getFeeds(genealogyId) { return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/feeds` }) : familyFeeds }, async createFeed(genealogyId, payload) { if (hasRemoteConfig()) return request({ url: `/genealogy/app/genealogies/${genealogyId}/feeds`, method: 'POST', data: payload }) const feed = { id: Date.now(), genealogyId, author: currentUser.name, title: '家族动态', content: payload.feedContent, date: '刚刚', type: '族务' } familyFeeds.unshift(feed) return feed }, async getArticles(genealogyId) { return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/articles` }) : familyContent.article }, async getAlbums(genealogyId) { return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/albums` }) : familyContent.album }, async getCeremonies(genealogyId) { return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/ceremonies` }) : familyContent.ceremony }, async getGrowthRecords(genealogyId) { return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/growth-records` }) : familyContent.record }, async getContent(type, genealogyId) { const loaders = { article: this.getArticles, album: this.getAlbums, ceremony: this.getCeremonies, record: this.getGrowthRecords } if (!loaders[type]) throw new Error('未知内容类型') return loaders[type].call(this, genealogyId) }, async getNotifications() { return hasRemoteConfig() ? request({ url: '/genealogy/app/notifications' }) : notifications }, async markNotificationRead(notificationId) { if (hasRemoteConfig()) return request({ url: `/genealogy/app/notifications/${notificationId}/read`, method: 'POST' }) const notification = notifications.find((item) => String(item.id) === String(notificationId)) if (notification) notification.unread = false return { success: true } }, async markAllNotificationsRead() { if (hasRemoteConfig()) return request({ url: '/genealogy/app/notifications/read-all', method: 'POST' }) notifications.forEach((item) => { item.unread = false }) return { success: true } }, async getPublicGenealogies() { return hasRemoteConfig() ? request({ url: '/genealogy/app/genealogies/public' }) : genealogies.filter((item) => item.visibility === '公开可申请') }, async applyToJoin(genealogyId, payload) { if (hasRemoteConfig()) return request({ url: `/genealogy/app/genealogies/${genealogyId}/join-applies`, method: 'POST', data: payload }) joinApplications.unshift({ id: Date.now(), genealogyId, name: currentUser.name, phone: currentUser.phone, relation: payload.message || '申请加入家谱', appliedAt: '刚刚', status: 'PENDING' }) return { success: true } }, async getPendingApplications(genealogyId) { return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/join-applies/pending` }) : joinApplications.filter((item) => String(item.genealogyId || 1001) === String(genealogyId)) }, async auditApplication(genealogyId, applicationId, approved) { return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/join-applies/${applicationId}/audit`, method: 'PUT', data: { approved } }) : { success: true } } }