fix: align content queue ordering with external dashboard

This commit is contained in:
江林铖 2026-08-28 15:55:14 +08:00
parent 5ef8e77eeb
commit 4e58fde4d6
3 changed files with 45 additions and 3 deletions

View File

@ -281,4 +281,41 @@ describe('Ding workflow delivery SLA', () => {
expect(second.queueShiftDays).toBe(2)
expect(second.startedAt).toBe('2026-08-03T01:00:00.000Z')
})
it('uses businessId ordering and ignores the non-queue content-owner node', () => {
const makeInput = (businessId: string, workflowId: string, extraOwnerTask = false) => ({
processCode: CONTENT_PROCESS_CODE,
sourceCreatedAt: '2026-08-01T00:00:00.000Z',
sourceFinishedAt: '2026-08-02T00:00:00.000Z',
formFields: formFields({ demandType: '抖音-千川素材', taskType: '布局拍摄' }),
tasks: [
task('内容主管分配任务', '2026-08-01T01:00:00.000Z', '2026-08-01T01:00:00.000Z'),
...(extraOwnerTask
? [task('内容部负责人', '2026-08-02T01:00:00.000Z', '2026-08-02T01:00:00.000Z')]
: []),
task('编导审核', '2026-08-02T00:00:00.000Z', '2026-08-02T00:00:00.000Z'),
],
businessId,
workflowId,
workflowStatus: 'COMPLETED',
})
const assessments = assessDingWorkflowDeliveryBatch([
makeInput('B-002', 'a', true),
makeInput('B-001', 'z'),
makeInput('B-003', 'b'),
makeInput('B-004', 'c'),
])
// The external queue uses businessId for equal assignment timestamps,
// and the unrelated 内容部负责人 task must not move the base date.
expect(assessments.slice(0, 3).map((assessment) => assessment.queueSequence)).toEqual([2, 1, 3])
expect(assessments[0].queueShiftDays).toBe(0)
expect(assessments[3]).toMatchObject({
queueDeferred: true,
queueSequence: 1,
queueShiftDays: 1,
startedAt: '2026-08-02T01:00:00.000Z',
})
})
})

View File

@ -52,6 +52,8 @@ type DingWorkflowDeliveryInput = {
sourceFinishedAt: string | null
formFields: DingWorkflowDetailFormFields
tasks: OngoingDingWorkflowActiveTask[]
/** Stable DingTalk business number used by the external queue as tie-breaker. */
businessId?: string | null
/** Used only by the batch queue calculation to produce a deterministic tie-breaker. */
workflowId?: string | null
/** Raw workflow status; processing rows occupy queue slots, rejected rows do not. */
@ -85,6 +87,7 @@ const CONTENT_QUEUE_GROUP_TAOXI_LAYOUT = new Set([
'淘系-逛逛+淘系主图视频',
])
const CONTENT_QUEUE_GROUP_TAOXI_SECOND_EDIT = new Set(['淘系-逛逛', '淘系主图'])
const CONTENT_QUEUE_ASSIGNMENT_NODE_NAMES = ['内容主管分配任务', '内容负责人分配任务']
const ART_RULES: ArtRule[] = [
{ type: '视觉 打板', detail: '主图+sku+详情+视频', days: 15, byQuantity: true },
@ -552,8 +555,7 @@ export function assessDingWorkflowDeliveryBatch(inputs: DingWorkflowDeliveryInpu
if (!policy) return
// The external dashboard schedules by the latest assignment timestamp,
// while the unqueued assessment still starts at the first assignment.
const assignmentNames = ['内容主管分配任务', '内容负责人分配任务', '内容部负责人']
const baseStart = latestTaskTimeByField(input.tasks, assignmentNames, 'createdAt')?.timestamp
const baseStart = latestTaskTimeByField(input.tasks, CONTENT_QUEUE_ASSIGNMENT_NODE_NAMES, 'createdAt')?.timestamp
?? parseIsoTime(assessments[index].startedAt)
if (baseStart === null) return
const entries = queueGroups.get(policy.key) || []
@ -561,7 +563,9 @@ export function assessDingWorkflowDeliveryBatch(inputs: DingWorkflowDeliveryInpu
index,
policy,
baseStart,
workflowId: normalized(input.workflowId) || String(index).padStart(8, '0'),
workflowId: normalized(input.businessId)
|| normalized(input.workflowId)
|| String(index).padStart(8, '0'),
})
queueGroups.set(policy.key, entries)
})

View File

@ -1944,6 +1944,7 @@ async function readOnTimeDeliveryInsightsWithoutAccessCheck(now = new Date()): P
sourceFinishedAt,
formFields: normalizeDetailFormFields(row.formFields),
tasks: allTasks,
businessId: clean(row.businessId),
workflowId: clean(row.processInstanceId),
workflowStatus: clean(row.status),
}