fix(work-order): 收紧补资料链接过期校验

This commit is contained in:
2026-11-01 11:12:08 +08:00
parent 15956e704f
commit 8fb28b98c7
2 changed files with 12 additions and 8 deletions
@@ -128,7 +128,7 @@ export async function markWorkOrderMaterialLinkUsedWithClient(
const result = await client.query<WorkOrderMaterialLinkRow>(
`UPDATE work_order_material_links
SET status = 'used', used_at = $2, updated_at = $2
WHERE id = $1 AND status = 'active'
WHERE id = $1 AND status = 'active' AND expires_at > $2
RETURNING *`,
[Number(linkId), now],
)
@@ -149,7 +149,6 @@ export async function createAdminWorkOrderMaterialLink(
workOrderId: number | string,
actorName = '',
) {
const now = nowIso()
const { ttlMinutes } = getWorkerMaterialLinkConfig()
return withTransaction(async (client) => {
@@ -169,6 +168,8 @@ export async function createAdminWorkOrderMaterialLink(
}
assertWorkOrderAllowedForMaterialLink(order)
// 锁到手后再取时间,避免等锁期间旧链接过期却仍按旧时间复用。
const now = nowIso()
const existing = await findActiveWorkOrderMaterialLinkWithClient(client, order.id, now)
if (existing) {
return {
@@ -254,7 +255,6 @@ export async function submitPublicWorkOrderMaterialLink(token: string, payload:
})
}
const now = nowIso()
const result = await withTransaction(async (client) => {
// 先锁工单再锁链接,和后台生成链接保持同样的加锁顺序,避免死锁。
const order = await getWorkOrderForUpdateWithClient(client, preliminary.work_order_id)
@@ -272,6 +272,8 @@ export async function submitPublicWorkOrderMaterialLink(token: string, payload:
})
}
// 锁到手后再取时间:等锁期间链接可能已经过期,不能沿用进入事务前的时间。
const now = nowIso()
assertMaterialLinkRecordUsable(link, now)
assertWorkOrderAllowedForMaterialLink(order)
@@ -328,11 +330,13 @@ export async function submitPublicWorkOrderMaterialLink(token: string, payload:
const used = await markWorkOrderMaterialLinkUsedWithClient(client, link.id, now)
if (!used) {
// 并发提交时另一个请求已经把链接用掉,当前事务整体回滚。
throw createHttpError('链接已使用,如需修改资料请联系客服重新获取', {
statusCode: 410,
errorCode: 'material_link_used',
})
// 并发提交,或刚好在写入瞬间过期:按当前状态给出准确原因,事务整体回滚。
const latest = await getWorkOrderMaterialLinkForUpdateWithClient(client, link.id)
if (latest) {
const reason = resolveWorkOrderMaterialLinkUnavailableReason(latest, now)
if (reason) throwMaterialLinkUnavailable(reason)
}
throwMaterialLinkUnavailable('used')
}
await createWorkOrderEventWithClient(client, {