feat: show platform for conversion product ids

This commit is contained in:
江林铖 2026-09-18 09:27:49 +08:00
parent d0cd7ab979
commit 88f6c10799
6 changed files with 40 additions and 3 deletions

View File

@ -32,6 +32,7 @@ const workflowItem = {
brand: '亿家达' as const,
productId: '1041432226288',
spu: 'SPU-001',
platform: '天猫',
completedAt: '2026-07-12T08:00:00.000Z',
monitorStartDate: '2026-07-12',
monitorEndDate: '2027-01-12',
@ -134,6 +135,22 @@ describe('PerformanceConversionDashboard filters', () => {
expect(detailUrl.searchParams.get('completedFrom')).toBe('2026-07-01')
expect(detailUrl.searchParams.get('completedTo')).toBe('2026-07-31')
expect(detailUrl.searchParams.get('page')).toBe('3')
expect(screen.getAllByText('平台 天猫')).toHaveLength(2)
expect(screen.queryByText(`实例 ${workflowItem.workflowId}`)).not.toBeInTheDocument()
expect(fetchMock).not.toHaveBeenCalledWith(expect.stringContaining('/timeseries'), expect.anything())
})
it('shows an explicit placeholder when the workflow platform is missing', async () => {
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: async () => ({
success: true,
data: { ...emptyList, items: [{ ...workflowItem, platform: null }], total: 1 },
}),
} as Response)
render(<PerformanceConversionDashboard />)
expect(await screen.findAllByText('平台 —')).toHaveLength(2)
})
})

View File

