219 lines
9.3 KiB
JavaScript
219 lines
9.3 KiB
JavaScript
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 }
|
||
}
|
||
}
|