Merge branch 'main' into dev

This commit is contained in:
2026-11-03 10:33:10 +08:00
121 changed files with 5265 additions and 1629 deletions
@@ -9,6 +9,8 @@ import { Router } from 'express'
import { ensureVoucherChannelRules } from '../../services/fulfillment/channel-assign-service.js'
import {
archiveHistoricalManualProducts,
archiveSupersededManualProducts,
backfillAdminNormalizedQuantities,
backfillAdminProductMappings,
createAdminChannelMapping,
@@ -161,6 +163,38 @@ router.post(
}),
)
router.post(
'/cost-center/products/archive-historical-manual',
requireAdminPermission('finance.config'),
createJsonHandler((req) => archiveHistoricalManualProducts(bodyOf(req)), {
successMessage: '历史人工商品已归档',
errorMessage: '归档历史人工商品失败',
scope: '[admin/cost-center/products/archive-historical-manual]',
audit: (_req, data) => ({
action: 'cost_historical_manual_products_archived',
targetType: 'product_catalog',
targetId: '',
data: data as Record<string, unknown>,
}),
}),
)
router.post(
'/cost-center/products/archive-superseded-manual',
requireAdminPermission('finance.config'),
createJsonHandler((req) => archiveSupersededManualProducts(bodyOf(req)), {
successMessage: '重复人工归一商品已归档',
errorMessage: '归档重复人工归一商品失败',
scope: '[admin/cost-center/products/archive-superseded-manual]',
audit: (_req, data) => ({
action: 'cost_superseded_manual_products_archived',
targetType: 'product_catalog',
targetId: '',
data: data as Record<string, unknown>,
}),
}),
)
router.patch(
'/cost-center/products/:productId',
requireAdminPermission('finance.config'),
@@ -374,8 +408,8 @@ router.post(
errorMessage: '补齐卡券渠道规则失败',
scope: '[admin/cost-center/voucher-products/ensure-rules]',
audit: (_req, data) => ({
action: 'voucher_channel_rules_ensured',
targetType: 'sku_channel_rules',
action: 'voucher_internal_channels_ensured',
targetType: 'internal_sku_channels',
targetId: '',
data: data as Record<string, unknown>,
}),
@@ -12,8 +12,10 @@ import {
assignChannelBySku,
assignChannelToOrderItems,
deleteChannelRule,
listChannelRules,
deleteChannelRules,
listChannelRulesPage,
listPendingChannelGroups,
switchChannelForOrderItems,
unlockChannel,
upsertChannelRule,
} from '../../services/fulfillment/channel-assign-service.js'
@@ -143,13 +145,44 @@ router.post(
),
)
router.post(
'/fulfillment/switch',
requireAdminPermission('finance.config'),
createJsonHandler(
(req) => {
const body = bodyOf(req)
return switchChannelForOrderItems({
orderItemIds: Array.isArray(body.orderItemIds)
? body.orderItemIds.map((value: unknown) => Number(value))
: [Number(body.orderItemId)],
channel: String(body.channel || ''),
adminUserId: req.adminSession?.userId ?? null,
note: String(body.note || ''),
})
},
{
successMessage: '履约渠道已切换',
errorMessage: '切换履约渠道失败',
scope: '[admin/fulfillment/switch]',
audit: (req, data) => ({
action: 'fulfillment.switch_channel',
targetType: 'order_item',
targetId: String(bodyOf(req).orderItemId || ''),
data: { payload: bodyOf(req), result: data as Record<string, unknown> },
}),
},
),
)
router.get(
'/fulfillment/rules',
requireAdminPermission('finance.view'),
createJsonHandler(
(req) =>
listChannelRules({
listChannelRulesPage({
channel: String(req.query.channel || ''),
page: Number(req.query.page),
pageSize: Number(req.query.pageSize),
...(req.query.enabled === undefined
? {}
: { enabled: String(req.query.enabled) !== 'false' }),
@@ -170,9 +203,7 @@ router.post(
const body = bodyOf(req)
return upsertChannelRule({
...(body.id ? { id: Number(body.id) } : {}),
provider: String(body.provider || ''),
shopId: String(body.shopId || ''),
matchKeyType: String(body.matchKeyType || 'sku_code'),
matchKeyType: String(body.matchKeyType || 'internal_sku'),
matchKey: String(body.matchKey || ''),
channel: String(body.channel || ''),
priority: Number(body.priority),
@@ -188,7 +219,7 @@ router.post(
scope: '[admin/fulfillment/rules]',
audit: (req, data) => ({
action: 'fulfillment.upsert_rule',
targetType: 'sku_channel_rule',
targetType: 'internal_sku_channel',
targetId: String((data as { id?: number })?.id || ''),
data: { payload: bodyOf(req) },
}),
@@ -206,6 +237,22 @@ router.delete(
}),
)
router.post(
'/fulfillment/rules/batch-delete',
requireAdminPermission('finance.config'),
createJsonHandler((req) => deleteChannelRules(Array.isArray(req.body?.ids) ? req.body.ids : []), {
successMessage: '渠道规则已批量删除',
errorMessage: '批量删除渠道规则失败',
scope: '[admin/fulfillment/rules/batch-delete]',
audit: (_req, data) => ({
action: 'fulfillment.batch_delete_rules',
targetType: 'internal_sku_channel',
targetId: '',
data: data as Record<string, unknown>,
}),
}),
)
router.get(
'/fulfillment/pipeline/status',
requireAdminPermission('finance.view'),
@@ -1,6 +1,5 @@
import { Router } from 'express'
import { setHttpCache } from '../../../middleware/http-cache.js'
import { syncAdminWorkOrderTemplateCostVersions } from '../../../services/cost-center/work-order-product-service.js'
import {
deleteAdminWorkCategory,
@@ -151,25 +150,17 @@ router.get(
router.post(
'/worker-platform/product-rules',
requireAdminRoles(['admin', 'operator']),
createJsonHandler(
async (req) => {
const result = await saveAdminWorkProductRule(req.body || {})
const ruleId = Number((result as { rule?: { id?: number } })?.rule?.id || 0)
const templateCost = ruleId ? await syncAdminWorkOrderTemplateCostVersions(ruleId) : null
return { ...result, templateCost }
},
{
successMessage: '接单物品规则已保存',
errorMessage: '保存接单物品规则失败',
scope: '[admin/worker-platform/product-rules]',
audit: (_req, data) => ({
action: 'work_product_rule_saved',
targetType: 'work_product_rule',
targetId: String((data as { rule?: { ruleKey?: string } })?.rule?.ruleKey || ''),
data: data && typeof data === 'object' ? (data as Record<string, unknown>) : {},
}),
},
),
createJsonHandler((req) => saveAdminWorkProductRule(req.body || {}), {
successMessage: '接单物品规则已保存',
errorMessage: '保存接单物品规则失败',
scope: '[admin/worker-platform/product-rules]',
audit: (_req, data) => ({
action: 'work_product_rule_saved',
targetType: 'work_product_rule',
targetId: String((data as { rule?: { ruleKey?: string } })?.rule?.ruleKey || ''),
data: data && typeof data === 'object' ? (data as Record<string, unknown>) : {},
}),
}),
)
router.post(
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* 历史短链只读跳转入口(/s/:code)。
* 新 claim 交付请使用本站 /#/claim/{token},勿再为 feifei 生成 shortLink。
* 新 claim 交付请使用本站 /h5/{订单号},勿再为 feifei 生成 shortLink。
*/
import { Router } from 'express'