import { Router } from 'express' import { createFailureCooldownMiddleware, createRateLimitMiddleware, getBodyFieldRateLimitKey, getParamRateLimitKey, } from '../middleware/rate-limit.js' import { collectLookupWorkOrder, collectSubmitWorkOrder, getPublicWorkOrderMaterialLink, getPublicWorkOrderMaterialLinkUploadContext, submitPublicWorkOrderMaterialLink, } from '../services/worker-platform/index.js' import { uploadFileAsset } from '../services/file-storage/file-storage-service.js' import { buildNotFoundPayload, createRouteHandler } from '../utils/http.js' import { uploadSingleFile } from './file-upload.js' const router = Router() const collectRateLimit = createRateLimitMiddleware({ scope: 'collect', windowMs: 60_000, max: 60, key: getBodyFieldRateLimitKey('orderNo'), }) const materialLinkReadRateLimit = createRateLimitMiddleware({ scope: 'collect:link:read', windowMs: 60_000, max: 120, key: getParamRateLimitKey('code'), }) /** 平台订单号可被枚举,额外加一层只按 IP 的总限流,避免换 code 绕过单链接限流。 */ const materialLinkReadIpRateLimit = createRateLimitMiddleware({ scope: 'collect:link:read:ip', windowMs: 60_000, max: 300, }) /** 查询失败(404)后按 IP 冷却 2 秒;正常查询不计数,避免误伤用户。 */ const materialLinkReadFailureCooldown = createFailureCooldownMiddleware({ scope: 'collect:link:read:fail:ip', cooldownMs: 2_000, }) const materialLinkWriteRateLimit = createRateLimitMiddleware({ scope: 'collect:link:write', windowMs: 60_000, max: 20, 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('code'), }) const materialLinkUploadIpRateLimit = createRateLimitMiddleware({ scope: 'collect:link:upload:ip', windowMs: 60_000, max: 60, }) router.post( '/lookup', collectRateLimit, createRouteHandler((req) => collectLookupWorkOrder(req.body || {}), { successMessage: 'ok', errorMessage: '查询待完善订单失败', scope: '[collect/lookup]', }), ) router.post( '/submit', collectRateLimit, createRouteHandler((req) => collectSubmitWorkOrder(req.body || {}), { successMessage: '资料已提交', errorMessage: '提交补充资料失败', scope: '[collect/submit]', }), ) router.post( '/upload', collectRateLimit, uploadSingleFile, createRouteHandler( (req) => { return uploadFileAsset({ file: req.file, scene: req.body?.scene || 'collect-material', uploaderType: 'collect', uploaderId: String(req.body?.orderNo || 'anonymous'), }) }, { successMessage: '文件已上传', errorMessage: '文件上传失败', scope: '[collect/upload]', }, ), ) router.get( '/link/:code', materialLinkReadFailureCooldown, materialLinkReadIpRateLimit, materialLinkReadRateLimit, createRouteHandler((req) => getPublicWorkOrderMaterialLink(String(req.params.code || '')), { errorMessage: '读取补资料链接失败', scope: '[collect/link/:code]', }), ) router.post( '/link/:code/submit', materialLinkWriteIpRateLimit, materialLinkWriteRateLimit, createRouteHandler( (req) => submitPublicWorkOrderMaterialLink(String(req.params.code || ''), req.body || {}), { successMessage: '资料已提交', errorMessage: '提交补资料失败', scope: '[collect/link/:code/submit]', }, ), ) router.post( '/link/:code/upload', materialLinkUploadIpRateLimit, materialLinkUploadRateLimit, uploadSingleFile, createRouteHandler( async (req) => { const { order } = await getPublicWorkOrderMaterialLinkUploadContext( String(req.params.code || ''), req.query.workOrderId, ) return uploadFileAsset({ file: req.file, scene: req.body?.scene || 'collect-material', uploaderType: 'collect', uploaderId: `work-order-${order.id}`, }) }, { successMessage: '文件已上传', errorMessage: '文件上传失败', scope: '[collect/link/:code/upload]', }, ), ) router.use((req, res) => { res.status(404).json(buildNotFoundPayload(req)) }) export default router