fix: id兼容, 通知入口
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m0s

This commit is contained in:
luo
2026-07-28 15:19:29 +08:00
parent 0e8a11430a
commit 261f5f2853
21 changed files with 271 additions and 71 deletions

58
src/utils/business/id.ts Normal file
View File

@@ -0,0 +1,58 @@
/**
* 兼容后端实体 ID 字段的大小写差异。
* 后端历史接口可能返回 `ID`,新接口返回 `id`,前端统一优先使用小写 id。
*/
export type CompatibleId = number | string
export interface CompatibleIdFields {
id?: CompatibleId | null
ID?: CompatibleId | null
}
/** 从实体中读取兼容的 ID 字段。0 也是有效 ID不能使用 ||。 */
export const getCompatibleId = (value: CompatibleIdFields | null | undefined) => {
const id = value?.id ?? value?.ID
return id === null || id === undefined || id === '' ? undefined : id
}
/** 读取并转换为数字 ID供只接受 number 的接口参数使用。 */
export const getCompatibleNumericId = (
value: CompatibleIdFields | null | undefined
): number | undefined => {
const id = getCompatibleId(value)
if (id === undefined) return undefined
const numericId = Number(id)
return Number.isFinite(numericId) ? numericId : undefined
}
const isPlainObject = (value: unknown): value is Record<string, unknown> => {
if (value === null || typeof value !== 'object') return false
const prototype = Object.getPrototypeOf(value)
return prototype === Object.prototype || prototype === null
}
/** 递归补齐响应中的 id/ID 别名,不影响 Blob、FormData、Date 等特殊数据。 */
export const syncCompatibleIdFields = <T>(value: T): T => {
if (Array.isArray(value)) {
value.forEach((item) => syncCompatibleIdFields(item))
return value
}
if (!isPlainObject(value)) return value
const record = value as Record<string, unknown>
const hasId = Object.prototype.hasOwnProperty.call(record, 'id')
const hasUpperId = Object.prototype.hasOwnProperty.call(record, 'ID')
if (hasId || hasUpperId) {
const id = record.id ?? record.ID
if (id !== null && id !== undefined && id !== '') {
if (!hasId) record.id = id
if (!hasUpperId) record.ID = id
}
}
Object.values(record).forEach((item) => syncCompatibleIdFields(item))
return value
}

View File

@@ -9,3 +9,4 @@ export * from './voucher'
export * from './apiRateLimit'
export * from './approvalSummary'
export * from './expiryEstimate'
export * from './id'