完成80%
This commit is contained in:
+332
-20
@@ -12,7 +12,7 @@ import {
|
||||
listNotificationFixtures
|
||||
} from '@/data/mock.js'
|
||||
import { hasRemoteConfig, resolveRuntimeMode, runtimeConfig } from '@/utils/config.js'
|
||||
import { AUTH_TAC_SCENE, assertSmsCode } from '@/utils/auth-verification.js'
|
||||
import { AUTH_VERIFICATION_OPERATION, assertSmsCode } from '@/utils/auth-verification.js'
|
||||
import { GENEALOGY_ACCESS_PRESET, fromApiGenealogyAccess } from '@/utils/genealogy-contracts.js'
|
||||
import { session } from '@/utils/session.js'
|
||||
|
||||
@@ -302,6 +302,64 @@ const requestNativeFileChunk = ({ uploadId, chunkIndex, chunkMd5, filePath }, {
|
||||
}
|
||||
})
|
||||
|
||||
const requestBrowserFileChunk = ({ uploadId, chunkIndex, chunkMd5, file }, { requestController = null } = {}) => new Promise((resolve, reject) => {
|
||||
if (typeof globalThis.fetch !== 'function' || typeof globalThis.FormData !== 'function') {
|
||||
reject(createRequestError('当前运行环境不支持文件上传', 'FILE_UPLOAD_UNAVAILABLE'))
|
||||
return
|
||||
}
|
||||
if (requestController !== null && (
|
||||
typeof requestController.bind !== 'function' ||
|
||||
typeof requestController.release !== 'function'
|
||||
)) {
|
||||
reject(new TypeError('文件上传控制器格式无效'))
|
||||
return
|
||||
}
|
||||
let settled = false
|
||||
const abortController = new AbortController()
|
||||
const release = () => requestController?.release(abort)
|
||||
const resolveOnce = (value) => {
|
||||
if (settled) return
|
||||
settled = true
|
||||
release()
|
||||
resolve(value)
|
||||
}
|
||||
const rejectOnce = (error) => {
|
||||
if (settled) return
|
||||
settled = true
|
||||
release()
|
||||
reject(error)
|
||||
}
|
||||
const abort = () => {
|
||||
if (settled) return
|
||||
abortController.abort()
|
||||
rejectOnce(createRequestCancelledError())
|
||||
}
|
||||
const query = `uploadId=${encodeURIComponent(uploadId)}&chunkIndex=${encodeURIComponent(chunkIndex)}&chunkMd5=${encodeURIComponent(chunkMd5)}`
|
||||
const token = session.getToken()
|
||||
const formData = new globalThis.FormData()
|
||||
formData.append('file', file, file.name || 'image')
|
||||
if (requestController) requestController.bind(abort)
|
||||
globalThis.fetch(`${runtimeConfig.baseUrl}/genealogy/app/files/resumable/chunk?${query}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
clientid: runtimeConfig.clientId,
|
||||
tenantId: runtimeConfig.tenantId,
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {})
|
||||
},
|
||||
body: formData,
|
||||
signal: abortController.signal
|
||||
}).then(async (response) => {
|
||||
if (response.status !== 200) {
|
||||
throw createRequestError(`文件分片上传失败(HTTP ${response.status})`, 'HTTP_ERROR', { httpStatus: response.status })
|
||||
}
|
||||
parseStrictUploadResponse(await response.text(), false)
|
||||
resolveOnce(null)
|
||||
}).catch((error) => {
|
||||
if (settled) return
|
||||
rejectOnce(error?.name === 'AbortError' ? createRequestCancelledError() : error)
|
||||
})
|
||||
})
|
||||
|
||||
const requestNativeSingleFileUpload = ({ filePath }, { requestController = null } = {}) => new Promise((resolve, reject) => {
|
||||
if (requestController !== null && (
|
||||
typeof requestController.bind !== 'function' ||
|
||||
@@ -371,11 +429,11 @@ const requireRemoteAuth = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const assertAuthScene = (sceneCode) => {
|
||||
if (!Object.values(AUTH_TAC_SCENE).includes(sceneCode)) {
|
||||
throw new TypeError('短信场景不属于当前认证合同')
|
||||
const assertAuthVerificationOperation = (operationCode) => {
|
||||
if (!Object.values(AUTH_VERIFICATION_OPERATION).includes(operationCode)) {
|
||||
throw new TypeError('认证动作不属于当前认证合同')
|
||||
}
|
||||
return sceneCode
|
||||
return operationCode
|
||||
}
|
||||
|
||||
const assertValidToken = (validToken) => {
|
||||
@@ -385,6 +443,9 @@ const assertValidToken = (validToken) => {
|
||||
return validToken.trim()
|
||||
}
|
||||
|
||||
const normalizeOptionalValidToken = (validToken) =>
|
||||
validToken === undefined ? undefined : assertValidToken(validToken)
|
||||
|
||||
const assertPasswordHash = (passwordHash) => {
|
||||
if (typeof passwordHash !== 'string' || !/^[a-f0-9]{32}$/.test(passwordHash)) {
|
||||
throw new TypeError('密码摘要必须是 32 位小写 MD5')
|
||||
@@ -579,11 +640,13 @@ const normalizeResumableInitResult = (payload) => {
|
||||
if (typeof payload.instant !== 'boolean') {
|
||||
throw createRequestError('文件初始化响应缺少 instant', 'FILE_RESPONSE_INVALID')
|
||||
}
|
||||
const uploadId = normalizeUploadId(payload.uploadId, '响应 uploadId')
|
||||
const ossId = payload.ossId === undefined || payload.ossId === null ? null : normalizeUploadOssId(payload.ossId, 'ossId')
|
||||
if (payload.instant && !ossId) {
|
||||
throw createRequestError('秒传响应缺少 ossId', 'FILE_RESPONSE_INVALID')
|
||||
}
|
||||
const uploadId = payload.instant && (payload.uploadId === undefined || payload.uploadId === null)
|
||||
? null
|
||||
: normalizeUploadId(payload.uploadId, '响应 uploadId')
|
||||
return {
|
||||
uploadId,
|
||||
instant: payload.instant,
|
||||
@@ -844,6 +907,40 @@ const normalizeOptionalNonnegativeInteger = (value, label, code) => {
|
||||
return value
|
||||
}
|
||||
|
||||
const normalizeGenealogyQuota = (value) => {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
throw createRequestError('家谱配额响应无效', 'GENEALOGY_QUOTA_RESPONSE_INVALID')
|
||||
}
|
||||
const normalizeUsage = (field) => {
|
||||
if (!Number.isSafeInteger(value[field]) || value[field] < 0) {
|
||||
throw createRequestError(`家谱配额字段 ${field} 无效`, 'GENEALOGY_QUOTA_RESPONSE_INVALID')
|
||||
}
|
||||
return value[field]
|
||||
}
|
||||
const normalizeLimit = (field) => {
|
||||
if (!Number.isSafeInteger(value[field]) || value[field] < -1) {
|
||||
throw createRequestError(`家谱配额字段 ${field} 无效`, 'GENEALOGY_QUOTA_RESPONSE_INVALID')
|
||||
}
|
||||
return value[field]
|
||||
}
|
||||
const normalizePermission = (field) => {
|
||||
if (typeof value[field] !== 'boolean') {
|
||||
throw createRequestError(`家谱配额字段 ${field} 无效`, 'GENEALOGY_QUOTA_RESPONSE_INVALID')
|
||||
}
|
||||
return value[field]
|
||||
}
|
||||
return {
|
||||
createUsed: normalizeUsage('createUsed'),
|
||||
createLimit: normalizeLimit('createLimit'),
|
||||
createRemaining: normalizeLimit('createRemaining'),
|
||||
canCreate: normalizePermission('canCreate'),
|
||||
joinUsed: normalizeUsage('joinUsed'),
|
||||
joinLimit: normalizeLimit('joinLimit'),
|
||||
joinRemaining: normalizeLimit('joinRemaining'),
|
||||
canJoin: normalizePermission('canJoin')
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeOptionalNumericId = (value, label, code) => {
|
||||
if (value === undefined || value === null || value === '') return null
|
||||
if (typeof value === 'string' && /^[1-9]\d*$/.test(value)) return value
|
||||
@@ -896,6 +993,31 @@ const normalizeRegionParentCode = (value) => {
|
||||
return value.trim()
|
||||
}
|
||||
|
||||
const normalizeRegionCode = (value) => {
|
||||
if (typeof value !== 'string' || !value.trim()) throw new TypeError('行政区划编码无效')
|
||||
return value.trim()
|
||||
}
|
||||
|
||||
const normalizeRegionSearchPayload = (payload) => {
|
||||
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
||||
throw new TypeError('行政区划搜索参数无效')
|
||||
}
|
||||
const keyword = typeof payload.keyword === 'string' ? payload.keyword.trim() : ''
|
||||
if (!keyword) throw new TypeError('行政区划搜索关键词无效')
|
||||
const data = { keyword }
|
||||
if (payload.level !== undefined && payload.level !== null && payload.level !== '') {
|
||||
if (!Number.isInteger(payload.level) || payload.level < 1 || payload.level > 5) {
|
||||
throw new TypeError('行政区划级别无效')
|
||||
}
|
||||
data.level = payload.level
|
||||
}
|
||||
if (payload.limit !== undefined && payload.limit !== null && payload.limit !== '') {
|
||||
if (!Number.isInteger(payload.limit)) throw new TypeError('行政区划搜索数量上限无效')
|
||||
data.limit = payload.limit
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
const normalizeRegionSelectorItems = (value) => {
|
||||
if (!Array.isArray(value)) {
|
||||
throw createRequestError('地区选择响应不是列表', 'REGION_RESPONSE_INVALID')
|
||||
@@ -1269,7 +1391,7 @@ const normalizeFamilyFeedRows = (value, expectedGenealogyId) => {
|
||||
mediaOssIds: normalizeOptionalGenealogyText(item.mediaOssIds, 'mediaOssIds'),
|
||||
likeCount: normalizeOptionalNonnegativeInteger(item.likeCount, '动态点赞数', 'FEED_RESPONSE_INVALID'),
|
||||
commentCount: normalizeOptionalNonnegativeInteger(item.commentCount, '动态评论数', 'FEED_RESPONSE_INVALID'),
|
||||
likedByMe: item.likedByMe === true,
|
||||
likedByMe: typeof item.likedByMe === 'boolean' ? item.likedByMe : null,
|
||||
pinned: item.pinned === true
|
||||
}
|
||||
})
|
||||
@@ -1666,6 +1788,33 @@ const normalizeJoinAuditPayload = (payload) => {
|
||||
return data
|
||||
}
|
||||
|
||||
const normalizeCeremonyInviteePayload = (payload) => {
|
||||
assertPlainPayload(payload, new Set(['inviteeUserIds']), '活动受邀人请求')
|
||||
if (!Array.isArray(payload.inviteeUserIds)) {
|
||||
throw new TypeError('inviteeUserIds必须是数组')
|
||||
}
|
||||
const inviteeUserIds = payload.inviteeUserIds.map((value) => {
|
||||
const id = normalizeResourcePathId(value, '受邀业务用户标识')
|
||||
const numericId = Number(id)
|
||||
if (!Number.isSafeInteger(numericId)) {
|
||||
throw createRequestError('受邀业务用户标识超出 APP 安全整数范围,需后端统一字符串 ID 合同', 'CEREMONY_INVITEE_ID_UNSAFE')
|
||||
}
|
||||
return numericId
|
||||
})
|
||||
if (new Set(inviteeUserIds).size !== inviteeUserIds.length) {
|
||||
throw new TypeError('inviteeUserIds不能包含重复标识')
|
||||
}
|
||||
return { inviteeUserIds }
|
||||
}
|
||||
|
||||
const normalizeCeremonyInvitationResponsePayload = (payload) => {
|
||||
assertPlainPayload(payload, new Set(['inviteStatus']), '活动邀请响应请求')
|
||||
if (!['ACCEPTED', 'DECLINED'].includes(payload.inviteStatus)) {
|
||||
throw new TypeError('inviteStatus仅允许ACCEPTED或DECLINED')
|
||||
}
|
||||
return { inviteStatus: payload.inviteStatus }
|
||||
}
|
||||
|
||||
const normalizeGenerationPoemWritePayload = (payload) => {
|
||||
assertPlainPayload(payload, new Set(['generationNo', 'generationText', 'description', 'sortOrder', 'status']), '字辈请求')
|
||||
const generationNo = Number(payload.generationNo)
|
||||
@@ -1798,9 +1947,15 @@ const normalizeLineageWritePayload = (payload) => {
|
||||
throw new TypeError('人物写入请求必须是普通对象')
|
||||
}
|
||||
const allowedFields = new Set([
|
||||
'appUserId',
|
||||
'personNo',
|
||||
'name',
|
||||
'aliasName',
|
||||
'sex',
|
||||
'generationName',
|
||||
'fatherId',
|
||||
'motherId',
|
||||
'avatarOssId',
|
||||
'birthDate',
|
||||
'birthLunar',
|
||||
'birthPlace',
|
||||
@@ -1811,7 +1966,9 @@ const normalizeLineageWritePayload = (payload) => {
|
||||
'biography',
|
||||
'remark',
|
||||
'relationName',
|
||||
'generation'
|
||||
'generation',
|
||||
'personStatus',
|
||||
'sortOrder'
|
||||
])
|
||||
for (const field of Object.keys(payload)) {
|
||||
if (!allowedFields.has(field)) throw new TypeError(`人物写入包含未声明字段:${field}`)
|
||||
@@ -1819,6 +1976,15 @@ const normalizeLineageWritePayload = (payload) => {
|
||||
const name = normalizeLineagePersonText(payload.name, '姓名', { required: true })
|
||||
if (name.length > 20) throw new TypeError('人物姓名长度超出当前页面合同')
|
||||
const data = { name }
|
||||
for (const [field, label] of [
|
||||
['appUserId', '绑定用户标识'],
|
||||
['fatherId', '父亲标识'],
|
||||
['motherId', '母亲标识'],
|
||||
['avatarOssId', '头像文件标识']
|
||||
]) {
|
||||
if (payload[field] === undefined || payload[field] === null || payload[field] === '') continue
|
||||
data[field] = normalizeLineagePersonIdentity(payload[field], label)
|
||||
}
|
||||
if (payload.generation !== undefined) {
|
||||
if (!Number.isSafeInteger(payload.generation) || payload.generation < 1) {
|
||||
throw new TypeError('人物世代必须是正安全整数')
|
||||
@@ -1826,6 +1992,8 @@ const normalizeLineageWritePayload = (payload) => {
|
||||
data.generation = payload.generation
|
||||
}
|
||||
for (const [field, label, maxLength] of [
|
||||
['personNo', '人物编号', null],
|
||||
['sex', '性别', null],
|
||||
['generationName', '字辈', 12],
|
||||
['aliasName', '别名或曾用名', null],
|
||||
['birthLunar', '出生农历', null],
|
||||
@@ -1835,7 +2003,8 @@ const normalizeLineageWritePayload = (payload) => {
|
||||
['burialPlace', '安葬地', null],
|
||||
['biography', '人物简介', 500],
|
||||
['remark', '备注', null],
|
||||
['relationName', '关系显示名称', null]
|
||||
['relationName', '关系显示名称', null],
|
||||
['personStatus', '人物状态', null]
|
||||
]) {
|
||||
if (payload[field] === undefined) continue
|
||||
const value = normalizeLineagePersonText(payload[field], label)
|
||||
@@ -1853,6 +2022,11 @@ const normalizeLineageWritePayload = (payload) => {
|
||||
if (data.birthDate && data.deathDate && data.deathDate < data.birthDate) {
|
||||
throw new TypeError('人物离世日期不能早于出生日期')
|
||||
}
|
||||
if (payload.sortOrder !== undefined && payload.sortOrder !== null && payload.sortOrder !== '') {
|
||||
const sortOrder = typeof payload.sortOrder === 'number' ? payload.sortOrder : Number(payload.sortOrder)
|
||||
if (!Number.isSafeInteger(sortOrder)) throw new TypeError('人物排序值必须是安全整数')
|
||||
data.sortOrder = sortOrder
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -1877,33 +2051,55 @@ const requireLineageWriteRequestController = (requestOptions) => {
|
||||
}
|
||||
|
||||
export const appApi = {
|
||||
async getCaptchaRequirement({ sceneCode, subject }, requestOptions = {}) {
|
||||
async getCaptchaRequirement({ operationCode, subject }, requestOptions = {}) {
|
||||
requireRemoteAuth()
|
||||
return requestAuth({
|
||||
url: '/captcha/require',
|
||||
url: `/genealogy/app/auth/verification/${encodeURIComponent(assertAuthVerificationOperation(operationCode))}/require`,
|
||||
method: 'GET',
|
||||
data: authPayload({ sceneCode: assertAuthScene(sceneCode), subject })
|
||||
data: { tenantId: runtimeConfig.tenantId, subject }
|
||||
}, requestOptions)
|
||||
},
|
||||
async sendSmsCode({ sceneCode, phone, validToken }, requestOptions = {}) {
|
||||
async sendSmsCode({ operationCode, phone, validToken }, requestOptions = {}) {
|
||||
requireRemoteAuth()
|
||||
const normalizedValidToken = normalizeOptionalValidToken(validToken)
|
||||
return requestAuthVoid({
|
||||
url: `/genealogy/app/auth/sms/${encodeURIComponent(assertAuthVerificationOperation(operationCode))}/code`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
tenantId: runtimeConfig.tenantId,
|
||||
grantType: 'sms',
|
||||
phone,
|
||||
...(normalizedValidToken ? { validToken: normalizedValidToken } : {})
|
||||
}
|
||||
}, requestOptions)
|
||||
},
|
||||
async sendLegacySmsCode({ phone, validToken }, requestOptions = {}) {
|
||||
requireRemoteAuth()
|
||||
const normalizedValidToken = normalizeOptionalValidToken(validToken)
|
||||
return requestAuthVoid({
|
||||
url: '/genealogy/app/auth/sms/code',
|
||||
method: 'POST',
|
||||
data: authPayload({
|
||||
data: {
|
||||
tenantId: runtimeConfig.tenantId,
|
||||
grantType: 'sms',
|
||||
sceneCode: assertAuthScene(sceneCode),
|
||||
phone,
|
||||
validToken: assertValidToken(validToken)
|
||||
})
|
||||
...(normalizedValidToken ? { validToken: normalizedValidToken } : {})
|
||||
}
|
||||
}, requestOptions)
|
||||
},
|
||||
async loginWithPassword({ phone, passwordHash }, requestOptions = {}) {
|
||||
async loginWithPassword({ phone, passwordHash, validToken }, requestOptions = {}) {
|
||||
requireRemoteAuth()
|
||||
const normalizedValidToken = normalizeOptionalValidToken(validToken)
|
||||
const result = await requestAuth({
|
||||
url: '/genealogy/app/auth/login',
|
||||
method: 'POST',
|
||||
data: authPayload({ grantType: 'password', phone, password: assertPasswordHash(passwordHash) })
|
||||
data: {
|
||||
tenantId: runtimeConfig.tenantId,
|
||||
grantType: 'password',
|
||||
phone,
|
||||
password: assertPasswordHash(passwordHash),
|
||||
...(normalizedValidToken ? { validToken: normalizedValidToken } : {})
|
||||
}
|
||||
}, requestOptions)
|
||||
return saveLogin(result)
|
||||
},
|
||||
@@ -2069,6 +2265,25 @@ export const appApi = {
|
||||
filePath: payload.filePath.trim()
|
||||
}, requestOptions)
|
||||
},
|
||||
async uploadBrowserResumableChunk(payload, file, requestOptions = {}) {
|
||||
const allowedFields = new Set(['uploadId', 'chunkIndex', 'chunkMd5'])
|
||||
assertPlainPayload(payload, allowedFields, '浏览器文件分片请求')
|
||||
if (!Number.isInteger(payload.chunkIndex) || payload.chunkIndex < 0 || payload.chunkIndex > 2147483647) {
|
||||
throw new TypeError('chunkIndex必须是非负 int32')
|
||||
}
|
||||
if (!file || typeof file !== 'object') {
|
||||
throw new TypeError('浏览器文件分片必须提供文件对象')
|
||||
}
|
||||
if (!hasRemoteConfig()) {
|
||||
throw createRequestError('文件上传需要真实服务,当前本地预览不伪造回执', 'REMOTE_WRITE_REQUIRED')
|
||||
}
|
||||
return requestBrowserFileChunk({
|
||||
uploadId: normalizeUploadId(payload.uploadId, 'uploadId'),
|
||||
chunkIndex: payload.chunkIndex,
|
||||
chunkMd5: normalizeUploadMd5(payload.chunkMd5, 'chunkMd5'),
|
||||
file
|
||||
}, requestOptions)
|
||||
},
|
||||
async completeResumableUpload(payload, requestOptions = {}) {
|
||||
const data = normalizeResumableCompletePayload(payload)
|
||||
if (!hasRemoteConfig()) {
|
||||
@@ -2111,7 +2326,7 @@ export const appApi = {
|
||||
throw error
|
||||
}
|
||||
const result = await requestStrict({
|
||||
url: '/genealogy/app/region/children',
|
||||
url: '/genealogy/region/children',
|
||||
method: 'GET',
|
||||
data: { parentCode: normalizeRegionParentCode(parentCode) }
|
||||
}, {
|
||||
@@ -2119,6 +2334,46 @@ export const appApi = {
|
||||
})
|
||||
return normalizeRegionSelectorItems(result)
|
||||
},
|
||||
async getRegionPath(regionCode, requestOptions = {}) {
|
||||
requireRemoteResource('行政区划路径', '读取')
|
||||
const result = await requestStrict({
|
||||
url: `/genealogy/region/path/${encodeURIComponent(normalizeRegionCode(regionCode))}`,
|
||||
method: 'GET'
|
||||
}, {
|
||||
requestController: requestOptions.requestController ?? null
|
||||
})
|
||||
if (!Array.isArray(result)) {
|
||||
throw createRequestError('行政区划路径响应不是列表', 'REGION_PATH_RESPONSE_INVALID')
|
||||
}
|
||||
return result
|
||||
},
|
||||
async searchRegions(payload, requestOptions = {}) {
|
||||
requireRemoteResource('行政区划搜索', '读取')
|
||||
const result = await requestStrict({
|
||||
url: '/genealogy/region/search',
|
||||
method: 'GET',
|
||||
data: normalizeRegionSearchPayload(payload)
|
||||
}, {
|
||||
requestController: requestOptions.requestController ?? null
|
||||
})
|
||||
if (!Array.isArray(result)) {
|
||||
throw createRequestError('行政区划搜索响应不是列表', 'REGION_SEARCH_RESPONSE_INVALID')
|
||||
}
|
||||
return result
|
||||
},
|
||||
async getRegion(regionCode, requestOptions = {}) {
|
||||
requireRemoteResource('行政区划详情', '读取')
|
||||
const result = await requestStrict({
|
||||
url: `/genealogy/region/${encodeURIComponent(normalizeRegionCode(regionCode))}`,
|
||||
method: 'GET'
|
||||
}, {
|
||||
requestController: requestOptions.requestController ?? null
|
||||
})
|
||||
if (!result || typeof result !== 'object' || Array.isArray(result)) {
|
||||
throw createRequestError('行政区划详情响应不是对象', 'REGION_DETAIL_RESPONSE_INVALID')
|
||||
}
|
||||
return result
|
||||
},
|
||||
async getMyGenealogies(requestOptions = {}) {
|
||||
if (!hasRemoteConfig()) {
|
||||
const error = new Error('我的家谱需要真实读取服务,当前本地预览不会伪造家谱列表')
|
||||
@@ -2133,6 +2388,16 @@ export const appApi = {
|
||||
})
|
||||
return normalizeMyGenealogies(result)
|
||||
},
|
||||
async getGenealogyQuota(requestOptions = {}) {
|
||||
requireRemoteResource('家谱配额', '读取')
|
||||
const result = await requestStrict({
|
||||
url: '/genealogy/app/genealogies/quota',
|
||||
method: 'GET'
|
||||
}, {
|
||||
requestController: requestOptions.requestController ?? null
|
||||
})
|
||||
return normalizeGenealogyQuota(result)
|
||||
},
|
||||
async createGenealogy(payload, requestOptions = {}) {
|
||||
const data = normalizeGenealogyCreatePayload(payload)
|
||||
if (!hasRemoteConfig()) {
|
||||
@@ -2820,6 +3085,40 @@ export const appApi = {
|
||||
if (!hasRemoteConfig()) return listCeremonyFixtures(normalizedGenealogyId)
|
||||
return readRemoteList(`/genealogy/app/genealogies/${normalizedGenealogyId}/ceremonies`, '礼仪活动', requestOptions)
|
||||
},
|
||||
async replaceCeremonyInvitees(genealogyId, ceremonyId, payload, requestOptions = {}) {
|
||||
const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId)
|
||||
const normalizedCeremonyId = normalizeFeedCommentId(ceremonyId, '礼仪活动标识')
|
||||
return writeRemoteObject(
|
||||
`/genealogy/app/genealogies/${normalizedGenealogyId}/ceremonies/${normalizedCeremonyId}/invitees`,
|
||||
'PUT',
|
||||
normalizeCeremonyInviteePayload(payload),
|
||||
'活动受邀人',
|
||||
requestOptions
|
||||
)
|
||||
},
|
||||
async getCeremonyInvitations(genealogyId, ceremonyId, requestOptions = {}) {
|
||||
const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId)
|
||||
const normalizedCeremonyId = normalizeFeedCommentId(ceremonyId, '礼仪活动标识')
|
||||
return readRemoteList(
|
||||
`/genealogy/app/genealogies/${normalizedGenealogyId}/ceremonies/${normalizedCeremonyId}/invitations`,
|
||||
'活动邀请名单',
|
||||
requestOptions
|
||||
)
|
||||
},
|
||||
async respondToCeremonyInvitation(genealogyId, ceremonyId, payload, requestOptions = {}) {
|
||||
const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId)
|
||||
const normalizedCeremonyId = normalizeFeedCommentId(ceremonyId, '礼仪活动标识')
|
||||
return writeRemoteObject(
|
||||
`/genealogy/app/genealogies/${normalizedGenealogyId}/ceremonies/${normalizedCeremonyId}/invitations/me`,
|
||||
'PUT',
|
||||
normalizeCeremonyInvitationResponsePayload(payload),
|
||||
'活动邀请响应',
|
||||
requestOptions
|
||||
)
|
||||
},
|
||||
async getMyCeremonyInvitations(requestOptions = {}) {
|
||||
return readRemoteList('/genealogy/app/genealogies/ceremony-invitations/mine', '我的活动邀请', requestOptions)
|
||||
},
|
||||
async getCeremonyDetail(genealogyId, ceremonyId, requestOptions = {}) {
|
||||
const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId)
|
||||
const normalizedCeremonyId = normalizeFeedCommentId(ceremonyId, '礼仪活动标识')
|
||||
@@ -3014,6 +3313,19 @@ export const appApi = {
|
||||
if (!hasRemoteConfig()) return listNotificationFixtures()
|
||||
return readRemoteList('/genealogy/app/notifications', '消息通知', requestOptions)
|
||||
},
|
||||
async getUnreadNotificationCount(requestOptions = {}) {
|
||||
requireRemoteResource('未读通知数量', '读取')
|
||||
const value = await requestStrict({
|
||||
url: '/genealogy/app/notifications/unread-count',
|
||||
method: 'GET'
|
||||
}, {
|
||||
requestController: requestOptions.requestController ?? null
|
||||
})
|
||||
if (!Number.isSafeInteger(value) || value < 0) {
|
||||
throw createRequestError('未读通知数量响应无效', 'NOTIFICATION_COUNT_RESPONSE_INVALID')
|
||||
}
|
||||
return value
|
||||
},
|
||||
async markNotificationRead(notificationId, requestOptions = {}) {
|
||||
const normalizedNotificationId = normalizeResourcePathId(notificationId, '通知标识')
|
||||
if (hasRemoteConfig()) {
|
||||
|
||||
Reference in New Issue
Block a user