@ -93,12 +93,12 @@ function WorkflowTable({ items, detailHref }: {
<caption className="sr-only">2026 年 4 月 1 日以后已完结美术流程关联的商品 ID</caption>
<thead className="bg-slate-50 text-xs font-bold text-slate-500"><tr><th className="w-[18%] px-5 py-3">商品 ID</th><th className="w-[24%] px-4 py-3">审批编号</th><th className="w-[18%] px-4 py-3">完结时间</th><th className="w-[25%] px-4 py-3">监控区间</th><th className="w-[15%] px-4 py-3">状态</th></tr></thead>
<tbody className="divide-y divide-slate-200">
{items.map((item) => <tr key={item.key} className="bg-white hover:bg-slate-50"><td className="px-5 py-3"><Link href={detailHref(item)} className="break-all font-mono text-sm font-bold text-[#0050cb] underline-offset-4 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1677ff]">{item.productId}</Link>{item.spu && <p className="mt-1 truncate text-xs text-slate-500">SPU {item.spu}</p>}</td><td className="px-4 py-3"><p className="break-all font-mono text-xs text-slate-700">{item.approvalNumber || '—'}</p><p className="mt-1 truncate text-xs text-slate-500" title={item.workflowId}>实例 {item.workflowId}</p></td><td className="px-4 py-3 tabular-nums text-slate-700">{formatDate(item.completedAt, true)}</td><td className="px-4 py-3 tabular-nums text-slate-700">{item.monitorStartDate} 至 {item.monitorEndDate}</td><td className="px-4 py-3"><span className={`inline-flex min-h-7 items-center rounded-md px-2 text-xs font-bold ${item.monitoringStatus === 'monitoring' ? 'bg-amber-50 text-amber-800 ring-1 ring-inset ring-amber-200' : 'bg-emerald-50 text-emerald-800 ring-1 ring-inset ring-emerald-200'}`}>{item.monitoringStatus === 'monitoring' ? '监控中' : '已满 6 个月'}</span></td></tr>)}
{items.map((item) => <tr key={item.key} className="bg-white hover:bg-slate-50"><td className="px-5 py-3"><Link href={detailHref(item)} className="break-all font-mono text-sm font-bold text-[#0050cb] underline-offset-4 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1677ff]">{item.productId}</Link><p className="mt-1 flex flex-wrap gap-x-2 text-xs text-slate-500">{item.spu && <span className="min-w-0 max-w-full break-all">SPU {item.spu}</span>}<span className="min-w-0 max-w-full break-all">平台 {item.platform || '—'}</span></p></td><td className="px-4 py-3"><p className="break-all font-mono text-xs text-slate-700">{item.approvalNumber || '—'}</p></td><td className="px-4 py-3 tabular-nums text-slate-700">{formatDate(item.completedAt, true)}</td><td className="px-4 py-3 tabular-nums text-slate-700">{item.monitorStartDate} 至 {item.monitorEndDate}</td><td className="px-4 py-3"><span className={`inline-flex min-h-7 items-center rounded-md px-2 text-xs font-bold ${item.monitoringStatus === 'monitoring' ? 'bg-amber-50 text-amber-800 ring-1 ring-inset ring-amber-200' : 'bg-emerald-50 text-emerald-800 ring-1 ring-inset ring-emerald-200'}`}>{item.monitoringStatus === 'monitoring' ? '监控中' : '已满 6 个月'}</span></td></tr>)}
</tbody>
</table>
</div>
<ul className="divide-y divide-slate-200 md:hidden" aria-label="美术流程商品 ID 列表">
{items.map((item) => <li key={item.key} className="bg-white p-4"><Link href={detailHref(item)} className="block w-full text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1677ff]"><span className="font-mono text-sm font-bold text-[#0050cb]">{item.productId}</span><span className="mt-1 block text-xs text-slate-500">审批编号 {item.approvalNumber || '—'}</span><dl className="mt-3 grid grid-cols-2 gap-3 text-xs"><div><dt className="text-slate-500">完结时间</dt><dd className="mt-1 font-semibold text-slate-800">{formatDate(item.completedAt)}</dd></div><div><dt className="text-slate-500">状态</dt><dd className="mt-1 font-semibold text-slate-800">{item.monitoringStatus === 'monitoring' ? '监控中' : '已满 6 个月'}</dd></div><div className="col-span-2"><dt className="text-slate-500">监控区间</dt><dd className="mt-1 font-semibold text-slate-800">{item.monitorStartDate} 至 {item.monitorEndDate}</dd></div></dl></Link></li>)}
{items.map((item) => <li key={item.key} className="bg-white p-4"><Link href={detailHref(item)} className="block w-full text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1677ff]"><span className="font-mono text-sm font-bold text-[#0050cb]">{item.productId}</span><span className="mt-1 flex flex-wrap gap-x-2 text-xs text-slate-500">{item.spu && <span className="min-w-0 max-w-full break-all">SPU {item.spu}</span>}<span className="min-w-0 max-w-full break-all">平台 {item.platform || '—'}</span></span><span className="mt-1 block break-all text-xs text-slate-500">审批编号 {item.approvalNumber || '—'}</span><dl className="mt-3 grid grid-cols-2 gap-3 text-xs"><div><dt className="text-slate-500">完结时间</dt><dd className="mt-1 font-semibold text-slate-800">{formatDate(item.completedAt)}</dd></div><div><dt className="text-slate-500">状态</dt><dd className="mt-1 font-semibold text-slate-800">{item.monitoringStatus === 'monitoring' ? '监控中' : '已满 6 个月'}</dd></div><div className="col-span-2"><dt className="text-slate-500">监控区间</dt><dd className="mt-1 font-semibold text-slate-800">{item.monitorStartDate} 至 {item.monitorEndDate}</dd></div></dl></Link></li>)}
</ul>
</>
)

View File

@ -17,6 +17,7 @@ const detailData = {
brand: '亿家达' as const,
productId: '1041432226288',
spu: 'SPU-001',
platform: '天猫',
completedAt: '2026-07-12T08:00:00.000Z',
monitorStartDate: '2026-07-12',
monitorEndDate: '2027-01-12',

View File

@ -30,6 +30,7 @@ export type PerformanceConversionWorkflowItem = {
brand: PerformanceConversionBrand
productId: string
spu: string | null
platform: string | null
completedAt: string
monitorStartDate: string
monitorEndDate: string

View File

@ -34,6 +34,7 @@ function snapshot(
brand: string,
sourceFinishedAtRaw: string,
productId: string | null,
platform: string | null = '天猫',
) {
return {
processInstanceId,
@ -45,7 +46,7 @@ function snapshot(
processCode: 'art-process',
brand,
sourceFinishedAtRaw,
formFields: productId ? { productId } : {},
formFields: productId ? { productId, platform } : {},
syncedAt: new Date('2026-09-17T00:00:00.000Z'),
}
}
@ -71,6 +72,7 @@ describe('performance conversion completion date filters', () => {
})
expect(result.items.map((item) => item.productId)).toEqual(['222222', '333333'])
expect(result.items.map((item) => item.platform)).toEqual(['天猫', '天猫'])
expect(result.total).toBe(2)
expect(result.invalidProductIdCount).toBe(1)
expect(result.brands.find((item) => item.brand === '亿家达')?.count).toBe(2)
@ -82,6 +84,21 @@ describe('performance conversion completion date filters', () => {
})
})
it('returns the workflow platform for every product id and keeps missing values explicit', async () => {
findMany.mockResolvedValue([
snapshot('multiple-ids', '亿家达', '2026-07-12T08:00:00.000Z', '111111, 222222', '京东'),
snapshot('missing-platform', '亿家达', '2026-07-11T08:00:00.000Z', '333333', null),
])
const result = await getPerformanceConversionWorkflows(null, { brand: '亿家达' })
expect(result.items.map(({ productId, platform }) => ({ productId, platform }))).toEqual([
{ productId: '111111', platform: '京东' },
{ productId: '222222', platform: '京东' },
{ productId: '333333', platform: null },
])
})
it('rejects invalid or reversed date ranges before reading snapshots', async () => {
await expect(getPerformanceConversionWorkflows(null, {
completedFrom: '2026-02-29',

View File

@ -107,6 +107,7 @@ function workflowItems(row: SnapshotRow): PerformanceConversionWorkflowItem[] {
brand: brand as PerformanceConversionBrand,
productId,
spu: clean(fields.spu),
platform: clean(fields.platform),
completedAt,
monitorStartDate,
monitorEndDate,