From 042574dea306005c44dde7d2a73b603c2974c3b1 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 10 Jul 2026 15:56:25 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E6=94=B6=E7=B4=A7=20TypeScri?= =?UTF-8?q?pt=EF=BC=9AClaimIdentity=20=E5=A5=91=E7=BA=A6=E4=B8=8E=20flow?= =?UTF-8?q?=20=E5=87=BA=E5=8F=A3=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 types/claim-identity,统一 ClaimIdentity / Payload / AdminSummary - 导出 KuaishouCloudFlow、KuaishouFeifeiFlow 作为 normalize 出口 - 生产路径 JsonObject 统一从 types/json 导入,去掉 rebind as any - 前后端 claimIdentity 类型字段对齐 --- .../admin/write/kuaishou-cloud-helpers.ts | 13 +-- .../src/services/claim/claim-identity.ts | 29 +++--- .../fulfillment/kuaishou-cloud/domain.ts | 98 ++++++++++++++++++- .../fulfillment/kuaishou-cloud/index.ts | 4 + .../kuaishou-cloud/probe-bind-url.ts | 7 +- .../fulfillment/kuaishou-cloud/rebind-role.ts | 15 ++- .../fulfillment/kuaishou-cloud/role-state.ts | 4 +- .../fulfillment/kuaishou-feifei/index.ts | 27 ++++- .../src/services/fulfillment/planner.ts | 3 +- .../fulfillment/routing-config-service.ts | 3 +- .../src/services/notification/bark-service.ts | 2 +- .../notification/domain-notifications.ts | 2 +- .../services/notification/wpush-service.ts | 2 +- apps/backend/src/types/admin/task-detail.ts | 13 +-- apps/backend/src/types/claim-identity.ts | 32 ++++++ apps/backend/src/types/task-context.ts | 14 ++- apps/backend/src/utils/json.ts | 3 +- apps/frontend/src/types/admin/tasks.ts | 25 ++--- apps/frontend/src/types/claim.ts | 1 + docs/履约配置/TypeScript类型收紧.md | 10 +- 20 files changed, 219 insertions(+), 88 deletions(-) create mode 100644 apps/backend/src/types/claim-identity.ts diff --git a/apps/backend/src/services/admin/write/kuaishou-cloud-helpers.ts b/apps/backend/src/services/admin/write/kuaishou-cloud-helpers.ts index f169d43c..e055b41d 100644 --- a/apps/backend/src/services/admin/write/kuaishou-cloud-helpers.ts +++ b/apps/backend/src/services/admin/write/kuaishou-cloud-helpers.ts @@ -34,18 +34,7 @@ export type CloudSkuLikeItem = { [key: string]: unknown } -export type KuaishouCloudFlow = JsonObject & { - configId: string - internalSkuCode: string - internalSkuName: string - ticket: JsonObject - binding: JsonObject - role: JsonObject - purchase: JsonObject - dispatch: JsonObject - returnNumber: JsonObject - consume: JsonObject -} +export type { KuaishouCloudFlow } from '../../fulfillment/kuaishou-cloud/domain.js' export type KuaishouCloudBindingResources = { skuId: number diff --git a/apps/backend/src/services/claim/claim-identity.ts b/apps/backend/src/services/claim/claim-identity.ts index fc7dbcfd..92e4909f 100644 --- a/apps/backend/src/services/claim/claim-identity.ts +++ b/apps/backend/src/services/claim/claim-identity.ts @@ -1,14 +1,19 @@ import { createHttpError } from '../../utils/http.js' import { parseTaskContext } from '../../utils/task-json.js' import { isTaskFinalStatus, normalizeTaskStatus, TASK_STATUS } from '../../domain/task-status.js' +import type { + ClaimIdentity, + ClaimIdentityAdminSummary, + ClaimIdentityPayload, +} from '../../types/claim-identity.js' import type { TaskRow } from '../../types/repository/rows.js' import type { JsonObject } from '../../types/json.js' -export type ClaimIdentity = { - expectedUid: string - submittedAt: string | null - source: string -} +export type { + ClaimIdentity, + ClaimIdentityAdminSummary, + ClaimIdentityPayload, +} from '../../types/claim-identity.js' const CLAIM_UID_MAX_LENGTH = 64 @@ -130,18 +135,6 @@ export function assertLewanAutoFulfillmentUidReady( return expectedUid } -export type ClaimIdentityAdminSummary = { - expectedUid: string - submittedAt: string | null - source: string - ready: boolean - boundUid: string - boundRoleName: string - uidMatched: boolean | null - compatibilityMode: 'uid' | 'legacy_no_uid' - note: string -} - export function buildClaimIdentityAdminSummary( context: unknown, options: { flowLike?: unknown; taskRoleId?: unknown; taskRoleName?: unknown } = {}, @@ -261,7 +254,7 @@ export function appendUidToUrl(targetUrl: unknown, uid: unknown): string { } } -export function buildClaimIdentityPayload(value: unknown) { +export function buildClaimIdentityPayload(value: unknown): ClaimIdentityPayload { const identity = normalizeClaimIdentity(value) return { expectedUid: identity.expectedUid, diff --git a/apps/backend/src/services/fulfillment/kuaishou-cloud/domain.ts b/apps/backend/src/services/fulfillment/kuaishou-cloud/domain.ts index 2ecb505f..e85d484d 100644 --- a/apps/backend/src/services/fulfillment/kuaishou-cloud/domain.ts +++ b/apps/backend/src/services/fulfillment/kuaishou-cloud/domain.ts @@ -18,7 +18,103 @@ export function isIndustryEVoucherTask(task: unknown) { return String(source.executor_key || '').trim() === 'kuaishou-industry' } -export function normalizeKuaishouCloudFlow(value: unknown) { +export type KuaishouCloudDeliveryItem = { + cloudSkuId: number + cloudSkuName: string + quantity: number +} + +/** normalizeKuaishouCloudFlow 出口类型(履约主路径应经此 normalize) */ +export type KuaishouCloudFlow = { + configId: string + internalSkuCode: string + internalSkuName: string + deliveryItems: KuaishouCloudDeliveryItem[] + ticket: { + code: string + status: string + capturedAt: unknown + capturedBy: unknown + verifiedAt: unknown + oid: string + formToken: string + leftCount: number + goodsTitle: string + } + binding: { + prepareStatus: string + cloudSourceKeys: string[] + resolvedSourceKey: string + skuId: number + skuName: string + vnKey: string + vnId: number + vnPhone: string + bindUrl: string + bindPreparedAt: unknown + bindExpiresAt: unknown + bindProbeAt: unknown + bindProbeStatus: string + bindProbeMessage: string + roleName: string + roleId: string + } + role: { + status: string + name: string + rid: string + refreshedAt: unknown + errorMessage: string + rawInfo: unknown + defaultName: string + defaultRid: string + defaultCapturedAt: unknown + defaultCaptureStatus: string + defaultErrorMessage: string + isDefaultRole: boolean + } + purchase: { + autoBuyEnabled: boolean + minAssetReserve: number + usedKnapsack: boolean + purchaseTriggered: boolean + assetBefore: number + assetAfter: number + purchaseAt: unknown + items: unknown[] + } + dispatch: { + status: string + dispatchAt: unknown + dispatchBy: unknown + sendType: number + note: string + items: unknown[] + } + returnNumber: { + status: string + returnedAt: unknown + returnedBy: unknown + autoReturnEnabled: boolean + } + consume: { + status: string + shopId: string + shopName: string + autoConsumeEnabled: boolean + consumedAt: unknown + errorMessage: string + } + rebind: { + currentAttempt: number + history: unknown[] + } + notes: string + /** 未建模扩展(mock 等)仍可落库 */ + [key: string]: unknown +} + +export function normalizeKuaishouCloudFlow(value: unknown): KuaishouCloudFlow { const source: JsonObject = value && typeof value === 'object' ? (value as JsonObject) : {} const binding = asJsonObject(source.binding) const role = asJsonObject(source.role) diff --git a/apps/backend/src/services/fulfillment/kuaishou-cloud/index.ts b/apps/backend/src/services/fulfillment/kuaishou-cloud/index.ts index bb1343f4..80f2407f 100644 --- a/apps/backend/src/services/fulfillment/kuaishou-cloud/index.ts +++ b/apps/backend/src/services/fulfillment/kuaishou-cloud/index.ts @@ -14,6 +14,10 @@ export { isSameKuaishouCloudRoleIdentity, resolveKuaishouCloudBindUrlExpiresAt, } from './domain.js' +export type { + KuaishouCloudDeliveryItem, + KuaishouCloudFlow, +} from './domain.js' export { resolvePersistedCloudtentaclesContextBySourceKeys } from './cloudtentacles-context.js' export { ensureTaskClaimLink } from './ensure-claim-link.js' export { diff --git a/apps/backend/src/services/fulfillment/kuaishou-cloud/probe-bind-url.ts b/apps/backend/src/services/fulfillment/kuaishou-cloud/probe-bind-url.ts index cf2d67e1..0282b858 100644 --- a/apps/backend/src/services/fulfillment/kuaishou-cloud/probe-bind-url.ts +++ b/apps/backend/src/services/fulfillment/kuaishou-cloud/probe-bind-url.ts @@ -41,10 +41,13 @@ export async function probeKuaishouCloudTaskBindUrl(task: TaskRow, options: Json const probeIntervalMs = Number(resolveCloudtentaclesConfig().bindUrlProbeIntervalSeconds || 30) * 1000 + const lastProbeAt = flow.binding.bindProbeAt + ? Date.parse(String(flow.binding.bindProbeAt)) + : NaN if ( !options.force && - flow.binding.bindProbeAt && - Date.now() - Date.parse(flow.binding.bindProbeAt) < probeIntervalMs + Number.isFinite(lastProbeAt) && + Date.now() - lastProbeAt < probeIntervalMs ) { return { task, diff --git a/apps/backend/src/services/fulfillment/kuaishou-cloud/rebind-role.ts b/apps/backend/src/services/fulfillment/kuaishou-cloud/rebind-role.ts index e6248f83..618d382c 100644 --- a/apps/backend/src/services/fulfillment/kuaishou-cloud/rebind-role.ts +++ b/apps/backend/src/services/fulfillment/kuaishou-cloud/rebind-role.ts @@ -38,14 +38,13 @@ export async function rebindKuaishouCloudTaskRole(task: TaskRow, options: JsonOb } const normalizedStatus = normalizeTaskStatus(task.task_status) - if ( - ![ - TASK_STATUS.WAITING_BINDING, - TASK_STATUS.ROLE_CONFIRMED, - TASK_STATUS.MANUAL_REVIEW, - TASK_STATUS.RETRY_PENDING, - ].includes(normalizedStatus as any) - ) { + const rebindAllowedStatuses = new Set([ + TASK_STATUS.WAITING_BINDING, + TASK_STATUS.ROLE_CONFIRMED, + TASK_STATUS.MANUAL_REVIEW, + TASK_STATUS.RETRY_PENDING, + ]) + if (!rebindAllowedStatuses.has(normalizedStatus)) { throw createHttpError('当前任务状态不可换绑角色', { statusCode: 409, errorCode: 'kuaishou_cloud_rebind_not_allowed', diff --git a/apps/backend/src/services/fulfillment/kuaishou-cloud/role-state.ts b/apps/backend/src/services/fulfillment/kuaishou-cloud/role-state.ts index 05bbf9e2..5741431f 100644 --- a/apps/backend/src/services/fulfillment/kuaishou-cloud/role-state.ts +++ b/apps/backend/src/services/fulfillment/kuaishou-cloud/role-state.ts @@ -6,9 +6,9 @@ import { getCloudtentaclesBindInfo } from '../../platforms/cloudtentacles/virtua import { hasKuaishouCloudDefaultRoleSnapshot, isSameKuaishouCloudRoleIdentity, - normalizeKuaishouCloudFlow, normalizeKuaishouCloudRoleInfo, type JsonObject, + type KuaishouCloudFlow, } from './domain.js' export type KuaishouCloudDefaultRoleSnapshot = { @@ -79,7 +79,7 @@ export function buildRoleStateFromCurrentInfo({ now, emptyMessage, }: { - flow: ReturnType + flow: KuaishouCloudFlow roleInfo: { name: string; rid: string; rawInfo?: unknown } now: string emptyMessage: string diff --git a/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts b/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts index 26c61958..75c0fd04 100644 --- a/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts +++ b/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts @@ -207,7 +207,28 @@ export async function syncKuaishouFeifeiTaskStatus(task: TaskRow) { return updatedTask || task } -export function normalizeKuaishouFeifeiFlow(value: unknown) { +/** normalizeKuaishouFeifeiFlow 出口类型 */ +export type KuaishouFeifeiFlow = { + flowType: 'kuaishou_feifei' + productCode: string + productName: string + platformOrderNo: string + orderNo: string + rechargeStatus: number + rechargeStatusLabel: string + rechargeResultMessage: string + claimUrl: string + consumeStatus: string + shortLink: ReturnType + h5: { + entryUrl: string + rechargeUrl: string + } + lastSyncedAt: unknown + raw: unknown +} + +export function normalizeKuaishouFeifeiFlow(value: unknown): KuaishouFeifeiFlow { const source = value && typeof value === 'object' && !Array.isArray(value) ? value as JsonObject : {} @@ -270,7 +291,7 @@ export function resolveKuaishouFeifeiH5UrlWithTaskUid( } function mergeKuaishouFeifeiOrder( - flow: ReturnType, + flow: KuaishouFeifeiFlow, order: Awaited>, patch: { platformOrderNo?: string @@ -299,7 +320,7 @@ function mergeKuaishouFeifeiOrder( } function resolveKuaishouFeifeiDirectClaimUrl( - flow: ReturnType, + flow: KuaishouFeifeiFlow, ) { const h5ClaimUrl = flow.h5.rechargeUrl || flow.h5.entryUrl if (h5ClaimUrl) { diff --git a/apps/backend/src/services/fulfillment/planner.ts b/apps/backend/src/services/fulfillment/planner.ts index 736f9e18..080fc31d 100644 --- a/apps/backend/src/services/fulfillment/planner.ts +++ b/apps/backend/src/services/fulfillment/planner.ts @@ -1,4 +1,5 @@ import { getFulfillmentProfileByKey } from '../../repositories/fulfillment-profile-repo.js' +import type { JsonObject } from '../../types/json.js' import type { OrderItemRow, OrderRow } from '../../types/repository/rows.js' import { FULFILLMENT_EXECUTOR_KEYS, @@ -6,8 +7,6 @@ import { isKuaishouFeifeiExecutor, } from './executors/types.js' -type JsonObject = Record - export type FulfillmentBindingLike = { id: number profile_id?: number diff --git a/apps/backend/src/services/fulfillment/routing-config-service.ts b/apps/backend/src/services/fulfillment/routing-config-service.ts index 072c06da..5a38c7e7 100644 --- a/apps/backend/src/services/fulfillment/routing-config-service.ts +++ b/apps/backend/src/services/fulfillment/routing-config-service.ts @@ -7,10 +7,9 @@ import { saveAppConfigEntry, } from '../config/app-config-store.js' import { normalizeCloudtentaclesMatchName } from '../order/cloudtentacles-match-utils.js' +import type { JsonObject } from '../../types/json.js' import { FULFILLMENT_EXECUTOR_KEYS } from './executors/types.js' -type JsonObject = Record - export type FulfillmentRoutingRuleMatchType = 'exact' | 'contains' export type FulfillmentRoutingExecutorConfig = { diff --git a/apps/backend/src/services/notification/bark-service.ts b/apps/backend/src/services/notification/bark-service.ts index 7cf771d8..8c873872 100644 --- a/apps/backend/src/services/notification/bark-service.ts +++ b/apps/backend/src/services/notification/bark-service.ts @@ -1,8 +1,8 @@ +import type { JsonObject } from '../../types/json.js' import { createHttpError } from '../../utils/http.js' const DEFAULT_TIMEOUT_MS = 10000 -type JsonObject = Record type BarkSendInput = { serverUrl?: string recipient?: { diff --git a/apps/backend/src/services/notification/domain-notifications.ts b/apps/backend/src/services/notification/domain-notifications.ts index bad11428..8a229d6c 100644 --- a/apps/backend/src/services/notification/domain-notifications.ts +++ b/apps/backend/src/services/notification/domain-notifications.ts @@ -1,10 +1,10 @@ +import type { JsonObject } from '../../types/json.js' import { logWarn } from '../../utils/logger.js' import { sendInternalNotification } from './notification-service.js' const DEFAULT_COOLDOWN_MS = 10 * 60 * 1000 const notificationCooldownMap = new Map() -type JsonObject = Record type InternalNotificationPayload = { title: string body?: string diff --git a/apps/backend/src/services/notification/wpush-service.ts b/apps/backend/src/services/notification/wpush-service.ts index 69ae72a2..79c63f43 100644 --- a/apps/backend/src/services/notification/wpush-service.ts +++ b/apps/backend/src/services/notification/wpush-service.ts @@ -1,12 +1,12 @@ import http from 'node:http' import https from 'node:https' +import type { JsonObject } from '../../types/json.js' import { createHttpError } from '../../utils/http.js' const DEFAULT_WPUSH_ENDPOINT = 'https://api.wpush.cn/api/v1/send' const DEFAULT_TIMEOUT_MS = 10000 -type JsonObject = Record type NodeHttpResponse = { ok: boolean status: number diff --git a/apps/backend/src/types/admin/task-detail.ts b/apps/backend/src/types/admin/task-detail.ts index ebea3a6b..b2f232cb 100644 --- a/apps/backend/src/types/admin/task-detail.ts +++ b/apps/backend/src/types/admin/task-detail.ts @@ -1,20 +1,11 @@ +import type { ClaimIdentityAdminSummary } from '../claim-identity.js' import type { JsonObject } from '../json.js' /** * 后台任务详情 API 返回形状(与前端 AdminTaskDetail 对齐的后端契约)。 * 深层履约字段仍用 JsonObject,避免一次改完所有 mapper。 */ -export type AdminClaimIdentitySummary = { - expectedUid: string - submittedAt: string | null - source: string - ready: boolean - boundUid: string - boundRoleName: string - uidMatched: boolean | null - compatibilityMode: 'uid' | 'legacy_no_uid' - note: string -} +export type AdminClaimIdentitySummary = ClaimIdentityAdminSummary export type AdminTaskDetailView = { task: JsonObject diff --git a/apps/backend/src/types/claim-identity.ts b/apps/backend/src/types/claim-identity.ts new file mode 100644 index 00000000..4a13c4a6 --- /dev/null +++ b/apps/backend/src/types/claim-identity.ts @@ -0,0 +1,32 @@ +/** + * 领取页 UID 身份与后台摘要的共享类型(纯数据契约,无运行时依赖)。 + */ + +export type ClaimIdentity = { + expectedUid: string + submittedAt: string | null + source: string +} + +/** 领取页 / claim API 返回的 UID 身份(含 ready) */ +export type ClaimIdentityPayload = ClaimIdentity & { + ready: boolean +} + +/** 后台任务详情中的 UID 兼容摘要 */ +export type ClaimIdentityAdminSummary = ClaimIdentityPayload & { + boundUid: string + boundRoleName: string + uidMatched: boolean | null + compatibilityMode: 'uid' | 'legacy_no_uid' + note: string +} + +/** context_json 中 claimIdentity 的宽松写入形态(兼容历史 uid 字段) */ +export type ClaimIdentityContext = { + expectedUid?: string + submittedAt?: string | null + source?: string + /** @deprecated 历史字段,normalize 时映射到 expectedUid */ + uid?: string +} diff --git a/apps/backend/src/types/task-context.ts b/apps/backend/src/types/task-context.ts index ad0ab223..a2dee2d3 100644 --- a/apps/backend/src/types/task-context.ts +++ b/apps/backend/src/types/task-context.ts @@ -1,18 +1,16 @@ +import type { ClaimIdentityContext } from './claim-identity.js' import type { JsonObject } from './json.js' +export type { ClaimIdentityContext } + /** * 履约任务 context_json 的已知字段。 * 未建模扩展仍可通过索引访问(JsonObject)。 + * kuaishouCloudFulfillment / kuaishouFeifei 在 normalize 出口有具名类型; + * 此处保留 JsonObject 以兼容未 normalize 的原始落库形态。 */ -export type ClaimIdentityContext = { - expectedUid?: string - submittedAt?: string | null - source?: string - uid?: string -} - export type TaskContext = JsonObject & { - claimIdentity?: ClaimIdentityContext | JsonObject + claimIdentity?: ClaimIdentityContext kuaishouCloudFulfillment?: JsonObject kuaishouFeifei?: JsonObject kuaishouIndustryVoucher?: JsonObject diff --git a/apps/backend/src/utils/json.ts b/apps/backend/src/utils/json.ts index 401ce6ee..4738ca5d 100644 --- a/apps/backend/src/utils/json.ts +++ b/apps/backend/src/utils/json.ts @@ -1,9 +1,8 @@ +import type { JsonObject } from '../types/json.js' import { createHttpError } from './http.js' const LARGE_INTEGER_PATTERN = /(:\s*)(-?\d{16,})(\s*[,}\]])/g -type JsonObject = Record - type ParseJsonObjectOptions = { preserveLargeIntegers?: boolean throwOnError?: boolean diff --git a/apps/frontend/src/types/admin/tasks.ts b/apps/frontend/src/types/admin/tasks.ts index 9e9b84db..79378840 100644 --- a/apps/frontend/src/types/admin/tasks.ts +++ b/apps/frontend/src/types/admin/tasks.ts @@ -58,6 +58,19 @@ export interface AdminTaskActionResponse { token?: string } +/** 与后端 ClaimIdentityAdminSummary / AdminClaimIdentitySummary 对齐 */ +export interface AdminClaimIdentitySummary { + expectedUid: string + submittedAt: string | null + source: string + ready: boolean + boundUid: string + boundRoleName: string + uidMatched: boolean | null + compatibilityMode: 'uid' | 'legacy_no_uid' + note: string +} + export interface AdminTaskDetail { task: AdminTaskListItem order: null | { @@ -105,17 +118,7 @@ export interface AdminTaskDetail { resultMessage: string }> } - claimIdentity: null | { - expectedUid: string - submittedAt: string | null - source: string - ready: boolean - boundUid: string - boundRoleName: string - uidMatched: boolean | null - compatibilityMode: 'uid' | 'legacy_no_uid' - note: string - } + claimIdentity: AdminClaimIdentitySummary | null kuaishouCloudFulfillment: null | { flowType: string configId: string diff --git a/apps/frontend/src/types/claim.ts b/apps/frontend/src/types/claim.ts index 4f732760..65d517a5 100644 --- a/apps/frontend/src/types/claim.ts +++ b/apps/frontend/src/types/claim.ts @@ -4,6 +4,7 @@ export type ClaimTokenStatus = 'active' | 'used' | 'expired' | 'revoked' | (stri export type ClaimTaskStatus = KnownTaskStatus | (string & {}) +/** 与后端 ClaimIdentityPayload 对齐 */ export interface ClaimIdentityInfo { expectedUid: string submittedAt: string | null diff --git a/docs/履约配置/TypeScript类型收紧.md b/docs/履约配置/TypeScript类型收紧.md index a1bff67d..bf1e6c30 100644 --- a/docs/履约配置/TypeScript类型收紧.md +++ b/docs/履约配置/TypeScript类型收紧.md @@ -7,10 +7,13 @@ - `JsonObject`:中心定义(当前值仍为 any 以兼容深层 context 访问) - `StrictJson` / `StrictJsonObject`:新代码可用的 unknown 严格形态 - `asJsonObject` / `isPlainObject` 工具 -3. **去掉约 60 处本地** `type JsonObject = Record` +3. **去掉生产路径本地** `type JsonObject = …`(统一从 `types/json` 导入;scripts 可保留本地别名) 4. **TaskContext**:`types/task-context.ts`,`parseTaskContext` 返回 `TaskContext` -5. **Admin 任务详情契约**:`types/admin/task-detail.ts`,`getAdminTaskDetail(): Promise` -6. **测试 fixture**:`delivery-task-service.test.ts` 减少 `as any` +5. **ClaimIdentity 契约**:`types/claim-identity.ts`(`ClaimIdentity` / `ClaimIdentityPayload` / `ClaimIdentityAdminSummary`) +6. **Admin 任务详情契约**:`types/admin/task-detail.ts`,`getAdminTaskDetail(): Promise` +7. **normalize 出口具名类型**:`KuaishouCloudFlow`、`KuaishouFeifeiFlow` +8. **前后端 claimIdentity 字段对齐**:前端 `ClaimIdentityInfo` / `AdminClaimIdentitySummary` +9. **测试 fixture**:`delivery-task-service.test.ts` 减少 `as any`;生产路径去掉 `as any` 状态列表 ## 未做完(后续) @@ -18,6 +21,7 @@ - 前后端共享类型包 / OpenAPI - 前端对齐 `noUncheckedIndexedAccess` - 全量测试 fixture 工厂化 +- scripts 本地 JsonObject 别名可按需迁入中心类型 ## 约定