function count(value) { const number = Number(value) return Number.isFinite(number) ? number : NaN } function shanghaiDate(offsetDays) { return new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', }).format(new Date(Date.now() + offsetDays * 86_400_000)) } export async function run(context) { const module = 'appointment-work' const base = '/api/platform/v1/last-leg/appointment-work' const from = shanghaiDate(-7) const to = shanghaiDate(7) const calendarResponse = await context.request('GET', `${base}/calendar?from=${from}&to=${to}`) const calendar = context.apiSuccess(module, '查询预约工作日历', calendarResponse) const days = Array.isArray(calendar?.days) ? calendar.days : [] const dayTotal = days.reduce((sum, day) => sum + Number(day?.total || 0), 0) context.assert(module, '日历区间、日汇总与总数守恒', calendarResponse, calendar?.from === from && calendar?.to === to && count(calendar?.total) === dayTotal, `from=${calendar?.from || 'missing'},to=${calendar?.to || 'missing'},total=${calendar?.total ?? 'missing'},dayTotal=${dayTotal}`) for (const day of days.slice(0, 3)) { const typeTotal = Object.values(day?.typeCounts || {}).reduce((sum, value) => sum + Number(value || 0), 0) context.assert(module, `日历日 ${day?.date} 类型汇总不超过当日总数`, calendarResponse, Number(day?.total || 0) >= typeTotal, `date=${day?.date || 'missing'},total=${day?.total ?? 'missing'},typeTotal=${typeTotal}`) } const summaryResponse = await context.request('GET', `${base}/summary`) const summary = context.apiSuccess(module, '查询预约工作汇总', summaryResponse) const summaryFields = ['all', 'followToday', 'overdue', 'undated', 'review'] context.assert(module, '工作汇总五项计数完整', summaryResponse, summaryFields.every((field) => Number.isFinite(Number(summary?.[field]))), summaryFields.map((field) => `${field}=${summary?.[field] ?? 'missing'}`).join(',')) const pageResponse = await context.request('GET', `${base}/items?view=ALL&page=1&size=20`) const page = context.apiSuccess(module, '查询预约工作事项首页', pageResponse) const records = Array.isArray(page?.records) ? page.records : [] const total = count(page?.total) context.assert(module, '工作事项分页完整且不截断', pageResponse, count(page?.page) === 1 && count(page?.size) === 20 && Number.isFinite(total) && records.length === Math.min(20, total) && count(page?.pages) === Math.ceil(total / 20), `page=${page?.page},size=${page?.size},total=${page?.total},records=${records.length},pages=${page?.pages}`) const first = records[0] if (!first) { context.addCoverageGap(module, 'appointment-work', '预约工作项详情与命令', '本轮主链未产生预约工作事项;需先完成订单、柜、船节点或排车链后重跑', 'not-implemented', { blocking: false }) return } const detailResponse = await context.request('GET', `${base}/items/${encodeURIComponent(first.workItemKey)}`) const detail = context.apiSuccess(module, '查询当前工作事项详情', detailResponse) context.assert(module, '工作详情包含身份、来源与可执行动作', detailResponse, detail?.item?.workItemKey === first.workItemKey && ['ROUTE', 'PICKUP', 'WAREHOUSE_RECEIPT', 'DELIVERY', 'TRANSPORT', 'EMPTY_RETURN'].includes(detail?.item?.type) && ['UNKNOWN', 'TRANSFER', 'DIRECT'].includes(detail?.item?.flow) && Array.isArray(detail?.item?.containers) && detail.item.containers.length > 0 && Array.isArray(detail?.item?.allowedActions) && Array.isArray(detail?.references) && Array.isArray(detail?.history), `key=${detail?.item?.workItemKey},type=${detail?.item?.type},flow=${detail?.item?.flow},` + `actions=${detail?.item?.allowedActions?.length ?? 'missing'}`) const invalidKey = 'FULL-ACCEPTANCE-MISSING-WORK-ITEM' const invalidResponse = await context.request('POST', `${base}/items/${encodeURIComponent(invalidKey)}/commands`, { command: 'SAVE_FOLLOWUP', payload: { ownerId: '1', followupDate: to, note: `missing-${context.marker}` }, expectedVersion: 1, idempotencyKey: `${context.marker}-missing-work`, }) context.assert(module, '不存在的工作事项命令被拒绝', invalidResponse, invalidResponse.http === 200 && ['A0403', 'A0404'].includes(String(invalidResponse.body?.code)), `HTTP=${invalidResponse.http},code=${invalidResponse.body?.code ?? 'missing'}`) context.addCoverageGap(module, 'appointment-work', '预约写命令正向链路', '当前仅验证拒绝路径;SAVE_FOLLOWUP/CONFIRM_ROUTE/SAVE_REQUIREMENT 需要本轮可编辑工作项与操作人 ID,待下一批接入', 'not-implemented', { blocking: false }) }