feat(consignment): 补齐寄卖前端权限门控,收口调拨状态与运营体验
权限门控(P1-8 剩余): - 新增 resolveAdminPermission(sessionPermissions, code):会话接口的最新权限列表优先, 接口异常才回退登录时的本地快照;空数组也判无权限,避免"刚被收回权限仍能点"。 - 寄售仓库面板:consignment.manage 控制手动入库/编辑/作废/退单回库/匹配开关(无权限时开关禁用)。 - 寄卖提报面板:consignment.review 控制审核入库/驳回。 - 接单平台页:无 consignment.view 不渲染两个寄卖 tab,并把 URL/localStorage 的旧选择拉回工单页。 数据约束(P2-19 剩余): - 迁移 080:allocation_status 历史非法值归位「待安排」(备注留痕)后加 CHECK(待安排/已安排), 并顺手修正 077 遗留的"收集链接/玩家提交"权限描述文案。 - 编辑接口拒绝直接改 allocationStatus(400 consignment_allocation_status_update_forbidden)。 运营体验: - 两个面板补 isError 错误态与重试;审核通过/驳回加 confirmLoading 与提交中守卫(防双提交)。 - 待审徽标改用后端真实总数(原来只统计当前页);编辑弹窗押金不再兜底 ¥100(会误写押金)。 - 履约拦截弹窗冷静期文案不再硬编码"3 天",改为按商品规则设定。 - 核实并撤回"倒计时时区偏移"误报:deadlineAt 为带 Z 的 ISO,前端无偏移问题。 验证:后端 412 例(409 通过)、前端 21 例(新增 5 例权限解析)全绿, typecheck / lint / format / check:sql / build 通过;迁移 001→080 在全新空库跑通并在 dev 库应用。
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
-- 080_consignment_allocation_status_and_copy.sql —— 寄卖收口:调拨状态加约束 + 修正下线渠道遗留文案
|
||||
--
|
||||
-- 背景:
|
||||
-- 1) consignment_inventories.allocation_status 自 077 起是自由文本,系统只会写「待安排 / 已安排」,
|
||||
-- 但没有任何约束挡住别的值进来;列表角标与筛选口径依赖这两个值,必须锁死。
|
||||
-- 2) 077 写入的权限描述还写着「收集链接」(该渠道已在 079 整体下线)与「玩家提交」,
|
||||
-- 需要在权限说明里改成实际口径(打手通过接单平台提报)。
|
||||
-- 历史数据不做兼容处理(测试阶段),仅把非法值归位到「待安排」并在备注留痕。
|
||||
|
||||
-- 1) 历史非法值归位(先归位再加约束,否则 ADD CONSTRAINT 会因为存量脏数据直接失败)
|
||||
UPDATE consignment_inventories
|
||||
SET
|
||||
remark = remark || ' [allocation_status 非法值归位: ' || allocation_status || ']',
|
||||
allocation_status = '待安排',
|
||||
updated_at = NOW()
|
||||
WHERE allocation_status NOT IN ('待安排', '已安排');
|
||||
|
||||
-- 2) 调拨状态约束:与系统写入口径一致,重复执行安全
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'consignment_inventories_allocation_status_check'
|
||||
AND conrelid = 'consignment_inventories'::regclass
|
||||
) THEN
|
||||
ALTER TABLE consignment_inventories
|
||||
DROP CONSTRAINT consignment_inventories_allocation_status_check;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
ALTER TABLE consignment_inventories
|
||||
ADD CONSTRAINT consignment_inventories_allocation_status_check
|
||||
CHECK (allocation_status IN ('待安排', '已安排'));
|
||||
|
||||
COMMENT ON COLUMN consignment_inventories.allocation_status IS
|
||||
'调拨状态:待安排 在库待匹配 / 已安排 已被工单占用;由系统在占库、回库时写入,编辑接口不可改';
|
||||
|
||||
-- 3) 权限描述修正(收集链接渠道已下线,提报来源改为接单平台打手端)
|
||||
INSERT INTO admin_permissions (permission_key, permission_name, permission_group, description)
|
||||
VALUES
|
||||
('consignment.view', '查看寄卖库存', '寄卖管理', '查看寄售仓库与寄卖提报单'),
|
||||
('consignment.manage', '管理寄卖库存', '寄卖管理', '录入、修改、下架寄售物品(状态与调拨由系统动作变更)'),
|
||||
('consignment.review', '审核寄卖提报', '寄卖管理', '审核接单平台提报的寄卖申请并入库'),
|
||||
('consignment.fulfill', '寄卖内部履约', '寄卖管理', '使用内部寄卖库存直接履约工单、指派打手与撤销重选')
|
||||
ON CONFLICT (permission_key) DO UPDATE SET
|
||||
permission_name = EXCLUDED.permission_name,
|
||||
permission_group = EXCLUDED.permission_group,
|
||||
description = EXCLUDED.description,
|
||||
updated_at = NOW();
|
||||
@@ -261,6 +261,14 @@ export async function updateAdminConsignmentInventory(
|
||||
errorCode: 'consignment_status_update_forbidden',
|
||||
})
|
||||
}
|
||||
// 调拨状态由系统在占库/回库时写入,前端也从不展示该字段的编辑入口;
|
||||
// 开放自由文本会让"待安排/已安排"之外的值进库,导致列表角标与筛选口径失真。
|
||||
if (payload.allocationStatus !== undefined) {
|
||||
throw createHttpError('编辑接口不支持直接修改调拨状态', {
|
||||
statusCode: 400,
|
||||
errorCode: 'consignment_allocation_status_update_forbidden',
|
||||
})
|
||||
}
|
||||
const updated = await updateConsignmentInventory(id, {
|
||||
itemName: payload.itemName !== undefined ? String(payload.itemName).trim() : undefined,
|
||||
system: payload.system !== undefined ? String(payload.system).trim() : undefined,
|
||||
|
||||
@@ -68,6 +68,7 @@ import {
|
||||
returnConsignmentInventoryToStockWithClient,
|
||||
} from '../../repositories/worker-platform/consignment-repo.js'
|
||||
import { ADMIN_PERMISSION_KEYS } from '../admin/admin-rbac-service.js'
|
||||
import { updateAdminConsignmentInventory } from './consignment-admin-service.js'
|
||||
import {
|
||||
assertConsignmentRevocableWorkOrderStatus,
|
||||
resolveRevocableConsignmentInventoryId,
|
||||
@@ -2027,3 +2028,23 @@ test('内部履约的电子凭证核销失败要写事件留痕(不能静默
|
||||
assert.ok(src.includes('consignment_fulfill_voucher_consume_failed'))
|
||||
assert.ok(src.includes('if (!voucherConsume.ok)'))
|
||||
})
|
||||
|
||||
test('编辑接口拒绝直接改调拨状态,避免自由文本进库破坏角标口径', async () => {
|
||||
await assert.rejects(
|
||||
() =>
|
||||
updateAdminConsignmentInventory(1, {
|
||||
allocationStatus: '随便写的状态',
|
||||
}),
|
||||
(error: unknown) =>
|
||||
(error as { errorCode?: string }).errorCode ===
|
||||
'consignment_allocation_status_update_forbidden',
|
||||
)
|
||||
})
|
||||
|
||||
test('编辑接口仍然拒绝直接改物品状态(回归)', async () => {
|
||||
await assert.rejects(
|
||||
() => updateAdminConsignmentInventory(1, { status: 'archived' }),
|
||||
(error: unknown) =>
|
||||
(error as { errorCode?: string }).errorCode === 'consignment_status_update_forbidden',
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user