cost-center.ts 从 898 行、48 个端点拆成 4 个子路由 + 54 行入口: - cost-center-catalog 商品主数据、渠道映射、模板/自动发货/接单平台回填 - cost-center-cost 成本项、成本版本、快照、异常 - cost-center-manual 人工履约 SKU 配置 - cost-center-credits 客服平台积分钱包 入口只保留角色校验、overview/options 与挂载顺序;子路由路径仍是完整路径, 不需要再加前缀。 验证: - 静态核对:拆分前后 (method, path) 集合完全一致,48 → 48; - 运行时核对:遍历 Express router stack,仍注册 48 条 /cost-center 路由; - 端点正文逐字保留,只有 import 与文件边界变化。
294 lines
9.5 KiB
TypeScript
294 lines
9.5 KiB
TypeScript
/**
|
|
* 成本中心 · 成本项/版本/快照/异常路由。
|
|
*
|
|
* 改价只走 cost_versions;订单只保存快照;匹配不到成本进异常队列。
|
|
*/
|
|
import { Router } from 'express'
|
|
import {
|
|
createAdminCostItem,
|
|
createAdminCostVersion,
|
|
deleteAdminCostItem,
|
|
deleteAdminCostVersion,
|
|
listAdminCostExceptions,
|
|
listAdminCostItems,
|
|
listAdminCostVersions,
|
|
resolveAdminCostException,
|
|
retireAdminCostVersion,
|
|
summarizeAdminCostExceptions,
|
|
updateAdminCostItem,
|
|
} from '../../services/cost-center/cost-definition-service.js'
|
|
import {
|
|
createAdminCostSnapshot,
|
|
previewAdminCost,
|
|
} from '../../services/cost-center/cost-resolution-service.js'
|
|
import {
|
|
syncActualWorkOrderCostSnapshots,
|
|
syncEstimatedOrderItemCostSnapshots,
|
|
} from '../../services/cost-center/cost-snapshot-sync-service.js'
|
|
import type { JsonObject } from '../../types/json.js'
|
|
import { bodyOf, idOf, sessionOf } from './request-helpers.js'
|
|
import { createJsonHandler, requireAdminPermission } from './session.js'
|
|
|
|
const router = Router()
|
|
|
|
router.get(
|
|
'/cost-center/cost-items',
|
|
requireAdminPermission('finance.view'),
|
|
createJsonHandler((req) => listAdminCostItems(req.query as JsonObject), {
|
|
successMessage: 'ok',
|
|
errorMessage: '读取成本项目失败',
|
|
scope: '[admin/cost-center/cost-items]',
|
|
}),
|
|
)
|
|
|
|
router.post(
|
|
'/cost-center/cost-items',
|
|
requireAdminPermission('finance.config'),
|
|
createJsonHandler((req) => createAdminCostItem(bodyOf(req), sessionOf(req)), {
|
|
successMessage: '成本项目已创建',
|
|
errorMessage: '创建成本项目失败',
|
|
scope: '[admin/cost-center/cost-items]',
|
|
audit: (_req, data) => {
|
|
const item = (data as { costItem?: { id?: number; code?: string } })?.costItem
|
|
return {
|
|
action: 'cost_item_created',
|
|
targetType: 'cost_item',
|
|
targetId: String(item?.id || ''),
|
|
data: { code: item?.code || '' },
|
|
}
|
|
},
|
|
}),
|
|
)
|
|
|
|
router.patch(
|
|
'/cost-center/cost-items/:costItemId',
|
|
requireAdminPermission('finance.config'),
|
|
createJsonHandler((req) => updateAdminCostItem(idOf(req, 'costItemId'), bodyOf(req)), {
|
|
successMessage: '成本项目已更新',
|
|
errorMessage: '更新成本项目失败',
|
|
scope: '[admin/cost-center/cost-items/:costItemId]',
|
|
audit: (req, data) => {
|
|
const item = (data as { costItem?: { id?: number; code?: string } })?.costItem
|
|
return {
|
|
action: 'cost_item_updated',
|
|
targetType: 'cost_item',
|
|
targetId: String(item?.id || req.params.costItemId || ''),
|
|
data: { code: item?.code || '' },
|
|
}
|
|
},
|
|
}),
|
|
)
|
|
|
|
router.delete(
|
|
'/cost-center/cost-items/:costItemId',
|
|
requireAdminPermission('finance.config'),
|
|
createJsonHandler((req) => deleteAdminCostItem(idOf(req, 'costItemId')), {
|
|
successMessage: '成本项目已删除',
|
|
errorMessage: '删除成本项目失败',
|
|
scope: '[admin/cost-center/cost-items/:costItemId]',
|
|
audit: (req, data) => {
|
|
const item = (data as { costItem?: { id?: number; code?: string } })?.costItem
|
|
return {
|
|
action: 'cost_item_deleted',
|
|
targetType: 'cost_item',
|
|
targetId: String(item?.id || req.params.costItemId || ''),
|
|
data: { code: item?.code || '' },
|
|
}
|
|
},
|
|
}),
|
|
)
|
|
|
|
router.get(
|
|
'/cost-center/cost-versions',
|
|
requireAdminPermission('finance.view'),
|
|
createJsonHandler((req) => listAdminCostVersions(req.query as JsonObject), {
|
|
successMessage: 'ok',
|
|
errorMessage: '读取成本版本失败',
|
|
scope: '[admin/cost-center/cost-versions]',
|
|
}),
|
|
)
|
|
|
|
router.post(
|
|
'/cost-center/cost-versions',
|
|
requireAdminPermission('finance.config'),
|
|
createJsonHandler((req) => createAdminCostVersion(bodyOf(req), sessionOf(req)), {
|
|
successMessage: '成本版本已创建',
|
|
errorMessage: '创建成本版本失败',
|
|
scope: '[admin/cost-center/cost-versions]',
|
|
audit: (_req, data) => {
|
|
const version = (data as { version?: { id?: number; costItemId?: number } })?.version
|
|
return {
|
|
action: 'cost_version_created',
|
|
targetType: 'cost_version',
|
|
targetId: String(version?.id || ''),
|
|
data: { costItemId: version?.costItemId || 0 },
|
|
}
|
|
},
|
|
}),
|
|
)
|
|
|
|
router.patch(
|
|
'/cost-center/cost-versions/:versionId/retire',
|
|
requireAdminPermission('finance.config'),
|
|
createJsonHandler((req) => retireAdminCostVersion(idOf(req, 'versionId'), bodyOf(req)), {
|
|
successMessage: '成本版本已停用',
|
|
errorMessage: '停用成本版本失败',
|
|
scope: '[admin/cost-center/cost-versions/:versionId/retire]',
|
|
audit: (req, data) => {
|
|
const version = (data as { version?: { id?: number } })?.version
|
|
return {
|
|
action: 'cost_version_retired',
|
|
targetType: 'cost_version',
|
|
targetId: String(version?.id || req.params.versionId || ''),
|
|
data: {},
|
|
}
|
|
},
|
|
}),
|
|
)
|
|
|
|
router.delete(
|
|
'/cost-center/cost-versions/:versionId',
|
|
requireAdminPermission('finance.config'),
|
|
createJsonHandler((req) => deleteAdminCostVersion(idOf(req, 'versionId')), {
|
|
successMessage: '成本版本已删除',
|
|
errorMessage: '删除成本版本失败',
|
|
scope: '[admin/cost-center/cost-versions/:versionId]',
|
|
audit: (req, data) => {
|
|
const version = (data as { version?: { id?: number } })?.version
|
|
return {
|
|
action: 'cost_version_deleted',
|
|
targetType: 'cost_version',
|
|
targetId: String(version?.id || req.params.versionId || ''),
|
|
data: {},
|
|
}
|
|
},
|
|
}),
|
|
)
|
|
|
|
router.get(
|
|
'/cost-center/exceptions',
|
|
requireAdminPermission('finance.view'),
|
|
createJsonHandler((req) => listAdminCostExceptions(req.query as JsonObject), {
|
|
successMessage: 'ok',
|
|
errorMessage: '读取成本异常失败',
|
|
scope: '[admin/cost-center/exceptions]',
|
|
}),
|
|
)
|
|
|
|
// 必须注册在 /cost-center/exceptions/:exceptionId 之前,避免 summary 被当成 ID。
|
|
router.get(
|
|
'/cost-center/exceptions/summary',
|
|
requireAdminPermission('finance.view'),
|
|
createJsonHandler((req) => summarizeAdminCostExceptions(req.query as JsonObject), {
|
|
successMessage: 'ok',
|
|
errorMessage: '读取成本异常汇总失败',
|
|
scope: '[admin/cost-center/exceptions/summary]',
|
|
}),
|
|
)
|
|
|
|
router.patch(
|
|
'/cost-center/exceptions/:exceptionId',
|
|
requireAdminPermission('finance.config'),
|
|
createJsonHandler(
|
|
(req) => resolveAdminCostException(idOf(req, 'exceptionId'), bodyOf(req), sessionOf(req)),
|
|
{
|
|
successMessage: '成本异常已处理',
|
|
errorMessage: '处理成本异常失败',
|
|
scope: '[admin/cost-center/exceptions/:exceptionId]',
|
|
audit: (req, data) => {
|
|
const exception = (data as { exception?: { id?: number; status?: string } })?.exception
|
|
return {
|
|
action: 'cost_exception_resolved',
|
|
targetType: 'cost_exception',
|
|
targetId: String(exception?.id || req.params.exceptionId || ''),
|
|
data: { status: exception?.status || '' },
|
|
}
|
|
},
|
|
},
|
|
),
|
|
)
|
|
|
|
router.post(
|
|
'/cost-center/preview',
|
|
requireAdminPermission('finance.view'),
|
|
createJsonHandler((req) => previewAdminCost(bodyOf(req)), {
|
|
successMessage: 'ok',
|
|
errorMessage: '成本预览失败',
|
|
scope: '[admin/cost-center/preview]',
|
|
}),
|
|
)
|
|
|
|
router.post(
|
|
'/cost-center/snapshots',
|
|
requireAdminPermission('finance.config'),
|
|
createJsonHandler((req) => createAdminCostSnapshot(bodyOf(req), sessionOf(req)), {
|
|
successMessage: '成本快照已生成',
|
|
errorMessage: '生成成本快照失败',
|
|
scope: '[admin/cost-center/snapshots]',
|
|
audit: (req, data) => {
|
|
const result = data as {
|
|
createdCount?: number
|
|
exceptionCreatedCount?: number
|
|
preview?: { context?: { orderItemId?: number } }
|
|
}
|
|
return {
|
|
action: 'cost_snapshot_created',
|
|
targetType: 'order_item',
|
|
targetId: String(result.preview?.context?.orderItemId || req.body?.orderItemId || ''),
|
|
data: {
|
|
createdCount: result.createdCount || 0,
|
|
exceptionCreatedCount: result.exceptionCreatedCount || 0,
|
|
},
|
|
}
|
|
},
|
|
}),
|
|
)
|
|
|
|
router.post(
|
|
'/cost-center/snapshots/sync',
|
|
requireAdminPermission('finance.config'),
|
|
createJsonHandler(
|
|
async (req) => {
|
|
const body = bodyOf(req)
|
|
const mode = String(body.mode || 'both').trim()
|
|
const estimated = mode === 'actual' ? null : await syncEstimatedOrderItemCostSnapshots(body)
|
|
const actual = mode === 'estimated' ? null : await syncActualWorkOrderCostSnapshots(body)
|
|
return { mode, estimated, actual }
|
|
},
|
|
{
|
|
successMessage: '成本快照回填完成',
|
|
errorMessage: '回填成本快照失败',
|
|
scope: '[admin/cost-center/snapshots/sync]',
|
|
audit: (req, data) => {
|
|
const result = data as {
|
|
mode?: string
|
|
estimated?: { createdCount?: number; exceptionCreatedCount?: number } | null
|
|
actual?: {
|
|
createdCount?: number
|
|
directRewardCount?: number
|
|
upgradedCount?: number
|
|
estimatedRewardCount?: number
|
|
} | null
|
|
}
|
|
return {
|
|
action: 'cost_snapshot_synced',
|
|
targetType: 'cost_snapshot',
|
|
targetId: '',
|
|
data: {
|
|
mode: result.mode || '',
|
|
estimatedCreated: result.estimated?.createdCount || 0,
|
|
actualCreated:
|
|
(result.actual?.directRewardCount || 0) + (result.actual?.upgradedCount || 0),
|
|
estimatedRewardCreated: result.actual?.estimatedRewardCount || 0,
|
|
exceptions: result.estimated?.exceptionCreatedCount || 0,
|
|
dateFrom: req.body?.dateFrom || '',
|
|
dateTo: req.body?.dateTo || '',
|
|
},
|
|
}
|
|
},
|
|
},
|
|
),
|
|
)
|
|
|
|
export default router
|