feat(collect): 补资料改为订单号直连并按状态一次性提交

- 公开补资料页 /supplement/:订单号 直接按平台订单号查询,不再依赖链接记录;旧 token 链接一律 404
- 仅「待完善 + 未指派 + 无拼单份额」可提交,提交后状态变为待分配,重复提交返回 409
- 非可编辑工单只返回商品、工单号、状态与客服提示,不返回已填资料和截图
- 管理端新增「重置补资料」:待分配且未指派、无份额、未发布时一键重置回待完善
- 新增 canResetMaterial 能力与 material_reset 流转事件,工单状态文案统一为「待分配」
This commit is contained in:
2026-11-01 18:12:44 +08:00
parent aed76e047e
commit a2cf48f5fc
24 changed files with 1223 additions and 494 deletions
@@ -23,6 +23,7 @@ import {
reopenAdminCancelledWorkOrder,
reprocessAdminKuaishouSendCodeWorkOrders,
resetAdminWorkOrderGiftMaterial,
resetAdminWorkOrderMaterialStatus,
submitAdminWorkOrderMaterial,
unassignAdminWorkOrder,
unpublishAdminWorkOrder,
@@ -216,6 +217,29 @@ router.post(
),
)
router.post(
'/worker-platform/orders/:workOrderId/reset-material',
requireAdminPermission('worker_order.update_material'),
createJsonHandler(
(req) =>
resetAdminWorkOrderMaterialStatus(
String(req.params.workOrderId || ''),
req.adminSession?.displayName || req.adminSession?.username || '',
),
{
successMessage: '补资料状态已重置为待完善',
errorMessage: '重置补资料状态失败',
scope: '[admin/worker-platform/orders/:workOrderId/reset-material]',
audit: (req) => ({
action: 'work_order_material_reset',
targetType: 'work_order',
targetId: String(req.params.workOrderId || ''),
data: {},
}),
},
),
)
router.put(
'/worker-platform/orders/:workOrderId',
requireAdminPermission('worker_order.edit'),
+35 -12
View File
@@ -29,21 +29,40 @@ const materialLinkReadRateLimit = createRateLimitMiddleware({
scope: 'collect:link:read',
windowMs: 60_000,
max: 120,
key: getParamRateLimitKey('token'),
key: getParamRateLimitKey('code'),
})
/** 平台订单号可被枚举,额外加一层只按 IP 的总限流,避免换 code 绕过单链接限流。 */
const materialLinkReadIpRateLimit = createRateLimitMiddleware({
scope: 'collect:link:read:ip',
windowMs: 60_000,
max: 300,
})
const materialLinkWriteRateLimit = createRateLimitMiddleware({
scope: 'collect:link:write',
windowMs: 60_000,
max: 20,
key: getParamRateLimitKey('token'),
key: getParamRateLimitKey('code'),
})
const materialLinkWriteIpRateLimit = createRateLimitMiddleware({
scope: 'collect:link:write:ip',
windowMs: 60_000,
max: 40,
})
const materialLinkUploadRateLimit = createRateLimitMiddleware({
scope: 'collect:link:upload',
windowMs: 60_000,
max: 30,
key: getParamRateLimitKey('token'),
key: getParamRateLimitKey('code'),
})
const materialLinkUploadIpRateLimit = createRateLimitMiddleware({
scope: 'collect:link:upload:ip',
windowMs: 60_000,
max: 60,
})
router.post(
@@ -88,35 +107,39 @@ router.post(
)
router.get(
'/link/:token',
'/link/:code',
materialLinkReadIpRateLimit,
materialLinkReadRateLimit,
createRouteHandler((req) => getPublicWorkOrderMaterialLink(String(req.params.token || '')), {
createRouteHandler((req) => getPublicWorkOrderMaterialLink(String(req.params.code || '')), {
errorMessage: '读取补资料链接失败',
scope: '[collect/link/:token]',
scope: '[collect/link/:code]',
}),
)
router.post(
'/link/:token/submit',
'/link/:code/submit',
materialLinkWriteIpRateLimit,
materialLinkWriteRateLimit,
createRouteHandler(
(req) => submitPublicWorkOrderMaterialLink(String(req.params.token || ''), req.body || {}),
(req) => submitPublicWorkOrderMaterialLink(String(req.params.code || ''), req.body || {}),
{
successMessage: '资料已提交',
errorMessage: '提交补资料失败',
scope: '[collect/link/:token/submit]',
scope: '[collect/link/:code/submit]',
},
),
)
router.post(
'/link/:token/upload',
'/link/:code/upload',
materialLinkUploadIpRateLimit,
materialLinkUploadRateLimit,
uploadSingleFile,
createRouteHandler(
async (req) => {
const { order } = await getPublicWorkOrderMaterialLinkUploadContext(
String(req.params.token || ''),
String(req.params.code || ''),
req.query.workOrderId,
)
return uploadFileAsset({
file: req.file,
@@ -128,7 +151,7 @@ router.post(
{
successMessage: '文件已上传',
errorMessage: '文件上传失败',
scope: '[collect/link/:token/upload]',
scope: '[collect/link/:code/upload]',
},
),
)