Compare commits

...

707 Commits
海棠 ... main

Author SHA1 Message Date
居居
1c1efebf27 fix(supply-chain): 清空停产退市商品限价
(cherry picked from commit 42b374801df5926aba8f302af518a538e2baede1)
2026-10-20 17:24:25 +08:00
WuYongJie
0509df6408 fix(installation-demo): synchronize clockwise screw and tool playback 2026-10-20 15:08:37 +08:00
居居
89eb66c6f6 feat(supply-chain): 限价筛选条件支持自定义 2026-10-20 14:51:20 +08:00
居居
7ce4b93706 feat(supply-chain): 限价顶部新增编码筛选 2026-10-20 14:51:20 +08:00
WuYongJie
75ba586ac8 feat(production-stop-demands): 完善档案字段、筛选、视图与通知中心联动
- 明细新增图片、产品标签、含大单近30日日销(条码/SKU 数值快照)

- 表头漏斗筛选(模糊/多选/数值比较/日期区间)、批量搜索、筛选设置

- 列设置拖动排序、快捷查询保存与套用

- 视图统计:日/周/月趋势、部门/需求人/原因/状态分布、饼图与明细下钻

- 通知中心:停产确认/待走流程/需求驳回通知、待办快照同步、钉钉全屏链接

- 新增迁移与历史数据回填脚本
2026-10-20 14:32:41 +08:00
居居
c34b72304d feat(supply-chain): 新增组合限价导出 2026-10-20 13:10:28 +08:00
居居
3cc85ed5d2 feat(supply-chain): 精简破价订单筛选条件 2026-10-20 13:10:28 +08:00
居居
8153f284e5 feat(supply-chain): 品牌页纳入三类国标前缀 2026-10-20 13:10:28 +08:00
WuYongJie
1614bd5e95 feat(diy): add text replacement and template URL import
Add image text detection/replacement with original preservation, compact result actions, and stored template imports. Keep image editor and template layout dialogs at consistent viewport sizes with internal scrolling.
2026-10-20 12:12:53 +08:00
WuYongJie
cadb917d90 feat(diy): add image adjustment, resizing and template layout tools 2026-10-20 09:43:40 +08:00
WuYongJie
09f36608de Use furniture Z-up axes in installation demo editing
Map X to width, Y to depth, and Z to height in numeric movement, drag and rotation controls, and feedback. Preserve existing Y-up model poses and saved data, and extract transform-anchor helpers.
2026-10-19 17:55:11 +08:00
WuYongJie
cba68a8cc7 fix(print): allow C-Lodop embedded preview frames 2026-10-19 17:07:48 +08:00
WuYongJie
414b86fd56 fix(home): open saved websites in a new tab 2026-10-19 16:46:53 +08:00
WuYongJie
df253a470d Optimize installation model preview and repair exploded layouts
Parse and cache OBJ geometry off the main thread, reuse the viewport, and adapt interaction rendering. Pull drawers out as complete groups, enforce panel and hardware clearance, and frame the applied layout. Add relative X/Y/Z movement in millimeters with existing persistence.
2026-10-19 16:44:30 +08:00
居居
4e949067d3 fix(supply-chain): 仅在品牌页按GBSFY归类 2026-10-19 16:22:34 +08:00
居居
8aa17b7a34 fix(supply-chain): 按GBSFY前缀纠正索菲亚品牌 2026-10-19 16:22:34 +08:00
居居
d5b5a60141 feat(supply-chain): 限价列表按负责人限价降序 2026-10-19 16:22:33 +08:00
WuYongJie
ef4e4e36ed feat(supply-chain): 商品库存快照落库并改读快照
- 新增 ProductInventorySnapshot / ProductInventorySnapshotRun 模型与正式迁移(条码×实际仓库)
- 同步任务复用现有库存/云仓/档案读取,临时表+事务原子替换,advisory lock 防重叠,失败保留上一份快照
- 新增 15 分钟 systemd timer 安装脚本与同步脚本(身份校验、凭据走环境变量)
- 列表/导出接口改读快照并展示 syncedAt,保留全零行过滤与导出字段选择
2026-10-19 15:40:57 +08:00
WuYongJie
5cf84a19c8 Merge latest main into accepted homepage release 2026-10-19 15:17:01 +08:00
WuYongJie
7210dd2111 fix(home): render homepage without the product sidebar 2026-10-19 15:12:29 +08:00
WuYongJie
071cd23ac9 feat(home): add fan carousel and double-click overview 2026-10-19 15:12:29 +08:00
居居
211aff2578 feat(supply-chain): 查询框直接支持批量筛选 2026-10-19 15:07:49 +08:00
居居
c691669101 feat(supply-chain): 限价筛选支持批量查询 2026-10-19 15:07:49 +08:00
居居
2b4eb17ec7 fix(supply-chain): 排除停产退市组合限价 2026-10-19 15:07:49 +08:00
居居
c62d951228 fix(supply-chain): 限价筛选候选值联动 2026-10-19 15:07:49 +08:00
居居
da4b77e6d6 style(supply-chain): 对齐表头列宽拖拽热区 2026-10-19 15:07:49 +08:00
居居
9360c553a2 fix(supply-chain): 修复组合单行表头遮挡明细 2026-10-19 15:07:49 +08:00
居居
a843b643ed fix(supply-chain): 放行组合运营负责人筛选 2026-10-19 15:07:49 +08:00
居居
05e1f94e46 fix(supply-chain): 修复组合限价筛选交互 2026-10-19 15:07:49 +08:00
居居
de26559e3e fix(supply-chain): 完善组合限价筛选与运营负责人 2026-10-19 15:07:49 +08:00
居居
c2f005712b feat(supply-chain): 新增组合限价页 2026-10-19 15:07:49 +08:00
居居
177d11dd47 style(supply-chain): 列名「非负责人限价(新)」改为「非负责人限价」
- 限价明细页与产品全量档案页的限价分组同步改名(导出列头随列定义一起生效)
2026-10-19 15:07:49 +08:00
居居
c104a029c9 fix(supply-chain): 限价明细「负责人限价」筛选补上候选值与筛选逻辑
- 负责人限价是 SupplyChainStyleLimitPrice.ownerLimitPrice 的派生列(按货号),此前 option 接口没有对应分支,
  下拉框显示「没有可选值」、选了也不生效
- 候选值:无其他筛选时取限价表的全部去重价格(两位小数,与界面一致);有筛选时只取当前结果集里货号的限价;
  无流程限价的商品提供「空值」选项
- 筛选:选中的价格先经 ROUND(price,2) 换回货号再按 sku 过滤;选中的价格无对应货号返回空结果;
  「空值」= 货号不在限价表里
- 补 3 条单测(价格→货号、无匹配、空值)
2026-10-19 15:07:49 +08:00
WuYongJie
ee983673d5 feat(supply-chain): 产品全量档案新增安装方式列(定厂优先+商品标签推导) 2026-10-19 12:06:41 +08:00
居居
132331733e feat(supply-chain): 限价流程同步改为服务器端可运行 + systemd 每日定时器
- sync-limit-workflows.mjs 路径参数化:新增 --out-dir/--initial-dir/--env-file/--skip-excel,
  环境变量 LIMIT_SYNC_OUT_DIR / LIMIT_SYNC_INITIAL_DIR / LIMIT_SYNC_ENV_FILE / LIMIT_SYNC_SKIP_EXCEL / LIMIT_SYNC_HISTORY_KEEP;
  默认目录改为应用根目录,不再写死 Mac 桌面
- 期初数据:优先显式目录,其次仓库自带 scripts/data/limit-sync/期初数据(首次部署可直接使用),
  服务器上业务更新只需把新的「限价期初数据*.xlsx」放进 shared 目录
- 写库安全:校验 DATABASE_URL、打印目标库(不含密码)、DELETE+INSERT 包在单事务里,
  失败自动回滚保留上一版限价/授权;Excel 导出失败不影响已提交的库数据
- 输出治理:输出目录自动创建;历史版本默认保留最近 20 份,避免服务器磁盘每天累积 30MB
- 新增 run-limit-workflow-sync-production.cjs:预检 shared/.env 变量(DATABASE_URL / JST_REPORT_DB_*)、
  打印库目标、透传参数、30 分钟硬超时、失败写 job 日志
- 新增 install-limit-workflow-sync-timer.sh:照 marketing-sales 样板安装 systemd service+timer,
  EnvironmentFile=$BASE/shared/.env,默认每天北京时间 07:30,输出/期初数据放 $BASE/shared/limit-workflow-sync
- 文档补充「正式站同步」章节;顺带修正 supply-chain-page-permissions 测试的菜单页数(新增限价管理后 27 页)
- 未改动 20261014/20261015 三个迁移目录(正式库已按现名建表)
2026-10-19 10:13:34 +08:00
WuYongJie
1b849b0209 feat(supply-chain): 新增商品库存管理(库存/云仓/筛选/导出/快捷查询)
- 页面:成品库存 × 实际仓库,按条码匹配产品档案与仓库配置
- 服务:聚水潭成品库存全量读取与缓存、筛选/排序/分页、小计、Excel 导出
- 云仓:链接服务器 OPENQUERY 汇总 order_able_qty,支持云仓明细下钻
- 接入:菜单、标签页、权限节点 productInventory
2026-10-19 09:54:57 +08:00
WuYongJie
d8b5006e44 feat(diy): add permanent deletion for saved schemes
(cherry picked from commit 2d1486b64c5f7dcd3a3a1e247ebe0c6d12e41d21)
2026-10-19 09:50:58 +08:00
居居
7fc43502fd fix(supply-chain): 修复限价管理合并入 mainline 后的 18 处类型错误(type-check 0 错误)
提交自身的错误:
- breach-orders/route.ts:filtersSchema 去掉重复的 platform / shopName 键(保留带长度上限的一份)
- supply-chain-breach-order.service.ts:BreachOrderListFilters 去掉重复声明的 platform / shopName
- BreachOrderTab.tsx:MultiFilterChip 的 field 联合类型补齐店铺款式编码/店铺商品编码/申诉原因/平台/店铺(与 filter-options 接口支持的字段一致)
- breach-orders/[id]/route.ts 与 breach-orders/route.ts:多结果分支的处理器统一声明为 handleRequest<unknown>(…): Promise<NextResponse<unknown>>,
  与既有 reference-image-estimate 路由同一写法,解决 ctx.user 可选态与返回形状不匹配

补齐缺失的配套改动:
- errors.ts:新增 ErrorCode.RESOURCE_NOT_FOUND(含 ERROR_MESSAGES 与 HTTP 404 映射)
- ProductArchiveFilters:补 materialLevel / inspectionReport 两个筛选项类型(查询逻辑此前已随功能提交)
- bom-costing-multimodal-gateway.ts:BomCostingMultimodalOperation 增加 IMAGE_EXTRACTION(破价申诉凭证识别用),
  breach-evidence.service 对可空的 providerConfig 加空值保护
- supply-chain-product-archive-select.ts:去掉重复的 sabcRating 选择项
- supply-chain-product-archive.repository.ts:storageType 候选值已在专用分支处理,去掉恒为 false 的重复判断

验证:git 干净检出后 npm run type-check(next typegen + tsc --noEmit)0 错误;
breach/supply-chain/permission 相关 230 个测试套件 1650 用例通过,失败 7 个套件均为 mainline 原有问题。
2026-10-19 09:23:04 +08:00
居居
62452b6343 fix(supply-chain): 检测报告按 SPU 文本里的编码匹配(平台 SPU 是自由文本)
- 平台 spu 形如「SJ0380书架 / 顾家穿衣镜DS5154 / BJ0056」,与档案 SPU 编码不一致;
  解析时从 spu 与报告标题里提取编码(SJ0380、GBGJ-SJZ0021、DS5154…)再匹配,纯编码仍原样收下
- 通用报告(「茶几通用」等)不产出 SPU;无文件记录继续忽略
- 单测补自由文本/标题提取用例
2026-10-19 09:23:04 +08:00
居居
2fde21f5da feat(supply-chain): 限价明细「是否有检测报告」接入素材平台检测报告接口
- 新增检测报告索引服务:GET {素材平台}/api/external/certificate-reports(X-API-Key),只认有报告文件的记录,按 SPU 汇总,进程内缓存 10 分钟
- 列取自 external 接口:限价页数据接口与「导出明细(含图片)」都带上 inspectionReport(有 / 无 / 空)
- 表头筛选接入:候选值「有 / 无」,筛选按报告 SPU 清单(接口未配置时不加条件、候选值为空)
- 配置:MATERIAL_LIBRARY_CERTIFICATE_REPORT_API_BASE_URL/API_KEY,不填复用 MATERIAL_LIBRARY_WHITE_BACKGROUND_API_*;未配置时该列显示为空
- 增补单测(解析/缓存/未配置/接口报错)与文档说明
2026-10-19 09:23:04 +08:00
居居
eca1d63842 feat(supply-chain): 限价管理的查看/导出/申诉权限统一由管理员配置
- 查看:页面与数据仍走「限价管理 · 可见」;导出改按「限价管理 · 下载」,申诉/审核/生成/导入改按「限价管理 · 操作」(原来是经营管理 edit)
- 服务端:下载中心 limit-export 任务与产品档案导出接口的 columnSet=limit 都按「限价管理 · 下载」鉴权
- 前端:无下载权限不展示导出入口(限价明细两个导出、破价订单导出当前筛选/乐捐明细);规则配置仍单独走 breachRuleConfig 节点
- 申诉保留兜底:破价单负责人本人即使无「操作」权限也可申诉本人订单,审核必须「操作」
- 测试:CustomTableView 补导出入口权限用例并修正过期的表头断言;文档补充权限章节
2026-10-19 09:23:04 +08:00
居居
f276164532 feat(supply-chain): 限价管理(限价明细 + 破价订单审核 + 规则配置 + 流程同步)
- 限价明细:抬头列/筛选/列宽/排序可配置,批量查询(商品编码/订单号/负责人等),筛选条件回显与一键清空
- 列取数口径:入库/代发按仓库名推导(含仓库配置覆盖)、产品标签取商品SABC打标、材质等级从「材质」识别 E0/E1/ENF
- 导出:导出明细 / 导出明细(含图片)改为全局下载中心后台生成,进度/取消/保存都在下载中心;页签名与文件名一致
- 破价订单审核:按天增量生成、链接级与订单级申诉、审核通过/驳回/二次申诉冲销乐捐、豁免(同价/活动)与证据凭证要求
- 规则配置页与权限:管理员开通后可编辑(权限节点 breachRuleConfig),无权限不可见
- 限价流程同步脚本:4 类钉钉限价流程 + 产品限价页面已确认限价 + 限价期初数据;流程/页面/期初优先级与「期初更新日期更新则以期初为准」
- 非负责人限价统一按最新口径派生(×1.05 且不低于 +10 元)
- 迁移:breach order review / breach rule config / limit flow sync
- 与 mainline 合并:保留字段权限(blockedFields)、负责人在职接管、限价目标 limitManagement 等新特性;
  规则配置权限并入新的权限节点结构,档案列去掉重复的 opsPriceLimit 列,相关测试断言按当前口径更新
2026-10-19 09:23:03 +08:00
WuYongJie
399fd8182d refactor(diy): simplify canvas controls and unify capsule styling
(cherry picked from commit 1429b870f08b34e37b02c3077053f17385e9738a)
2026-10-16 18:04:54 +08:00
WuYongJie
5c899b4705 revert: 限价管理两个提交(类型检查不通过,待功能线程修复后重新提交) 2026-10-16 17:55:56 +08:00
居居
fbe276d4f6 feat(supply-chain): 限价管理的查看/导出/申诉权限统一由管理员配置
- 查看:页面与数据仍走「限价管理 · 可见」;导出改按「限价管理 · 下载」,申诉/审核/生成/导入改按「限价管理 · 操作」(原来是经营管理 edit)
- 服务端:下载中心 limit-export 任务与产品档案导出接口的 columnSet=limit 都按「限价管理 · 下载」鉴权
- 前端:无下载权限不展示导出入口(限价明细两个导出、破价订单导出当前筛选/乐捐明细);规则配置仍单独走 breachRuleConfig 节点
- 申诉保留兜底:破价单负责人本人即使无「操作」权限也可申诉本人订单,审核必须「操作」
- 测试:CustomTableView 补导出入口权限用例并修正过期的表头断言;文档补充权限章节
2026-10-16 17:46:01 +08:00
居居
4be4bd1e09 feat(supply-chain): 限价管理(限价明细 + 破价订单审核 + 规则配置 + 流程同步)
- 限价明细:抬头列/筛选/列宽/排序可配置,批量查询(商品编码/订单号/负责人等),筛选条件回显与一键清空
- 列取数口径:入库/代发按仓库名推导(含仓库配置覆盖)、产品标签取商品SABC打标、材质等级从「材质」识别 E0/E1/ENF
- 导出:导出明细 / 导出明细(含图片)改为全局下载中心后台生成,进度/取消/保存都在下载中心;页签名与文件名一致
- 破价订单审核:按天增量生成、链接级与订单级申诉、审核通过/驳回/二次申诉冲销乐捐、豁免(同价/活动)与证据凭证要求
- 规则配置页与权限:管理员开通后可编辑(权限节点 breachRuleConfig),无权限不可见
- 限价流程同步脚本:4 类钉钉限价流程 + 产品限价页面已确认限价 + 限价期初数据;流程/页面/期初优先级与「期初更新日期更新则以期初为准」
- 非负责人限价统一按最新口径派生(×1.05 且不低于 +10 元)
- 迁移:breach order review / breach rule config / limit flow sync
- 与 mainline 合并:保留字段权限(blockedFields)、负责人在职接管、限价目标 limitManagement 等新特性;
  规则配置权限并入新的权限节点结构,档案列去掉重复的 opsPriceLimit 列,相关测试断言按当前口径更新
2026-10-16 17:46:01 +08:00
WuYongJie
bc8a4a15e9 feat(intake): notify quality inspectors and keep product photo uploads available 2026-10-16 17:28:45 +08:00
WuYongJie
77c91bd689 fix(supply-chain): 产品全量档案 DF 条码成本价改为统一取 jst_sku.cost_price
- enrichment 价格补丁不再区分 DF/非 DF:purchasePrice 与 costPrice 均取 rpt.jst_sku(purchase_price / cost_price)
- 删除 dws_sn_sales_full_info_di.goods_cost 取价链路(dws-sales-goods-cost.repository.ts 整删,DWS_SALES_GOODS_COST 来源常量移除)
- 单测与《供应链产品全量档案字段来源对照表》同步更新

预期效果(部署后下一轮同步):约 1874 条 DF 成本价由空变有值、约 230 条由 dws 金额口径变为 jst 单价口径、约 4516 条数值不变、0 条丢值。
2026-10-16 16:27:31 +08:00
WuYongJie
4bd48d8bc0 fix(diy): keep wood grain continuous and compact inspector
(cherry picked from commit 9af20a14b07f6da46691a7356c5fd1d08aa48887)
2026-10-16 14:47:27 +08:00
WuYongJie
d8e43e89ba fix(supply-chain): 仓库管理列表只取聚水潭仓库档案,档案历史名称不再补充展示 2026-10-16 14:00:51 +08:00
WuYongJie
12f04a2f96 feat(supply-chain): 产品全量档案移除「数据字典」占位入口
数据字典属内部开发口径,不对外暴露;「更多」菜单仅保留使用说明,清理占位提示状态;使用说明对应条目同步。
2026-10-16 13:56:48 +08:00
Codex
47eed75bca feat(supply-chain): 仓库管理模块全量源码(筛选排序/导入导出/属性覆盖/负责人对齐)
- 表头:除操作列外全列筛选下拉 + 升/降序排序(服务端排序,空值恒排末尾)
- 导入导出:模板含仓库名称/仓库属性/仓库归属/负责人/入库代发;导出支持字段勾选、跟随筛选与排序;导入弹窗(下载模板+选择文件)与进度面板,导入后可一键「仅查看导入的仓库」
- 仓库属性人工覆盖表(新增迁移 20261016103000)与单元格内联编辑
- 仓库负责人:工厂仓取产品档案采购,档案读取走只读库(回退主库)
- 菜单/页签/页面标题统一为「仓库管理」
2026-10-16 13:32:52 +08:00
WuYongJie
0ca4e4f8f1 fix(supply-chain): 产品全量档案导入入口补挂「操作」权限门控 2026-10-16 13:14:36 +08:00
WuYongJie
a54d32941f fix(diy): fit transparent basket patterns with five millimeter clearance 2026-10-16 12:31:53 +08:00
WuYongJie
559d025e6d feat(users): 字段权限集中配置——产品全量档案与店铺账号管理 2026-10-16 11:29:50 +08:00
居居
141ac4654c fix(supply-chain): 创建天数正好 90 天归入「创建60-90天」
- 新品区间分布边界修正:90 天整算 60-90 天,91 天起才是「创建90天以上」
- 读取已生成月份快照时按快照里的创建天数重算该字段,旧月份无需重新核算即可按新口径显示
2026-10-16 11:20:24 +08:00
WuYongJie
9465c98702 feat(product-archive): add batch code filters and compact sales controls 2026-10-15 17:51:10 +08:00
居居
11fd22f356 feat(supply-chain): 产品中心/采购中心不再依赖运营负责人
- 有设计师即计入产品中心、有采购即计入采购中心,即使该行没有运营负责人
- 运营中心仍只统计有运营负责人的行;「山东项目组」设计师/采购继续排除
- 快照增加口径版本号:读到旧口径快照时先按实时口径出数,并在后台自动重建该月快照(无需手动点保存配置)
2026-10-15 17:20:10 +08:00
居居
fc862c80fc feat(supply-chain): 各部门数据分类支持按事业部导出,事业部按一/二/三/四部排序
- 运营中心卡片「导出」改为先勾选事业部(默认全选;页面已筛事业部时默认只选它),只导出勾选范围
- 导出接口本已支持 department 过滤,前端补上多选入口(后端无需改动)
- 事业部/品类部名称排序改为按序号解析:事业一部→二部→三部→四部,品类九部→十部→十一部,不再按拼音乱序
- 新增部门名称排序工具与单测
2026-10-15 17:20:10 +08:00
WuYongJie
bcfd191657 fix(diy): add generated image deletion 2026-10-15 16:26:10 +08:00
WuYongJie
a9635163bc Merge shared mainline for Relay native image generation
Preserve the five existing production owner fixes and apply the reviewed AI task migration. Resolve DIY add/add conflicts only after checking that the feature baseline and production versions were identical. Merged AI tests and type-check passed; the retained production write guards were also checked in production mode.
2026-10-15 15:10:10 +08:00
WuYongJie
928a7b37a9 feat(ai): use durable Relay native image tasks
Persist image submissions with fixed idempotency keys, leased execution, provider
identity, reference snapshots and staged results. Integrate DIY, assisted assembly
and non-integrated white-background sessions without holding generation in a
browser request. Recover ambiguous submissions through owner-scoped reads and
stop a new eight-view batch when restoring an earlier request.

Add the NativeImageGenerationJob migration and a dedicated worker to both release
entry points. Protect job inputs/results from storage GC. Existing production
configuration and business data are not changed by this commit.

Validation: 256 focused Jest tests, isolated PostgreSQL migration/lease/transaction
checks (8), deployment guard tests (9), type-check, production build and desktop/
mobile browser component scenarios. Live native 2K reference-image request worked;
reusing its idempotency key returned the same task. Build uses local placeholders
for otherwise absent Drawing System configuration. Three unrelated material tests
also fail with the original mainline test file; they remain outside this change.

Architecture/visual/root-cause review: passed with warnings. Independent review
completed for the integration and shared input/storage paths; worker additionally
reviewed by the primary agent. Production merge/deployment requires authorization.
2026-10-15 14:55:46 +08:00
WuYongJie
8e14aa5fb1 feat(diy): add shared assets, uploaded styles and AI rendering 2026-10-15 11:36:32 +08:00
WuYongJie
dc2c86275e feat(diy): add shared assets, uploaded styles and AI rendering 2026-10-15 11:35:34 +08:00
WuYongJie
e0c324ce06 feat(supply-chain): 全量档案清除筛选移入筛选行并优化筛选交互(设置抽屉/回车确认)
- 「清除 N」从工具栏移到筛选行尾部(设置图标旁),收起细条保留清除入口
2026-10-15 11:11:47 +08:00
WuYongJie
67541f9208 feat(supply-chain): 全量档案清除筛选移入筛选行并优化筛选交互(设置抽屉/回车确认)
- 「清除 N」从工具栏移到筛选行尾部(设置图标旁),收起细条保留清除入口
2026-10-15 11:10:50 +08:00
WuYongJie
e2a237f43b fix(product-archive): 结算价公式修正为成本价÷0.9(模块化)
- 模块化分支由 ×0.9 改为 ÷0.9:聚水潭里模块化成本价按 9 折记,
  结算价需除以 0.9 还原(如成本 34.00 → 结算 37.78)
- 非模块化仍为成本价原样展示;先取整到分再除、四舍五入保留两位
- 同步更新测试期望值(76.20→84.67、123.45→137.17、85→94.44、34.00→37.78)
2026-10-15 09:18:26 +08:00
WuYongJie
d1496b0c11 fix(product-archive): 结算价公式修正为成本价÷0.9(模块化)
- 模块化分支由 ×0.9 改为 ÷0.9:聚水潭里模块化成本价按 9 折记,
  结算价需除以 0.9 还原(如成本 34.00 → 结算 37.78)
- 非模块化仍为成本价原样展示;先取整到分再除、四舍五入保留两位
- 同步更新测试期望值(76.20→84.67、123.45→137.17、85→94.44、34.00→37.78)
2026-10-15 09:17:57 +08:00
WuYongJie
46ca3bf786 feat(product-archive): 全量档案第二个成本价列改为结算价(模块化 ×0.9)
- 列与变更记录标签:成本价(新品)→ 结算价 / 结算价(新品),表头与导出同步生效
- 结算价在查询映射层实时计算:非模块化沿用成本价原样,模块化按 0.9 折
  (先取整到分再打折、四舍五入保留两位);模块化以商品标签含「模块化」判断,不落库
- 修复列设置合并时 localStorage 旧 label 覆盖默认列名的问题:内置列名
  一律以当前默认定义为准,改名对已保存列设置的用户才能生效
2026-10-15 08:48:16 +08:00
WuYongJie
67834358d2 fix(product-archive): allow trusted HTTPS image host on port 3333 2026-10-14 18:18:50 +08:00
WuYongJie
b6a326d996 fix(product-archive): allow trusted HTTPS image host on port 3333 2026-10-14 18:14:47 +08:00
WuYongJie
ec6067facc feat(supply-chain): 产品全量档案新增独立图文使用说明页(更多菜单新标签页打开)
- 新增 /product-archive-guide 独立路由:模块树目录(产品全量档案 → 1.1~1.10),无系统壳,登录+productArchive 查看权限
2026-10-14 17:45:18 +08:00
WuYongJie
e57f95c489 feat(supply-chain): 产品全量档案新增独立图文使用说明页(更多菜单新标签页打开)
- 新增 /product-archive-guide 独立路由:模块树目录(产品全量档案 → 1.1~1.10),无系统壳,登录+productArchive 查看权限
2026-10-14 17:43:53 +08:00
WuYongJie
9ef800001f feat(product-archive): 全量档案第二个成本价列改为结算价(模块化 ×0.9)
- 列与变更记录标签:成本价(新品)→ 结算价 / 结算价(新品),表头与导出同步生效
- 结算价在查询映射层实时计算:非模块化沿用成本价原样,模块化按 0.9 折
  (先取整到分再打折、四舍五入保留两位);模块化以商品标签含「模块化」判断,不落库
- 修复列设置合并时 localStorage 旧 label 覆盖默认列名的问题:内置列名
  一律以当前默认定义为准,改名对已保存列设置的用户才能生效
2026-10-14 17:09:30 +08:00
WuYongJie
89fbe9231b feat(supply-chain): 全量档案运营负责人切换 js_sku_info_manage.sku_manager 数据源 2026-10-14 14:28:05 +08:00
WuYongJie
289926294d Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-10-14 14:27:19 +08:00
WuYongJie
9492eba22d feat(supply-chain): 全量档案运营负责人切换 js_sku_info_manage.sku_manager 数据源 2026-10-14 14:27:02 +08:00
居居
bfe3073aa1 feat(supply-chain): 人员信息总负责人支持多中心总监并列显示(如 子龙/妙玉) 2026-10-14 13:03:08 +08:00
WuYongJie
49ec0be34f feat(supply-chain): 日销任务改为每日 08:00 单次执行,未到数即告警并支持手动补跑 2026-10-14 12:28:45 +08:00
WuYongJie
1e0a8decc1 feat(supply-chain): 日销任务改为每日 08:00 单次执行,未到数即告警并支持手动补跑 2026-10-14 12:28:40 +08:00
WuYongJie
2c4128e065 feat(supply-chain): 日销任务改为每日 08:00 单次执行,未到数即告警并支持手动补跑 2026-10-14 12:28:24 +08:00
WuYongJie
d5443e9f77 fix(product-archive): display complete column headers and preserve readable widths 2026-10-14 11:56:12 +08:00
WuYongJie
bcb0636625 fix(product-archive): display complete column headers and preserve readable widths 2026-10-14 11:55:26 +08:00
WuYongJie
822df76e19 fix(product-archive): display complete column headers and preserve readable widths 2026-10-14 11:55:00 +08:00
WuYongJie
fc50035d65 feat(owners): include growth center employees in business owner options 2026-10-14 11:35:12 +08:00
WuYongJie
b0d531e1af feat(owners): include growth center employees in business owner options 2026-10-14 11:33:58 +08:00
WuYongJie
bf2f114762 feat(owners): include growth center employees in business owner options 2026-10-14 11:33:22 +08:00
WuYongJie
a917949a5e fix(supply-chain): 基础同步回退视图不存在的 item_type 列引用并修复失败告警原因为空 2026-10-14 10:39:04 +08:00
WuYongJie
cf11c4251e fix(supply-chain): 基础同步回退视图不存在的 item_type 列引用并修复失败告警原因为空 2026-10-14 10:38:37 +08:00
WuYongJie
7f1358aa68 fix(supply-chain): 基础同步回退视图不存在的 item_type 列引用并修复失败告警原因为空 2026-10-14 10:38:22 +08:00
WuYongJie
83f0fbee86 feat(supply-chain): 产品全量档案负责人治理与聚水潭式表头改造 2026-10-14 10:08:08 +08:00
WuYongJie
f84abed356 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration
# Conflicts:
#	furniture-3d-system/src/server/repositories/__tests__/product-archive-owner-workflow.repository.test.ts
#	furniture-3d-system/src/server/repositories/product-archive-owner-workflow.repository.ts
2026-10-14 10:04:53 +08:00
WuYongJie
42d2447481 feat(supply-chain): 产品全量档案负责人治理与聚水潭式表头改造 2026-10-14 10:04:06 +08:00
WuYongJie
fb1c74ae67 fix(diy): retry transient storage failures during catalog import 2026-10-14 09:20:36 +08:00
WuYongJie
20eaf3589c fix(diy): retry transient storage failures during catalog import 2026-10-14 09:20:00 +08:00
WuYongJie
ce66215764 fix(storage): handle Qiniu CommonJS SDK default export for DIY import 2026-10-14 08:59:22 +08:00
WuYongJie
d76d024d62 fix(storage): handle Qiniu CommonJS SDK default export for DIY import 2026-10-14 08:58:44 +08:00
WuYongJie
56548a731e Merge production main into shared baseline for SABC release
Preserve SABC storage-type and batch-search changes, logistics column resizing, and production-main employee-hours and route export compatibility. Verified 54 focused tests and type-check; no pending production migrations.
2026-10-13 16:24:15 +08:00
居居
252624708d Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline 2026-10-13 10:15:47 +08:00
居居
7ae628f9f1 feat(supply-chain): SABC 明细新增「入库/代发」列,货号/商品编码/款式编码支持空格批量查询
- 明细与导出新增「入库/代发」,取值来源产品全量档案 storageType(入仓/代发)
- 已生成月份仍读快照;旧快照缺该字段时按 barcode 从档案补齐,无需重新核算
- 货号/商品编码/款式编码额外支持空格/制表符分隔(可直接粘贴 Excel 多行或多列),服务端最多取 200 个
- 入库/代发取值兼容字典值(INBOUND/DROPSHIP)与中文直填(入仓/入库/代发),产品全量档案列表同步显示
2026-10-13 10:15:34 +08:00
WuYongJie
f8b8959347 feat(diy): persist shared catalog and assets in database and object storage 2026-10-13 09:56:39 +08:00
WuYongJie
409e35e232 feat(diy): persist shared catalog and assets in database and object storage 2026-10-13 09:55:55 +08:00
WuYongJie
462fee6a46 feat(diy): persist shared catalog and assets in database and object storage 2026-10-13 09:48:09 +08:00
WuYongJie
ee033a1e08 feat(diy): add cabinet workbench with saved versions and BOM exports 2026-10-12 18:16:14 +08:00
WuYongJie
12de551328 feat(diy): add cabinet workbench with saved versions and BOM exports 2026-10-12 18:14:15 +08:00
WuYongJie
96f0373e79 feat(diy): add cabinet workbench with saved versions and BOM exports 2026-10-12 18:12:57 +08:00
WuYongJie
ecd11db3af docs: 删除已废弃的苹果风格视觉标准(验收只按白曜石工坊口径) 2026-10-12 14:05:53 +08:00
WuYongJie
b45b584871 chore(installation-demo): 移除安装演示V2试验链并保留成对迁移历史 2026-10-12 14:05:53 +08:00
WuYongJie
b237bbed7c feat(supply-chain): 产品全量档案定时任务通知——业绩快照每日简报与四任务失败兜底 2026-10-12 14:05:53 +08:00
WuYongJie
b8093f899e feat(supply-chain): 产品全量档案定时任务通知——业绩快照每日简报与四任务失败兜底 2026-10-12 14:05:18 +08:00
WuYongJie
f7811468f7 feat(supply-chain): 产品全量档案定时任务通知——业绩快照每日简报与四任务失败兜底 2026-10-12 14:05:06 +08:00
WuYongJie
92cd5f2903 docs: 删除已废弃的苹果风格视觉标准(验收只按白曜石工坊口径) 2026-10-12 11:08:32 +08:00
WuYongJie
e612936125 chore(installation-demo): 移除安装演示V2试验链并保留成对迁移历史 2026-10-12 11:08:32 +08:00
WuYongJie
c9c62f4b22 docs: 删除已废弃的苹果风格视觉标准(验收只按白曜石工坊口径) 2026-10-12 11:08:10 +08:00
WuYongJie
1dbcc0d84c chore(installation-demo): 移除安装演示V2试验链并保留成对迁移历史 2026-10-12 11:08:10 +08:00
WuYongJie
1de64d05f6 fix(supply-chain): keep daily sales notification fields in separate paragraphs 2026-10-12 10:47:05 +08:00
WuYongJie
553c237268 fix(supply-chain): keep daily sales notification fields in separate paragraphs 2026-10-12 10:47:05 +08:00
WuYongJie
5ca82d1033 fix(supply-chain): keep daily sales notification fields in separate paragraphs 2026-10-12 10:46:24 +08:00
WuYongJie
00dd1b2f89 fix(supply-chain): 日销通知改为纯文字
去掉新通知的卡片按钮字段,正文采用普通换行。
保留旧投递的幂等重试载荷,公共通知链路不变。
验证:相关25项测试、ESLint及diff检查通过。
2026-10-12 10:26:40 +08:00
WuYongJie
75dd248046 fix(supply-chain): 日销通知改为纯文字
去掉新通知的卡片按钮字段,正文采用普通换行。
保留旧投递的幂等重试载荷,公共通知链路不变。
验证:相关25项测试、ESLint及diff检查通过。
2026-10-12 10:26:40 +08:00
WuYongJie
81cffc020a fix(supply-chain): 日销通知改为纯文字
去掉新通知的卡片按钮字段,正文采用普通换行。
保留旧投递的幂等重试载荷,公共通知链路不变。
验证:相关25项测试、ESLint及diff检查通过。
2026-10-12 10:25:34 +08:00
WuYongJie
34e8426bb6 feat(store-account): display exact source platforms and filter before pagination 2026-10-12 10:24:17 +08:00
WuYongJie
a7554bebad feat(store-account): display exact source platforms and filter before pagination 2026-10-12 10:23:48 +08:00
WuYongJie
520706df0d feat(store-account): display exact source platforms and filter before pagination 2026-10-12 10:22:51 +08:00
WuYongJie
6a94f1b215 fix(supply-chain): 日销08点同步、失败补跑及管理员结果通知
北京时间08:00开始,未到数或失败按固定10分钟刻度补跑,成功当天停止源查询。
保留重启/PG失败记录和到数时间,正式任务定向徐霞客发送去重结果通知。
验证:31项定向测试、ESLint、全仓TypeScript、脚本语法与本地真实运行通过。
2026-10-12 10:05:44 +08:00
WuYongJie
9ca2f2d382 fix(supply-chain): 日销08点同步、失败补跑及管理员结果通知
北京时间08:00开始,未到数或失败按固定10分钟刻度补跑,成功当天停止源查询。
保留重启/PG失败记录和到数时间,正式任务定向徐霞客发送去重结果通知。
验证:31项定向测试、ESLint、全仓TypeScript、脚本语法与本地真实运行通过。
2026-10-12 10:04:42 +08:00
WuYongJie
c4f3063e88 fix(supply-chain): 日销08点同步、失败补跑及管理员结果通知
北京时间08:00开始,未到数或失败按固定10分钟刻度补跑,成功当天停止源查询。
保留重启/PG失败记录和到数时间,正式任务定向徐霞客发送去重结果通知。
验证:31项定向测试、ESLint、全仓TypeScript、脚本语法与本地真实运行通过。
2026-10-12 10:03:13 +08:00
WuYongJie
cb91d7a42f fix(intake): calibrate Gujia 2026 certificate print positions 2026-10-12 09:22:56 +08:00
WuYongJie
6edf13e974 fix(intake): calibrate Gujia 2026 certificate print positions 2026-10-12 09:22:20 +08:00
WuYongJie
4885e8fc75 fix(intake): calibrate Gujia 2026 certificate print positions 2026-10-12 09:21:26 +08:00
WuYongJie
ffa4290c4a feat(product-archive): 全量档案图片自动增量索引、换图失效与失败重试 2026-10-12 08:51:02 +08:00
WuYongJie
7b9fd60f6e feat(product-archive): 全量档案图片自动增量索引、换图失效与失败重试 2026-10-12 08:50:39 +08:00
WuYongJie
0ca07f8c5e feat(product-archive): 全量档案图片自动增量索引、换图失效与失败重试 2026-10-12 08:49:10 +08:00
WuYongJie
393f149c05 feat(product-archive): sync approved owner workflows daily 2026-10-10 16:14:24 +08:00
WuYongJie
50062a4c3d feat(product-archive): sync approved owner workflows daily 2026-10-10 16:13:53 +08:00
WuYongJie
9c2f5f6085 feat(product-archive): sync approved owner workflows daily 2026-10-10 16:13:25 +08:00
WuYongJie
abcffc424a fix(supply-chain): isolate permissions for every menu page 2026-10-10 14:31:38 +08:00
WuYongJie
9b7ab9e532 fix(supply-chain): isolate permissions for every menu page 2026-10-10 14:31:21 +08:00
apple
729eaf492d fix(logistics-arrival): 换货单不再把已换货的 0 数量行算成条码
内部单号 9627695(赔偿换货单)看板显示 X00005、SJ0061100N430V01 两个条码,
但聚水潭里 SJ0061100N430V01 是 qty=0 / item_status=Replaced 的"已换货"原始行,
实际发出的是手工换货替换品 X00005,看板应只显示 X00005。

- 条码匹配时跳过 qty<=0 的明细行(已换货/取消的原始行不产生条码);
- 明细查询补上 oi.qty 字段;
- 补单测:换货单只保留实际发出条码、0/负数行不计入,并把原有两条条码用例补上 qty。

实测(10-01~10-09 窗口):jst_OrderItem 2436 行、jst_qmcust_orderlist_item 3007 行
数量为 0 且全部是 Replaced;过滤后没有任何订单会变成无条码。

注:快照里的条码是生成时固化的,快照区间需重跑当天批次后才会更新。
任务名:物流到货签收时长-deepseek
2026-10-10 14:18:40 +08:00
apple
253c803be6 fix(logistics-arrival): 换货单不再把已换货的 0 数量行算成条码
内部单号 9627695(赔偿换货单)看板显示 X00005、SJ0061100N430V01 两个条码,
但聚水潭里 SJ0061100N430V01 是 qty=0 / item_status=Replaced 的"已换货"原始行,
实际发出的是手工换货替换品 X00005,看板应只显示 X00005。

- 条码匹配时跳过 qty<=0 的明细行(已换货/取消的原始行不产生条码);
- 明细查询补上 oi.qty 字段;
- 补单测:换货单只保留实际发出条码、0/负数行不计入,并把原有两条条码用例补上 qty。

实测(10-01~10-09 窗口):jst_OrderItem 2436 行、jst_qmcust_orderlist_item 3007 行
数量为 0 且全部是 Replaced;过滤后没有任何订单会变成无条码。

注:快照里的条码是生成时固化的,快照区间需重跑当天批次后才会更新。
任务名:物流到货签收时长-deepseek
2026-10-10 13:48:03 +08:00
apple
91eeb6402a fix(docs): 恢复生产部署说明中被覆盖的 SABC 章节并重放停产收口章节
上一次提交把 deploy 文档替换成旧版本,误删了「商品 SABC 月度核算」等主线内容;本次恢复主线版本并仅追加「停产需求确认超时收口」章节。
2026-10-10 13:21:18 +08:00
apple
f2480700ee chore(production-stop-demands): 补齐超时自动收口定时器与部署说明
- 新增 scripts/install-production-stop-confirmation-finalize-timer.sh:systemd service+timer,每 10 分钟检查一次(Asia/Shanghai),失败 5 分钟后自动重试,日志进 journald
- 生产部署入口说明新增「停产需求确认超时收口」章节(脚本、systemd 名称、调度、日志、启用步骤与授权边界)
2026-10-10 13:21:18 +08:00
apple
158995b71b fix(docs): 恢复生产部署说明中被覆盖的 SABC 章节并重放停产收口章节
上一次提交把 deploy 文档替换成旧版本,误删了「商品 SABC 月度核算」等主线内容;本次恢复主线版本并仅追加「停产需求确认超时收口」章节。
2026-10-10 12:14:38 +08:00
apple
26ebec4c40 chore(production-stop-demands): 补齐超时自动收口定时器与部署说明
- 新增 scripts/install-production-stop-confirmation-finalize-timer.sh:systemd service+timer,每 10 分钟检查一次(Asia/Shanghai),失败 5 分钟后自动重试,日志进 journald
- 生产部署入口说明新增「停产需求确认超时收口」章节(脚本、systemd 名称、调度、日志、启用步骤与授权边界)
2026-10-10 12:13:28 +08:00
apple
8270ae9b38 Merge branch 'codex/wuyongjie-production-stop-demands' into codex/xuxiake-mainline
feat(production-stop-demands): 停产需求管理全流程(提单→需求确认→走停产流程→流程结果同步)
- 按条码拆单提交;批量同意/不同意、批量走流程、批量修改停产原因
- 需求确认人与流程负责人分开配置(批量新增/删除/查询)
- 确认截止时间(15:00/16:00 规则)与超时默认同意;任一人不同意即整单驳回
- PLM 流程结果同步(拒绝→需求驳回;商品停产/退市→流程审批通过)与盘停建议配置

(cherry picked from commit 33555c7f6bc0881452a074b06c89b6caf911ca1d)
2026-10-10 11:41:51 +08:00
apple
33555c7f6b Merge branch 'codex/wuyongjie-production-stop-demands' into codex/xuxiake-mainline
feat(production-stop-demands): 停产需求管理全流程(提单→需求确认→走停产流程→流程结果同步)
- 按条码拆单提交;批量同意/不同意、批量走流程、批量修改停产原因
- 需求确认人与流程负责人分开配置(批量新增/删除/查询)
- 确认截止时间(15:00/16:00 规则)与超时默认同意;任一人不同意即整单驳回
- PLM 流程结果同步(拒绝→需求驳回;商品停产/退市→流程审批通过)与盘停建议配置
2026-10-10 11:28:48 +08:00
apple
737926de7e feat(production-stop-demands): 停产需求管理全流程(提单/确认/走流程/结果同步)
- 按条码拆单提交,支持批量同意/不同意、批量走流程、批量修改停产原因
- 需求确认人与流程负责人分别配置,支持批量新增/删除/查询
- 确认截止时间规则(当天15:00前当天16:00截止,否则次日16:00)与超时默认同意;任一人不同意即整单驳回
- PLM 流程结果同步:流程拒绝→需求驳回;商品状态变为停产/退市→流程审批通过
- 盘停建议配置:批量导入(模板/文件/三种模式)、表格式维护、提单按条码自动带出
- 列表支持列宽拖拽、表头排序、日期区间/需求人/采购筛选、需求状态流程步骤条与批量操作
2026-10-10 11:17:18 +08:00
WuYongJie
de7518bb16 feat(product-archive): use marketing daily sales with daily sync and arrival monitor
Aggregate rpt.op_marketingDailySales by complete SKU and calculate weighted purchase sales.
Persist daily source fingerprints, skip unchanged archive writes and prevent old enrichment from overwriting sales.
Run daily at 09:30 Beijing time and observe source arrival every 10 minutes on 2026-10-11 08:00-09:30.
2026-10-10 11:09:51 +08:00
WuYongJie
cd8d5f2799 feat(product-archive): use marketing daily sales with daily sync and arrival monitor
Aggregate rpt.op_marketingDailySales by complete SKU and calculate weighted purchase sales.
Persist daily source fingerprints, skip unchanged archive writes and prevent old enrichment from overwriting sales.
Run daily at 09:30 Beijing time and observe source arrival every 10 minutes on 2026-10-11 08:00-09:30.
2026-10-10 11:08:29 +08:00
WuYongJie
a944d5eecd feat(product-archive): use marketing daily sales with daily sync and arrival monitor
Aggregate rpt.op_marketingDailySales by complete SKU and calculate weighted purchase sales.
Persist daily source fingerprints, skip unchanged archive writes and prevent old enrichment from overwriting sales.
Run daily at 09:30 Beijing time and observe source arrival every 10 minutes on 2026-10-11 08:00-09:30.
2026-10-10 11:07:23 +08:00
apple
ce3658a33d fix(logistics-arrival): 条码不再并集同一物流单号上的其它订单
内部单号 9627695(线上单 351207604012)实际只有 X00005 和 SJ0061100N430V01
两项,看板却显示 4 个条码——多出来的 SJ0061100N430V01-5/-7 属于同一物流单号
75627908174893 上的另一张订单 9593499(合并发货)。

根因:buildBarcodeMap 把"物流单号"键的条码无条件并入,而一个物流单号可能合并
发货多张订单,于是一张单会串上别单的条码;同一张单还会因为分页里恰好有没有
那张邻单而显示不同条码。

- 条码匹配规则抽成纯函数 matchLogisticsArrivalSigningDurationBarcodes:
  先用内部单号/线上订单号/物流单号+付款时间匹配本单自己的明细,
  物流单号只在精确键一个都匹配不到时兜底;
- 补单测覆盖"合并发货不串单"和"精确键为空才兜底且去重"。

注:快照里的条码是生成时固化的,快照区间需重跑当天批次后才会更新。
任务名:物流到货签收时长-deepseek
2026-10-10 10:52:42 +08:00
apple
3679c247bf feat(logistics-arrival): 订单明细支持拖动调整各字段列宽
20 个字段原来只有统一的 max-width: 260px + 省略号,长文本(备注/留言/地址)
看不全也没法放大。现在每列表头右侧都有拖拽把手:

- 拖动调整列宽(72~640px),键盘 ←/→ 微调,双击把手恢复该列默认宽度;
- 列宽写进 localStorage,刷新/切页后保留;改过列宽后表头出现"恢复默认列宽"按钮;
- colgroup + table-layout: fixed,列宽所见即所得,表格总宽随列宽变化并横向滚动。

任务名:物流到货签收时长-deepseek
2026-10-10 10:12:36 +08:00
apple
7e1ca79dc0 fix(logistics-arrival): 变体条码回退基准条码,商品维度不再是未填写
聚水潭同款不同规格会给基准条码加 `-数字`(如 SJ0061100N430V01-5),
产品档案只登记基准条码,于是变体条码查不到档案、供应商/采购为空;
而订单明细行是按整单所有明细聚合的,只要单里有基准条码就有值——
同一张订单因此出现"商品维度未填写、订单明细有值"。

- 档案查询两步走:先精确匹配,未命中再用基准条码(去掉结尾 `-数字`)兜底;
- 基准条码可能与本批一起查询,两轮结果都参与回退;
- 新增 logisticsArrivalSigningDurationArchiveFallbackBarcodes 纯函数 + 单测。

注:快照里的 productItems 是生成时固化的,需重跑当天批次后分析看板才会带上兜底结果。
任务名:物流到货签收时长-deepseek
2026-10-10 10:06:10 +08:00
apple
f665a6d271 fix(logistics-arrival): 快照覆盖判定改用实际数据覆盖,修当天少算/导出缺单
看板/导出的快照覆盖判定此前只比对批次"声明窗口"(windowTo = 跑批当天),
但每天 03:00/05:30 跑批时当天还没签收完,数据只到跑批那一刻,于是把
"还没签收完的当天"误判成已覆盖:10-08 生成的批次只会算到 10-07(约 3.8 万),
当天签收的单(用户清单里那 5,820 条)整批缺失。

- 快照批次新增 dataThrough(该批真实最大签收时间),覆盖上限改为
  min(windowTo - 1, dataThrough 当天):跑批当天永远不完整,只信任到前一天;
  数据源本身滞后时再由 dataThrough 收窄。
- 生成任务逐片记录 dataThrough 并写回批次;旧批次缺值时按 windowTo - 1 兜底。
- 定时器默认 05:30 → 03:00;文档同步更新覆盖规则。
- migration: LogisticsArrivalSigningDurationSnapshotRun.dataThrough。

任务名:物流到货签收时长-deepseek

(cherry picked from commit eca96b3067d73bc01e54f72dd11dd7253b324b97)
2026-10-09 18:01:32 +08:00
居居
1a53aa31a3 feat(supply-chain): 各部门数据分类去掉「数据权限例外」,权限统一以用户管理后台为准
- 配置弹窗移除「数据权限例外」输入项,保存时不再写入该字段(旧配置下次保存即清空)
- 数据范围不再按人收窄:部门负责人/总负责人/普通账号一律看全部利润中心
- 设置结构、settings 接口返回与权限节点描述同步移除 dataScopeGrants
2026-10-09 17:47:34 +08:00
apple
eca96b3067 fix(logistics-arrival): 快照覆盖判定改用实际数据覆盖,修当天少算/导出缺单
看板/导出的快照覆盖判定此前只比对批次"声明窗口"(windowTo = 跑批当天),
但每天 03:00/05:30 跑批时当天还没签收完,数据只到跑批那一刻,于是把
"还没签收完的当天"误判成已覆盖:10-08 生成的批次只会算到 10-07(约 3.8 万),
当天签收的单(用户清单里那 5,820 条)整批缺失。

- 快照批次新增 dataThrough(该批真实最大签收时间),覆盖上限改为
  min(windowTo - 1, dataThrough 当天):跑批当天永远不完整,只信任到前一天;
  数据源本身滞后时再由 dataThrough 收窄。
- 生成任务逐片记录 dataThrough 并写回批次;旧批次缺值时按 windowTo - 1 兜底。
- 定时器默认 05:30 → 03:00;文档同步更新覆盖规则。
- migration: LogisticsArrivalSigningDurationSnapshotRun.dataThrough。

任务名:物流到货签收时长-deepseek
2026-10-09 17:16:12 +08:00
居居
1779d8a697 style(supply-chain): 各部门数据分类配置弹窗标题去掉「(仅管理员)」 2026-10-09 16:42:01 +08:00
居居
e95748dee9 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline 2026-10-09 16:30:54 +08:00
居居
530a35711f style(supply-chain): 各部门数据分类配置弹窗标题去掉「(仅管理员)」 2026-10-09 16:30:45 +08:00
居居
810f3a8a07 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline 2026-10-09 16:19:12 +08:00
居居
c4c370df2c feat(supply-chain): 各部门数据分类去掉「数据权限例外」,权限统一以用户管理后台为准
- 配置弹窗移除「数据权限例外」输入项,保存时不再写入该字段(旧配置下次保存即清空)
- 数据范围不再按人收窄:部门负责人/总负责人/普通账号一律看全部利润中心
- 设置结构、settings 接口返回与权限节点描述同步移除 dataScopeGrants
2026-10-09 16:18:56 +08:00
codex
1187a7ec56 fix(logistics-arrival): 经营维度点选不再自动滚到 09 订单明细
之前任何下钻(经营对象维度点选、经营总览异常卡片)都会把 09 订单明细 scrollIntoView 到视口顶部,
点平台/店铺等维度时会直接把页面翻到底,用户没法接着看其他维度、还得手动往上拉。

- 自动滚动只保留给经营总览的异常卡片(卡片写着"点击看明细")
- 经营对象维度(平台/店铺/发货仓/物流/采购/供应商/商品/签收日期)点选不再抢滚动条,
  下钻筛选照常生效,09 明细在原位置更新
- 测试拆成两条:维度点选不滚动(且仍触发 /drilldown)、异常卡片仍滚动到明细锚点
2026-10-09 15:48:00 +08:00
codex
e4fa4a2880 fix(logistics-arrival): 下钻/导出接口接受按字段分组的排除关键词
下钻和导出两个接口的 zod schema 还停留在 string | array,
而前端下发的是新的分组对象 { 字段: [词] },
于是点商品/店铺等对象下钻时 09 明细直接报"下钻条件格式不正确"(导出报"导出筛选条件格式不正确")。

- 两个接口的 excludeKeywords 都接受 对象 / `字段:词,词` 字符串 / 旧平铺数组
- 字符串形态改用 parseExcludeKeywords 解析(老链接按配置归位),不会把字段名当成关键词
- 新增两条路由单测:分组对象下钻/导出、字符串与平铺兼容、非法形态返回 400
2026-10-09 15:47:59 +08:00
居居
9b7d691ca9 feat(supply-chain): 商品SABC打标查看/导出默认全员开放,限制只在用户管理后台收紧
- 新增「商品SABC打标 · 商品明细」权限节点:默认全员可见+可导出,后台可单独关闭
- 侧边栏「商品SABC打标」入口与明细/经营总览/趋势/筛选候选/导出接口统一改用该节点
- 「各部门数据分类」同样默认全员可见+导出(移除商品企划的默认下载限制)
- 默认开放节点支持「显式关闭优先」:后台关掉某业务类型或个人例外,立即生效
- 「执行核算」仍由「商品SABC打标 · 执行核算」节点控制,未授权账号返回 403
- 补充权限映射与默认开放语义单测
2026-10-09 15:00:37 +08:00
居居
8d08176ab2 feat(supply-chain): 数据权限支持「全部」授权写法 2026-10-09 15:00:37 +08:00
居居
18eaaa4c5f feat(supply-chain): 部门数据配置改为独立权限节点,默认商品企划与管理员可用 2026-10-09 15:00:37 +08:00
居居
fb1d25b373 feat(supply-chain): 各部门数据分类改为独立权限模块(可见/下载分开配置) 2026-10-09 15:00:37 +08:00
居居
b2ca7bf7f1 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline 2026-10-09 14:42:53 +08:00
居居
e2254c50f5 feat(supply-chain): 各部门数据分类默认全员可见全部利润中心,只有「数据权限例外」才收窄
- 未配置数据权限例外时不再默认只看本人:所有人可见全部利润中心数据(查看/导出仍由用户管理后台节点控制,默认开放)
- 移除按人员信息表(部门负责人/总负责人)自动收窄到本事业部的旧规则
- 显式配置了范围的人仍按配置收窄,写「全部/所有/*/全公司」表示不受限
- 配置弹窗提示语同步说明「默认不需要配置」
2026-10-09 14:42:38 +08:00
居居
93293258d1 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline 2026-10-09 14:24:53 +08:00
居居
f86d27cae1 feat(supply-chain): 商品SABC打标查看/导出默认全员开放,限制只在用户管理后台收紧
- 新增「商品SABC打标 · 商品明细」权限节点:默认全员可见+可导出,后台可单独关闭
- 侧边栏「商品SABC打标」入口与明细/经营总览/趋势/筛选候选/导出接口统一改用该节点
- 「各部门数据分类」同样默认全员可见+导出(移除商品企划的默认下载限制)
- 默认开放节点支持「显式关闭优先」:后台关掉某业务类型或个人例外,立即生效
- 「执行核算」仍由「商品SABC打标 · 执行核算」节点控制,未授权账号返回 403
- 补充权限映射与默认开放语义单测
2026-10-09 14:24:45 +08:00
codex
d727421dca fix(logistics-arrival): 经营维度点选不再自动滚到 09 订单明细
之前任何下钻(经营对象维度点选、经营总览异常卡片)都会把 09 订单明细 scrollIntoView 到视口顶部,
点平台/店铺等维度时会直接把页面翻到底,用户没法接着看其他维度、还得手动往上拉。

- 自动滚动只保留给经营总览的异常卡片(卡片写着"点击看明细")
- 经营对象维度(平台/店铺/发货仓/物流/采购/供应商/商品/签收日期)点选不再抢滚动条,
  下钻筛选照常生效,09 明细在原位置更新
- 测试拆成两条:维度点选不滚动(且仍触发 /drilldown)、异常卡片仍滚动到明细锚点
2026-10-09 14:21:08 +08:00
居居
f94c86bcc8 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
部门数据分类数据权限支持「全部」授权写法。
2026-10-09 13:08:24 +08:00
居居
5fdeea9a51 feat(supply-chain): 数据权限支持「全部」授权写法 2026-10-09 13:07:36 +08:00
居居
5a00ad317d Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
部门数据配置改为独立权限节点(默认商品企划 + 管理员)。
2026-10-09 11:27:32 +08:00
居居
cf5f3b2d49 feat(supply-chain): 部门数据配置改为独立权限节点,默认商品企划与管理员可用 2026-10-09 11:27:01 +08:00
居居
28e972f663 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
各部门数据分类改为独立权限模块(可见/下载分开配置),无权限自动隐藏页签。
2026-10-09 10:26:28 +08:00
居居
ca08d89946 feat(supply-chain): 各部门数据分类改为独立权限模块(可见/下载分开配置) 2026-10-09 10:25:36 +08:00
WuYongJie
bffa3ec1a6 fix(intake): enable Gujia 2026 certificate template 2026-10-09 10:09:37 +08:00
WuYongJie
3fd4c0fac0 Merge Gujia 2026 certificate enablement into shared mainline 2026-10-09 10:09:06 +08:00
WuYongJie
ee95abd0b0 fix(intake): enable Gujia 2026 certificate template 2026-10-09 10:03:34 +08:00
WuYongJie
6989354a42 feat(supply-chain): 档案设计师绑定新品流程结构设计师(只填空值,以新品流程表为准) 2026-10-09 10:02:26 +08:00
WuYongJie
8a4e0ac797 feat(supply-chain): 档案设计师绑定新品流程结构设计师(只填空值,以新品流程表为准) 2026-10-09 10:02:18 +08:00
WuYongJie
afe9b806d5 feat(supply-chain): 档案设计师绑定新品流程结构设计师(只填空值,以新品流程表为准) 2026-10-09 10:01:58 +08:00
WuYongJie
6d3891fc44 style(layout): 产品开发侧栏对齐供应链 224px,修复系统切换器「产品开发」截断
侧栏变量 --layout-app-sidebar-width 11rem→14rem(浅色/深色两处),切换器簇/按钮随之与供应链逐项一致(224/192/144);切换器簇改 min-width、触发器 flex-1→flex-auto 作为防截断兜底。已实测四主页面无挤压无横向溢出。
2026-10-09 09:39:26 +08:00
WuYongJie
4cd07bdda5 Merge origin/codex/xuxiake-mainline into codex/local-3001-integration (sync before sidebar alignment push) 2026-10-09 09:39:06 +08:00
WuYongJie
8617493f8a style(layout): 产品开发侧栏对齐供应链 224px,修复系统切换器「产品开发」截断
侧栏变量 --layout-app-sidebar-width 11rem→14rem(浅色/深色两处),切换器簇/按钮随之与供应链逐项一致(224/192/144);切换器簇改 min-width、触发器 flex-1→flex-auto 作为防截断兜底。已实测四主页面无挤压无横向溢出。
2026-10-09 09:38:44 +08:00
WuYongJie
c3b20aace6 fix(supply-chain): 运营负责人改为一次性定标冻结(摘除跟随最新销售的每日翻转) 2026-10-09 09:00:53 +08:00
WuYongJie
5e842eeeb8 fix(supply-chain): 运营负责人改为一次性定标冻结(摘除跟随最新销售的每日翻转) 2026-10-09 09:00:46 +08:00
WuYongJie
b5dc4a1b1b fix(supply-chain): 运营负责人改为一次性定标冻结(摘除跟随最新销售的每日翻转) 2026-10-09 09:00:12 +08:00
WuYongJie
139aec0e70 feat(oa-board): 出图进度页改为美术设计申请源数据视图
一行=一条流程实例,列=流程实例ID/编号/标题/状态/时间+128 个表单原字段(按全量填写率排序,与源库导出 xlsx 同口径)。新快照表 oa_art_flow_raw_snapshot(关键筛选列+fields_json 全字段清洗值)+ oa_art_flow_raw_meta 字段顺序;同步脚本入 timer 链;API /api/intake/oa-board/art-flow-raw。旧 SKU 维度视图从页面移除,后端服务与导出消费方保留。
2026-10-08 17:24:06 +08:00
WuYongJie
1ba16cbc6b Merge origin/codex/xuxiake-mainline into codex/local-3001-integration (sync before art-flow raw view push) 2026-10-08 17:23:44 +08:00
WuYongJie
eaf11faea1 feat(oa-board): 出图进度页改为美术设计申请源数据视图
一行=一条流程实例,列=流程实例ID/编号/标题/状态/时间+128 个表单原字段(按全量填写率排序,与源库导出 xlsx 同口径)。新快照表 oa_art_flow_raw_snapshot(关键筛选列+fields_json 全字段清洗值)+ oa_art_flow_raw_meta 字段顺序;同步脚本入 timer 链;API /api/intake/oa-board/art-flow-raw。旧 SKU 维度视图从页面移除,后端服务与导出消费方保留。
2026-10-08 17:23:18 +08:00
WuYongJie
13b81e5822 feat(supply-chain): 新增替换款规则每日同步定时器安装脚本
SupplyChainProductArchiveReplacementRule 的上游是 rpt.pm_replacementManage,
此前只有 local-sync-archive.mjs 会调用 sync-product-replacement-rules.ts,
正式机没有任何定时任务,表建好后仍然是空的,SABC 的「同款商品编码」也就一直是空。

新增 install-product-replacement-rule-sync-timer.sh,按仓库既有 install-*-timer.sh
同款写法安装 furniture-product-replacement-rule-sync.{service,timer},
默认每天 07:40(Asia/Shanghai)同步一次,早于 09:00 的 SABC 打标日结。
脚本仅安装定时任务,改动生产数据由 sync-product-replacement-rules.ts 自己完成。
2026-10-08 16:43:58 +08:00
WuYongJie
bb777d4880 feat(supply-chain): 新增替换款规则每日同步定时器安装脚本
SupplyChainProductArchiveReplacementRule 的上游是 rpt.pm_replacementManage,
此前只有 local-sync-archive.mjs 会调用 sync-product-replacement-rules.ts,
正式机没有任何定时任务,表建好后仍然是空的,SABC 的「同款商品编码」也就一直是空。

新增 install-product-replacement-rule-sync-timer.sh,按仓库既有 install-*-timer.sh
同款写法安装 furniture-product-replacement-rule-sync.{service,timer},
默认每天 07:40(Asia/Shanghai)同步一次,早于 09:00 的 SABC 打标日结。
脚本仅安装定时任务,改动生产数据由 sync-product-replacement-rules.ts 自己完成。
2026-10-08 16:43:37 +08:00
居居
1bea953315 fix(supply-chain): 补齐替换规则表迁移(正式环境缺表导致同款商品编码全为空) 2026-10-08 16:30:35 +08:00
WuYongJie
03741083fa feat(supply-chain): 产品全量档案产品标签每日跟随SABC打标结果(有变化才更新) 2026-10-08 16:28:31 +08:00
WuYongJie
c23f616003 feat(supply-chain): 产品全量档案产品标签每日跟随SABC打标结果(有变化才更新) 2026-10-08 16:28:14 +08:00
WuYongJie
a3e236e986 feat(supply-chain): 产品全量档案产品标签每日跟随SABC打标结果(有变化才更新) 2026-10-08 16:26:54 +08:00
居居
9baee61769 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
补齐 SupplyChainProductArchiveReplacementRule 迁移(正式环境缺表 → 同款商品编码全为空)。
2026-10-08 16:20:35 +08:00
居居
64a820a998 fix(supply-chain): 补齐替换规则表迁移(正式环境缺表导致同款商品编码全为空) 2026-10-08 16:20:27 +08:00
WuYongJie
c81b696a62 fix(supply-chain): 店铺账号列筛选支持全选后排除个别项(exclude 换算正面清单+筛选值截断上限提升) 2026-10-08 14:23:57 +08:00
WuYongJie
f13ed3d3fb fix(supply-chain): 店铺账号列筛选支持全选后排除个别项(exclude 换算正面清单+筛选值截断上限提升) 2026-10-08 14:23:53 +08:00
WuYongJie
ad0b094af1 fix(supply-chain): 店铺账号列筛选支持全选后排除个别项(exclude 换算正面清单+筛选值截断上限提升) 2026-10-08 14:23:02 +08:00
WuYongJie
2925818a75 feat(supply-chain): 店铺账号三级负责人离职改为文字后缀(已离职)并支持任一列命中筛选 2026-10-08 14:02:06 +08:00
WuYongJie
b7dd8ac490 feat(supply-chain): 店铺账号三级负责人离职改为文字后缀(已离职)并支持任一列命中筛选 2026-10-08 14:02:03 +08:00
WuYongJie
b295e128eb feat(supply-chain): 店铺账号三级负责人离职改为文字后缀(已离职)并支持任一列命中筛选 2026-10-08 14:01:43 +08:00
居居
8a3f32c5a2 fix(supply-chain): 修正月末创建时间边界,避免最后一天16点后创建的商品整月漏收录 2026-10-08 13:42:53 +08:00
WuYongJie
da1872c59d feat(supply-chain): 店铺账号离职负责人红色标注与负责人状态筛选(自定义下拉) 2026-10-08 12:59:32 +08:00
WuYongJie
4e594a4eee feat(supply-chain): 店铺账号离职负责人红色标注与负责人状态筛选(自定义下拉) 2026-10-08 12:59:23 +08:00
WuYongJie
ba1209deb3 feat(supply-chain): 店铺账号离职负责人红色标注与负责人状态筛选(自定义下拉) 2026-10-08 12:59:04 +08:00
WuYongJie
8e38894a2b fix(supply-chain): 全量档案运营层级匹配加固(不可见字符清洗+档案已有运营负责人兜底) 2026-10-08 12:01:59 +08:00
WuYongJie
605fb1347b fix(supply-chain): 全量档案运营层级匹配加固(不可见字符清洗+档案已有运营负责人兜底) 2026-10-08 12:01:38 +08:00
WuYongJie
bcfadc1e3f fix(supply-chain): 全量档案运营层级匹配加固(不可见字符清洗+档案已有运营负责人兜底) 2026-10-08 12:01:11 +08:00
WuYongJie
867b5bf897 feat(supply-chain): 店铺授权提醒每日定时推送(11:00)+ 负责人不在内部整店跳过 2026-10-08 11:36:30 +08:00
WuYongJie
cde4d59afb feat(supply-chain): 店铺授权提醒每日定时推送(11:00)+ 负责人不在内部整店跳过 2026-10-08 11:36:17 +08:00
WuYongJie
7979671a3d feat(supply-chain): 店铺授权提醒每日定时推送(11:00)+ 负责人不在内部整店跳过 2026-10-08 11:36:02 +08:00
WuYongJie
3a10aa3d9f feat(oa-board): 新品-视频进度(内容部)看板,钉钉内容视频流程独立接入
独立快照表 oa_video_flow_board_snapshot + 双驱动同步脚本 + 分页筛选接口 + 新 Tab;不进新品主链项目聚合/总状态/时效。阶段推进按表单字段推断(编导→摄像→剪辑→封面→验收/评分),节点名清单到位后可升级 activityId 映射。附数据字典 docs/数据字典/13-内容视频流程看板.md 供下游只读接入。
2026-10-08 11:22:46 +08:00
WuYongJie
9996ee83b2 Merge origin/codex/xuxiake-mainline into codex/local-3001-integration (sync before video-flow board push) 2026-10-08 11:22:14 +08:00
WuYongJie
af378a9d00 feat(oa-board): 新品-视频进度(内容部)看板,钉钉内容视频流程独立接入
独立快照表 oa_video_flow_board_snapshot + 双驱动同步脚本 + 分页筛选接口 + 新 Tab;不进新品主链项目聚合/总状态/时效。阶段推进按表单字段推断(编导→摄像→剪辑→封面→验收/评分),节点名清单到位后可升级 activityId 映射。附数据字典 docs/数据字典/13-内容视频流程看板.md 供下游只读接入。
2026-10-08 11:20:48 +08:00
codex
e6241481aa fix(logistics-arrival): 下钻/导出接口接受按字段分组的排除关键词
下钻和导出两个接口的 zod schema 还停留在 string | array,
而前端下发的是新的分组对象 { 字段: [词] },
于是点商品/店铺等对象下钻时 09 明细直接报"下钻条件格式不正确"(导出报"导出筛选条件格式不正确")。

- 两个接口的 excludeKeywords 都接受 对象 / `字段:词,词` 字符串 / 旧平铺数组
- 字符串形态改用 parseExcludeKeywords 解析(老链接按配置归位),不会把字段名当成关键词
- 新增两条路由单测:分组对象下钻/导出、字符串与平铺兼容、非法形态返回 400
2026-10-08 11:16:57 +08:00
WuYongJie
12feabd17f feat(supply-chain): 店铺账号授权提醒推送(正常+未授权店铺推送给运营负责人,离职顺延) 2026-10-08 11:05:56 +08:00
WuYongJie
24e403b183 feat(supply-chain): 店铺账号授权提醒推送(正常+未授权店铺推送给运营负责人,离职顺延) 2026-10-08 11:05:35 +08:00
WuYongJie
2342c53704 feat(supply-chain): 店铺账号授权提醒推送(正常+未授权店铺推送给运营负责人,离职顺延) 2026-10-08 10:59:54 +08:00
居居
246313c45a Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
修正商品SABC打标月末创建时间边界:最后一天 16:00 之后创建的商品不再被整月漏收录。
2026-10-08 09:27:37 +08:00
居居
838dcce410 fix(supply-chain): 修正月末创建时间边界,避免最后一天16点后创建的商品整月漏收录 2026-10-08 09:27:27 +08:00
WuYongJie
2528229c08 fix(supply-chain): 全量档案表宽固定为列宽之和,修复滚动时横向滚动条跳动 2026-10-08 09:27:05 +08:00
WuYongJie
91af9c5284 fix(supply-chain): 全量档案表宽固定为列宽之和,修复滚动时横向滚动条跳动 2026-10-08 09:26:56 +08:00
WuYongJie
f3f3bfa08b fix(supply-chain): 全量档案表宽固定为列宽之和,修复滚动时横向滚动条跳动 2026-10-08 09:26:38 +08:00
WuYongJie
685f3cc756 perf(supply-chain): 产品全量档案表体行虚拟化(视口窗口渲染+占位行) 2026-10-08 08:50:19 +08:00
WuYongJie
17474f0d85 perf(supply-chain): 产品全量档案表体行虚拟化(视口窗口渲染+占位行) 2026-10-08 08:50:10 +08:00
WuYongJie
174b1eec25 perf(supply-chain): 产品全量档案表体行虚拟化(视口窗口渲染+占位行) 2026-10-08 08:49:52 +08:00
WuYongJie
6363ed87b7 style(layout): 系统切换器灰底与下拉面板右边缘对齐左侧栏 2026-10-07 17:58:59 +08:00
WuYongJie
173c594c24 style(supply-chain): 物流到货签收时长看板移除顶部表头区块 2026-10-07 17:58:59 +08:00
WuYongJie
62f693223f style(supply-chain): 移除多处页面标题/说明块与冗余计数文本(工具条保持右对齐) 2026-10-07 17:58:59 +08:00
WuYongJie
bc79cd6321 style(layout): 系统切换器灰底与下拉面板右边缘对齐左侧栏 2026-10-07 17:58:56 +08:00
WuYongJie
48a8de5d77 style(supply-chain): 物流到货签收时长看板移除顶部表头区块 2026-10-07 17:58:56 +08:00
WuYongJie
bd0a5c81f3 style(supply-chain): 移除多处页面标题/说明块与冗余计数文本(工具条保持右对齐) 2026-10-07 17:58:56 +08:00
WuYongJie
5394319205 style(layout): 系统切换器灰底与下拉面板右边缘对齐左侧栏 2026-10-07 17:58:43 +08:00
WuYongJie
5779dcf5be style(supply-chain): 物流到货签收时长看板移除顶部表头区块 2026-10-07 17:58:42 +08:00
WuYongJie
97fffc4f3d style(supply-chain): 移除多处页面标题/说明块与冗余计数文本(工具条保持右对齐) 2026-10-07 17:58:42 +08:00
WuYongJie
5b8e1945af perf(supply-chain): 列设置关闭面板改为非阻塞过渡,修复整表同步重渲染卡顿 2026-10-07 17:33:36 +08:00
WuYongJie
45ba5c1005 perf(supply-chain): 列设置关闭面板改为非阻塞过渡,修复整表同步重渲染卡顿 2026-10-07 17:33:29 +08:00
WuYongJie
70fb47f529 perf(supply-chain): 列设置关闭面板改为非阻塞过渡,修复整表同步重渲染卡顿 2026-10-07 17:33:15 +08:00
WuYongJie
d5762b3e12 style(supply-chain): 产品全量档案工具栏移除标题与字段统计文案 2026-10-07 17:19:28 +08:00
WuYongJie
e54976df03 perf(supply-chain): 产品全量档案按页聚合+缓存+候选项SQL化(含SWR后台刷新与全选提速) 2026-10-07 17:19:27 +08:00
WuYongJie
f14d796056 style(supply-chain): 产品全量档案工具栏移除标题与字段统计文案 2026-10-07 17:19:03 +08:00
WuYongJie
0a0b925d99 perf(supply-chain): 产品全量档案按页聚合+缓存+候选项SQL化(含SWR后台刷新与全选提速) 2026-10-07 17:18:44 +08:00
WuYongJie
1f8e8bab74 style(supply-chain): 产品全量档案工具栏移除标题与字段统计文案 2026-10-07 17:18:26 +08:00
WuYongJie
1b3498e785 perf(supply-chain): 产品全量档案按页聚合+缓存+候选项SQL化(含SWR后台刷新与全选提速) 2026-10-07 17:18:25 +08:00
codex
ed865a3c2d fix(logistics-arrival): 修复订单明细"每页条数"选了没反应
BoardPagination 的下拉框在 onChange 里同时调了 onPageSizeChange(新值) 和 onPageChange(1)。
服务端分页(09 订单明细未下钻)这两次都会 router.push,而后一次是拿调用方当前的 filters
(pageSize 还是旧值)拼的 URL,把前一次覆盖掉——所以选 100/200 后又被拉回 50 条/页。

- BoardPagination 只通知"每页条数变了",回到第 1 页由调用方负责
- 本地分页(经营对象各维度表、发货仓/物流公司排名)在 setState 里带 page: 1
- 服务端分页继续用 href(1, { ...filters, pageSize })
- OrderDetailsBoard 测试补回归断言:切换每页条数只触发一次跳转
2026-10-07 15:26:12 +08:00
WuYongJie
1aef696ae6 chore(deploy): 生产 heap 上限 768MB 提升到 2048MB(缓解看板等重查询导致的 OOM 重启) 2026-10-07 15:26:11 +08:00
WuYongJie
b9a9d4b426 chore(deploy): 生产 heap 上限 768MB 提升到 2048MB(缓解看板等重查询导致的 OOM 重启) 2026-10-07 15:26:09 +08:00
WuYongJie
4fc857f1bd Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-10-07 15:25:56 +08:00
WuYongJie
c1dbfaa145 chore(deploy): 生产 heap 上限 768MB 提升到 2048MB(缓解看板等重查询导致的 OOM 重启) 2026-10-07 15:25:55 +08:00
codex
debdfb454b fix(logistics-arrival): 修复订单明细"每页条数"选了没反应
BoardPagination 的下拉框在 onChange 里同时调了 onPageSizeChange(新值) 和 onPageChange(1)。
服务端分页(09 订单明细未下钻)这两次都会 router.push,而后一次是拿调用方当前的 filters
(pageSize 还是旧值)拼的 URL,把前一次覆盖掉——所以选 100/200 后又被拉回 50 条/页。

- BoardPagination 只通知"每页条数变了",回到第 1 页由调用方负责
- 本地分页(经营对象各维度表、发货仓/物流公司排名)在 setState 里带 page: 1
- 服务端分页继续用 href(1, { ...filters, pageSize })
- OrderDetailsBoard 测试补回归断言:切换每页条数只触发一次跳转
2026-10-07 14:35:46 +08:00
居居
255130b31f fix(supply-chain): 修正导出 dense 选项的类型声明(xlsx 0.18 类型未含 dense) 2026-10-07 14:28:52 +08:00
居居
5367bc4d27 perf(supply-chain): SABC 导出改 dense 表格+结果缓存+串行生成,降低内存峰值并支持秒级重试 2026-10-07 14:28:52 +08:00
WuYongJie
dfdfe019f2 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-10-07 14:27:58 +08:00
WuYongJie
5f31eca7ac fix(build): 外置 mssql,修复正式构建下 mssql 参数类型校验失败(物流看板等 mssql 功能) 2026-10-07 14:10:08 +08:00
WuYongJie
cb3e973bf6 fix(build): 外置 mssql,修复正式构建下 mssql 参数类型校验失败(物流看板等 mssql 功能) 2026-10-07 14:09:58 +08:00
WuYongJie
855cce7034 fix(build): 外置 mssql,修复正式构建下 mssql 参数类型校验失败(物流看板等 mssql 功能) 2026-10-07 14:09:47 +08:00
居居
ed893bd598 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
修正 SABC 导出 dense 选项类型声明。
2026-10-07 14:08:24 +08:00
居居
3d64e3e332 fix(supply-chain): 修正导出 dense 选项的类型声明(xlsx 0.18 类型未含 dense) 2026-10-07 14:08:23 +08:00
居居
60ea2f923b Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
商品SABC打标导出:dense 表格 + 压缩输出 + 结果缓存 + 串行生成,降低 502/超时概率。
2026-10-07 14:00:21 +08:00
居居
faa60655f7 perf(supply-chain): SABC 导出改 dense 表格+结果缓存+串行生成,降低内存峰值并支持秒级重试 2026-10-07 14:00:13 +08:00
WuYongJie
0498a8f2e6 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-10-07 13:33:45 +08:00
居居
47f446bf62 perf(supply-chain): 导出压缩输出(8月明细 64MB→17MB)并补充导出耗时日志与错误详情 2026-10-07 13:04:59 +08:00
居居
14cb9939ad Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
商品SABC打标导出改为压缩输出(8 月明细 64MB → 17MB),并补充导出耗时日志与失败原因提示。
2026-10-07 12:00:26 +08:00
居居
9d8b75ca4d perf(supply-chain): 导出压缩输出(8月明细 64MB→17MB)并补充导出耗时日志与错误详情 2026-10-07 12:00:19 +08:00
codex
1b49125ffa feat(logistics-arrival): 重置 / 恢复默认 / 清空排除 三个按钮语义拆开
过去三个按钮都只是"改输入、等用户点查询",而且重置只清日期+关键词、另两个只改排除词,
最终看起来结果一样。现在各自一件事,点下去立即查询:

- 重置:签收日期、关键词、排除关键词全部清空 → 没有任何筛选条件
  (空日期仍由服务端兜底成本月 1 号~昨日;新增 dateRangeCleared 标记,
   让输入框保持空白而不是被兜底值回填)
- 恢复默认:签收日期回到本月 1 号~昨日(显式填回输入框)+ 排除关键词恢复系统默认配置
- 清空排除:签收日期回到本月 1 号~昨日 + 排除关键词置空

配套:`logisticsArrivalSigningDurationDefaultRange()` 从过滤器规范化里抽出来共用(单一来源)、
按钮 hover 说明 + 筛选栏底部图例、去掉旧的"填好后点查询"提示、补视图层测试。
2026-10-07 11:46:00 +08:00
codex
787fcb67bb perf(logistics-arrival): 看板冷启动从 7 秒降到 2 秒出头
整区间总览/环节目标原来 SELECT 全列,PG 只能对 900MB 堆表做 Seq Scan
(28 天区间 read=106409 页、6.3s);percentile_cont 又把 14 列宽的输入行
整行送进排序(两个分位数 4.3s,环节分位数叠加到分钟级);CTE 里的
CASE+EXTRACT 还会在每处引用被重复内联。

- 按用途收窄列集合:时间戳 / 时间戳+维度列,并新增覆盖索引
  (snapshotDate, signTime) INCLUDE (payTime, sendTime, fetchTime,
  warehouseName, platformName, shopName, logisticCompanyName) → Index Only Scan
- 分位数按列拆成独立语句、只投影一个浮点列,与计数语句并行下发
- measured / durations 改 AS MATERIALIZED,时长表达式只算一次
- 环节目标(上月平均 × 98%)接入 10 分钟内存缓存

实测(本机快照 648,689 行、冷启动):
28 天 7.3s→2.3s、10 天 4.4s→0.4s、2 天 1.7s→0.3s;
浏览器首屏出数 ~7-10s→2.7s。2 天区间与 JS 现算逐项对账一致。
2026-10-07 11:46:00 +08:00
codex
83d10ec9e7 fix(logistics-arrival): 修复排除关键词弹窗透明与保存报格式错误
- globals.css:在 :root 与 [data-theme='dark'] 补齐 --sc-* 主题别名。
  弹窗通过 createPortal 挂在 document.body 上,位于 .sc-layout 之外,
  之前取不到 --sc-surface/--sc-border 等变量,正式环境里表现为面板透明、
  边框与分隔线丢失(本地开发因为另一分支已临时补过这段而看不出来)。
- settings 接口:接受按字段分组的排除关键词对象(同时兼容历史平铺数组与字符串),
  修复管理员在选择器里新增关键词时报"排除关键词格式不正确"。
- 补充 settings 路由单测:分组对象落库、平铺数组兼容、非法格式返回 400。
2026-10-07 11:46:00 +08:00
WuYongJie
9a2bff07ab Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-10-07 11:39:58 +08:00
WuYongJie
ff722ab12e chore(installation-demo-v2): 合并主线前落盘本地在途 V2 模型(仅 schema.prisma) 2026-10-07 11:39:55 +08:00
codex
7620e4bcb6 feat(logistics-arrival): 重置 / 恢复默认 / 清空排除 三个按钮语义拆开
过去三个按钮都只是"改输入、等用户点查询",而且重置只清日期+关键词、另两个只改排除词,
最终看起来结果一样。现在各自一件事,点下去立即查询:

- 重置:签收日期、关键词、排除关键词全部清空 → 没有任何筛选条件
  (空日期仍由服务端兜底成本月 1 号~昨日;新增 dateRangeCleared 标记,
   让输入框保持空白而不是被兜底值回填)
- 恢复默认:签收日期回到本月 1 号~昨日(显式填回输入框)+ 排除关键词恢复系统默认配置
- 清空排除:签收日期回到本月 1 号~昨日 + 排除关键词置空

配套:`logisticsArrivalSigningDurationDefaultRange()` 从过滤器规范化里抽出来共用(单一来源)、
按钮 hover 说明 + 筛选栏底部图例、去掉旧的"填好后点查询"提示、补视图层测试。
2026-10-07 11:05:35 +08:00
居居
bc10af7b62 feat(supply-chain): 商品SABC打标执行核算改由权限配置节点控制 2026-10-07 10:58:34 +08:00
居居
9f63e51702 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
商品SABC打标「执行核算」改由权限配置节点(商品SABC打标 · 执行核算)控制,前端按钮与接口同步;清理未随提交带上的 WorkflowMonitorView 引用。
2026-10-07 10:01:28 +08:00
居居
e57604ac81 feat(supply-chain): 商品SABC打标执行核算改由权限配置节点控制 2026-10-07 10:01:19 +08:00
WuYongJie
c981ff3bb0 以图搜图粘贴与放大体验修复;部署脚本补齐 onnxruntime 原生库
- ProductArchiveImageSearchDialog:弹窗打开期间监听 document 级 paste,页面任意位置 Ctrl+V 直接粘贴图片(点击上传区仍是文件选择);搜索结果小图可点击,复用 ProductArchiveImagePreview 放大核对
- yydd-prod-release-guards.sh:prepare_standalone_runtime 补齐 Turbopack standalone 产物遗漏的 onnxruntime-node 原生二进制(libonnxruntime.so.*),根治部署后本地图索层加载失败;符号链接场景自动跳过
2026-10-07 09:57:43 +08:00
WuYongJie
8370ef2ddc 以图搜图粘贴与放大体验修复;部署脚本补齐 onnxruntime 原生库
- ProductArchiveImageSearchDialog:弹窗打开期间监听 document 级 paste,页面任意位置 Ctrl+V 直接粘贴图片(点击上传区仍是文件选择);搜索结果小图可点击,复用 ProductArchiveImagePreview 放大核对
- yydd-prod-release-guards.sh:prepare_standalone_runtime 补齐 Turbopack standalone 产物遗漏的 onnxruntime-node 原生二进制(libonnxruntime.so.*),根治部署后本地图索层加载失败;符号链接场景自动跳过
2026-10-07 09:57:23 +08:00
WuYongJie
a308eb6c81 以图搜图粘贴与放大体验修复;部署脚本补齐 onnxruntime 原生库
- ProductArchiveImageSearchDialog:弹窗打开期间监听 document 级 paste,页面任意位置 Ctrl+V 直接粘贴图片(点击上传区仍是文件选择);搜索结果小图可点击,复用 ProductArchiveImagePreview 放大核对
- yydd-prod-release-guards.sh:prepare_standalone_runtime 补齐 Turbopack standalone 产物遗漏的 onnxruntime-node 原生二进制(libonnxruntime.so.*),根治部署后本地图索层加载失败;符号链接场景自动跳过
2026-10-07 09:57:08 +08:00
WuYongJie
dbb2d4965a fix(layout): 浏览器标签页标题统一为工作区名,不再随子页面跳变
供应链中心 layout 新增统一标题「由壹点「供应链中心」」并删除其下 25 个页面的页级 metadata title;产品开发侧产品开发日志、BOM核算、AI出图工作台、安装演示工作台(V1)、通知记录、更新日志、回收站 8 处删除旧标题覆盖,统一显示「由壹点「产品开发」」。纯 metadata 变更,无 UI 与逻辑改动。3001 逐页 SSR title 实测通过。
2026-10-07 09:51:18 +08:00
codex
eac3344e2d perf(logistics-arrival): 看板冷启动从 7 秒降到 2 秒出头
整区间总览/环节目标原来 SELECT 全列,PG 只能对 900MB 堆表做 Seq Scan
(28 天区间 read=106409 页、6.3s);percentile_cont 又把 14 列宽的输入行
整行送进排序(两个分位数 4.3s,环节分位数叠加到分钟级);CTE 里的
CASE+EXTRACT 还会在每处引用被重复内联。

- 按用途收窄列集合:时间戳 / 时间戳+维度列,并新增覆盖索引
  (snapshotDate, signTime) INCLUDE (payTime, sendTime, fetchTime,
  warehouseName, platformName, shopName, logisticCompanyName) → Index Only Scan
- 分位数按列拆成独立语句、只投影一个浮点列,与计数语句并行下发
- measured / durations 改 AS MATERIALIZED,时长表达式只算一次
- 环节目标(上月平均 × 98%)接入 10 分钟内存缓存

实测(本机快照 648,689 行、冷启动):
28 天 7.3s→2.3s、10 天 4.4s→0.4s、2 天 1.7s→0.3s;
浏览器首屏出数 ~7-10s→2.7s。2 天区间与 JS 现算逐项对账一致。
2026-10-07 09:45:35 +08:00
WuYongJie
1da917bb01 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-10-07 09:40:10 +08:00
WuYongJie
08bb5a96a4 fix(layout): 浏览器标签页标题统一为工作区名,不再随子页面跳变
供应链中心 layout 新增统一标题「由壹点「供应链中心」」并删除其下 25 个页面的页级 metadata title;产品开发侧产品开发日志、BOM核算、AI出图工作台、安装演示工作台(V1)、通知记录、更新日志、回收站 8 处删除旧标题覆盖,统一显示「由壹点「产品开发」」。纯 metadata 变更,无 UI 与逻辑改动。3001 逐页 SSR title 实测通过。
2026-10-07 09:37:21 +08:00
WuYongJie
b0a6cdd1e7 feat(installation-demo): 工作台顶栏改为目录/制作/分享并新增工具抽屉与制作流程卡
- 标签行:模型→目录、详情→制作;工具从跳转链接改为面板内抽屉,勾选即保存,完整工具箱保留次级入口
2026-10-07 08:50:28 +08:00
WuYongJie
69618965c7 feat(installation-demo): 工作台顶栏改为目录/制作/分享并新增工具抽屉与制作流程卡
- 标签行:模型→目录、详情→制作;工具从跳转链接改为面板内抽屉,勾选即保存,完整工具箱保留次级入口
2026-10-06 18:21:35 +08:00
WuYongJie
f6e4d7f2e1 feat(installation-demo): 工作台顶栏改为目录/制作/分享并新增工具抽屉与制作流程卡
- 标签行:模型→目录、详情→制作;工具从跳转链接改为面板内抽屉,勾选即保存,完整工具箱保留次级入口
2026-10-06 18:20:36 +08:00
WuYongJie
614e3dabe2 feat(supply-chain): 产品全量档案「产品标签」接入 SABC 打标与未打标兜底 2026-10-06 18:19:40 +08:00
WuYongJie
ce2a6ebbaf feat(supply-chain): 产品全量档案「产品标签」接入 SABC 打标与未打标兜底 2026-10-06 18:19:34 +08:00
WuYongJie
b1f55faebb feat(supply-chain): 产品全量档案「产品标签」接入 SABC 打标与未打标兜底 2026-10-06 18:19:10 +08:00
codex
c5cae01ecd fix(logistics-arrival): 修复排除关键词弹窗透明与保存报格式错误
- globals.css:在 :root 与 [data-theme='dark'] 补齐 --sc-* 主题别名。
  弹窗通过 createPortal 挂在 document.body 上,位于 .sc-layout 之外,
  之前取不到 --sc-surface/--sc-border 等变量,正式环境里表现为面板透明、
  边框与分隔线丢失(本地开发因为另一分支已临时补过这段而看不出来)。
- settings 接口:接受按字段分组的排除关键词对象(同时兼容历史平铺数组与字符串),
  修复管理员在选择器里新增关键词时报"排除关键词格式不正确"。
- 补充 settings 路由单测:分组对象落库、平铺数组兼容、非法格式返回 400。
2026-10-06 18:15:57 +08:00
WuYongJie
7e6cc35199 fix(intake): 搜索下拉支持清空已选值 2026-10-06 17:56:20 +08:00
WuYongJie
57b2b405cf fix(intake): 商品企划候选人排除供应商账号 2026-10-06 17:56:20 +08:00
WuYongJie
e1b8989621 fix(intake): 搜索下拉支持清空已选值 2026-10-06 17:56:07 +08:00
WuYongJie
f5f20a8b18 fix(intake): 商品企划候选人排除供应商账号 2026-10-06 17:56:06 +08:00
WuYongJie
a110714305 fix(intake): 搜索下拉支持清空已选值 2026-10-06 17:55:36 +08:00
WuYongJie
f430b3e18d fix(intake): 商品企划候选人排除供应商账号 2026-10-06 17:55:24 +08:00
居居
5cd69fb9db Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline 2026-10-06 17:24:22 +08:00
WuYongJie
a4e61b44a1 Merge origin/codex/xuxiake-mainline (SABC 模块等 74 个提交) into codex/local-3001-integration 2026-10-06 17:21:50 +08:00
居居
2be45f7a74 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline 2026-10-06 17:02:56 +08:00
居居
14a5e1c5f7 feat(supply-chain): 人员信息同步支持自动标记离职(方案B:抓取比对+钉钉确认) 2026-10-06 16:53:13 +08:00
WuYongJie
6d5a8a65d3 fix(intake): 商品企划候选人改为全部内部人员(商品企划部解散后搜不到人) 2026-10-06 16:29:59 +08:00
WuYongJie
e95a2365f5 fix(intake): 商品企划候选人改为全部内部人员(商品企划部解散后搜不到人) 2026-10-06 16:29:36 +08:00
WuYongJie
294d6d3dfa fix(intake): 商品企划候选人改为全部内部人员(商品企划部解散后搜不到人) 2026-10-06 16:29:20 +08:00
居居
cda264dd30 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
商品SABC打标:月度核算生成后冻结、各部门数据分类子页面(按利润中心分组 + 部门数据权限 +
可配置品牌范围/兜底/目标 + 月度快照)、导出明细与经营总览、同款商品编码匹配修复;
并移除未随提交带上的 WorkflowMonitorView 引用、修正行快照类型。
2026-10-06 16:12:42 +08:00
居居
cfe8cf950c Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
商品SABC打标:月度核算生成后冻结、各部门数据分类子页面(按利润中心分组 + 部门数据权限 +
可配置品牌范围/兜底/目标 + 月度快照)、导出明细与经营总览、同款商品编码匹配修复;
并移除未随提交带上的 WorkflowMonitorView 引用、修正行快照类型。
2026-10-06 16:07:22 +08:00
居居
3ef36c7648 fix(supply-chain): 移除未随提交带上的 WorkflowMonitorView 引用并修正 SABC 行快照类型 2026-10-06 16:05:54 +08:00
codex
83c10719ca feat(logistics-arrival): 物流到货签收时长看板
- 看板:经营总览(平均到货时长 + 本月目标 + 目标达成率、异常单点击下钻)、
  履约环节拆解(平均/中位/P90/目标/目标达成率,分栏可拖动)、发货→签收时效分布
- 经营对象问题排名:签收日期→平台→店铺→发货仓→物流→采购→供应商→商品→订单明细
  逐级联动 + 独立翻页;经营对象带「目标 = 该对象上月平均 × 98%」与目标达成率
- 09 订单明细:看板内翻页、高度可拖动、下钻后自动滚动定位
- 排除关键词按字段分组(发货仓/物流公司/订单标签/买家留言/卖家备注),
  选择器支持搜索、全部/已选项、全选/反选/清空、紧凑模式与按字段新增
- 每日订单明细快照:Prisma 模型与迁移、同步脚本、定时器安装脚本;
  命中快照时整区间精确统计,超过规模上限回落均匀样本并如实标注
- 接入侧边菜单、标签页标题与权限映射(businessManagement),补充架构文档
2026-10-06 15:37:48 +08:00
居居
ac572e2c4d Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
商品SABC打标:月度核算生成后冻结(每月3日自动生成上月)、各部门数据分类子页面
(按利润中心分组 + 部门数据权限 + 可配置品牌范围/兜底/目标)、导出明细与经营总览、
同款商品编码匹配修复等。
2026-10-06 15:37:48 +08:00
codex
77eff1744a feat(employee-hours): 工时记录列表支持按列排序
- 查询接口与管理端筛选条件新增 sortBy / sortDirection,服务端校验取值合法性
- 仓储层按列名映射排序字段,approvedScore / attachment 使用 nulls last,并保持 id 升序兜底
- 列表列头改为可点击排序(显示升序/降序箭头与 aria-sort),保留原有列宽拖拽
- 补齐仓储、服务、视图层单测(排序参数校验与列头交互)
2026-10-06 15:37:47 +08:00
codex
05007d3ae2 feat(logistics-arrival): 物流到货签收时长看板
- 看板:经营总览(平均到货时长 + 本月目标 + 目标达成率、异常单点击下钻)、
  履约环节拆解(平均/中位/P90/目标/目标达成率,分栏可拖动)、发货→签收时效分布
- 经营对象问题排名:签收日期→平台→店铺→发货仓→物流→采购→供应商→商品→订单明细
  逐级联动 + 独立翻页;经营对象带「目标 = 该对象上月平均 × 98%」与目标达成率
- 09 订单明细:看板内翻页、高度可拖动、下钻后自动滚动定位
- 排除关键词按字段分组(发货仓/物流公司/订单标签/买家留言/卖家备注),
  选择器支持搜索、全部/已选项、全选/反选/清空、紧凑模式与按字段新增
- 每日订单明细快照:Prisma 模型与迁移、同步脚本、定时器安装脚本;
  命中快照时整区间精确统计,超过规模上限回落均匀样本并如实标注
- 接入侧边菜单、标签页标题与权限映射(businessManagement),补充架构文档
2026-09-30 18:18:51 +08:00
居居
a771a9a5dc Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
商品SABC打标:月度核算生成后冻结(每月3日自动生成上月)、各部门数据分类子页面
(按利润中心分组 + 部门数据权限 + 可配置品牌范围/兜底/目标)、导出明细与经营总览、
同款商品编码匹配修复等。
2026-09-30 18:15:07 +08:00
居居
3f1de4e431 style(supply-chain): 导出下拉框与查询/重置按钮对齐 2026-09-30 18:14:09 +08:00
居居
155c93a0c5 feat(supply-chain): 产品中心/采购中心卡片新增导出按钮 2026-09-30 18:11:09 +08:00
居居
471b5b373e perf(supply-chain): 各部门数据分类改为核算时落库月度快照,页面直接读结果 2026-09-30 18:08:01 +08:00
codex
df206dbfe3 feat(employee-hours): 工时记录列表支持按列排序
- 查询接口与管理端筛选条件新增 sortBy / sortDirection,服务端校验取值合法性
- 仓储层按列名映射排序字段,approvedScore / attachment 使用 nulls last,并保持 id 升序兜底
- 列表列头改为可点击排序(显示升序/降序箭头与 aria-sort),保留原有列宽拖拽
- 补齐仓储、服务、视图层单测(排序参数校验与列头交互)
2026-09-30 18:04:41 +08:00
居居
dc0a9e740a style(supply-chain): 各部门数据分类按 A/B/C/G 优先排序 2026-09-30 17:56:43 +08:00
居居
398bc73450 feat(supply-chain): 产品中心/采购中心剔除山东项目组,导出补合计行 2026-09-30 17:51:18 +08:00
居居
4369e2f050 perf(supply-chain): 各部门数据分类增加结果/快照/花名册缓存,加载从秒级降到毫秒级 2026-09-30 17:11:52 +08:00
居居
e61772c2ce feat(supply-chain): 导出菜单去掉列表项,明细=仅商品表,明细(含经营总览)=商品表+汇总 2026-09-30 15:51:36 +08:00
居居
5bc45f4e93 fix(supply-chain): 品牌兜底行的部门负责人/总负责人改按人员信息逻辑取 2026-09-30 15:41:07 +08:00
居居
4c4807c72e feat(supply-chain): SABC 导出改为导出列表/导出明细/导出经营总览三档菜单 2026-09-30 15:39:14 +08:00
居居
baa2017106 feat(supply-chain): 山东专用/分销专用品牌运营负责人兜底(刘景刚/宋兵乙) 2026-09-30 15:34:17 +08:00
居居
38120d3f73 feat(supply-chain): 其他(未分配)分组核实花名册离职状态并置底展示 2026-09-30 14:37:55 +08:00
居居
9d2633420d style(supply-chain): 产品中心/采购中心改为与运营中心一致的卡片与合计行 2026-09-30 14:25:33 +08:00
居居
de6d9014b3 feat(supply-chain): 各部门数据分类产品中心/采购中心改为完整指标列,去掉重复的未匹配清单 2026-09-30 14:22:09 +08:00
居居
0a2a9c2f89 feat(supply-chain): 各部门数据分类筛选占位/单中心导出/部门回落利润中心/离职人员分组/运营中心E并入G 2026-09-30 14:10:38 +08:00
居居
6a36cae707 style(supply-chain): 各部门数据分类业绩按万元展示 2026-09-30 13:54:03 +08:00
居居
d47d550409 fix(supply-chain): 各部门数据分类配置改为弹窗,点击配置立即可见 2026-09-30 13:53:28 +08:00
居居
303694784c feat(supply-chain): SABC 导出新增「明细」/「明细(含经营总览)」两种模式 2026-09-30 13:49:38 +08:00
居居
da46e84526 feat(supply-chain): 新增「各部门数据分类」子页面(按利润中心分组 + 部门数据权限 + 可配置品牌范围) 2026-09-30 13:02:00 +08:00
居居
6eebb56ba0 feat(supply-chain): SABC 每月3日生成上月并冻结,执行核算仅管理员 2026-09-30 09:51:41 +08:00
居居
ad08dccf1c Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
商品SABC打标全部源码:页面/筛选/经营总览、服务端核算规则、同款商品编码匹配、
每日同步(聚水潭档案/销售日表/替换规则/类目档位/产品类型回填)、API 与导出、测试用例。
2026-09-29 18:04:53 +08:00
居居
6da6b0df76 Merge branch 'codex/xuxiake-sabc-sync-20260929' into codex/xuxiake-mainline
商品SABC打标全部源码:页面/筛选/经营总览、服务端核算规则、同款商品编码匹配、
每日同步(聚水潭档案/销售日表/替换规则/类目档位/产品类型回填)、API 与导出、测试用例。
2026-09-29 17:59:16 +08:00
WuYongJie
b9dc0bddb1 产品全量档案以图搜图改用壹点图搜接口并增加本地向量双层检索
- A 层:调壹点图搜上游(ownProductMatches 我司 SKU 命中),上游调用收口为共享 client,供应商推荐搜索市场同款同步复用
- B 层:本地 DINOv2 向量索引覆盖全量档案图(宽松排序、同图哈希置顶、模型未就绪自动降级)
- 上游编码与档案条码/SKU 大小写不敏感匹配,两路结果按条码去重
- 弹窗分「我司 SKU 命中」「全量档案相似」两区展示,本地索引通过 scripts/index-product-archive-images.ts 增量维护
2026-09-29 17:55:30 +08:00
WuYongJie
4008bcf061 产品全量档案以图搜图改用壹点图搜接口并增加本地向量双层检索
- A 层:调壹点图搜上游(ownProductMatches 我司 SKU 命中),上游调用收口为共享 client,供应商推荐搜索市场同款同步复用
- B 层:本地 DINOv2 向量索引覆盖全量档案图(宽松排序、同图哈希置顶、模型未就绪自动降级)
- 上游编码与档案条码/SKU 大小写不敏感匹配,两路结果按条码去重
- 弹窗分「我司 SKU 命中」「全量档案相似」两区展示,本地索引通过 scripts/index-product-archive-images.ts 增量维护
2026-09-29 17:54:54 +08:00
居居
39f603c091 fix(supply-chain): 同款编码改为直接正反向匹配,不再顺延替换链到末端 2026-09-29 17:21:24 +08:00
居居
34abc63edc feat(supply-chain): 同款编码正反向匹配并顺延替换链到最终编码 2026-09-29 17:05:49 +08:00
居居
0a8231d93c feat(supply-chain): 总览刷新与趋势支持强制取最新数据(绕过缓存) 2026-09-29 16:49:52 +08:00
居居
7838b82655 feat(supply-chain): 同款编码重复映射取创建时间最新的一条 2026-09-29 16:47:06 +08:00
居居
9f89ccbb7e feat(supply-chain): 不考核原因只保留 ODM,其余为空并同步是否考核 2026-09-29 16:35:12 +08:00
WuYongJie
1dcaa48baf fix(intake): 产品核价工作区接入受控列宽并修复长规格名称挤成多排 2026-09-29 16:19:38 +08:00
WuYongJie
51ff817dde fix(intake): 产品核价工作区接入受控列宽并修复长规格名称挤成多排 2026-09-29 16:18:19 +08:00
居居
791fcf083b style(supply-chain): 总览标签顺序恢复 S/A/B/C/D 2026-09-29 16:16:22 +08:00
居居
4cb5f9192a feat(supply-chain): 筛选栏新增运营部门负责人(含下拉候选) 2026-09-29 16:13:58 +08:00
居居
0f9bade034 feat(supply-chain): 总览维度改为运营部门负责人并支持联动筛选 2026-09-29 16:12:30 +08:00
居居
eedda35a29 fix(supply-chain): 补回被误删的 percent 格式化函数 2026-09-29 16:06:51 +08:00
居居
55d77fd989 style(supply-chain): 总览真实业绩统一按万元展示 2026-09-29 16:05:29 +08:00
居居
c45ec60635 style(supply-chain): 总览标签卡片与对比条按真实业绩降序 2026-09-29 16:04:55 +08:00
居居
efc437f0de feat(supply-chain): 维度汇总表展示全部分组,不再合并其他行 2026-09-29 16:04:23 +08:00
居居
3b1a629854 style(supply-chain): 维度汇总表去掉标签配色,与其他列保持一致 2026-09-29 16:03:08 +08:00
居居
a38aaef5d2 feat(supply-chain): 总览维度模块改为 S/A/B/C/D 数据汇总表 2026-09-29 16:01:06 +08:00
居居
7caa307042 style(supply-chain): 总览卡片保留数量与占比,对比条改为真实业绩(万)+业绩占比 2026-09-29 15:53:44 +08:00
居居
e0bad60b31 fix(supply-chain): 页面支持整体滚动,明细区不再被压扁 2026-09-29 15:48:53 +08:00
居居
8e0fc1d347 perf(supply-chain): 经营总览一次计算+短时缓存,首页刷新提速 2026-09-29 15:47:34 +08:00
居居
574f42c4ed fix(supply-chain): 修复筛选区与总览重叠,总览可点击联动明细 2026-09-29 15:42:45 +08:00
居居
5102516b10 feat(supply-chain): 经营总览新增维度对比与近6个月趋势 2026-09-29 15:37:14 +08:00
居居
189344626e feat(supply-chain): 经营总览改为 SABC 页内可折叠区块,移除独立页面与菜单 2026-09-29 15:35:24 +08:00
居居
fe6389a2a0 feat(supply-chain): 新增 SABC 经营总览(商品数 vs 业绩对比) 2026-09-29 15:31:48 +08:00
居居
9d53362466 style(supply-chain): SABC 筛选框固定单行排列,标签归位到行尾 2026-09-29 15:28:29 +08:00
居居
005cc37a65 style(supply-chain): SABC 筛选框按内容宽度排成一行 2026-09-29 15:26:57 +08:00
居居
2f081dd1f5 style(supply-chain): SABC 筛选改为圆角输入框样式 2026-09-29 15:25:10 +08:00
居居
b0701d8ae4 feat(supply-chain): 筛选候选取消服务端上限 2026-09-29 15:22:11 +08:00
居居
f0af29c62b fix(supply-chain): 筛选候选上限提高到 200,运营负责人等人员全部可选 2026-09-29 15:21:14 +08:00
居居
2a61ed2b34 style(supply-chain): 核算按钮移至核算月份后,查询/重置/导出统一按钮样式 2026-09-29 15:17:06 +08:00
WuYongJie
143dfe019d fix(layout): raise sidebar peek z-index above page content layers 2026-09-29 15:14:27 +08:00
WuYongJie
f5f4e2ec90 fix(layout): raise sidebar peek z-index above page content layers 2026-09-29 15:13:52 +08:00
居居
fefb1319c0 style(supply-chain): SABC 筛选头部与字段网格分离,消除空格 2026-09-29 15:05:35 +08:00
居居
cde8d617ad style(supply-chain): SABC 筛选区统一网格排版 2026-09-29 15:03:55 +08:00
居居
85c69041fe feat(supply-chain): SABC 新增运营负责人/采购/设计师筛选 2026-09-29 15:01:55 +08:00
居居
1762ba8387 feat(supply-chain): SABC 筛选字段支持下拉候选选择 2026-09-29 15:01:04 +08:00
居居
84515e895c feat(supply-chain): SABC 筛选改为人员信息页同款样式(字段网格 + 搜索/重置) 2026-09-29 14:57:58 +08:00
居居
852d42bf3a feat(supply-chain): SABC 筛选改为按字段独立输入框(支持批量、多字段取交集) 2026-09-29 14:53:59 +08:00
居居
8cd0f71ff7 feat(supply-chain): SABC 筛选支持批量查询,筛选区拆分为两行 2026-09-29 14:50:27 +08:00
WuYongJie
8c1c4bd938 feat(barcode-print): PLM 产品开发视图每日同步正式站条码打印脚本 2026-09-29 14:44:04 +08:00
居居
b1c3ba3bda feat(supply-chain): 新老编码两侧均按自身+同款编码合并计算 2026-09-29 14:43:26 +08:00
居居
ea7c5a953c fix(supply-chain): 老编码按自身数据照常打标(与人工表一致) 2026-09-29 14:40:41 +08:00
居居
117a5b2947 feat(supply-chain): 人员字段以正式库档案为准同步 2026-09-29 14:02:09 +08:00
居居
2dbc455b86 feat(supply-chain): 每日任务新增运营总负责人映射步骤 2026-09-29 14:00:19 +08:00
居居
c59647ac9f feat(supply-chain): SABC 页面新增运营/供应商/采购/设计师字段(置于创建时间前) 2026-09-29 13:44:40 +08:00
居居
7b3a7acb12 feat(supply-chain): 是否考核动销按产品类型判定(OEM 考核,其余不考核) 2026-09-29 13:40:36 +08:00
居居
95f5915c6a fix(supply-chain): 同款编码只展示单个编码,自映射规则不重复计算 2026-09-29 13:34:47 +08:00
居居
ff1ea6ce24 feat(supply-chain): 同款商品编码按替换款管理合并业绩与费比 2026-09-29 13:21:45 +08:00
WuYongJie
5eb7d4b1aa fix(layout): cast keydown target to HTMLElement for isContentEditable check 2026-09-29 13:18:15 +08:00
WuYongJie
d40cad8eed fix(layout): cast keydown target to HTMLElement for isContentEditable check 2026-09-29 13:18:06 +08:00
WuYongJie
110de56e7f feat(layout): add collapsible workbench sidebar with hover peek and space shortcut 2026-09-29 13:03:51 +08:00
WuYongJie
d7e348b236 merge origin/codex/xuxiake-mainline (employee-hours sync) 2026-09-29 13:03:09 +08:00
WuYongJie
b2f4242526 feat(layout): add collapsible workbench sidebar with hover peek and space shortcut 2026-09-29 13:03:04 +08:00
apple
4c771e0a82 feat(employee-hours): sync row copy and time entry improvements 2026-09-29 11:49:49 +08:00
apple
f6b8065580 feat(employee-hours): sync row copy and time entry improvements 2026-09-29 11:37:15 +08:00
WuYongJie
db01d9e445 fix(intake): 样包测量/产品限价/首批质检列宽整列联动(受控列宽) 2026-09-29 11:27:18 +08:00
WuYongJie
eb6fa9d672 fix(intake): 样包测量/产品限价/首批质检列宽整列联动(受控列宽) 2026-09-29 11:25:55 +08:00
居居
4c518b054e feat(supply-chain): SABC 不考核原因改用档案产品类型,并纳入每日回填 2026-09-29 11:19:17 +08:00
居居
9ae9423742 feat(supply-chain): 每日同步新增正式库类目档位配置步骤 2026-09-29 10:20:18 +08:00
居居
b976f075ff Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-sabc-sync-20260929
# Conflicts:
#	furniture-3d-system/prisma/schema.prisma
#	furniture-3d-system/src/server/repositories/supply-chain-product-archive-category-scale-row.repository.ts
#	furniture-3d-system/src/server/repositories/supply-chain-product-archive-sales-period.repository.ts
2026-09-29 10:14:29 +08:00
居居
648b1eee8d feat(supply-chain): 商品SABC打标(同步开发线前的本地快照) 2026-09-29 10:10:16 +08:00
WuYongJie
749c28452c Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-09-29 09:44:02 +08:00
WuYongJie
f2dfcf1419 feat(supply-chain): 产品全量档案新增「售后金额」列(after_sale_amount 按日/月/年周期聚合) 2026-09-28 18:13:15 +08:00
WuYongJie
0e85656279 feat(supply-chain): 产品全量档案新增「售后金额」列(after_sale_amount 按日/月/年周期聚合) 2026-09-28 18:12:31 +08:00
WuYongJie
fe99b46eef feat(supply-chain): 产品全量档案新增「售后金额」列(after_sale_amount 按日/月/年周期聚合) 2026-09-28 18:11:43 +08:00
WuYongJie
afbb7b3aad feat(supply-chain): refresh product type in daily archive sync
在每日产品档案同步链路中追加「产品类型」刷新步骤(定厂优先、JST vc_name 兜底),复用既有 timer 与库身份校验,无需单独回填或新增定时任务。
2026-09-28 17:00:23 +08:00
WuYongJie
7384163a2c feat(supply-chain): refresh product type in daily archive sync
在每日产品档案同步链路中追加「产品类型」刷新步骤(定厂优先、JST vc_name 兜底),复用既有 timer 与库身份校验,无需单独回填或新增定时任务。
2026-09-28 17:00:17 +08:00
WuYongJie
c473825b3d feat(supply-chain): refresh product type in daily archive sync
在每日产品档案同步链路中追加「产品类型」刷新步骤(定厂优先、JST vc_name 兜底),复用既有 timer 与库身份校验,无需单独回填或新增定时任务。
2026-09-28 17:00:08 +08:00
WuYongJie
5664958dfb feat(supply-chain): add product type column to product archive
产品全量档案在「商品状态」后新增「产品类型」:定厂「产品生产类型」(IntakeNonIntegratedProduct.payload.factoryRows[].productionType) 优先,rpt.jst_sku.vc_name 兜底,按条码对应,都没有留空;值原样写入。附迁移、筛选与回填脚本。
2026-09-28 16:31:09 +08:00
WuYongJie
199be5d7cb feat(supply-chain): add product type column to product archive
产品全量档案在「商品状态」后新增「产品类型」:定厂「产品生产类型」(IntakeNonIntegratedProduct.payload.factoryRows[].productionType) 优先,rpt.jst_sku.vc_name 兜底,按条码对应,都没有留空;值原样写入。附迁移、筛选与回填脚本。
2026-09-28 16:30:55 +08:00
WuYongJie
51c6c9f20b feat(supply-chain): add product type column to product archive
产品全量档案在「商品状态」后新增「产品类型」:定厂「产品生产类型」(IntakeNonIntegratedProduct.payload.factoryRows[].productionType) 优先,rpt.jst_sku.vc_name 兜底,按条码对应,都没有留空;值原样写入。附迁移、筛选与回填脚本。
2026-09-28 16:30:14 +08:00
WuYongJie
8b76cd57db feat(product-archive): 新增「销售金额」表头字段(取每日快照 sales_amount)
- 列表「数据信息」分组新增「销售金额」,位置固定在「真实业绩」前一列
- 取值来自每日快照表 SupplyChainProductArchiveSalesPerformanceDaily.sales_amount,
  与发货金额/退货金额/真实业绩走同一链路,随页面 日/月/年 周期聚合
- 表头漏斗支持按销售金额筛选(可搜索多选标准弹层),列设置/详情弹窗/资料导出随列配置自动生效,
  字段总数 89 → 90
- 新增一次性 localStorage 列序迁移,把上线前保存过的列设置里的新列挪回「真实业绩」前
- 不改数据库结构、不改每日同步脚本;旧的全周期汇总表不加列、不回填
2026-09-28 16:02:45 +08:00
WuYongJie
3f36d2b51e feat(product-archive): 新增「销售金额」表头字段(取每日快照 sales_amount)
- 列表「数据信息」分组新增「销售金额」,位置固定在「真实业绩」前一列
- 取值来自每日快照表 SupplyChainProductArchiveSalesPerformanceDaily.sales_amount,
  与发货金额/退货金额/真实业绩走同一链路,随页面 日/月/年 周期聚合
- 表头漏斗支持按销售金额筛选(可搜索多选标准弹层),列设置/详情弹窗/资料导出随列配置自动生效,
  字段总数 89 → 90
- 新增一次性 localStorage 列序迁移,把上线前保存过的列设置里的新列挪回「真实业绩」前
- 不改数据库结构、不改每日同步脚本;旧的全周期汇总表不加列、不回填
2026-09-28 16:02:22 +08:00
apple
0fde0bfe4b feat(employee-hours): add resizable columns and category import 2026-09-28 15:39:40 +08:00
codex
61a4d81658 feat(store-account): 新增账号按人员信息带出负责人层级,修复自动填充残留并明文显示密码 2026-09-28 14:58:24 +08:00
codex
2c4949f1cc feat(store-account): 新增账号按人员信息带出负责人层级,修复自动填充残留并明文显示密码 2026-09-28 14:58:13 +08:00
codex
c89e4248f0 feat(store-account): 新增账号按人员信息带出负责人层级,修复自动填充残留并明文显示密码 2026-09-28 14:57:53 +08:00
WuYongJie
2b14e8863e feat(supply-chain): sync product archive designer from js_sku_info_manage
按 barcode=sku_id 从 rpt.js_sku_info_manage 回填产品全量档案 designer:空值补来源值,冲突行按用户口径取辛环;写入 fieldSourceMap=JST_SKU_INFO_MANAGE 并保留来源记录。附正式站 systemd timer 安装脚本(每天 09:00 Asia/Shanghai)。
2026-09-28 14:45:30 +08:00
WuYongJie
97d618a25d feat(supply-chain): sync product archive designer from js_sku_info_manage
按 barcode=sku_id 从 rpt.js_sku_info_manage 回填产品全量档案 designer:空值补来源值,冲突行按用户口径取辛环;写入 fieldSourceMap=JST_SKU_INFO_MANAGE 并保留来源记录。附正式站 systemd timer 安装脚本(每天 09:00 Asia/Shanghai)。
2026-09-28 14:45:11 +08:00
WuYongJie
5f4170919d feat(supply-chain): sync product archive designer from js_sku_info_manage
按 barcode=sku_id 从 rpt.js_sku_info_manage 回填产品全量档案 designer:空值补来源值,冲突行按用户口径取辛环;写入 fieldSourceMap=JST_SKU_INFO_MANAGE 并保留来源记录。附正式站 systemd timer 安装脚本(每天 09:00 Asia/Shanghai)。
2026-09-28 14:44:54 +08:00
WuYongJie
b01a5bb574 fix(supplier-recommended): use upstream image search defaults 2026-09-28 14:22:40 +08:00
WuYongJie
da3a49f03f fix(supplier-recommended): use upstream image search defaults 2026-09-28 14:19:21 +08:00
apple
a2dab2cd49 feat(employee-hours): add resizable columns and category import 2026-09-28 14:15:33 +08:00
WuYongJie
a4e577e376 fix(supplier-recommended): use upstream image search defaults 2026-09-28 14:09:50 +08:00
WuYongJie
4f12fdfcac fix(intake): 分销部纳入运营负责人候选名单并默认归运营
1. intake-owner-options.service.ts: operation 口径在路径含运营中心之外,增加 江苏简尚品牌管理有限公司>分销部,SPU负责人与运营负责人共用该名单,运营中心原口径不变。

2. business-role-inference.ts: 分销部从 EXCLUDED_ROOTS 移出,改为默认归 OPERATIONS,避免每日花名册同步把分销部在职员工打回普通用户。
2026-09-28 12:35:36 +08:00
WuYongJie
4fb7802eb4 fix(intake): 分销部纳入运营负责人候选名单并默认归运营
1. intake-owner-options.service.ts: operation 口径在路径含运营中心之外,增加 江苏简尚品牌管理有限公司>分销部,SPU负责人与运营负责人共用该名单,运营中心原口径不变。

2. business-role-inference.ts: 分销部从 EXCLUDED_ROOTS 移出,改为默认归 OPERATIONS,避免每日花名册同步把分销部在职员工打回普通用户。
2026-09-28 12:35:21 +08:00
WuYongJie
d1aa93e17f fix(intake): 分销部纳入运营负责人候选名单并默认归运营
1. intake-owner-options.service.ts: operation 口径在路径含运营中心之外,增加 江苏简尚品牌管理有限公司>分销部,SPU负责人与运营负责人共用该名单,运营中心原口径不变。

2. business-role-inference.ts: 分销部从 EXCLUDED_ROOTS 移出,改为默认归 OPERATIONS,避免每日花名册同步把分销部在职员工打回普通用户。
2026-09-28 12:35:10 +08:00
apple
6a52c0aa4b feat(employee-hours): make business selector resizable 2026-09-28 09:27:59 +08:00
apple
daa0abbf03 feat(employee-hours): make business selector resizable 2026-09-28 09:00:22 +08:00
WuYongJie
48fcd34608 Revert "fix(deploy): support interactive password authentication"
This reverts commit d2f965611c15838a5b3e3e1f32cf1a5dbe7091bb.
2026-09-27 17:18:29 +08:00
WuYongJie
80f82d8ff0 fix(employee-hours): normalize attachment URLs before save 2026-09-27 17:14:02 +08:00
WuYongJie
9d7023df4a fix(employee-hours): normalize attachment URLs before save 2026-09-27 17:11:37 +08:00
WuYongJie
d2f965611c fix(deploy): support interactive password authentication 2026-09-27 17:10:21 +08:00
WuYongJie
a87a8b50b3 fix(employee-hours): normalize attachment URLs before save 2026-09-27 17:09:52 +08:00
WuYongJie
13e1dcb7b2 feat(barcode-print): 顾家模板改名「顾家(6x4)」并修复默认选中乱码匹配
- 下拉选项「顾家」改为「顾家(6x4)」(模板本身为 60mm×40mm 纸张)
2026-09-27 16:12:52 +08:00
WuYongJie
68b310f646 feat(store-account): add authorization status and header filters 2026-09-27 16:12:35 +08:00
WuYongJie
f9c32930da feat(barcode-print): 顾家模板改名「顾家(6x4)」并修复默认选中乱码匹配
- 下拉选项「顾家」改为「顾家(6x4)」(模板本身为 60mm×40mm 纸张)
2026-09-27 16:09:42 +08:00
WuYongJie
2ba2a37232 feat(store-account): add authorization status and header filters 2026-09-27 16:08:43 +08:00
apple
5a59e727c0 feat(employee-hours): complete statistics and ranking workflow 2026-09-27 15:40:53 +08:00
WuYongJie
52ab8578e5 feat(barcode-print): 顾家模板改名「顾家(6x4)」并修复默认选中乱码匹配
- 下拉选项「顾家」改为「顾家(6x4)」(模板本身为 60mm×40mm 纸张)
2026-09-27 15:32:06 +08:00
WuYongJie
4e52997d64 feat(layout): 侧栏分组归类与首页、操作日志体验统一
1) 首页快捷网址子项去图标改4px圆点(产品开发/供应链双侧栏共用生效)
2) 工作台选择卡片只留图标与名称,去掉描述小字
3) 产品开发侧栏按 新品流程/AI生图/模块化/仓库管理 分组(默认展开可折叠,一级带图标统一14px,二级圆点13px,BOM核算归新品流程),手机端下拉同步
4) /intake/operation-logs 去左侧栏,固定返回链接改为返回上一步按钮(无历史兜底/intake)
2026-09-27 15:20:20 +08:00
WuYongJie
ccf8b6480f feat(layout): 侧栏分组归类与首页、操作日志体验统一
1) 首页快捷网址子项去图标改4px圆点(产品开发/供应链双侧栏共用生效)
2) 工作台选择卡片只留图标与名称,去掉描述小字
3) 产品开发侧栏按 新品流程/AI生图/模块化/仓库管理 分组(默认展开可折叠,一级带图标统一14px,二级圆点13px,BOM核算归新品流程),手机端下拉同步
4) /intake/operation-logs 去左侧栏,固定返回链接改为返回上一步按钮(无历史兜底/intake)
2026-09-27 15:20:03 +08:00
apple
b96dfe9780 feat(employee-hours): complete statistics and ranking workflow 2026-09-27 14:30:00 +08:00
WuYongJie
aee6dc6c8f feat(store-account): add batch delete 2026-09-27 13:44:31 +08:00
WuYongJie
010cd87601 feat(store-account): add batch delete 2026-09-27 13:42:59 +08:00
WuYongJie
5013d709e4 feat(store-account): add batch delete 2026-09-27 13:41:04 +08:00
WuYongJie
ca0e2d884c fix(intake): persist AB package grouping and set counts 2026-09-27 13:14:50 +08:00
WuYongJie
02843032d2 fix(intake): persist AB package grouping and set counts 2026-09-27 13:13:28 +08:00
WuYongJie
90eebbc74d fix(intake): persist AB package grouping and set counts 2026-09-27 13:10:02 +08:00
WuYongJie
f988334005 style(warehouse-config): 表头靠左,筛选图标后置对齐产品全量档案 2026-09-27 12:33:04 +08:00
WuYongJie
e6545b6eae style(warehouse-config): 表头靠左,筛选图标后置对齐产品全量档案 2026-09-27 12:32:48 +08:00
WuYongJie
0cb88569d8 style(warehouse-config): 表头靠左,筛选图标后置对齐产品全量档案 2026-09-27 12:32:45 +08:00
WuYongJie
f9ff4af8c5 refactor(employee-hours): 工时独立业务配置表,与工作记录解耦
- 新增 EmployeeHoursBusinessOption/EmployeeHoursScoreRule 独立配置表与迁移
2026-09-27 11:37:39 +08:00
WuYongJie
3bd5384699 refactor(employee-hours): 工时独立业务配置表,与工作记录解耦
- 新增 EmployeeHoursBusinessOption/EmployeeHoursScoreRule 独立配置表与迁移
2026-09-27 11:36:16 +08:00
WuYongJie
5af8c6c539 refactor(employee-hours): 工时独立业务配置表,与工作记录解耦
- 新增 EmployeeHoursBusinessOption/EmployeeHoursScoreRule 独立配置表与迁移
2026-09-27 11:36:01 +08:00
WuYongJie
6f63f3936e Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-09-27 10:59:34 +08:00
WuYongJie
7fa65e3309 feat(warehouse-config): 导入导出+聚水潭字典候选+采购负责人回填+表头筛选改造
- 导入导出:模板下载/NDJSON流式导入(名称upsert+逐行报错)/ExcelJS导出,均跟随筛选
2026-09-27 10:40:24 +08:00
WuYongJie
997efc1084 feat(warehouse-config): 导入导出+聚水潭字典候选+采购负责人回填+表头筛选改造
- 导入导出:模板下载/NDJSON流式导入(名称upsert+逐行报错)/ExcelJS导出,均跟随筛选
2026-09-27 10:40:01 +08:00
WuYongJie
36493b75f8 feat(warehouse-config): 导入导出+聚水潭字典候选+采购负责人回填+表头筛选改造
- 导入导出:模板下载/NDJSON流式导入(名称upsert+逐行报错)/ExcelJS导出,均跟随筛选
2026-09-27 10:39:45 +08:00
WuYongJie
001a1e919e fix(intake): skip unconfigured jushuitan virtual inventory 2026-09-27 10:22:31 +08:00
WuYongJie
1422c2f5fa fix(intake): skip unconfigured jushuitan virtual inventory 2026-09-27 10:21:02 +08:00
WuYongJie
427448f48e fix(intake): skip unconfigured jushuitan virtual inventory 2026-09-27 10:19:53 +08:00
WuYongJie
218517d5af fix(build): keep upload route constants private 2026-09-27 10:01:21 +08:00
apple
3e31bcf29e feat(supply-chain): add employee hours statistics 2026-09-27 09:22:12 +08:00
apple
2e3a36b25d feat(supply-chain): add employee hours statistics 2026-09-24 19:49:35 +08:00
WuYongJie
842bdd3967 style(category-config): 重置按钮去图标纯文字 2026-09-24 18:04:58 +08:00
WuYongJie
0b7cb58418 style(category-config): 重置按钮去图标纯文字 2026-09-24 18:04:45 +08:00
WuYongJie
e3e572657a feat(category-config): 筛选栏增加重置按钮(清空搜索/分类/规模标签并回第一页) 2026-09-24 18:03:12 +08:00
WuYongJie
ab817eddfa feat(category-config): 筛选栏增加重置按钮(清空搜索/分类/规模标签并回第一页) 2026-09-24 18:02:59 +08:00
WuYongJie
3dcdeb5cf1 docs(数据字典): 类目配置更新为单表合并结构(两表口径+取数方式+消费方) 2026-09-24 17:34:49 +08:00
WuYongJie
0a2e0484b3 fix(category-config): buildRowInput 分类入参改可选(生产构建类型对齐) 2026-09-24 17:27:12 +08:00
WuYongJie
01cf89cd01 fix(category-config): buildRowInput 分类入参改可选(生产构建类型对齐) 2026-09-24 17:26:59 +08:00
WuYongJie
5984179dc9 fix(category-config): 规模行服务入参分类改为可选以匹配路由校验 2026-09-24 17:21:21 +08:00
WuYongJie
f75b05bda1 fix(category-config): 规模行服务入参分类改为可选以匹配路由校验 2026-09-24 17:20:59 +08:00
WuYongJie
9e7f8914f0 fix(category-config): 移除对已删除规模行生成方法的残留调用 2026-09-24 17:13:21 +08:00
WuYongJie
a45c691428 fix(category-config): 移除对已删除规模行生成方法的残留调用 2026-09-24 17:12:27 +08:00
WuYongJie
fff6675c7a refactor(category-config): 类目规模合并为单表展示结构(一级类目/分类并入规模行,移除分类档位模板表) 2026-09-24 17:07:00 +08:00
WuYongJie
9e91f18dfd refactor(category-config): 类目规模合并为单表展示结构(一级类目/分类并入规模行,移除分类档位模板表) 2026-09-24 17:06:41 +08:00
WuYongJie
2af84fd9ce feat(intake): allow manual installation asset ordering 2026-09-24 16:36:24 +08:00
WuYongJie
9808224718 fix(category-config): 规模行导入同步一级类目与分类到类目配置;档位导入表头兼容带单位列名 2026-09-24 16:32:53 +08:00
WuYongJie
cb3efaa50c fix(category-config): 规模行导入同步一级类目与分类到类目配置;档位导入表头兼容带单位列名 2026-09-24 16:31:44 +08:00
WuYongJie
d52fd19c49 fix(category-config): 规模行导入同步一级类目与分类到类目配置(留空保留原值) 2026-09-24 15:47:04 +08:00
WuYongJie
eb6a2e6571 fix(category-config): 规模行导入同步一级类目与分类到类目配置(留空保留原值) 2026-09-24 15:46:30 +08:00
WuYongJie
ebf9e6a6c4 fix(category-config): 规模行导入表头对齐导出格式(含一级类目/分类/带单位列名)
导入、导出、下载模板三处表头统一为导出格式;导入兼容带单位列名(业绩起点(元)/日销起点(件/天)),一级类目与分类列接受但忽略
2026-09-24 15:02:12 +08:00
WuYongJie
d42d500071 fix(category-config): 规模行导入表头对齐导出格式(含一级类目/分类/带单位列名)
导入、导出、下载模板三处表头统一为导出格式;导入兼容带单位列名(业绩起点(元)/日销起点(件/天)),一级类目与分类列接受但忽略
2026-09-24 15:00:27 +08:00
WuYongJie
e8a374e93c feat(category-config): 类目配置增加分类、规模标签与业绩/日销阈值(含导入导出与删除)
- 类目配置单表新增:分类、规模标签、业绩起点/终点、日销起点/终点,操作列含编辑/删除
2026-09-24 14:36:43 +08:00
WuYongJie
fddf877d9e feat(category-config): 类目配置增加分类、规模标签与业绩/日销阈值(含导入导出与删除)
- 类目配置单表新增:分类、规模标签、业绩起点/终点、日销起点/终点,操作列含编辑/删除
2026-09-24 14:35:46 +08:00
WuYongJie
cc7219a24e feat(intake): sync installation assets to drawing system by ID
(cherry picked from commit c066c60494a9103e3d33655cbe09a0b7e03ffb18)
2026-09-24 13:37:30 +08:00
WuYongJie
c066c60494 feat(intake): sync installation assets to drawing system by ID 2026-09-24 13:35:04 +08:00
WuYongJie
9875a28716 feat(product-archive): include intake products in base sync and add 08:20 timer 2026-09-24 11:50:09 +08:00
WuYongJie
97a173eb9c Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-09-24 11:48:37 +08:00
WuYongJie
a8b6d31c5e feat(product-archive): include intake products in base sync and add 08:20 timer 2026-09-24 10:56:21 +08:00
WuYongJie
46d0521105 merge: release DingTalk login resilience 2026-09-24 09:56:24 +08:00
WuYongJie
44be8f58b8 fix(auth): harden DingTalk login network failures 2026-09-24 09:50:38 +08:00
WuYongJie
02a25b1e75 修复:补齐创建人解析服务缺失的 ResolutionCache 类型声明(服务器构建失败根因) 2026-09-23 17:29:38 +08:00
WuYongJie
2d4965dfc7 修复:补齐创建人解析服务缺失的 ResolutionCache 类型声明(服务器构建失败根因) 2026-09-23 17:29:09 +08:00
WuYongJie
272219a4eb 1688代发异常事项:创建人列(纳廷创建人+人员信息在职校验+离职/兜底解析链)与业绩范围切换 2026-09-23 17:20:32 +08:00
WuYongJie
da4be64bfe 1688代发异常事项:创建人列(纳廷创建人+人员信息在职校验+离职/兜底解析链)与业绩范围切换 2026-09-23 17:20:05 +08:00
WuYongJie
c9431b7e26 feat(supply-chain): 店铺状态落库(StoreAccount.shopState 列 + 每日同步任务) 2026-09-23 14:05:25 +08:00
WuYongJie
a93159dddc feat(supply-chain): 店铺状态落库(StoreAccount.shopState 列 + 每日同步任务) 2026-09-23 14:03:13 +08:00
WuYongJie
1b293985d6 1688代发异常事项:上传图纸改为页内直传图纸系统(条码分组绑定V01/V02)并接入每日图纸对账 2026-09-23 13:16:56 +08:00
WuYongJie
291485dbda 1688代发异常事项:上传图纸改为页内直传图纸系统(条码分组绑定V01/V02)并接入每日图纸对账 2026-09-23 13:16:08 +08:00
WuYongJie
aff07c4375 feat(intake): support batch lead time with row overrides 2026-09-23 13:15:43 +08:00
WuYongJie
84ac3ed6a4 feat(intake): support batch lead time with row overrides 2026-09-23 13:14:21 +08:00
WuYongJie
791f6bfa40 feat(supply-chain): 店铺账号管理新增店铺状态列与筛选(实时取公司报表库 rpt.fys_shopInfo) 2026-09-23 12:45:04 +08:00
WuYongJie
eabc26a044 feat(supply-chain): 店铺账号管理新增店铺状态列与筛选(实时取公司报表库 rpt.fys_shopInfo) 2026-09-23 12:40:33 +08:00
WuYongJie
4b800d0b3b Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-main-merge
# Conflicts:
#	furniture-3d-system/docs/workflow/生产部署入口说明.md
#	furniture-3d-system/scripts/install-dingtalk-directory-sync-timer.sh
#	furniture-3d-system/src/server/services/download-center/download-center.service.ts
2026-09-23 10:48:04 +08:00
WuYongJie
3d22d627fb fix(auth): separate DingTalk login credentials from API credentials 2026-09-23 10:43:16 +08:00
WuYongJie
fa17ffa9a8 fix(download-center): 生成中删除任务时丢弃产物,并忽略本地运行目录 2026-09-23 09:48:21 +08:00
WuYongJie
159035f98a fix(download-center): correct error code enum and failAllActive return shape 2026-09-22 17:40:30 +08:00
WuYongJie
8a95dbcbf0 fix(download-center): correct error code enum and failAllActive return shape 2026-09-22 17:40:21 +08:00
WuYongJie
71a7f0a09a feat(supply-chain): global download center with background export tasks 2026-09-22 17:32:53 +08:00
WuYongJie
9bfc3e826f feat(product-archive): factory stock column and ops-owner hierarchy rework 2026-09-22 17:32:53 +08:00
WuYongJie
89ed5ee355 chore(sync): schedule product archive source sync daily at 08:10 2026-09-22 17:32:53 +08:00
WuYongJie
24c668e1c8 feat(supply-chain): global download center with background export tasks 2026-09-22 17:32:32 +08:00
WuYongJie
870632bd5e feat(product-archive): factory stock column and ops-owner hierarchy rework 2026-09-22 17:32:23 +08:00
WuYongJie
152df22a1a chore(sync): schedule product archive source sync daily at 08:10 2026-09-22 17:32:10 +08:00
WuYongJie
6e35bb42ee chore(product-archive): 补提交发货方式历史数据回填脚本 2026-09-22 15:53:29 +08:00
WuYongJie
6f55f490f6 fix(supply-chain): complete personnel information source sync
(content-sync from origin/codex/xuxiake-mainline 0f52aca9)
2026-09-22 12:48:12 +08:00
居居
0f52aca923 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-09-22 12:01:43 +08:00
居居
c0cc39a565 fix(supply-chain): complete personnel information source sync 2026-09-22 11:59:28 +08:00
WuYongJie
41030ec7f2 feat(oa-board): 流程状态筛选新增「全部」口径
流程状态下拉新增「全部」:overallStatus=__all__ 时服务端与客户端过滤均跳过状态限制,包含已驳回、已终止、已取消及无状态标记数据;默认口径(进行中+已完成)不变,导出明细同口径生效。
2026-09-22 11:29:19 +08:00
WuYongJie
687e80b6cd feat(oa-board): 流程状态筛选新增「全部」口径
流程状态下拉新增「全部」:overallStatus=__all__ 时服务端与客户端过滤均跳过状态限制,包含已驳回、已终止、已取消及无状态标记数据;默认口径(进行中+已完成)不变,导出明细同口径生效。
2026-09-22 11:24:16 +08:00
居居
5512be5371 fix(supply-chain): 固化人员信息界面配置 2026-09-22 11:08:10 +08:00
居居
7b5f66dfcf fix(supply-chain): 固化人员信息界面配置 2026-09-22 10:33:18 +08:00
Codex
271c7b24b4 feat(supplier-recommended): add image similarity search 2026-09-22 10:20:05 +08:00
Codex
f94286464b feat(supplier-recommended): add image similarity search 2026-09-22 10:17:35 +08:00
WuYongJie
e3dcc2463a Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into HEAD 2026-09-21 21:27:48 +08:00
WuYongJie
4b54f3e531 fix(sync): guard against null skuoid rows in dws ops owners listing 2026-09-21 21:27:19 +08:00
WuYongJie
669fbd63fd Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into HEAD 2026-09-21 20:58:35 +08:00
WuYongJie
191e050374 fix(sync): drop server-only marker imports from the archive sync dependency chain 2026-09-21 20:58:07 +08:00
WuYongJie
9e0081724e Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into HEAD 2026-09-21 20:35:51 +08:00
WuYongJie
fd3b451af9 chore(sync): log full stack on enrichment failures 2026-09-21 20:34:25 +08:00
WuYongJie
616f985dee Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into HEAD 2026-09-21 19:28:31 +08:00
WuYongJie
16587f62af fix(sync): alias server-only to an empty stub for plain-node tooling runs 2026-09-21 19:28:06 +08:00
WuYongJie
f57fd6d354 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into HEAD 2026-09-21 19:15:48 +08:00
WuYongJie
407946d5d2 chore(sync): record server-only and uuid in the deploy lock file 2026-09-21 19:15:24 +08:00
WuYongJie
37d09f0ca1 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into HEAD 2026-09-21 19:11:37 +08:00
WuYongJie
3269f4409f fix(sync): declare server-only and uuid deps and drop the marker import so node-side scripts can run 2026-09-21 19:11:12 +08:00
WuYongJie
65b32ce405 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into HEAD 2026-09-21 17:36:54 +08:00
WuYongJie
c4943e6b04 fix(product-archive): include stat_date in ops owners group by to unblock daily sync 2026-09-21 17:35:36 +08:00
居居
475478b273 feat(supply-chain): 新增人员信息与钉钉组织同步 2026-09-21 16:38:13 +08:00
WuYongJie
ec807b3217 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration
# Conflicts:
#	furniture-3d-system/prisma/schema.prisma
#	furniture-3d-system/src/app/api/supply-chain/new-products/dropship-issues/route.test.ts
#	furniture-3d-system/src/app/api/supply-chain/new-products/dropship-issues/route.ts
#	furniture-3d-system/src/app/api/supply-chain/new-products/dropship-issues/update/route.test.ts
#	furniture-3d-system/src/app/api/supply-chain/new-products/dropship-issues/update/route.ts
#	furniture-3d-system/src/app/supply-chain/config/supply-chain-menu.test.ts
#	furniture-3d-system/src/app/supply-chain/new-products/dropship-issues/DropshipIssueEditDialog.tsx
#	furniture-3d-system/src/app/supply-chain/new-products/dropship-issues/DropshipIssuesPagination.tsx
#	furniture-3d-system/src/app/supply-chain/new-products/dropship-issues/DropshipIssuesTable.tsx
#	furniture-3d-system/src/app/supply-chain/new-products/dropship-issues/DropshipOpsFilter.tsx
#	furniture-3d-system/src/app/supply-chain/new-products/dropship-issues/DropshipSkuPanel.tsx
#	furniture-3d-system/src/app/supply-chain/new-products/dropship-issues/dropship-issues.module.css
#	furniture-3d-system/src/app/supply-chain/new-products/dropship-issues/page.tsx
#	furniture-3d-system/src/app/supply-chain/new-products/dropship-issues/types.ts
#	furniture-3d-system/src/lib/supply-chain-permission-settings.test.ts
#	furniture-3d-system/src/lib/supply-chain-permission-settings.ts
#	furniture-3d-system/src/server/repositories/supply-chain-dropship-issue.repository.ts
#	furniture-3d-system/src/server/services/__tests__/supply-chain-dropship-issue.service.test.ts
#	furniture-3d-system/src/server/services/supply-chain-dropship-issue.service.ts
2026-09-21 15:58:02 +08:00
居居
e9cb61e18c Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-09-21 14:25:22 +08:00
居居
1cf72e3c81 feat(supply-chain): 新增人员信息与钉钉组织同步 2026-09-21 14:25:05 +08:00
WuYongJie
4df5e800fe fix(1688): show zero for missing cost values
(cherry picked from commit ce48ab7944bed1a81c29eb36145bcd9d76bf01f8)
2026-09-21 11:50:13 +08:00
WuYongJie
ce48ab7944 fix(1688): show zero for missing cost values 2026-09-21 11:42:05 +08:00
WuYongJie
bb730dd533 安装演示工作台 5 项 bug 根因修复接手
(cherry picked from commit 073437c8bfe809f3992bb1a5288aa2c57f3c3567)
2026-09-21 11:33:53 +08:00
WuYongJie
073437c8bf 安装演示工作台 5 项 bug 根因修复接手 2026-09-21 11:02:32 +08:00
WuYongJie
b92d56c090 feat(intake): extend barcode import permissions
(cherry picked from commit d4778d29e9f618745f0777335336dd8986614d9f)
2026-09-21 11:01:48 +08:00
WuYongJie
d4778d29e9 feat(intake): extend barcode import permissions 2026-09-21 10:54:14 +08:00
WuYongJie
ba5eeaea33 feat(supply-chain): add estimated freight to 1688 dropship data
Task: supply-chain-1688-freight-inclusive-unit-cost-20260918
(cherry picked from commit 4450ac5ce610a45c5c06f7cced6bfa7a2f4af82a)
2026-09-21 10:00:40 +08:00
WuYongJie
4450ac5ce6 feat(supply-chain): add estimated freight to 1688 dropship data
Task: supply-chain-1688-freight-inclusive-unit-cost-20260918
2026-09-21 09:43:43 +08:00
WuYongJie
87a28cc5f7 fix(supply-chain): restore 1688 data menu order 2026-09-18 15:53:51 +08:00
WuYongJie
7d8f6d3848 fix(supply-chain): restore 1688 data menu order 2026-09-18 15:53:36 +08:00
WuYongJie
76420b8ecd fix(supply-chain): merge rapid 1688 field filters 2026-09-18 15:19:10 +08:00
伯阳
5a6f09b0df feat(supply-chain): complete 1688 dropship sourcing workflow 2026-09-18 15:18:56 +08:00
WuYongJie
009685cdd7 fix(supply-chain): merge rapid 1688 field filters 2026-09-18 15:16:55 +08:00
伯阳
9244b9d570 merge: sync xuxiake mainline for 1688 sourcing 2026-09-18 13:25:12 +08:00
伯阳
a22657e28e feat(supply-chain): complete 1688 dropship sourcing workflow 2026-09-18 11:17:47 +08:00
WuYongJie
446c550320 feat(operation-logs): 失败操作日志实时推送徐霞客
- 失败日志(FAILED/RETRY)写入后经壹点通知中心个人推送徐霞客
- 聚水潭固定虚拟库存错误码按 1 小时折叠成一条,其余按 动作+错误码+SPU+SKU 10 分钟合并
- 正文空行分段(钉钉 Markdown 会吞单个换行),纯文字无按钮
- record 挂载异步通知不阻塞业务,NODE_ENV=test 静默
- 新增带 worker 密钥的手动测试入口 /api/internal/operation-logs/failure-notification/run
2026-09-18 10:10:36 +08:00
WuYongJie
8ea81f886a feat(product-archive): 定厂「发货方式」同步进全量档案的入库/代发列
全量档案的「入库/代发」列(storageType)此前 73,278 行 100% 为空:产品定厂时
采购填的「发货方式」(入仓/代发)从来没有回流到档案表,列上没有任何来源映射。

规则(按用户口径):全量的入库/代发只有 代发/入仓 两个值——
- 新品(产品开发)定厂带了发货方式的,直接带进档案
- 没填的,等仓库配置同步兜底填空
- 无法识别的值(如 海运)不写,保持空

实现要点:
- sync.repository:产品开发来源的 SELECT/类型加 shipping_method;
  buildArchiveUpdate 白名单补 storageType(此前该列不在白名单里,值写不进档案表)
- sync.service:宽表 shipping_method 经 storageTypeFromShippingMethodLabel
  映射(入仓/入库→INBOUND、代发→DROPSHIP、无法识别→空串不动),
  仅非空时写入;fieldSourceMap 记 storageType ← shipping_method
- enrichment mapper:仓库配置来源拆成两条——warehouseHead 照常无条件更新;
  storageType 改 fillEmptyOnly(只填空),定厂带回的值不被仓库配置覆盖
- lib/values:新增 storageTypeFromShippingMethodLabel(入仓/入库→INBOUND、
  代发→DROPSHIP,兼容「入库」写法)
- 页面显示「入库」→「入仓」(与定厂填值一致)
- 测试:sync.service 加 3 用例;新增 warehouse-config-fill 套件 3 用例
  (已有值不覆盖 / 为空才填 / 初始化过不再重复应用);
  enrichment.service 计数断言 8→9 并补仓库配置两条的断言

验证:type-check EXIT=0;相关套件 sync 11 / enrichment 9 / warehouse-config-fill 3
全过;真库端到端——跑一次 XG0012 的产品开发同步,档案 storageType 从空变 INBOUND、
来源记 PRODUCT_DEVELOPMENT/shipping_method,再跑仓库配置 enrichment 后仍为 INBOUND
(不被覆盖);3001 真机按「入仓」筛选出 1 行、页面列显示「入仓」。
2026-09-18 10:10:36 +08:00
WuYongJie
f3f59adcbc 供应商新品推荐:页面默认视图改为明细数据 2026-09-18 10:10:36 +08:00
WuYongJie
b0bfd3d65a feat(operation-logs): 失败操作日志实时推送徐霞客
- 失败日志(FAILED/RETRY)写入后经壹点通知中心个人推送徐霞客
- 聚水潭固定虚拟库存错误码按 1 小时折叠成一条,其余按 动作+错误码+SPU+SKU 10 分钟合并
- 正文空行分段(钉钉 Markdown 会吞单个换行),纯文字无按钮
- record 挂载异步通知不阻塞业务,NODE_ENV=test 静默
- 新增带 worker 密钥的手动测试入口 /api/internal/operation-logs/failure-notification/run
2026-09-18 09:46:16 +08:00
WuYongJie
fc12269838 fix(jushuitan): batch fixed virtual inventory pushes 2026-09-18 09:39:26 +08:00
WuYongJie
9647a1f49f feat(operation-logs): 失败操作日志实时推送徐霞客
- 失败日志(FAILED/RETRY)写入后经壹点通知中心个人推送徐霞客
- 聚水潭固定虚拟库存错误码按 1 小时折叠成一条,其余按 动作+错误码+SPU+SKU 10 分钟合并
- 正文空行分段(钉钉 Markdown 会吞单个换行),纯文字无按钮
- record 挂载异步通知不阻塞业务,NODE_ENV=test 静默
- 新增带 worker 密钥的手动测试入口 /api/internal/operation-logs/failure-notification/run
2026-09-18 09:21:41 +08:00
WuYongJie
dc07310cda fix(jushuitan): batch fixed virtual inventory pushes 2026-09-18 09:20:17 +08:00
WuYongJie
e8f8087f78 fix(intake): 保存实时供应商目录映射 2026-09-17 17:28:08 +08:00
WuYongJie
be9f844c5a fix(intake): 保存实时供应商目录映射 2026-09-17 17:25:52 +08:00
WuYongJie
ca6e22ac66 feat(product-archive): 定厂「发货方式」同步进全量档案的入库/代发列
全量档案的「入库/代发」列(storageType)此前 73,278 行 100% 为空:产品定厂时
采购填的「发货方式」(入仓/代发)从来没有回流到档案表,列上没有任何来源映射。

规则(按用户口径):全量的入库/代发只有 代发/入仓 两个值——
- 新品(产品开发)定厂带了发货方式的,直接带进档案
- 没填的,等仓库配置同步兜底填空
- 无法识别的值(如 海运)不写,保持空

实现要点:
- sync.repository:产品开发来源的 SELECT/类型加 shipping_method;
  buildArchiveUpdate 白名单补 storageType(此前该列不在白名单里,值写不进档案表)
- sync.service:宽表 shipping_method 经 storageTypeFromShippingMethodLabel
  映射(入仓/入库→INBOUND、代发→DROPSHIP、无法识别→空串不动),
  仅非空时写入;fieldSourceMap 记 storageType ← shipping_method
- enrichment mapper:仓库配置来源拆成两条——warehouseHead 照常无条件更新;
  storageType 改 fillEmptyOnly(只填空),定厂带回的值不被仓库配置覆盖
- lib/values:新增 storageTypeFromShippingMethodLabel(入仓/入库→INBOUND、
  代发→DROPSHIP,兼容「入库」写法)
- 页面显示「入库」→「入仓」(与定厂填值一致)
- 测试:sync.service 加 3 用例;新增 warehouse-config-fill 套件 3 用例
  (已有值不覆盖 / 为空才填 / 初始化过不再重复应用);
  enrichment.service 计数断言 8→9 并补仓库配置两条的断言

验证:type-check EXIT=0;相关套件 sync 11 / enrichment 9 / warehouse-config-fill 3
全过;真库端到端——跑一次 XG0012 的产品开发同步,档案 storageType 从空变 INBOUND、
来源记 PRODUCT_DEVELOPMENT/shipping_method,再跑仓库配置 enrichment 后仍为 INBOUND
(不被覆盖);3001 真机按「入仓」筛选出 1 行、页面列显示「入仓」。
2026-09-17 16:58:54 +08:00
WuYongJie
8d38f07626 fix(intake): 修复大产品保存事务超时与聚水潭分批推送
- 非集成产品保存事务(formal SPU 主事务 / saveActiveWithLock / createRecord /
  updateRecord)统一加 { maxWait: 10s, timeout: 60s }:修复 224 SKU 大产品保存时
  Prisma 交互式事务超过默认 5s 触发 P2028 整笔回滚、页面提示保存成功但数据未落库
  的问题(正式站 LRSF0095 实际案例,单日 11 次保存失败,供应商数据全部丢失)
- 聚水潭商品资料推送分批:itemsku/upload 与 combinesku/upload 按每批 100 个 items
  推送(单次上限 200,LRSF0095 一次推 224 个被 code=130 拒绝),逐批成功落锁,
  中间批次失败抛 PartialFailureError 保护已成功 SKU
- 新增回归用例:250 SKU 断言 itemsku/upload 分 3 批(100/100/50)且总数无遗漏

验证:type-check 通过;intake-non-integrated-product.service.test.ts 108/108;
jushuitan-product-pricing-push.service.test.ts 33/34(唯一失败为既有环境问题)
2026-09-17 15:42:40 +08:00
WuYongJie
a310362839 fix(intake): 修复大产品保存事务超时与聚水潭分批推送
- 非集成产品保存事务(formal SPU 主事务 / saveActiveWithLock / createRecord /
  updateRecord)统一加 { maxWait: 10s, timeout: 60s }:修复 224 SKU 大产品保存时
  Prisma 交互式事务超过默认 5s 触发 P2028 整笔回滚、页面提示保存成功但数据未落库
  的问题(正式站 LRSF0095 实际案例,单日 11 次保存失败,供应商数据全部丢失)
- 聚水潭商品资料推送分批:itemsku/upload 与 combinesku/upload 按每批 100 个 items
  推送(单次上限 200,LRSF0095 一次推 224 个被 code=130 拒绝),逐批成功落锁,
  中间批次失败抛 PartialFailureError 保护已成功 SKU
- 新增回归用例:250 SKU 断言 itemsku/upload 分 3 批(100/100/50)且总数无遗漏

验证:type-check 通过;intake-non-integrated-product.service.test.ts 108/108;
jushuitan-product-pricing-push.service.test.ts 33/34(唯一失败为既有环境问题)
2026-09-17 15:37:45 +08:00
WuYongJie
14974d20ef fix(intake): 修复大产品保存事务超时与聚水潭分批推送
- 非集成产品保存事务(formal SPU 主事务 / saveActiveWithLock / createRecord /
  updateRecord)统一加 { maxWait: 10s, timeout: 60s }:修复 224 SKU 大产品保存时
  Prisma 交互式事务超过默认 5s 触发 P2028 整笔回滚、页面提示保存成功但数据未落库
  的问题(正式站 LRSF0095 实际案例,单日 11 次保存失败,供应商数据全部丢失)
- 聚水潭商品资料推送分批:itemsku/upload 与 combinesku/upload 按每批 100 个 items
  推送(单次上限 200,LRSF0095 一次推 224 个被 code=130 拒绝),逐批成功落锁,
  中间批次失败抛 PartialFailureError 保护已成功 SKU
- 新增回归用例:250 SKU 断言 itemsku/upload 分 3 批(100/100/50)且总数无遗漏

验证:type-check 通过;intake-non-integrated-product.service.test.ts 108/108;
jushuitan-product-pricing-push.service.test.ts 33/34(唯一失败为既有环境问题)
2026-09-17 15:36:22 +08:00
WuYongJie
26754ae266 供应商新品推荐:页面默认视图改为明细数据 2026-09-17 14:56:22 +08:00
WuYongJie
98482bccec feat(product-archive): 定厂「发货方式」同步进全量档案的入库/代发列
全量档案的「入库/代发」列(storageType)此前 73,278 行 100% 为空:产品定厂时
采购填的「发货方式」(入仓/代发)从来没有回流到档案表,列上没有任何来源映射。

规则(按用户口径):全量的入库/代发只有 代发/入仓 两个值——
- 新品(产品开发)定厂带了发货方式的,直接带进档案
- 没填的,等仓库配置同步兜底填空
- 无法识别的值(如 海运)不写,保持空

实现要点:
- sync.repository:产品开发来源的 SELECT/类型加 shipping_method;
  buildArchiveUpdate 白名单补 storageType(此前该列不在白名单里,值写不进档案表)
- sync.service:宽表 shipping_method 经 storageTypeFromShippingMethodLabel
  映射(入仓/入库→INBOUND、代发→DROPSHIP、无法识别→空串不动),
  仅非空时写入;fieldSourceMap 记 storageType ← shipping_method
- enrichment mapper:仓库配置来源拆成两条——warehouseHead 照常无条件更新;
  storageType 改 fillEmptyOnly(只填空),定厂带回的值不被仓库配置覆盖
- lib/values:新增 storageTypeFromShippingMethodLabel(入仓/入库→INBOUND、
  代发→DROPSHIP,兼容「入库」写法)
- 页面显示「入库」→「入仓」(与定厂填值一致)
- 测试:sync.service 加 3 用例;新增 warehouse-config-fill 套件 3 用例
  (已有值不覆盖 / 为空才填 / 初始化过不再重复应用);
  enrichment.service 计数断言 8→9 并补仓库配置两条的断言

验证:type-check EXIT=0;相关套件 sync 11 / enrichment 9 / warehouse-config-fill 3
全过;真库端到端——跑一次 XG0012 的产品开发同步,档案 storageType 从空变 INBOUND、
来源记 PRODUCT_DEVELOPMENT/shipping_method,再跑仓库配置 enrichment 后仍为 INBOUND
(不被覆盖);3001 真机按「入仓」筛选出 1 行、页面列显示「入仓」。
2026-09-17 14:55:11 +08:00
WuYongJie
ba930d1df2 feat(product-archive): SPU 列支持「只看正常编码」与可累加排除规则
产品全量档案的 SPU 列筛选原来只有「勾选具体值 = 包含」一种语义,没法表达
「不要 DF- 开头的 / 不要纯数字的」这类排除需求,只能靠不断放宽正选来绕。

新增两类能力(原有「勾选值包含」语义一字未改):
- 「只看正常编码」:字母前缀(2~6位) + 可选「-字母段」 + 数字编号(3~8位),
  一次收掉纯数字 / 1643A 型 / 含中文的商品名 / 32 位哈希等非正常形态
- 「排除以…开头」:可累加的前缀黑名单(如 WM- 外贸、DF- 代发、MKH),
  对应「还有不想看就再填一条」

实现要点:
- lib:新增 ProductArchiveTextFilterKey / ProductArchiveExcludeRule 与
  PRODUCT_ARCHIVE_NORMAL_CODE_PATTERN;ProductArchiveFilters 只追加
  normalCodeOnly、excludeStartsWith 两个可选字段,老字段类型与语义不动
- 校验:zod schema 是 .strict(),同步放行两个新字段(否则整包筛选会被 400 拒掉)
- 服务端:buildProductArchiveWhere 保持同步,只追加 NOT startsWith;
  需要查库的「正常编码」放在新增的异步 buildProductArchiveWhereWithTextRules
  (Prisma 7.3 的字符串过滤不支持 regex,改为原生 SQL 取符合形态的取值清单拼 IN,
  带 5 分钟缓存)。preparePeriodArchiveQuery 是全站唯一取数入口,
  列表、候选项、导出、图片导出自动生效
- 文本规则故意不参与 omitFilter:若参与,候选值列表会冒出已被排除的取值,
  用户勾了却得到 0 行
- UI:文本列弹层改左右双排(左=形态与排除规则,右=搜索与候选值),
  弹层高度与横向偏移按触发按钮所在位置动态计算,保证清除/应用在任何视口都可见
- 偏好:两个新字段进入 sanitizeFilters 白名单,规则可持久化,点清除即清空

验证:本地真实库 66033 行——只勾正常编码 27645 行、再接 WM- 26852 行、
再接 DF- 26788 行,与 SQL 基准逐一致;3001 HTTP 对账五个口径全对、
非法列返回 400、候选项接口在勾了正常编码后返回的 SPU 全部符合形态;
真机 UI 12/12(应用 26788、刷新记住、回显、清除回 66033);
布局专项 36/36(1600x900 / 1440x720 / 1366x640 / 1280x600 下弹层均在视口内、
按钮可见、不超左右边界);新增单测 15/15;type-check EXIT=0。
2026-09-17 11:46:18 +08:00
WuYongJie
3cd14c4f0d feat(product-archive): SPU 列支持「只看正常编码」与可累加排除规则
产品全量档案的 SPU 列筛选原来只有「勾选具体值 = 包含」一种语义,没法表达
「不要 DF- 开头的 / 不要纯数字的」这类排除需求,只能靠不断放宽正选来绕。

新增两类能力(原有「勾选值包含」语义一字未改):
- 「只看正常编码」:字母前缀(2~6位) + 可选「-字母段」 + 数字编号(3~8位),
  一次收掉纯数字 / 1643A 型 / 含中文的商品名 / 32 位哈希等非正常形态
- 「排除以…开头」:可累加的前缀黑名单(如 WM- 外贸、DF- 代发、MKH),
  对应「还有不想看就再填一条」

实现要点:
- lib:新增 ProductArchiveTextFilterKey / ProductArchiveExcludeRule 与
  PRODUCT_ARCHIVE_NORMAL_CODE_PATTERN;ProductArchiveFilters 只追加
  normalCodeOnly、excludeStartsWith 两个可选字段,老字段类型与语义不动
- 校验:zod schema 是 .strict(),同步放行两个新字段(否则整包筛选会被 400 拒掉)
- 服务端:buildProductArchiveWhere 保持同步,只追加 NOT startsWith;
  需要查库的「正常编码」放在新增的异步 buildProductArchiveWhereWithTextRules
  (Prisma 7.3 的字符串过滤不支持 regex,改为原生 SQL 取符合形态的取值清单拼 IN,
  带 5 分钟缓存)。preparePeriodArchiveQuery 是全站唯一取数入口,
  列表、候选项、导出、图片导出自动生效
- 文本规则故意不参与 omitFilter:若参与,候选值列表会冒出已被排除的取值,
  用户勾了却得到 0 行
- UI:文本列弹层改左右双排(左=形态与排除规则,右=搜索与候选值),
  弹层高度与横向偏移按触发按钮所在位置动态计算,保证清除/应用在任何视口都可见
- 偏好:两个新字段进入 sanitizeFilters 白名单,规则可持久化,点清除即清空

验证:本地真实库 66033 行——只勾正常编码 27645 行、再接 WM- 26852 行、
再接 DF- 26788 行,与 SQL 基准逐一致;3001 HTTP 对账五个口径全对、
非法列返回 400、候选项接口在勾了正常编码后返回的 SPU 全部符合形态;
真机 UI 12/12(应用 26788、刷新记住、回显、清除回 66033);
布局专项 36/36(1600x900 / 1440x720 / 1366x640 / 1280x600 下弹层均在视口内、
按钮可见、不超左右边界);新增单测 15/15;type-check EXIT=0。
2026-09-17 11:36:12 +08:00
WuYongJie
57e422308b feat(product-archive): SPU 列支持「只看正常编码」与可累加排除规则
产品全量档案的 SPU 列筛选原来只有「勾选具体值 = 包含」一种语义,没法表达
「不要 DF- 开头的 / 不要纯数字的」这类排除需求,只能靠不断放宽正选来绕。

新增两类能力(原有「勾选值包含」语义一字未改):
- 「只看正常编码」:字母前缀(2~6位) + 可选「-字母段」 + 数字编号(3~8位),
  一次收掉纯数字 / 1643A 型 / 含中文的商品名 / 32 位哈希等非正常形态
- 「排除以…开头」:可累加的前缀黑名单(如 WM- 外贸、DF- 代发、MKH),
  对应「还有不想看就再填一条」

实现要点:
- lib:新增 ProductArchiveTextFilterKey / ProductArchiveExcludeRule 与
  PRODUCT_ARCHIVE_NORMAL_CODE_PATTERN;ProductArchiveFilters 只追加
  normalCodeOnly、excludeStartsWith 两个可选字段,老字段类型与语义不动
- 校验:zod schema 是 .strict(),同步放行两个新字段(否则整包筛选会被 400 拒掉)
- 服务端:buildProductArchiveWhere 保持同步,只追加 NOT startsWith;
  需要查库的「正常编码」放在新增的异步 buildProductArchiveWhereWithTextRules
  (Prisma 7.3 的字符串过滤不支持 regex,改为原生 SQL 取符合形态的取值清单拼 IN,
  带 5 分钟缓存)。preparePeriodArchiveQuery 是全站唯一取数入口,
  列表、候选项、导出、图片导出自动生效
- 文本规则故意不参与 omitFilter:若参与,候选值列表会冒出已被排除的取值,
  用户勾了却得到 0 行
- UI:文本列弹层改左右双排(左=形态与排除规则,右=搜索与候选值),
  弹层高度与横向偏移按触发按钮所在位置动态计算,保证清除/应用在任何视口都可见
- 偏好:两个新字段进入 sanitizeFilters 白名单,规则可持久化,点清除即清空

验证:本地真实库 66033 行——只勾正常编码 27645 行、再接 WM- 26852 行、
再接 DF- 26788 行,与 SQL 基准逐一致;3001 HTTP 对账五个口径全对、
非法列返回 400、候选项接口在勾了正常编码后返回的 SPU 全部符合形态;
真机 UI 12/12(应用 26788、刷新记住、回显、清除回 66033);
布局专项 36/36(1600x900 / 1440x720 / 1366x640 / 1280x600 下弹层均在视口内、
按钮可见、不超左右边界);新增单测 15/15;type-check EXIT=0。
2026-09-17 11:32:53 +08:00
WuYongJie
cffa8b64f8 merge: release BOM 预估 v2(价格回退工具/case 级预估卡/预览页/缓存表) 2026-09-16 17:37:42 +08:00
WuYongJie
b0036bcc48 merge: BOM 预估 v2 落地——价格回退工具、case 级预估卡编排、预估卡预览页、结果缓存表 2026-09-16 17:37:26 +08:00
WuYongJie
f30866530a Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-estimate-agent-phase0 2026-09-16 17:37:23 +08:00
WuYongJie
48871454e9 feat(bom-costing): BOM 预估 v2 —— 价格回退工具、case 级预估卡编排与预览页
- agent 工具:尺寸感知镜像检索 + 分类画像三级级联(包装 v1 回退链,导出纯函数复用)
- case 级编排:首批代表 SKU 自动精算,其余按需点算;结果持久缓存(新表 BomCostingAgentSkuEstimate,幂等迁移)
- EstimateCard 契约:代码侧聚合五行构成/区间包络/最小置信度/不确定点并集
- API:/api/bom-costing/cases/[id]/agent-estimate(GET 缓存卡 + POST 运行,复用模块鉴权)
- 预览页:/bom-costing/estimate(预估卡 + SKU 列表按需点算 + 竞品图片浏览,旁路不过闸门不设默认入口)
- Select 组件:透传 id/aria 到触发按钮;修复 skill 知识未注入循环的缺陷
- 基线工具:report:bom-agent-baseline(定价覆盖率 + shadow 聚合,含隧道重试)
- 设计文档:docs/架构设计/BOM成本预估v2-agent方案设计-20260916.md
2026-09-16 17:37:06 +08:00
WuYongJie
a3b9fa0fbb merge: release 产品全量档案流式导出 2026-09-16 16:21:14 +08:00
WuYongJie
6f023e3f0d fix(product-archive): 导出改为流式写出,修复全量导出 OOM 崩溃
正式站导出「产品全量档案资料」必然崩溃:整包写法在 89 列下约 60MB/千行,
生产 Node 堆被 PM2 限到 768MB,实际只能撑约 5000 行,而档案表已有 7 万余行。
PM2 记录 exited with code [0] via signal [SIGABRT],error log 19 次
FATAL ERROR: Reached heap limit,最后一次 GC dump 763.5MB(= 堆上限)。

改成「先冻结行 id 快照 → 每 2000 行分批取数 → ExcelJS 流式写出 → 边生成边下发」,
服务端堆占用与导出总行数解耦;并加同机并发上限 3(超限 429)兜底。

- repository:listRows 全量取数改为 openRowSource。先冻结本次要导的有序 id 快照,
  再按 id 分批取完整行并按冻结顺序重排,避免导出期间上游同步导致行集合错位。
- service:createWorkbook 整包 writeBuffer 改为 openWorkbook 流式写出。
  useSharedStrings:false(共享字符串表会累积全部唯一文本且无法回收);
  冻结首行走 addWorksheet 的 views 选项(流式 worksheet 的 views 只有 getter,
  直接赋值会被静默忽略);并发额度在「正常写完」与「客户端断开」两条路径都归还。
- route:改为流式响应,不再发 Content-Length(改 chunked);监听 request.signal,
  客户端断开时销毁生成流;响应头发出后生成失败只能掐断流,故在路由层记日志。
- 前端:下载态文案改为「正在生成并下载文件...」;无 Content-Length 时显示「已接收 X」;
  收流中断统一提示为可读文案,不再只报网络错误。
- 测试:5 个用例,含冻结首行回归守护与「客户端中途断开不泄漏并发额度」守护。

验证:本地真实库 66033 行 × 89 列,NODE_OPTIONS=--max-old-space-size=768 下
首字节 0.5s / 总耗时 26.7s / 产物 25.8MB / 峰值 heapUsed 152.5MB(对照:整包写法
同规模在 4GB 堆直接 OOM)。3001 真实路由 13951 行:status 200、无 Content-Length、
chunked、首字节 0.2s、5.5s / 5.6MB。产物特性 10/10(表名、冻结首行、autoFilter
A1:CK1、89 列宽度、表头加粗与文本、行数),并用 openpyxl 独立交叉复核一致。
jest 5/5 通过、type-check EXIT=0。

已知边界:只在本机 next dev 验证,未跑 next build / next start。
2026-09-16 16:07:31 +08:00
WuYongJie
c5e1c50418 fix(product-archive): 导出改为流式写出,修复全量导出 OOM 崩溃
正式站导出「产品全量档案资料」必然崩溃:整包写法在 89 列下约 60MB/千行,
生产 Node 堆被 PM2 限到 768MB,实际只能撑约 5000 行,而档案表已有 7 万余行。
PM2 记录 exited with code [0] via signal [SIGABRT],error log 19 次
FATAL ERROR: Reached heap limit,最后一次 GC dump 763.5MB(= 堆上限)。

改成「先冻结行 id 快照 → 每 2000 行分批取数 → ExcelJS 流式写出 → 边生成边下发」,
服务端堆占用与导出总行数解耦;并加同机并发上限 3(超限 429)兜底。

- repository:listRows 全量取数改为 openRowSource。先冻结本次要导的有序 id 快照,
  再按 id 分批取完整行并按冻结顺序重排,避免导出期间上游同步导致行集合错位。
- service:createWorkbook 整包 writeBuffer 改为 openWorkbook 流式写出。
  useSharedStrings:false(共享字符串表会累积全部唯一文本且无法回收);
  冻结首行走 addWorksheet 的 views 选项(流式 worksheet 的 views 只有 getter,
  直接赋值会被静默忽略);并发额度在「正常写完」与「客户端断开」两条路径都归还。
- route:改为流式响应,不再发 Content-Length(改 chunked);监听 request.signal,
  客户端断开时销毁生成流;响应头发出后生成失败只能掐断流,故在路由层记日志。
- 前端:下载态文案改为「正在生成并下载文件...」;无 Content-Length 时显示「已接收 X」;
  收流中断统一提示为可读文案,不再只报网络错误。
- 测试:5 个用例,含冻结首行回归守护与「客户端中途断开不泄漏并发额度」守护。

验证:本地真实库 66033 行 × 89 列,NODE_OPTIONS=--max-old-space-size=768 下
首字节 0.5s / 总耗时 26.7s / 产物 25.8MB / 峰值 heapUsed 152.5MB(对照:整包写法
同规模在 4GB 堆直接 OOM)。3001 真实路由 13951 行:status 200、无 Content-Length、
chunked、首字节 0.2s、5.5s / 5.6MB。产物特性 10/10(表名、冻结首行、autoFilter
A1:CK1、89 列宽度、表头加粗与文本、行数),并用 openpyxl 独立交叉复核一致。
jest 5/5 通过、type-check EXIT=0。

已知边界:只在本机 next dev 验证,未跑 next build / next start。
2026-09-16 16:02:44 +08:00
WuYongJie
990a4bfeb1 merge: release 商品抠图模型目录合并与选择器升级 + BOM agent 旁路引擎
# Conflicts:
#	furniture-3d-system/src/app/supply-chain/config/supply-chain-menu.test.ts
#	furniture-3d-system/src/app/supply-chain/config/supply-chain-menu.ts
2026-09-16 15:19:50 +08:00
WuYongJie
9533c2e4ec merge: 商品抠图配置合并上游专用抠图模型目录 + BOM 核算 agent 旁路引擎与文本脱敏契约
- ai-config: 任一协议模式合并网关与异步两份模型目录,抠图模型置顶标记,选中自动对齐协议
- bom-costing/agent: 估算引擎(契约/工具/循环/网关适配/组装器,27 测试)旁路试验版
- lib: 预估文本对外脱敏契约共享模块(public-estimate-text)
- scripts: shadow 偏差报表与 agent 试跑工具(工程域专用)
2026-09-16 15:17:03 +08:00
WuYongJie
6492c2e400 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-estimate-agent-phase0 2026-09-16 15:14:00 +08:00
WuYongJie
20755dfdd1 feat(ai-config): 商品抠图配置合并上游专用抠图模型目录并升级选择器
- 目录服务:商品抠图任一协议模式都合并通用网关与异步媒体任务两份目录,
  模型带 source 来源标记,抠图模型置顶;提取器按端点路径选择
- 配置页:原生 select 换平台 Select(可搜索、暗色协调),抠图模型带
  【抠图】标记,选中后运行协议自动对齐异步媒体任务
- Select 组件:rest props 透传到触发按钮,支持 aria-label/id
2026-09-16 15:13:52 +08:00
WuYongJie
f8d74a5543 fix(intake): 补齐按SKU锁定与导入保护 2026-09-16 14:21:57 +08:00
WuYongJie
d73508bae6 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-estimate-agent-phase0 2026-09-16 14:18:51 +08:00
WuYongJie
2d93a22721 feat(bom-costing): agent 估算引擎(旁路试验版)
- agent/ 契约+工具+接线+循环+skill知识+网关适配+端到端组装器(27 测试)
- agent 只读仓储(归档素材/镜像价格/材质目录/案例与SKU简报)
- shadow 偏差报表与 trial 试跑脚本(工程域专用)
- 纪律:单价只来自工具结果、总额代码汇总、硬预算+trace、1688 仅中性摘要
2026-09-16 14:18:39 +08:00
WuYongJie
9983ab3a1a fix(product-archive): 固定列不再透字,操作列改图标按钮
问题:产品全量档案的「序号」「操作」是两个 position: sticky 固定列。
主题样式把半透明的 --sc-hover(rgba(17,19,21,0.035))用 !important 打到了
这两个固定列上,覆盖了组件自带的不透明底色;横向滚动时压在固定列下方的
单元格文字就会透出来,污染序号列与操作列。

修复:
- .archive-td-index / .archive-td-actions 从半透明 hover 规则中摘出,
  改为「不透明底色 var(--sc-surface) + linear-gradient(var(--sc-hover)) 叠加色层」,
  视觉与原来一致,但底色恒不透明(浅色与深色主题均适用)
- 操作列「查看 / 更多」文字按钮改为图标按钮(Eye / MoreHorizontal),
  补 title 与 aria-label,菜单项加图标并改用 --sc-* 主题变量配色
- 操作列宽度由 138px 收窄到 96px(colgroup / th / td 同步)

验证:3001 真机 puppeteer 复验 9/9 通过——hover 与横向滚动后两个固定列
背景均为不透明 rgb;操作列仅 2 个图标、无文字、宽度 96px;「更多」菜单与
「商品修改日志」入口正常,图标「查看」仍能打开条码档案详情。
type-check EXIT=0。jest 的 3 个失败用例均与本改动无关(主体文件属其他线程
未提交改动,或与 HEAD 逐字节一致属既有失败)。
2026-09-16 14:08:17 +08:00
WuYongJie
9c9a6a9a08 fix(product-archive): 固定列不再透字,操作列改图标按钮
问题:产品全量档案的「序号」「操作」是两个 position: sticky 固定列。
主题样式把半透明的 --sc-hover(rgba(17,19,21,0.035))用 !important 打到了
这两个固定列上,覆盖了组件自带的不透明底色;横向滚动时压在固定列下方的
单元格文字就会透出来,污染序号列与操作列。

修复:
- .archive-td-index / .archive-td-actions 从半透明 hover 规则中摘出,
  改为「不透明底色 var(--sc-surface) + linear-gradient(var(--sc-hover)) 叠加色层」,
  视觉与原来一致,但底色恒不透明(浅色与深色主题均适用)
- 操作列「查看 / 更多」文字按钮改为图标按钮(Eye / MoreHorizontal),
  补 title 与 aria-label,菜单项加图标并改用 --sc-* 主题变量配色
- 操作列宽度由 138px 收窄到 96px(colgroup / th / td 同步)

验证:3001 真机 puppeteer 复验 9/9 通过——hover 与横向滚动后两个固定列
背景均为不透明 rgb;操作列仅 2 个图标、无文字、宽度 96px;「更多」菜单与
「商品修改日志」入口正常,图标「查看」仍能打开条码档案详情。
type-check EXIT=0。jest 的 3 个失败用例均与本改动无关(主体文件属其他线程
未提交改动,或与 HEAD 逐字节一致属既有失败)。
2026-09-16 13:54:43 +08:00
WuYongJie
570dcacdc9 fix(product-archive): 固定列不再透字,操作列改图标按钮
问题:产品全量档案的「序号」「操作」是两个 position: sticky 固定列。
主题样式把半透明的 --sc-hover(rgba(17,19,21,0.035))用 !important 打到了
这两个固定列上,覆盖了组件自带的不透明底色;横向滚动时压在固定列下方的
单元格文字就会透出来,污染序号列与操作列。

修复:
- .archive-td-index / .archive-td-actions 从半透明 hover 规则中摘出,
  改为「不透明底色 var(--sc-surface) + linear-gradient(var(--sc-hover)) 叠加色层」,
  视觉与原来一致,但底色恒不透明(浅色与深色主题均适用)
- 操作列「查看 / 更多」文字按钮改为图标按钮(Eye / MoreHorizontal),
  补 title 与 aria-label,菜单项加图标并改用 --sc-* 主题变量配色
- 操作列宽度由 138px 收窄到 96px(colgroup / th / td 同步)

验证:3001 真机 puppeteer 复验 9/9 通过——hover 与横向滚动后两个固定列
背景均为不透明 rgb;操作列仅 2 个图标、无文字、宽度 96px;「更多」菜单与
「商品修改日志」入口正常,图标「查看」仍能打开条码档案详情。
type-check EXIT=0。jest 的 3 个失败用例均与本改动无关(主体文件属其他线程
未提交改动,或与 HEAD 逐字节一致属既有失败)。
2026-09-16 13:52:12 +08:00
WuYongJie
adac0dfc3a fix(intake): 补齐按SKU锁定与导入保护 2026-09-16 13:47:39 +08:00
WuYongJie
374a39b6dc feat(intake): 成品寄样支持一键填是/填否批量设置是否寄出
在产品开发明细-成品寄样工作区,「请寄样」旁新增「一键填否」「一键填是」,
一键把当前批次显示的 SKU 行的「是否寄出」填好,之后仍可单独改个别行
(例:30 个 SKU 先全填否,再把第 1 个改成是)。

实现要点:
- 只填空行,不覆盖已选过的行;目标为当前批次显示行的 skuId 集合。
- 单行默认值计算抽成 buildFinishedSampleShipmentRowWithShipped 复用,
  批量走 applyFinishedSampleShipmentDecisionToRows(无变化时返回原引用)。
- 权限沿用 canEditFinishedSampleShipmentDecision;无权限或无待填写行时按钮置灰。
- HeaderActions 经 DocumentWorkspaceHeaderActionsSlot 透传动作、权限与待填写行数。

验证:
- type-check EXIT=0
- 新增单测 8 例 + 组件测试 5 例 + 集成测试 3 例 全通过
- 既有回归 buildNonIntegratedDetailChrome / buildNonIntegratedDocumentWorkspaceHeaderActions 通过
- 3001 真机(puppeteer)确认按钮组与交互元素就位,用户目视验收通过
2026-09-16 13:12:28 +08:00
WuYongJie
60034dfff0 test(intake): 样包测量只读断言随锁定口径对齐
非集成权限工作流用例中原断言期望样包测量「已确认」行仍可编辑,
与本次落地的口径(已确认即不可改、取消确认后方可改)冲突,
随主提交一并校正为期望 readonly,并加注释说明规则来源。

补 d3dc8821 遗漏的这一个测试文件(主提交已含对应实现)。
2026-09-16 13:12:28 +08:00
WuYongJie
44c69bef3e feat(intake): 产品定厂与样包测量按SKU取消确认
为采购、跟单、管理员增加按当前批次取消样包测量 / 产品定厂确认的能力,
取消只退回未推送聚水潭的 SKU,已推送 SKU 保持锁定,且不清空任何业务字段。

主要改动:
- 取消确认:新增 workflow-confirmation/cancel 接口与服务端按结构化
  systemEnteredAt 逐 SKU 重算逻辑;严格要求先取消样包测量再取消产品定厂;
  「全部」页签不开放取消入口,仅具体批次页签可取消
- 编辑锁逐 SKU 化:基础信息、产品定厂、样包测量、产品核价改为逐 SKU 判定,
  旧页面保存、整件删除、并发聚水潭推送均增加已推送 SKU 保护
- 样包测量锁定口径对齐:已确认即不可改,取消确认后可改;
  界面只读、写入拦截与 Excel 导入共用同一判定
- 基础信息修订签名纳入产品公共字段,公共字段变更自动退回当前批次基础信息确认
- 修复产品定厂回写误退回基础信息确认:手动同步侧与派生侧统一使用
  buildBasicInfoRevisionSignature,避免两侧签名形态错位导致比对恒不等
- 修复 advisory lock 被 Prisma 按 void 结果反序列化导致的保存报错
  (无返回值加锁改用 executeRaw,产品行 FOR UPDATE 仍走 queryRaw)
- 并发保护:普通保存、正式 SPU 分配、资产中心系列部件写入、AI白底图乐观保存
  统一接入产品级锁与锁内最新数据重读,避免旧快照覆盖取消结果
2026-09-16 13:12:28 +08:00
WuYongJie
2a7d2b04a2 feat(intake): 成品寄样支持一键填是/填否批量设置是否寄出
在产品开发明细-成品寄样工作区,「请寄样」旁新增「一键填否」「一键填是」,
一键把当前批次显示的 SKU 行的「是否寄出」填好,之后仍可单独改个别行
(例:30 个 SKU 先全填否,再把第 1 个改成是)。

实现要点:
- 只填空行,不覆盖已选过的行;目标为当前批次显示行的 skuId 集合。
- 单行默认值计算抽成 buildFinishedSampleShipmentRowWithShipped 复用,
  批量走 applyFinishedSampleShipmentDecisionToRows(无变化时返回原引用)。
- 权限沿用 canEditFinishedSampleShipmentDecision;无权限或无待填写行时按钮置灰。
- HeaderActions 经 DocumentWorkspaceHeaderActionsSlot 透传动作、权限与待填写行数。

验证:
- type-check EXIT=0
- 新增单测 8 例 + 组件测试 5 例 + 集成测试 3 例 全通过
- 既有回归 buildNonIntegratedDetailChrome / buildNonIntegratedDocumentWorkspaceHeaderActions 通过
- 3001 真机(puppeteer)确认按钮组与交互元素就位,用户目视验收通过
2026-09-16 12:10:53 +08:00
WuYongJie
f9d29ba92f feat(intake): 成品寄样支持一键填是/填否批量设置是否寄出
在产品开发明细-成品寄样工作区,「请寄样」旁新增「一键填否」「一键填是」,
一键把当前批次显示的 SKU 行的「是否寄出」填好,之后仍可单独改个别行
(例:30 个 SKU 先全填否,再把第 1 个改成是)。

实现要点:
- 只填空行,不覆盖已选过的行;目标为当前批次显示行的 skuId 集合。
- 单行默认值计算抽成 buildFinishedSampleShipmentRowWithShipped 复用,
  批量走 applyFinishedSampleShipmentDecisionToRows(无变化时返回原引用)。
- 权限沿用 canEditFinishedSampleShipmentDecision;无权限或无待填写行时按钮置灰。
- HeaderActions 经 DocumentWorkspaceHeaderActionsSlot 透传动作、权限与待填写行数。

验证:
- type-check EXIT=0
- 新增单测 8 例 + 组件测试 5 例 + 集成测试 3 例 全通过
- 既有回归 buildNonIntegratedDetailChrome / buildNonIntegratedDocumentWorkspaceHeaderActions 通过
- 3001 真机(puppeteer)确认按钮组与交互元素就位,用户目视验收通过
2026-09-16 12:06:09 +08:00
WuYongJie
495ff9f30b test(intake): 样包测量只读断言随锁定口径对齐
非集成权限工作流用例中原断言期望样包测量「已确认」行仍可编辑,
与本次落地的口径(已确认即不可改、取消确认后方可改)冲突,
随主提交一并校正为期望 readonly,并加注释说明规则来源。

补 d3dc8821 遗漏的这一个测试文件(主提交已含对应实现)。
2026-09-16 12:01:21 +08:00
WuYongJie
3ae72e800f feat(intake): 产品定厂与样包测量按SKU取消确认
为采购、跟单、管理员增加按当前批次取消样包测量 / 产品定厂确认的能力,
取消只退回未推送聚水潭的 SKU,已推送 SKU 保持锁定,且不清空任何业务字段。

主要改动:
- 取消确认:新增 workflow-confirmation/cancel 接口与服务端按结构化
  systemEnteredAt 逐 SKU 重算逻辑;严格要求先取消样包测量再取消产品定厂;
  「全部」页签不开放取消入口,仅具体批次页签可取消
- 编辑锁逐 SKU 化:基础信息、产品定厂、样包测量、产品核价改为逐 SKU 判定,
  旧页面保存、整件删除、并发聚水潭推送均增加已推送 SKU 保护
- 样包测量锁定口径对齐:已确认即不可改,取消确认后可改;
  界面只读、写入拦截与 Excel 导入共用同一判定
- 基础信息修订签名纳入产品公共字段,公共字段变更自动退回当前批次基础信息确认
- 修复产品定厂回写误退回基础信息确认:手动同步侧与派生侧统一使用
  buildBasicInfoRevisionSignature,避免两侧签名形态错位导致比对恒不等
- 修复 advisory lock 被 Prisma 按 void 结果反序列化导致的保存报错
  (无返回值加锁改用 executeRaw,产品行 FOR UPDATE 仍走 queryRaw)
- 并发保护:普通保存、正式 SPU 分配、资产中心系列部件写入、AI白底图乐观保存
  统一接入产品级锁与锁内最新数据重读,避免旧快照覆盖取消结果
2026-09-16 12:01:21 +08:00
WuYongJie
a4bff5dd59 test(intake): 样包测量只读断言随锁定口径对齐
非集成权限工作流用例中原断言期望样包测量「已确认」行仍可编辑,
与本次落地的口径(已确认即不可改、取消确认后方可改)冲突,
随主提交一并校正为期望 readonly,并加注释说明规则来源。

补 d3dc8821 遗漏的这一个测试文件(主提交已含对应实现)。
2026-09-16 11:52:18 +08:00
WuYongJie
d3dc8821c2 feat(intake): 产品定厂与样包测量按SKU取消确认
为采购、跟单、管理员增加按当前批次取消样包测量 / 产品定厂确认的能力,
取消只退回未推送聚水潭的 SKU,已推送 SKU 保持锁定,且不清空任何业务字段。

主要改动:
- 取消确认:新增 workflow-confirmation/cancel 接口与服务端按结构化
  systemEnteredAt 逐 SKU 重算逻辑;严格要求先取消样包测量再取消产品定厂;
  「全部」页签不开放取消入口,仅具体批次页签可取消
- 编辑锁逐 SKU 化:基础信息、产品定厂、样包测量、产品核价改为逐 SKU 判定,
  旧页面保存、整件删除、并发聚水潭推送均增加已推送 SKU 保护
- 样包测量锁定口径对齐:已确认即不可改,取消确认后可改;
  界面只读、写入拦截与 Excel 导入共用同一判定
- 基础信息修订签名纳入产品公共字段,公共字段变更自动退回当前批次基础信息确认
- 修复产品定厂回写误退回基础信息确认:手动同步侧与派生侧统一使用
  buildBasicInfoRevisionSignature,避免两侧签名形态错位导致比对恒不等
- 修复 advisory lock 被 Prisma 按 void 结果反序列化导致的保存报错
  (无返回值加锁改用 executeRaw,产品行 FOR UPDATE 仍走 queryRaw)
- 并发保护:普通保存、正式 SPU 分配、资产中心系列部件写入、AI白底图乐观保存
  统一接入产品级锁与锁内最新数据重读,避免旧快照覆盖取消结果
2026-09-16 11:41:32 +08:00
WuYongJie
31dfacab23 1688代发异常事项:当月业绩列/供应商列/整装拆装维护 + Excel式列头筛选
- 列表新增当月真实业绩列(SPU 汇总条码当月值,条码展开行显示自身),当月无业绩整行过滤
- 新增供应商列(取 SPU 内非空 supplierName),列顺序调整为 SPU|业绩|供应商|运营
- 维护抽屉新增整装/拆装二选一,标记整装的 SPU 在"是否有图纸"列显示"整装不需要图纸"
- 顶部有无筛选与 Excel 式列头筛选(含 filter-options 接口)
- 新增迁移 20260915180000_add_dropship_issue_assembly_type(SupplyChainDropshipIssue.assemblyType)
2026-09-16 08:50:57 +08:00
WuYongJie
9e50804b2e 1688代发异常事项:当月业绩列/供应商列/整装拆装维护 + Excel式列头筛选
- 列表新增当月真实业绩列(SPU 汇总条码当月值,条码展开行显示自身),当月无业绩整行过滤
- 新增供应商列(取 SPU 内非空 supplierName),列顺序调整为 SPU|业绩|供应商|运营
- 维护抽屉新增整装/拆装二选一,标记整装的 SPU 在"是否有图纸"列显示"整装不需要图纸"
- 顶部有无筛选与 Excel 式列头筛选(含 filter-options 接口)
- 新增迁移 20260915180000_add_dropship_issue_assembly_type(SupplyChainDropshipIssue.assemblyType)
2026-09-16 08:43:27 +08:00
WuYongJie
b0fd9a8800 1688代发异常事项:SPU级维护表+运营筛选+图纸/视频筛选+条码搜索 2026-09-16 08:43:06 +08:00
WuYongJie
55ee1b421b fix(barcode-print): restore legacy split-code text layout
Full-Page scaling mis-measured the 3096x416 single-line PNG by its intrinsic size and shrank the whole layout. Templates two and three now share the restored legacy ADD_PRINT_TEXT segments.
2026-09-15 18:16:15 +08:00
WuYongJie
8a7d6cae5a fix(barcode-print): restore legacy split-code text layout
Full-Page scaling mis-measured the 3096x416 single-line PNG by its intrinsic size and shrank the whole layout. Templates two and three now share the restored legacy ADD_PRINT_TEXT segments.
2026-09-15 18:08:49 +08:00
WuYongJie
260fbc8428 feat(progress): add column header filters 2026-09-15 17:02:03 +08:00
WuYongJie
36616821cf feat(progress): add column header filters 2026-09-15 16:57:42 +08:00
WuYongJie
53febe8eb7 feat(supplier-recommendations): add guided editor options 2026-09-15 14:45:25 +08:00
WuYongJie
170d776aa6 feat(supplier-recommendations): add guided editor options 2026-09-15 14:44:48 +08:00
WuYongJie
64e3378125 fix(finished-progress): backfill sample merchandiser owner 2026-09-15 13:49:26 +08:00
WuYongJie
f6dd04eeee fix(finished-progress): backfill sample merchandiser owner 2026-09-15 13:45:40 +08:00
WuYongJie
1b301af15e feat(finished-progress): link automatic rows to sample details 2026-09-15 11:38:40 +08:00
WuYongJie
499cbf66d1 feat(finished-progress): link automatic rows to sample details 2026-09-15 11:26:52 +08:00
WuYongJie
78dcdae880 fix(finished-progress): exclude historical JST entries 2026-09-15 11:01:33 +08:00
WuYongJie
79f34971d6 fix(finished-progress): exclude historical JST entries 2026-09-15 10:58:04 +08:00
WuYongJie
dac31e736a fix(1688): accept opaque CDN image responses 2026-09-15 10:16:12 +08:00
WuYongJie
b63b2fe974 fix(1688): accept opaque CDN image responses 2026-09-15 10:05:15 +08:00
WuYongJie
200790b6fc feat(finished-progress): require completed samples and improve automatic purchase intake
Require a real completed sample before finished-product intake. Carry factory, brand, merchandiser, delivery date and minimum order quantity into editable records; leave order date blank and support bulk maintenance.

Generate one factory contract per source group, place new automatic orders first and enlarge the horizontal scrollbar. Include source identity migration and regressions.

Validation: TypeScript noEmit; 18 Jest suites / 164 tests; local 3001 browser drag, wheel, and bottom-row checks.
2026-09-14 18:13:13 +08:00
WuYongJie
ed15789a81 feat(finished-progress): require completed samples and improve automatic purchase intake
Require a real completed sample before finished-product intake. Carry factory, brand, merchandiser, delivery date and minimum order quantity into editable records; leave order date blank and support bulk maintenance.

Generate one factory contract per source group, place new automatic orders first and enlarge the horizontal scrollbar. Include source identity migration and regressions.

Validation: TypeScript noEmit; 18 Jest suites / 164 tests; local 3001 browser drag, wheel, and bottom-row checks.
2026-09-14 18:07:47 +08:00
WuYongJie
73be6c9019 1688代发异常事项:SPU级维护表+运营筛选+图纸/视频筛选+条码搜索
- 新表 SupplyChainDropshipIssue:SPU 级普票/专票、退货地址运营维护,带操作日志
- 页面重构:删除 header,SPU 汇总行+展开条码明细(一码一行),操作列图标化
- 运营独立筛选项:可搜索/单选/多选(DropshipOpsFilter),默认按近7天销量合计降序
- 新增 是否有图纸/是否有安装视频 三态筛选(下拉一行排布),搜索支持条码维度
- 菜单排序:新品经营总览→新品终止任务反馈→1688代发数据明细→1688代发异常事项
- 平台标签标题注册 dropship-issues,修复顶部标签空白
2026-09-14 17:03:29 +08:00
WuYongJie
027d17dd4a 1688代发异常事项:SPU级维护表+运营筛选+图纸/视频筛选+条码搜索
- 新表 SupplyChainDropshipIssue:SPU 级普票/专票、退货地址运营维护,带操作日志
- 页面重构:删除 header,SPU 汇总行+展开条码明细(一码一行),操作列图标化
- 运营独立筛选项:可搜索/单选/多选(DropshipOpsFilter),默认按近7天销量合计降序
- 新增 是否有图纸/是否有安装视频 三态筛选(下拉一行排布),搜索支持条码维度
- 菜单排序:新品经营总览→新品终止任务反馈→1688代发数据明细→1688代发异常事项
- 平台标签标题注册 dropship-issues,修复顶部标签空白
2026-09-14 16:00:55 +08:00
WuYongJie
01fd83d28b feat(supply-chain): 并入1688代发数据明细与代发异常事项占位
内容来源: codex/xuxiake-mainline 2aa85619(伯阳 1688 dropship analytics + 晓晓占位与图片预览),按文件集从 origin/main 干净并入。

- 新增 /supply-chain/new-products/1688-dropship-data 全链路(页面/筛选/趋势/API/repository/service)
- 新增 /supply-chain/new-products/dropship-issues 占位入口
- 权限 targetKey: newProduct1688DropshipData / newProductDropshipIssues
- 迁移 20260912120000_add_daily_sales_dropship_fields(salesAmount/skuOperator/unitCost)
- 产品图片点击弹窗预览
2026-09-14 10:23:47 +08:00
WuYongJie
2aa856197b feat(supply-chain): 1688代发数据明细产品图片点击弹窗预览
- 新增 DropshipImageCell:缩略图可点击,平台 Modal 弹窗放大(等比铺满,最高680px)
- 缩略图样式收口到 .imageButton,避免与就地渲染的 Modal 大图互相污染
- 大图复用 source-image 认证代理 URL,浏览器缓存零额外请求
2026-09-14 10:02:55 +08:00
伯阳
bc2104b169 test(supply-chain): verify merged 1688 entries and record integration checks 2026-09-12 18:32:24 +08:00
伯阳
e271d1b941 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-1688-xuxiake-integration 2026-09-12 18:29:30 +08:00
伯阳
dfdc3c3626 feat(supply-chain): integrate 1688 dropship analytics with shared daily sales
Preserve shared barcode/day facts and atomic synchronization; add DF sales, cost and operator enrichment, filters, trends, and module-scoped image access. Include additive migration and deployment/backfill handoff. Independent review: passed with warnings; existing master-data audit failures match baseline.
2026-09-12 18:29:16 +08:00
WuYongJie
5156ead4c1 供应链新品管理新增「1688代发异常事项」占位入口
- 新增 /supply-chain/new-products/dropship-issues 占位页(功能建设中)
- supply-chain-menu 新品管理分组加菜单项,targetKey=newProductDropshipIssues
- 权限体系 targetKey 类型/列表/路由映射同步
- 侧栏清理未使用的 Home 图标导入
2026-09-12 18:05:22 +08:00
WuYongJie
090c6b39c5 运营负责人SPU级兜底:同SPU最新销售条码的负责人下放给未匹配条码 2026-09-12 17:10:50 +08:00
WuYongJie
38259cc40c 运营负责人SPU级兜底:同SPU最新销售条码的负责人下放给未匹配条码 2026-09-12 17:10:28 +08:00
WuYongJie
d20d7e4dd6 运营负责人SPU级兜底:同SPU最新销售条码的负责人下放给未匹配条码
- dws listLatestOpsOwners 带回 lastSaleDate;新增 listBarcodeSpuPairs(全档案条码→spu)
- service 构建按 spu 聚合的运营负责人映射(逐字段取最新销售日期的非空值)
- mapper 兜底:条码无 dws 直配时按 spu 继承(精确匹配优先,来源追溯 spu: 前缀)
- 用户口径:能匹配到的都放上去,不留空
2026-09-12 17:07:29 +08:00
WuYongJie
687ef50bb5 fix(home): workbench entry inside quick-links fold with unified styles
- expand list now starts with a fixed workbench entry (router.push to
  the workspace picker) followed by configured links
- replace next/link with button + router.push inside the nav item so
  styled-jsx scoped styles apply; fixes icon/label wrapping and keeps
  font size and spacing consistent across entries
- tests updated for the new push-based navigation and roles
2026-09-12 17:03:28 +08:00
WuYongJie
f6dc344b71 fix(home): workbench entry inside quick-links fold with unified styles
- expand list now starts with a fixed workbench entry (router.push to
  the workspace picker) followed by configured links
- replace next/link with button + router.push inside the nav item so
  styled-jsx scoped styles apply; fixes icon/label wrapping and keeps
  font size and spacing consistent across entries
- tests updated for the new push-based navigation and roles
2026-09-12 17:02:38 +08:00
WuYongJie
2b192afd86 fix(installation-demo): allow customer service model upload with progress toast
- /api/upload/model 去掉 requireDesignWorkspaceWrite 硬性门槛:改为安装演示
  canEdit 或设计工作区权限二者其一即可上传(客服等已配置安装演示编辑权限
  的角色可上传模型,其他模块设计权限语义不变)
- 上传进度右下角浮层按系统既有导入进度样式实现(文件名+百分比+进度条,
  完成绿色停留 3 秒自动收起,失败红色保留可关闭)
- installationModelUpload 改用 XHR 以获得真实上传字节进度,工具箱共用不受影响
2026-09-12 15:51:36 +08:00
WuYongJie
6aba6e077c fix(installation-demo): allow customer service model upload with progress toast
- /api/upload/model 去掉 requireDesignWorkspaceWrite 硬性门槛:改为安装演示
  canEdit 或设计工作区权限二者其一即可上传(客服等已配置安装演示编辑权限
  的角色可上传模型,其他模块设计权限语义不变)
- 上传进度右下角浮层按系统既有导入进度样式实现(文件名+百分比+进度条,
  完成绿色停留 3 秒自动收起,失败红色保留可关闭)
- installationModelUpload 改用 XHR 以获得真实上传字节进度,工具箱共用不受影响
2026-09-12 15:51:11 +08:00
WuYongJie
9892fead34 fix(installation-demo): allow customer service model upload with progress toast
- /api/upload/model 去掉 requireDesignWorkspaceWrite 硬性门槛:改为安装演示
  canEdit 或设计工作区权限二者其一即可上传(客服等已配置安装演示编辑权限
  的角色可上传模型,其他模块设计权限语义不变)
- 上传进度右下角浮层按系统既有导入进度样式实现(文件名+百分比+进度条,
  完成绿色停留 3 秒自动收起,失败红色保留可关闭)
- installationModelUpload 改用 XHR 以获得真实上传字节进度,工具箱共用不受影响
2026-09-12 15:50:47 +08:00
WuYongJie
9e9d92306e feat(home): admin-configured quick links with sidebar home fold
- HomeQuickLink table + migration; CRUD API with admin-only writes and
  internal-only visibility (suppliers/external intake excluded)
- sidebar home item folds open configured links in both product and
  supply-chain sidebars, entries open in a new tab
- home portal top-right manage entry with sort/enable/delete sheet
- permission matrix default: home overview open to all internal subjects
  so switching to supply chain no longer lands on an empty page
2026-09-12 15:04:54 +08:00
WuYongJie
0ea1c3ecdd feat(home): admin-configured quick links with sidebar home fold
- HomeQuickLink table + migration; CRUD API with admin-only writes and
  internal-only visibility (suppliers/external intake excluded)
- sidebar home item folds open configured links in both product and
  supply-chain sidebars, entries open in a new tab
- home portal top-right manage entry with sort/enable/delete sheet
- permission matrix default: home overview open to all internal subjects
  so switching to supply chain no longer lands on an empty page
2026-09-12 15:04:08 +08:00
WuYongJie
43f8540c9a 是否有图纸/是否有视频接入图纸系统API+非DF铺货图兜底+筛选修复 2026-09-12 14:27:10 +08:00
WuYongJie
002aa9dc34 fix(installation-demo): route customer service through node permissions
删除安装演示权限里硬编码的客服强制只读分享口径:原 isCustomerServiceUser
(CUSTOMER_SERVICE 角色或部门路径含客服中心)直接返回 canEdit=false 且排在
节点权限 overrides 之前,导致正式站给客服配置的编辑权限永远无效,客服只能
查看和分享、无法新建编辑截取。

删除后客服与普通员工一致走节点权限配置:客服主体 customerService
(部门路径含客服中心),正式站已配置的编辑权限即刻生效,可新建图纸、
维护编号、截取、分享与下载。
2026-09-12 14:25:40 +08:00
WuYongJie
d9d1b39d1c fix(installation-demo): route customer service through node permissions
删除安装演示权限里硬编码的客服强制只读分享口径:原 isCustomerServiceUser
(CUSTOMER_SERVICE 角色或部门路径含客服中心)直接返回 canEdit=false 且排在
节点权限 overrides 之前,导致正式站给客服配置的编辑权限永远无效,客服只能
查看和分享、无法新建编辑截取。

删除后客服与普通员工一致走节点权限配置:客服主体 customerService
(部门路径含客服中心),正式站已配置的编辑权限即刻生效,可新建图纸、
维护编号、截取、分享与下载。
2026-09-12 14:24:58 +08:00
WuYongJie
4f2ea0bdf0 fix(installation-demo): route customer service through node permissions
删除安装演示权限里硬编码的客服强制只读分享口径:原 isCustomerServiceUser
(CUSTOMER_SERVICE 角色或部门路径含客服中心)直接返回 canEdit=false 且排在
节点权限 overrides 之前,导致正式站给客服配置的编辑权限永远无效,客服只能
查看和分享、无法新建编辑截取。

删除后客服与普通员工一致走节点权限配置:客服主体 customerService
(部门路径含客服中心),正式站已配置的编辑权限即刻生效,可新建图纸、
维护编号、截取、分享与下载。
2026-09-12 14:24:11 +08:00
WuYongJie
904b80d4c9 运营部门负责人/运营负责人接入聚水潭销售宽表cater/ider
- dws repository 新增 listLatestOpsOwners:rpt.dws_sn_sales_full_info_di 按 skuoid 取 stat_date 最新一条的 cater/ider,全条码范围
- enrichment 链路新增 ops owners patch:来源码 DWS_SALES_OPS 可追溯;字段级有值才覆盖(dws 无值保留档案原值)
- opsDirector(运营总负责人)不接
- 本地全量验证:抽样 10 条与上游 100% 一致;3001 API 验收通过
2026-09-12 11:14:04 +08:00
WuYongJie
aaa9c74eec Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-09-12 11:13:15 +08:00
WuYongJie
dd693dfbd5 运营部门负责人/运营负责人接入聚水潭销售宽表cater/ider
- dws repository 新增 listLatestOpsOwners:rpt.dws_sn_sales_full_info_di 按 skuoid 取 stat_date 最新一条的 cater/ider,全条码范围
- enrichment 链路新增 ops owners patch:来源码 DWS_SALES_OPS 可追溯;字段级有值才覆盖(dws 无值保留档案原值)
- opsDirector(运营总负责人)不接
- 本地全量验证:抽样 10 条与上游 100% 一致;3001 API 验收通过
2026-09-12 11:12:50 +08:00
WuYongJie
1b3f210e0f feat(barcode-print): add size template and keep package codes on one line 2026-09-11 17:13:52 +08:00
WuYongJie
04b24f7972 feat(barcode-print): add size template and keep package codes on one line 2026-09-11 17:12:52 +08:00
WuYongJie
222f3e6a97 feat(barcode-print): add size template and keep package codes on one line 2026-09-11 17:08:02 +08:00
WuYongJie
02ad38908e fix(installation-demo): recapture by current viewpoint, align material counts, add finished-frame ending
- 编号重截取按当前视角取景(engine 截图支持 viewpoint:current,默认行为不变),成功/失败给可见消息
- 动作分解物料数量改为该动作实际命中节点数(fixed/action 帧同口径),徽标与编号标注共用管理编号覆盖值
- 动作分解末尾固定追加成品展示收尾帧:复用成品展示截图,不参与截取与计数,支持调整画面与文字标注
- 编号标注对账按编号兜底认领并清理超量残留,修复部件重绑后改数量导致标注叠加翻倍
- 成品线稿统一 detail 风格,与动作图线宽一致
2026-09-11 14:37:05 +08:00
WuYongJie
6f49206964 fix(installation-demo): recapture by current viewpoint, align material counts, add finished-frame ending
- 编号重截取按当前视角取景(engine 截图支持 viewpoint:current,默认行为不变),成功/失败给可见消息
- 动作分解物料数量改为该动作实际命中节点数(fixed/action 帧同口径),徽标与编号标注共用管理编号覆盖值
- 动作分解末尾固定追加成品展示收尾帧:复用成品展示截图,不参与截取与计数,支持调整画面与文字标注
- 编号标注对账按编号兜底认领并清理超量残留,修复部件重绑后改数量导致标注叠加翻倍
- 成品线稿统一 detail 风格,与动作图线宽一致
2026-09-11 14:34:04 +08:00
WuYongJie
0c045ff906 chore(product-archive): default daily sales schedule to Beijing 08:00
- local scheduled task installer: compute next 08:00 Asia/Shanghai start boundary
- production systemd timer installer: default OnCalendar 08:00 Asia/Shanghai
- aligns installers with already-active 08:00 runtimes on both ends
2026-09-11 09:46:58 +08:00
WuYongJie
0c7d147129 chore(product-archive): default daily sales schedule to Beijing 08:00
- local scheduled task installer: compute next 08:00 Asia/Shanghai start boundary
- production systemd timer installer: default OnCalendar 08:00 Asia/Shanghai
- aligns installers with already-active 08:00 runtimes on both ends
2026-09-11 09:44:56 +08:00
WuYongJie
e075a81eb7 DF开头SKU档案图片接入聚水潭skumap三表平台图
- 新增 jst-skumap-pic.repository:rpt.jst_skumap/_pdd/_qmcust_add 三表 UNION,按 sku_id 取 modified 最新非空 pic,仅 DF 开头
- enrichment 同步链路:DF 条码 image 字段改用 skumap 平台图(无图清空),来源记录 JST_SKUMAP_PIC 可追溯;其他 SKU 图片逻辑不变
- 变更日志字段清单加入 image(图片)
- source-image 图片代理白名单放行 JST_SKUMAP_PIC,修复 DF 档案平台图 404 灰框
- 本地全量回填验证:331 批扫描 66033 failed=0;DF 有图 2281→24700;非 DF 14974 不变
2026-09-11 09:14:48 +08:00
WuYongJie
9e5a0d20b5 DF开头SKU档案图片接入聚水潭skumap三表平台图
- 新增 jst-skumap-pic.repository:rpt.jst_skumap/_pdd/_qmcust_add 三表 UNION,按 sku_id 取 modified 最新非空 pic,仅 DF 开头
- enrichment 同步链路:DF 条码 image 字段改用 skumap 平台图(无图清空),来源记录 JST_SKUMAP_PIC 可追溯;其他 SKU 图片逻辑不变
- 变更日志字段清单加入 image(图片)
- source-image 图片代理白名单放行 JST_SKUMAP_PIC,修复 DF 档案平台图 404 灰框
- 本地全量回填验证:331 批扫描 66033 failed=0;DF 有图 2281→24700;非 DF 14974 不变
2026-09-11 09:12:05 +08:00
WuYongJie
56560cd6d2 fix(deploy): verify complete releases before switching production 2026-09-10 18:29:17 +08:00
WuYongJie
e2f211c7ac fix(deploy): verify complete releases before switching production 2026-09-10 18:29:13 +08:00
WuYongJie
7f9adebf88 feat(installation-demo): add manual catalog, permanent shares and hinge binding 2026-09-10 18:12:46 +08:00
WuYongJie
46a732d892 feat(installation-demo): add manual catalog, permanent shares and hinge binding 2026-09-10 17:59:58 +08:00
WuYongJie
2cfd57d678 feat(product-archive): add daily monthly yearly sales and daily sync 2026-09-10 13:16:26 +08:00
WuYongJie
aa1ca0afcf feat(product-archive): add daily monthly yearly sales and daily sync 2026-09-10 13:15:23 +08:00
WuYongJie
b66e6b1f11 Merge remote-tracking branch 'origin/main' into HEAD 2026-09-10 12:59:21 +08:00
WuYongJie
0875c87058 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into HEAD 2026-09-10 12:59:20 +08:00
WuYongJie
a20b6cb79f fix(product-archive): add missing POST handlers for large filter payloads 2026-09-10 12:59:03 +08:00
WuYongJie
b9c85357e6 fix(intake): abandon permission checks creator and real designer keys
废弃权限白名单原读取 payload 中不存在的 appearanceDesigner 等键,导致外观/结构设计师无法废弃产品。改为按 创建人 + 外观设计师(designer 列/payload.designer) + 结构设计师(openingReference.structureDesigner,兼容顶层旧键) 判定,不再放行 SPU 负责人与采购负责人;管理员口径不变。
2026-09-10 12:32:19 +08:00
WuYongJie
5c7ebbe914 fix(intake): abandon permission checks creator and real designer keys
废弃权限白名单原读取 payload 中不存在的 appearanceDesigner 等键,导致外观/结构设计师无法废弃产品。改为按 创建人 + 外观设计师(designer 列/payload.designer) + 结构设计师(openingReference.structureDesigner,兼容顶层旧键) 判定,不再放行 SPU 负责人与采购负责人;管理员口径不变。
2026-09-10 12:32:06 +08:00
WuYongJie
2947ddebcf Merge remote-tracking branch 'origin/main' into HEAD 2026-09-10 11:53:26 +08:00
WuYongJie
69d19076bd Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into HEAD 2026-09-10 11:53:07 +08:00
WuYongJie
17d2e1d7bb Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-09-10 11:52:31 +08:00
WuYongJie
351b7d7d2a feat(product-archive): take dropship df sku cost from sales goods_cost 2026-09-10 11:52:14 +08:00
WuYongJie
b8e80204a0 fix(barcode-print): raise Gujia certificate fields 2026-09-10 08:44:31 +08:00
WuYongJie
1042f35090 fix(barcode-print): raise Gujia certificate fields 2026-09-10 08:44:28 +08:00
WuYongJie
eedea9f791 fix(barcode-print): raise Gujia certificate fields 2026-09-10 08:43:13 +08:00
WuYongJie
0271da1b69 fix(barcode-print): recalibrate Gujia card and disable 2026 template 2026-09-09 17:56:14 +08:00
WuYongJie
b647cc2943 fix(barcode-print): recalibrate Gujia card and disable 2026 template 2026-09-09 17:55:27 +08:00
WuYongJie
3360ccb801 fix(barcode-print): recalibrate Gujia card and disable 2026 template 2026-09-09 17:54:04 +08:00
WuYongJie
1c0beffcb6 feat(barcode-print): add calibrated Gujia certificate template 2026-09-09 15:47:45 +08:00
WuYongJie
254cd92c5b fix(product-archive): stream material library images through a same-origin endpoint
the material library api hands out signed preview urls whose objects 404 on its cdn, so archive rows kept rendering empty placeholders; the batch endpoint now returns same-origin streaming urls (material-image?file=1) that resolve server-side, try the reachable s3 direct link first with the original signed url as fallback, and stream the bytes to the browser; the resolved image object is copied before url mapping so the service in-memory cache is no longer poisoned across requests.
2026-09-09 15:45:58 +08:00
WuYongJie
3976f96fcb fix(product-archive): stream material library images through a same-origin endpoint
the material library api hands out signed preview urls whose objects 404 on its cdn, so archive rows kept rendering empty placeholders; the batch endpoint now returns same-origin streaming urls (material-image?file=1) that resolve server-side, try the reachable s3 direct link first with the original signed url as fallback, and stream the bytes to the browser; the resolved image object is copied before url mapping so the service in-memory cache is no longer poisoned across requests.
2026-09-09 15:45:18 +08:00
WuYongJie
6161bb3e59 feat(barcode-print): add calibrated Gujia certificate template 2026-09-09 15:43:08 +08:00
WuYongJie
3cd064a467 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into HEAD 2026-09-09 14:57:09 +08:00
WuYongJie
db05c1ca0a Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-09-09 14:42:30 +08:00
WuYongJie
1bb87a454d feat(product-archive): persist jianshang stock on main table and harden filter select-all 2026-09-09 14:42:17 +08:00
WuYongJie
cc1e15582e fix(product-archive): fall back signed qnycdn material image urls to reachable s3 links
the material library api now returns signed preview urls on qnycdn.jiansun.vip/prod whose objects 404 on the cdn, so every resolved archive material image failed to render; rewrite those urls to the same path on s3.jiansun.vip/gongxiangpan (verified reachable) and drop the signature at every exit of the material image service (barcode lookup, sku zip index, white background fallback, resolved cache reads) so matched sku/barcode images display again.
2026-09-09 13:35:55 +08:00
WuYongJie
560579b7f9 fix(product-archive): fall back signed qnycdn material image urls to reachable s3 links
the material library api now returns signed preview urls on qnycdn.jiansun.vip/prod whose objects 404 on the cdn, so every resolved archive material image failed to render; rewrite those urls to the same path on s3.jiansun.vip/gongxiangpan (verified reachable) and drop the signature at every exit of the material image service (barcode lookup, sku zip index, white background fallback, resolved cache reads) so matched sku/barcode images display again.
2026-09-09 13:35:35 +08:00
WuYongJie
f0cc716dd0 fix(intake): map reorganized planning department to PLANNING role
钉钉组织调整后商品企划部更名为企划部并挂到公司根节点。部门到业务角色推断、非集成产品权限、节点权限主体与商品企划候选人筛选统一识别 企划部 及其下级在职人员;保留旧 品牌中心>商品企划部 口径兼容存量数据;在职/外部过滤逻辑不变。
2026-09-09 12:12:01 +08:00
WuYongJie
66746bdc7e fix(intake): map reorganized planning department to PLANNING role
钉钉组织调整后商品企划部更名为企划部并挂到公司根节点。部门到业务角色推断、非集成产品权限、节点权限主体与商品企划候选人筛选统一识别 企划部 及其下级在职人员;保留旧 品牌中心>商品企划部 口径兼容存量数据;在职/外部过滤逻辑不变。
2026-09-09 12:11:32 +08:00
WuYongJie
d563513875 feat(product-development-todos): drop early-stage reminders and read structured factory rows 2026-09-09 11:45:40 +08:00
WuYongJie
1a0d3a7429 feat(product-development-todos): drop early-stage reminders and read structured factory rows 2026-09-09 11:45:01 +08:00
WuYongJie
c20bf5314a feat(product-development-todos): drop early-stage reminders and read structured factory rows 2026-09-09 11:43:21 +08:00
f615161d8c Merge pull request 'fix(store-accounts): 按明文存储规则修正密码状态' (#21) from codex/xuxiake-mainline into main 2026-09-09 11:05:13 +08:00
WuYongJie
ed339ceb3f merge: sync latest xuxiake before password status release
# Conflicts:
#	furniture-3d-system/docs/integrations/finance-store-account-password-reveal.md
2026-09-09 11:02:18 +08:00
WuYongJie
149cf46b63 fix(store-accounts): align password status with plaintext storage 2026-09-09 11:01:42 +08:00
17daaee224 保持当前页分块读取限流额度 (#20) 2026-09-08 18:13:20 +08:00
2224112c29 保持当前页分块读取限流额度 (#19) 2026-09-08 18:11:34 +08:00
4fd134f636 发布财务当前页批量读取店铺密码 (#16)
Co-authored-by: xuxiake <2644600472@qq.com>
Co-committed-by: xuxiake <2644600472@qq.com>
2026-09-08 17:21:32 +08:00
caa960d70a 支持财务当前页批量读取店铺密码 (#18) 2026-09-08 17:19:01 +08:00
WuYongJie
9965c93c8d feat(store-accounts): store password and bank account as plaintext 2026-09-08 15:07:35 +08:00
WuYongJie
811926e67b feat(store-accounts): store password and bank account as plaintext 2026-09-08 15:06:43 +08:00
WuYongJie
c9d2f37ead feat(product-archive): add select-all to header filter option lists 2026-09-08 14:44:26 +08:00
WuYongJie
4deea6518a feat(product-archive): add select-all to header filter option lists 2026-09-08 14:43:37 +08:00
WuYongJie
0b4311a882 feat(store-accounts): show password and bank account in plaintext in list 2026-09-08 14:07:06 +08:00
WuYongJie
ecfd596ad0 feat(store-accounts): show password and bank account in plaintext in list 2026-09-08 14:05:44 +08:00
WuYongJie
d2f71b3723 feat(intake): add product abandon action to the product list
the product list rows gain an abandon action next to edit/copy/delete for admins and product owners (operations brand / spu / appearance / structure / purchase / merchandiser / planner / space owners matched by name); abandoning sets projectStatus to rejected which makes every workspace read-only and the existing daily todo push already skips rejected products, so abandoned products drop out of daily pushes automatically; the server enforces the same permission via a new POST abandon endpoint. Abandoned rows keep the restore action to come back.
2026-09-08 12:26:44 +08:00
WuYongJie
ee8172c724 fix(product-development-todo): route missing purchaser reminders and use plain text 2026-09-08 10:21:19 +08:00
WuYongJie
54bf9107fb Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-09-08 09:01:57 +08:00
WuYongJie
41e962da37 feat(oa-board): make workflow link cells clickable to open product detail
workflow link cells on the oa board detail table now render as anchors when the value is an http(s) url, opening the linked product detail in a new tab with platform link styling and hover underline; empty or non-url text still renders the dash placeholder.
2026-09-08 09:01:37 +08:00
1703 changed files with 174000 additions and 10207 deletions

View File

@ -5,6 +5,10 @@ NEXT_PUBLIC_APP_URL=http://localhost:3000
# 数据库
DATABASE_URL=postgresql://USER:PASSWORD@HOST:5432/DB_NAME?schema=public
# 员工工时花名只读人员配置来源(可选;开发环境配置后会合并正式人员配置)
SUPPLY_CHAIN_PRODUCT_ARCHIVE_READ_DATABASE_URL=postgresql://READ_USER:READ_PASSWORD@READ_HOST:5432/READ_DB
SUPPLY_CHAIN_PRODUCT_ARCHIVE_READ_DATABASE_SSL_REJECT_UNAUTHORIZED=true
# 产品全量档案的聚水潭 SQL Server 只读来源
JST_REPORT_DB_HOST=
JST_REPORT_DB_PORT=1433
@ -74,11 +78,15 @@ JST_PLANNING_SQLSERVER_TRUST_SERVER_CERTIFICATE=true
JWT_SECRET=replace-with-random-secret
JWT_EXPIRES_IN=7d
# 钉钉登录
DINGTALK_CLIENT_ID=
DINGTALK_CLIENT_SECRET=
# 钉钉登录(OAuth 应用;只用于登录授权和用户信息获取)
DINGTALK_LOGIN_CLIENT_ID=
DINGTALK_LOGIN_CLIENT_SECRET=
DINGTALK_CALLBACK_ORIGIN=
# 钉钉开放平台 API(通讯录、待办、审批等服务端调用)
DINGTALK_API_CLIENT_ID=
DINGTALK_API_CLIENT_SECRET=
# 钉钉待办:OA 阶段超时预警,以及预计出货日前 15 天的品牌策划主管提醒。
DINGTALK_TODO_ENABLED=false
DINGTALK_TODO_DETAIL_BASE_URL=
@ -168,6 +176,9 @@ ALIBABA_1688_IMAGE_SEARCH_TAGS=4306497
MATERIAL_LIBRARY_WHITE_BACKGROUND_API_ENABLED=false
MATERIAL_LIBRARY_WHITE_BACKGROUND_API_BASE_URL=https://example.com
MATERIAL_LIBRARY_WHITE_BACKGROUND_API_KEY=replace-with-material-library-api-key
# 检测报告查询(不填则复用上面素材平台的 BaseUrl / API Key;都没有时「是否有检测报告」显示为空)
MATERIAL_LIBRARY_CERTIFICATE_REPORT_API_BASE_URL=
MATERIAL_LIBRARY_CERTIFICATE_REPORT_API_KEY=
# 产品全量档案以图搜图(模型文件放在仓库外,不提交到 Git)
IMAGE_SEARCH_MODEL_PATH=

View File

@ -2,6 +2,7 @@
# dependencies
/node_modules
/node_modules.pnpm-mixed
/.pnp
.pnp.*
.yarn/*
@ -15,6 +16,7 @@
# next.js
/.next/
/.next-*/
/.next-oa-preview*/
/.next-oa-webpack/
/out/
@ -74,3 +76,6 @@ local-packages/npm-cache/cache/_logs/
# 脚本监控 Agent 本机凭据
agents/script-monitor/config.json
# 下载中心本地运行时产物
.download-center/

View File

@ -1,15 +1,15 @@
# 财务系统店铺密码按需读取契约
# 财务系统店铺密码直接显示契约
状态:代码和本地测试交付;生产迁移、配置、发布及真实接口验收须单独批准。
状态:生产接口已上线;2026-09-09 按当前明文存储规则修正 `passwordConfigured`。
## 存储事实与授权边界
- 密码真相源是 `public."StoreAccount"."passwordCiphertext"`,使用现有 `src/lib/store-account-crypto.ts` 的 AES-256-GCM(随机 12 字节 IV、认证标签、`v1:` 封装)。不是明文或不可逆 Hash。
- 生产密钥来自 `STORE_ACCOUNT_ENCRYPTION_KEY`,不从数据库读取,不复用财务服务认证凭据。已有银行账号加密也使用此密钥,轮换时必须同时处理。
- 2026-09-08 对 `furniture3d_prod` 的只读核验:`accounts_v1` COUNT=958、DISTINCT id=958;957 条非空密文均可在生产主机内存中解密,空值 1、解密失败 0。过程只输出聚合,不导出密码或密文。
- 密码真相源是 `public."StoreAccount"."passwordCiphertext"`。字段名是历史遗留;当前规则将新值以明文保存,读取链路对历史 `v1:` AES-256-GCM 值保留兼容。
- 存量 `v1:` 值已由 `scripts/migrate-store-account-plaintext-20260908.js` 迁移。`STORE_ACCOUNT_ENCRYPTION_KEY` 仅用于可能残留或恢复出的历史 `v1:` 值,不参与当前明文值读取。
- `passwordConfigured` 只表示存储值是否非空,不校验字符集或密码强度;因此包含 `+`、`/`、`=` 等字符的密码也会正确返回 true。它不返回密码本身。
- 普通 `store_account_reader` 可读视图、不能读取基表或密码列。此次迁移不增加其基表权限。
- 本接口只信任专用服务身份 `finance-store-accounts`。财务系统必须在自身后端校验用户登录态、查看/复制权限及店铺范围,然后从可信会话中取 `requesterId`。不得把客户端传入的任意人员 ID 当成操作者。服务凭据仅保存在财务后端,禁止交给浏览器。
- 不提供列表、批量密码导出或自动预取。列表只读取 `passwordConfigured`;每次明确的查看/复制操作单独调用接口。上游审计记录的用户 ID 是专用财务服务的身份断言,未授权请求中的 ID 不可信,`serviceId=null`。
- 不提供密码列表或批量导出。财务页面可在已通过自身页面权限校验后,自动读取当前可见页最多 50 个账号;CSV 和共享镜像仍只读取 `passwordConfigured`。复制操作继续走单账号接口。上游审计记录的用户 ID 是专用财务服务的身份断言,未授权请求中的 ID 不可信,`serviceId=null`。
## HTTP 契约
@ -17,6 +17,8 @@
`POST https://cp.jiansun.vip/internal/store-accounts/{id}/password/reveal`
`POST https://cp.jiansun.vip/internal/store-accounts/visible-page/password/reveal`
`id` 为 `accounts_v1.id`,仅接受单个 1–128 位字母、数字、下划线或连字符。无查询参数;禁止在 URL、请求体、请求头中传待读取密码。
请求头:
@ -34,12 +36,30 @@ Content-Type: application/json
`purpose` 仅允许 `view` 或 `copy`。`requesterId` 是财务会话中的稳定人员标识,1–128 位 `[A-Za-z0-9_.:@-]`。不接受批量 ID、查询参数、普通登录 Cookie/JWT、普通数据库凭据或其他内部任务密钥。
当前页批量请求严格只接受以下字段,正文最大 8192 字节;`accountIds` 为 1 至 50 个互不重复且符合单账号 ID 规则的值:
```json
{ "requesterId": "finance-user-123", "purpose": "view", "accountIds": ["account-1", "account-2"] }
```
成功 `200`:
```json
{ "requestId": "<服务端生成的 UUID>", "password": "<仅本次响应体中的密码>" }
```
当前页批量成功的 HTTP 状态为 `200`,每个账号分别返回 `200/404/429/503`。每项都有独立 UUID;只有状态 `200` 的项包含密码,并按请求顺序完整返回:
```json
{
"requestId": "<批次 UUID>",
"items": [
{ "accountId": "account-1", "requestId": "<项目 UUID>", "status": 200, "password": "<密码>" },
{ "accountId": "account-2", "requestId": "<项目 UUID>", "status": 404 }
]
}
```
所有状态均返回 `Cache-Control: no-store`、`Pragma: no-cache`、`X-Content-Type-Options: nosniff`。`X-Request-Id` 与响应体、审计的追踪 ID 相同。不会在响应头中放密码。
错误体:
@ -59,7 +79,7 @@ Content-Type: application/json
## 限流和超时
- 数据库滚动窗口:服务全部账号 30 次/分钟、300 次/24 小时;同一人员 10 次/分钟;同一账号 5 次/分钟。查看和复制共享额度,轮换令牌不重置服务身份或额度。
- 单账号数据库滚动窗口:服务全部账号 30 次/分钟、300 次/24 小时;同一人员 10 次/分钟;同一账号 5 次/分钟。公网 TLS 网关保留 1 KiB 敏感请求正文上限,财务端把 50 行当前页拆成两个最多 25 个账号的批次;批次标记因此按服务 20 次/分钟、1000 次/24 小时、同一人员 10 次/分钟限流,等价于原每页额度。批次中的每个账号仍受同一账号 5 次/分钟限制。查看和复制共享账号额度,轮换令牌不重置服务身份或额度。
- 已认证的成功、404、参数拒绝和解密失败均占额度;429 不延长窗口。额度检查与审计在同一数据库事务中用事务级锁串行化,跨进程/重启共享状态。
- 429 不携带剩余数量或精确窗口,避免推测其他用户行为。客户端不得自动重试密码读取;提示用户稍后再试,持续超限联系管理员。
- 财务客户端设置总超时 10 秒、禁用自动重定向和重试。后端池上限 4、连接获取 1 秒、SQL 执行 1.5 秒/客户端查询 2 秒、锁等待 1 秒,路由运行预算 10 秒。网关必须配置请求体 1 秒和总请求截止,防止慢请求占用。
@ -69,12 +89,13 @@ Content-Type: application/json
| 名称 | 所在系统与要求 |
|---|---|
| STORE_ACCOUNT_ENCRYPTION_KEY | 上游已有独立加密密钥,至少 32 字符;生产不回退到 JWT_SECRET |
| STORE_ACCOUNT_ENCRYPTION_KEY | 仅用于兼容历史 `v1:` 密文,至少 32 字符;生产不回退到 JWT_SECRET |
| STORE_ACCOUNT_FINANCE_TOKEN | 上游专用服务令牌,密码学随机至少 32 字节,base64url 编码,43–128 字符;缺失时拒绝全部调用 |
| STORE_ACCOUNT_FINANCE_TOKEN_PREVIOUS | 上游可选旧令牌,仅用于短时轮换;主令牌缺失时旧令牌也不生效 |
| STORE_ACCOUNT_REVEAL_EDGE_TOKEN | 上游与可信 TLS 网关之间的独立随机凭据,base64url 43–128 字符;与财务令牌完全分离,不提供给财务系统;缺失时拒绝全部调用 |
| DATABASE_URL | 上游现有数据库连接;仅专用不输出查询/异常的连接池读取密文、写审计 |
| STORE_ACCOUNT_REVEAL_URL | 财务后端配置完整 HTTPS 地址模板 |
| STORE_ACCOUNT_REVEAL_VISIBLE_PAGE_URL | 财务后端配置当前页批量 HTTPS 地址;必须使用精确 `visible-page` 路径 |
| STORE_ACCOUNT_REVEAL_TOKEN | 财务后端保存与上游专用令牌对应的值 |
| STORE_REVEAL_TEST_ADMIN_URL | 仅本地集成测试;必须指向回环地址、有建测试库/角色权限的 PostgreSQL |
| STORE_REVEAL_TEST_POSTGRES_CONTAINER | 仅本地日志验收,指定本地 PostgreSQL 容器以只读扫描测试期间日志 |
@ -85,25 +106,25 @@ Content-Type: application/json
2. 应用不以 URL 或未经认证的转发头证明 TLS。必须由受控网关在实际 HTTPS 入口覆盖 `X-Forwarded-Proto: https` 及 `X-Store-Reveal-Edge-Token`(值来自独立 `STORE_ACCOUNT_REVEAL_EDGE_TOKEN`),剥除客户端原有同名头;HTTP 入口直接拒绝,不能注入上述凭据。应用对网关凭据做定时安全比较,缺失/错误或协议不是 https 时拒绝。凭据仅上游和网关持有,不提供给财务系统;配置文件 0600,不通过 `nginx -T` 等诊断输出展示。仍需封闭应用直连并逐跳验证 TLS,独立凭据不代替网络加密。
3. 对精确路径 `/internal/store-accounts/*/password/reveal` 禁止网关缓存、响应落盘缓冲、CDN缓存、请求/响应体及 Authorization 采集;包括 APM、WAF、异常上报、链路追踪和调试抓包。网关日志只允许时间、方法、路径(不带参数)、状态、耗时、追踪 ID。不要采集响应对象。
4. 网关自己的 403/404/429/503 也使用脱敏 JSON、`Cache-Control: no-store` 和追踪 ID;不得返回上游诊断页。禁止将该路由接入通用 `withApi`/request logger;目前路由直接返回白名单响应,不记录异常对象。
5. 数据库不执行明文解密,SQL 参数不包含明文密码。现有密码写入或密钥轮换时,关闭 PostgreSQL/pgAudit 的绑定参数及错误参数记录,防止记录密文。不要启用该连接池的 SQL 结果或参数追踪。
6. 财务后端同样禁用正文采集、缓存及异常对象打印。浏览器仅在用户点击后获取,避免 localStorage/sessionStorage/Service Worker 持久化;显示或复制完清除页面内存状态,不把密码放入通知、埋点或消息队列。
5. 数据库查询结果包含当前明文密码;严禁记录 SQL 结果、绑定参数或异常对象,不要启用该连接池的参数/结果追踪。密码写入及历史格式迁移期间同样关闭 PostgreSQL/pgAudit 的绑定参数和错误参数记录。
6. 财务后端同样禁用正文采集、缓存及异常对象打印。浏览器在进入已授权页面后只读取当前可见页,避免 localStorage/sessionStorage/Service Worker 持久化;页面隐藏或离开时清除页面密码,不把密码放入通知、埋点或消息队列。
## 视图与审计迁移
迁移:`prisma/migrations/20260908120000_store_account_password_reveal/migration.sql`。
初始迁移:`prisma/migrations/20260908120000_store_account_password_reveal/migration.sql`。明文状态修复:`prisma/migrations/20260909100000_fix_store_account_password_configured_plaintext/migration.sql`。
- 新增 `StoreAccountPasswordAccessAudit`,固定字段为请求追踪 ID、人员标识、服务身份、账号 ID、用途、HTTP状态、数据库时间;不关联用户/店铺外键,以保留未授权、不存在和删除账号的失败审计。状态 200 表示成功,其他状态表示失败。
- 只在原视图存在时,以原视图查询作为子查询,在末尾追加 `passwordConfigured boolean`,保留原字段、筛选、每行内容、ID、所有者与 ACL;`EXISTS` 不会产生重复行。未配置外部视图的环境不擅自创建外部读入口。
- `passwordConfigured` 表示存在格式完整的、非空 AES-GCM 密码封装。SQL 不接触密钥,所以不在每次列表查询时验证 GCM 标签/密钥健康;生产预检必须做主机内存中的解密聚合验证。密钥丢失或密文损坏属于 503 运维故障,不能把它伪装成未设置密码。
- 无历史明文迁移,无现有密码或银行账号数据变更,无额外只读权限。审计数据增量写入,上线后按安全审计要求保留至少 365 天;此 PR 不创建自动删除任务。
- `passwordConfigured` 的当前定义为 `passwordCiphertext <> ''`。修复迁移仅替换这个布尔表达式,并在事务内核对原字段逐行内容、视图所有者和 ACL 未变;不读取或输出密码。
- 明文迁移已由独立脚本完成。本状态修复不改任何密码或银行账号数据,也不增加普通只读账号权限。审计数据至少保留 365 天。
迁移前由 DBA 只读确认目标库、视图定义/所有者/ACL、字段顺序、行数与唯一性、密码聚合、普通读取账号和其成员角色权限。若生产仍以本次基线验收,COUNT 与 DISTINCT id 必须均为 958;若业务新增/删除则暂停并重新确认基线,不改数据凑数。
备份与执行顺序(生产必须在明确批准后执行):
1. 备份当前 `main`/部署 SHA、现有视图 DDL/ACL/所有者及 `_prisma_migrations` 状态。生成目标库 `pg_dump -Fc` 备份并验证可读取;备份进入生产主机权限 0700 目录,文件 0600,不进入 Git 或共享日志。备份可能含密文,按敏感材料管理。
2. 本次不换加密密钥;核实独立密钥在受限凭据存储中有可恢复副本,不在命令行、文档中显示。记录备份校验和、时间及恢复演练证据。
3. 保持新财务令牌未配置,执行批准的该迁移,视图短时获得 DDL 锁(最多等 5 秒,执行预算 30 秒),新增审计表及索引;不回放未审查的其他迁移。
2. 本次不修改任何密码、银行卡号或服务令牌;备份不得进入 Git 或普通日志。
3. 执行明文状态修复迁移,视图短时获得 DDL 锁(最多等 5 秒,执行预算 30 秒);不回放未审查的其他迁移。
4. 在同一只读快照比较迁移前后原字段行集、顺序/类型、行数/唯一性和权限;运行 `scripts/sql/check-store-account-password-reveal.sql`,确认最后字段 boolean,报告 true/false/null 聚合。预计当前数据为 957/1/0,但必须执行后实测。
5. 按项目主线发布流程部署,完成逐跳 TLS、网关日志/限流/缓存检查后再配令牌。生产虚构账号的创建、调用与删除属于额外生产写入,需单独批准;不要调用真实账号作为测试。
@ -115,17 +136,18 @@ Content-Type: application/json
网关凭据:暂停此接口,在网关及上游同时更新 `STORE_ACCOUNT_REVEAL_EDGE_TOKEN` 后验收并恢复;不支持旧值并行。不得将网关凭据与服务令牌设成相同值。
数据加密密钥:现有 `v1:` 不含 key ID,不能直接替换环境变量。必须另行批准维护窗口,暂停所有店铺密码及银行账号的写入/读取,备份对应表和旧密钥;用受控迁移进程在内存中旧钥解密、新钥重新 AES-GCM 加密,并在同一数据库事务中更新两类密文字段(SQL 永不接收明文)。验证全量解密聚合、行数及虚构账号后,协调所有应用实例切换新环境密钥,再恢复流量;失败时同时恢复密文备份和旧钥。关闭 SQL 参数/异常参数、APM及备份内容输出;本 PR 不执行此轮换,也不引入第二套密钥或把新旧密钥存入数据库。需要无停机轮换时,应另行实现带 key ID 的 Envelope Encryption/KMS 版本迁移。
历史兼容密钥:当前明文规则不需要为新值轮换数据密钥。若确认数据库中不存在 `v1:` 值并且备份保留策略允许,可另行评估退役历史解密密钥;退役前必须检查备份恢复场景,不能直接删除。
## 本次验证记录
- 当前页批量扩展的服务、HTTP 路由和代理边界共 3 个测试套件、41 项测试通过;`npm run type-check` 通过。测试覆盖 1–50 个账号、重复账号拒绝、部分 404/429/503、批次与每账号审计,以及失败时不返回明文。
- 单元/HTTP/代理路由测试:34 项通过,含伪造 HTTPS URL/转发头和错误网关凭据拒绝。
- 追加现有代理路由的 7 项回归,与 10 项集成测试合计 6 套件、51 项全部通过。
- 隔离本地 PostgreSQL 集成测试:10 项通过,覆盖完整加解密与 HTTP handler、已提交审计、403/404/429/no-store、并发/跨实例限流、分钟/24小时配额、审计失败拒绝泄露、原字段及筛选保留、958/958 与 boolean 957/1/0、普通只读角色拒绝基表/密码/审计读取。
- 开启本地测试连接的 PostgreSQL statement 日志,只在内存扫描 `docker logs`;应用日志、审计行、SQL请求与真实数据库日志均未检出随机测试密码。测试无需打印测试密码,断言只输出布尔结果。
- Type-check 通过;范围内 ESLint 通过。全仓库主数据边界巡检有 6 项已有违规,均位于本次未修改模块;不得据此声称全仓库通过。
- 生产仅完成只读基线核查;生产 `passwordConfigured` 尚未落库,生产接口尚未发布,因此没有生产新增字段/新接口的验收结论。
- 2026-09-09 生产只读复核确认:958 行、958 个唯一 ID、957 条非空密码、1 条空密码;旧正则导致状态误报为全 false。本迁移用于恢复预期聚合 true/false/null = 957/1/0。
运行:`npx jest --runInBand --runTestsByPath src/server/services/store-account-password-reveal.service.test.ts src/proxy-store-account-reveal.test.ts "src/app/internal/store-accounts/[id]/password/reveal/route.test.ts" tests/integration/store-account-password-reveal.test.ts`。未配置本地测试库变量时,集成套件明确 skip,不能算真实数据库验收通过。
收口:**passed with warnings**。这是自审,不是独立复审。Route → Service → Repository 边界保持;未修改 UI;复用密码真相源、加密和专用服务认证,未通过明文视图绕开根因。剩余门禁为生产批准、备份/TLS拓扑/网关配置及发布后真实验收。
收口:**passed with warnings**。这是自审,不是独立复审。Route → Service → Repository 边界保持;未修改 UI;状态字段与当前明文存储规则对齐,专用认证、审计和限流边界不变。

View File

@ -0,0 +1,101 @@
# 1688 Dropship Integration
## Scope
The module at `/supply-chain/new-products/1688-dropship-data` includes the DF
product table, date and multi-select column filters, real-performance ordering,
and daily/weekly/monthly trend details with prior-month comparisons.
It reads the shared product archive and daily sales fact table. The shared
`barcode + statDate` key, unmatched source barcodes, synchronization lock,
staging, and atomic publication are preserved. No second daily table or
archive foreign key is introduced.
## Field Contract
| Display | Source / calculation |
| --- | --- |
| Sales amount | `sales_amount` -> daily `salesAmount` |
| Real performance | `real_performance` |
| After-sale amount | `after_sale_amount` |
| After-sale cost rate | After-sale amount / real performance; blank for zero denominator |
| Gross profit | Real performance - clear sales cost - send freight |
| SKU operator | DF barcode/day distinct `ider`, latest nonempty value in selected period |
| Total cost | Latest available daily `unitCost + estimatedFreight` in selected period; missing components are displayed as zero |
| Unit cost | Latest available daily DF cost from `goods_count = 1` observations; the existing product cost excludes freight; missing values are displayed as zero |
| Estimated freight | Average `send_freight` for the same DF barcode/day where `goods_count = 1`; the stored value remains null when no quantity-one observations exist and the page displays zero |
The archive's existing `shipAmount` continues to use `clear_goods_cost`.
It must not be substituted for the dropship module's `salesAmount`.
Cost ties use quantity-one frequency, total frequency, then descending unit cost. The page displays total cost before unit cost and estimated freight.
## Deployment Prerequisites
Code publication is not production deployment or database/data synchronization.
No credentials, database dumps, local accounts, or local environment files are
included in this change.
1. Confirm the target PostgreSQL identity, backup, and migration readiness using
the repository deployment/database workflow. Apply migration
`20260912120000_add_daily_sales_dropship_fields` after the existing mainline
daily-sales migration. Do not replay the old local prototype's daily-table
creation migrations against the mainline database.
2. Generate the Prisma client and build the application with the target's
existing environment configuration.
3. Run the existing `npm run sync:supply-chain-product-archive-daily --` command
with `--backfill`, `--apply`,
`--confirm=sync-supply-chain-product-archive-sales-performance-daily`,
`--expected-database-identity=...`, and
`--expected-source-database-identity=...` using verified identities.
The upstream requires read access to the configured
`JST_REPORT_DB_SCHEMA.dws_sn_sales_full_info_di`; credentials remain in the
target environment's `JST_REPORT_DB_*` variables.
4. Check the completed sales sync run, historic DF `salesAmount` completeness,
module view permission `newProduct1688DropshipData`, images, filters, totals,
and trend comparisons. The existing daily schedule then maintains the data.
The migrations add `skuOperator` (empty by default), `unitCost` (nullable),
`estimatedFreight` (nullable), and `salesAmount` (nullable). Historical facts need a backfill; the ordinary
seven-day refresh does not populate all older dates. Until backfilled, unknown
sales totals display as `--`, not zero or shipped amount.
Filtering uses the existing cache service with user-scoped keys and a 24-hour
TTL. Multi-process deployments should provide the existing shared Redis cache.
The module uses its own authenticated DF-only image route under
`/api/supply-chain/1688-dropship-data/source-image`. Its view permission does not
require granting access to the full product archive; the shared image service
and its source/URL restrictions are retained.
Rollback can restore the prior application version while retaining the additive
columns; do not drop the daily table or discard its history.
## Validation And Review
Independent review conclusion: `passed with warnings`. Architecture and
root-cause handling passed, including the corrected image-permission boundary.
The existing visual implementation was retained; target-environment screenshots
and real-data acceptance were not performed in this integration.
Final integration includes shared-mainline update `5156ead4`, retaining its
separate dropship-issues entry and permissions. After merging, 20 focused Jest
suites (171 tests), TypeScript checking, relevant ESLint, and the production
build passed. The build retains two unrelated broad-file-pattern warnings in
the existing OCR route and DWG wrapper.
The integration was checked with focused Jest suites for the page, trends,
filters, permissions, image route, service/repository mapping, and shared daily
sync; TypeScript checking; changed-file ESLint; and a production build using
isolated placeholder configuration (no live database). An in-memory PostgreSQL
compatibility replay confirmed that the additive migration retains existing
DF/non-DF facts and the original primary key. Image-route tests exercise the
real request/auth wrapper, including module-only access and denied requests.
The repository-wide `db:verify-master-data:code` and
`db:verify-master-data:truth` audits still fail on existing baseline issues.
Independent comparison with clean shared-mainline commit `38259cc4` found no
new blocking findings: code-audit finding content is unchanged apart from
schema line offsets, and the truth-audit JSON is identical. These global audit
failures are not presented as passing checks.
No target deployment, live target database migration/backfill, or live-data
browser validation of the integrated version is included in this publication.
The existing local test application and its prototype database are unchanged.

View File

@ -0,0 +1,54 @@
# 产品全量档案图片索引操作说明
全量档案本地图片索引只读取 `SupplyChainProductArchive.image`。上游「我司 SKU 命中」继续使用现有独立图搜服务。
## 更新规则
- 有图片且没有索引:建立索引。
- 图片地址变化、旧索引失效、模型版本或向量规格变化:重新索引。
- 图片为空或地址已经变化:旧向量退出本地搜索。任务会把旧记录标为 `STALE`。
- 下载或计算失败:保存 `FAILED` 和原因,六小时后再次尝试;图片地址再次变化则立即处理。
- 图片内容相同、地址不同:复用向量并更新地址与状态,避免反复处理同一张图片。
- 下载、计算期间图片改变或产品删除:提交前锁定并核对档案行,丢弃过期结果,下一轮处理最新图片。
- 所有索引任务使用同一 PostgreSQL 会话锁,避免手动补索引、同步触发与定时任务并发计算。
- 正常增量任务不会重新下载全部已就绪图片;同一 URL 下替换了图片内容时,使用完整内容核验命令。
## 本地命令
在主应用目录执行,模型沿用现有 `IMAGE_SEARCH_MODEL_PATH` 或 `.local-services/models/dinov2-small`。
```bash
# 每次最多处理 200 条缺失、换图或到期重试记录
npm run image-search:index
# 补齐当前全部待处理记录,并立即重试历史失败
npm run image-search:index -- --limit=0 --retry-now
# 下载所有带图档案,核验同一 URL 下的图片内容变化
npm run image-search:index -- --all --limit=0
```
命令持续输出已处理、成功、复用、失败、跳过、过期结果丢弃和失效条数;失败行输出条码与原因。最后输出本轮结果及待处理数量。失败仍会进入后续重试,本轮失败数必须单独关注,`remaining` 只计算已到重试时间的记录。
## 生产启用
代码必须先进入被授权的 `origin/main`,再经统一部署入口发布。安装定时器与首次补索引按用户授权执行,不能把本地开发完成等同于生产已启用。
安装入口:`scripts/install-product-archive-image-index-timer.sh`。它要求现有生产模型文件齐全,并让任务显式使用共享模型目录。当前应用启动命令已注入模型路径,无须为本功能重写应用环境。
- 服务:`furniture-product-archive-image-index.service`。
- 定时器:`furniture-product-archive-image-index.timer`,每五分钟检查,单次最多处理 200 条,失败六小时后重试。
- 两个现有全量同步服务分别增加本功能自己的 `image-index.conf`,同步成功后异步启动索引服务。
- 前台保存、导入及其他来源的图片变化由五分钟定时检查覆盖;索引下载与推理不阻塞保存请求。
- 通过 `PRODUCT_ARCHIVE_IMAGE_INDEX_LIMIT` 可在安装时调整单次处理上限。
- 脚本和日志通过发布目录与 systemd 管理,不手工修改发布目录。
```bash
bash scripts/install-product-archive-image-index-timer.sh
systemctl status furniture-product-archive-image-index.timer --no-pager
journalctl -u furniture-product-archive-image-index.service -n 50 --no-pager
```
首次补齐历史缺口可等待定时任务分批完成;需要一次补完时,手动索引命令必须指向共享模型路径。运行中的另一索引任务会返回 `LOCKED`,稍后重试。
验收应核对有图档案与索引的关联,而不是只看 `READY` 行数:当前地址一致、状态 `READY`、模型版本与 768 维向量均符合要求才是可用索引。历史 404 或不可读取地址不能靠索引任务生成图片,需要按条码定位源图片问题。

View File

@ -34,6 +34,10 @@
- 定时频率:每天 `03:00` 和 `15:00`。
- 源库连接信息只允许放在生产本地 `/opt/furniture-3d-system-prod/shared/employee-directory-sync.env`,不能写入 Git 或共享文档。
- 生产应用内部接口密钥继续从 `/opt/furniture-3d-system-prod/shared/.env` 的 `HR_SYNC_SECRET` 读取。
- 人员信息表的钉钉组织同步使用 `furniture-3d-system/scripts/install-dingtalk-directory-sync-timer.sh`,生产 systemd 名称为 `furniture-dingtalk-directory-sync.service` / `furniture-dingtalk-directory-sync.timer`。
- 钉钉同步每天 `03:00` 执行一次,读取生产共享 `.env` 中的 `DINGTALK_API_CLIENT_ID` 和 `DINGTALK_API_CLIENT_SECRET`,并自动更新人员信息;正式页面仍保持只读。钉钉网页登录使用独立的 `DINGTALK_LOGIN_CLIENT_ID`、`DINGTALK_LOGIN_CLIENT_SECRET`,两组凭据不能混用。
- 人员信息的每日更新口径:钉钉新人自动新增一行;在职人员按钉钉更新部门/负责人;人工覆盖(manualOverride)的行保持不变;**离职人员不做删除,只在页面把「在职状态」变为离职**(方案 B:先比对上一轮在册且本轮抓取范围内消失的人,再逐个调 `user/get` 确认 60121 用户不存在才标记;抓取人数明显缩水时整轮跳过并写入告警)。
- 首次部署后需在正式服务器执行该安装脚本,并手动执行一次 `npm run sync:dingtalk-directory:apply` 完成首次填充;后续由 systemd 每日执行。
## BOM 成本参考镜像
@ -50,6 +54,19 @@
- 准确度回测:`npm run backtest:bom-cost-reference -- --sample-size=30`,只读最新成功镜像,不访问上游;样本限制 20 至 50 个,任一可比较 SKU 超过固定 10% 误差门槛即失败。
- 迁移、真实同步、安装/启用定时器和生产回测都必须另行获得生产操作授权;部署代码本身不代表这些动作已经执行。
## 商品 SABC 月度核算
- 核算命令:`npm run run:product-sabc -- --year=2026 --month=8 --mode=APPLY --confirm=run-product-sabc`;试算(只算不写库)用 `--mode=PREVIEW`,末尾加 `--skip-if-exists` 可跳过已生成月份。
- 8 月人工表对账命令:`npm run compare:product-sabc-xlsx`,默认读取本机下载的人工工作簿;生产执行前应先完成产品全量档案日销售数据同步。
- 生产定时器安装脚本:`furniture-3d-system/scripts/install-product-sabc-timer.sh`。
- 生产 systemd 名称:`furniture-product-sabc-monthly.service` / `furniture-product-sabc-monthly.timer`。
- 默认调度:每月 3 日中国标准时间 `03:00`,附带最多 10 分钟随机延迟;服务生成上一个自然月的数据,已生成过则跳过(不覆盖管理员的重算结果)。
- 生成即冻结:每次生成会把整行明细写入 `SupplyChainProductArchiveSabcResult.snapshot`,页面、筛选下拉、导出和经营总览都只读取该快照。生成之后档案的销售状态、人员字段等变化都不会再改变已生成月份的数据。
- 重新生成(页面「执行核算」)仅限管理员(`ADMIN` / `SUPER_ADMIN`);其他账号只能查看冻结结果。本地一键刷新 `node scripts/local-sync-archive.mjs` 在每月 3 日前会跳过 SABC 步骤,需要立刻重算时用 `--force-sabc`。
- SABC 的销售收款总金额、真实业绩、发货件数、商品毛利额和售后金额统一读取 `SupplyChainProductArchiveSalesPerformanceDaily`,与产品全量档案页面保持同一月度口径。
- 首次安装生成权限为 `600` 的 `/opt/furniture-3d-system-prod/shared/product-sabc-timer.env`,默认 `PRODUCT_SABC_TIMER_MODE=APPLY`;如需先观察可临时改为 `PREVIEW`(只试算不写库、不冻结)。
- SABC 自动任务只读取现有 `DATABASE_URL`;上游日销售同步任务继续使用 `JST_REPORT_DB_*`,凭据不写入定时器配置。安装、迁移、首次 APPLY 和生产核算都需要单独授权。
## 操作日志保留策略
- 清理脚本:`npm run db:cleanup-operation-logs` 默认只做 dry-run;只有显式传入 `--apply --confirm=cleanup-operation-logs` 才会删除数据。
@ -60,6 +77,15 @@
- 安装脚本会在 `/opt/furniture-3d-system-prod/shared/operation-log-retention.env` 保存权限为 `600` 的数据库身份门禁和非敏感参数;身份未填写或保留天数不是 30 天时拒绝启用。
- 首次启用前必须先在生产执行 dry-run,核对候选数量、数据库身份和 systemd 落点;生产安装、首次 `--apply` 和后续代码部署均需另行获得授权。
## 停产需求确认超时收口
- 收口脚本:`furniture-3d-system/scripts/finalize-production-stop-confirmations.ts`,把超过确认截止时间仍未反馈的停产需求按「默认同意停产」落库,并给未反馈的确认人记 `TIMEOUT` 票。
- 生产安装入口:`furniture-3d-system/scripts/install-production-stop-confirmation-finalize-timer.sh`。
- 生产 systemd 名称:`furniture-production-stop-confirmation-finalize.service` / `furniture-production-stop-confirmation-finalize.timer`,默认每 10 分钟检查一次(北京时间),附带最多 1 分钟随机延迟;可用 `PRODUCTION_STOP_FINALIZE_CALENDAR` 覆盖调度。
- 收口逻辑幂等:只处理「已过截止时间且仍待确认」的需求,重复执行不会重复收口;失败由 systemd 在 5 分钟后自动重试。
- 运行日志:`journalctl -u furniture-production-stop-confirmation-finalize -n 100 --no-pager`,每次运行输出本次收口的需求数量。
- 首次安装与启用:在项目根执行 `bash scripts/install-production-stop-confirmation-finalize-timer.sh`(需 root,脚本写入 `/etc/systemd/system/` 并 `enable --now`);安装/启用定时器属于生产操作,需要另行获得授权。
## 明确排除
- `192.168.58.20` 不是当前 `furniture-3d-system` 的生产部署目标。

View File

@ -0,0 +1,163 @@
# 类目配置取数与筛选逻辑
> 用途:给需要接入类目配置数据的开发人员使用。本文描述当前代码和数据库的真实口径(2026-09-24 更新:合并为单表展示结构)。
>
> 重要:本文不包含数据库连接串、账号、密码。连接信息统一见《README.md》。
## 1. 数据源与责任边界
- **权威数据源**:本系统 `DATABASE_URL` 指向的 PostgreSQL,schema `public`,共 2 张表(见第 2 节)。数据通过类目配置页面人工维护 + Excel 导入,**没有外部同步来源**。
- **业务作用**:
1. 维护「二级类目 → 一级类目」的唯一归属(产品全量档案类目列的规范值来源);
2. 维护每个类目的 4 档规模阈值(爆款/热卖款/动销款/低动销款 × 业绩区间、日销区间)与「分类」归属。
- **人工维护原则**:直接写表会绕过唯一键校验和页面权限,接入方一律只读。
### 1.1 代码依据
- Prisma 模型:`prisma/schema.prisma` 中 `SupplyChainProductArchiveCategoryConfig`、`SupplyChainProductArchiveCategoryScaleRow`。
- 页面:`src/app/supply-chain/database/category-config/`(单表展示,数据源即规模行表)。
- 服务:`supply-chain-product-archive-category-scale-row.service.ts`(页面主数据源,含类目清单同步)、`supply-chain-product-archive-category-config.service.ts`(类目主表维护)。
## 2. 物理表清单与分工
| 表名 | 角色 | 当前行数量级 |
| --- | --- | --- |
| `"SupplyChainProductArchiveCategoryScaleRow"` | **类目规模行(页面当前数据源)**:一行 = 二级类目 × 规模标签,自带一级类目、分类、阈值 | 类目数 × 4 |
| `"SupplyChainProductArchiveCategoryConfig"` | **类目主表**:一行一个二级类目的身份记录(唯一性、启停状态),供产品档案同步、类目下拉等消费方使用 | 80+ |
两表关系:
```text
ScaleRow(页面表:一级/二级/分类/阈值/标签,行=类目×档位,唯一键 categoryL2+scaleLabel)
│ 服务写入时自动同步(新增/改行会把一级类目、分类落到主表)
▼
Config(类目身份表:categoryL2 全表唯一 + status 启停,供外部消费方取类目清单)
```
> 历史说明:旧三表结构中的「分类档位模板表」(`SupplyChainProductArchiveCategoryGroupTier`)已于 2026-09-24 迁移(20260924160000)删除,其数据已按类目展开并入规模行表;旧接口 `/api/supply-chain/category-config/group-tiers/*` 已下线。
## 3. 表字段字典
### 3.1 `"SupplyChainProductArchiveCategoryScaleRow"`(类目规模行,页面数据源)
| 字段 | 类型/约束 | 业务含义 | 取值规则 |
| --- | --- | --- | --- |
| `id` | `TEXT` 主键 | 行 ID | 系统生成 |
| `categoryL1` | `TEXT NOT NULL DEFAULT ''` | 一级类目(≤160 字符) | 页面/导入必填 |
| `categoryL2` | `TEXT NOT NULL` | 二级类目名(≤160 字符) | 与 `scaleLabel` 联合唯一 |
| `categoryGroup` | `TEXT NOT NULL DEFAULT ''` | 分类(如 电脑桌/书桌、其他沙发类) | 空 = 未分类 |
| `scaleLabel` | `TEXT NOT NULL` | 规模标签 | 只能是 爆款/热卖款/动销款/低动销款 |
| `performanceStart` | `DECIMAL(14,2)` NOT NULL | 业绩起点(元) | |
| `performanceEnd` | `DECIMAL(14,2)` 可空 | 业绩终点(元) | **NULL = 不设上限** |
| `dailySalesStart` | `DECIMAL(12,2)` NOT NULL | 日销起点(件/天) | 支持小数 |
| `dailySalesEnd` | `DECIMAL(12,2)` 可空 | 日销终点(件/天) | NULL = 不设上限 |
| `sortOrder` | `INTEGER` | 档位顺序:爆款1 → 热卖款2 → 动销款3 → 低动销款4 | 判定从高档往低档取第一个命中 |
| `createdAt` / `updatedAt` | `TIMESTAMP(3)` | 系统字段 | |
约束:`UNIQUE(categoryL2, scaleLabel)`——一个类目每个标签只有一行;索引:`categoryL2`、`categoryGroup`。
### 3.2 `"SupplyChainProductArchiveCategoryConfig"`(类目主表)
| 字段 | 类型/约束 | 业务含义 | 取值规则 |
| --- | --- | --- | --- |
| `id` | `TEXT` 主键 | 配置记录 ID | 系统生成 |
| `categoryL2` | `TEXT` 唯一 | 二级类目名(≤160 字符) | 全表唯一 |
| `categoryL1` | `TEXT NOT NULL` | 一级类目(≤160 字符) | |
| `categoryGroup` | `TEXT NOT NULL DEFAULT ''` | 分类 | 空 = 未分类 |
| `scaleLabel` | `TEXT NOT NULL DEFAULT ''` | 【预留未使用】 | 阈值数据在 ScaleRow 表 |
| `performanceStart` / `performanceEnd` | `DECIMAL(14,2)` 可空 | 【预留未使用】 | 同上 |
| `dailySalesStart` / `dailySalesEnd` | `DECIMAL(12,2)` 可空 | 【预留未使用】 | 同上 |
| `status` | `TEXT`,`ACTIVE` / `INACTIVE` | 启用/停用;停用的不进类目选项 | 默认 `ACTIVE` |
| `createdAt` / `updatedAt` | `TIMESTAMP(3)` | 系统字段 | |
索引:`categoryL1`、`categoryGroup`、`status`;`categoryL2` 唯一。
## 4. 其他线程取数方式
### 4.1 只读 SQL(直连数据库)
```sql
-- 4.1.1 页面同款数据:全部规模行(一级类目、二级类目、分类、4 档阈值)
SELECT "categoryL1", "categoryL2", "categoryGroup", "scaleLabel",
"performanceStart", "performanceEnd", "dailySalesStart", "dailySalesEnd"
FROM "SupplyChainProductArchiveCategoryScaleRow"
ORDER BY "categoryL2" ASC, "sortOrder" ASC;
-- 4.1.2 某个类目的 4 档阈值
SELECT "scaleLabel", "performanceStart", "performanceEnd", "dailySalesStart", "dailySalesEnd"
FROM "SupplyChainProductArchiveCategoryScaleRow"
WHERE "categoryL2" = '住宅家具 - 书桌'
ORDER BY "sortOrder" ASC;
-- 4.1.3 类目身份清单(供下拉/同步用,只取启用的)
SELECT "categoryL1", "categoryL2", "categoryGroup"
FROM "SupplyChainProductArchiveCategoryConfig"
WHERE "status" = 'ACTIVE'
ORDER BY "categoryL1" ASC, "categoryL2" ASC;
-- 4.1.4 某分类下的全部类目及其阈值
SELECT "categoryL2", "scaleLabel", "performanceStart", "performanceEnd"
FROM "SupplyChainProductArchiveCategoryScaleRow"
WHERE "categoryGroup" = '电脑桌/书桌'
ORDER BY "categoryL2" ASC, "sortOrder" ASC;
```
注意事项:
- 表名/字段名**必须带双引号**保持大小写。
- 终点列为 `NULL` 表示不设上限,不要写成 0。
- 停用(`INACTIVE`)类目历史仍在 Config 表里,统计按 `status` 过滤。
- 商品/档案行上的类目与分类是写入时的快照,改配置不回刷历史数据。
### 4.2 代码内取数(同仓库服务)
```ts
// 规模行(页面同款数据:一行=类目×档位,含一级类目与分类)
import { supplyChainProductArchiveCategoryScaleRowService } from
'@/server/services/supply-chain-product-archive-category-scale-row.service'
await supplyChainProductArchiveCategoryScaleRowService.list({ page: 1, pageSize: 100 })
await supplyChainProductArchiveCategoryScaleRowService.listAll({ categoryGroup: '电脑桌/书桌' })
// 类目身份清单(产品全量档案同步、类目下拉在用)
import { supplyChainProductArchiveCategoryConfigService } from
'@/server/services/supply-chain-product-archive-category-config.service'
await supplyChainProductArchiveCategoryConfigService.list({ page: 1, pageSize: 100, status: 'ACTIVE' })
import { supplyChainProductArchiveCategoryConfigRepository } from
'@/server/repositories/supply-chain-product-archive-category-config.repository'
await supplyChainProductArchiveCategoryConfigRepository.findActiveByCategoryL2('住宅家具 - 书桌')
```
### 4.3 走 API(需要登录 + 供应链权限)
| 接口 | 方法 | 说明 |
| --- | --- | --- |
| `/api/supply-chain/category-config/scale-rows` | GET / POST | 规模行列表 / 新增(写入时自动同步类目主表) |
| `/api/supply-chain/category-config/scale-rows/{id}` | PATCH / DELETE | 规模行编辑 / 删除 |
| `/api/supply-chain/category-config/scale-rows/export` · `/import` | GET / POST | 导出(筛选跟随当前搜索/分类/标签) / 流式导入(表头:一级类目、二级类目、分类、规模标签、业绩起终点、日销起终点) |
| `/api/supply-chain/category-config/scale-rows/groups` | GET | 去重分类列表(筛选项) |
| `/api/supply-chain/category-config` · `/{id}` | GET/POST/PATCH | 类目身份维护(供应商新品推荐下拉也走这里) |
| `/api/supply-chain/category-config/export` · `/import` | GET / POST | 类目身份导出 / 导入 |
权限:查询走类目节点「查看」,导出走「下载」,写入走「编辑」;角色 `SUPER_ADMIN/ADMIN/SUPERVISOR` 或业务角色 `MANAGEMENT`。
## 5. 现有消费方(改动前必读)
| 消费方 | 取数点 | 依赖 |
| --- | --- | --- |
| 产品全量档案(DF/聚水潭同步) | `jst-sku-product-archive-sync.service.ts`、`jst-sku-product-archive-full-sync.service.ts` | `findActiveByCategoryL2`:仅启用类目作为规范值 |
| 供应商新品推荐类目下拉 | `/api/supply-chain/supplier-recommended-products/category-options` | Config `service.list`,仅 `ACTIVE` |
| 类目配置页面 | 第 4.3 节接口 | — |
改 `status`、`categoryL2` 名称、删除类目前先确认不影响以上消费方。
## 6. 接入前检查清单
- [ ] 表名/字段名带双引号保持大小写。
- [ ] 方向:`categoryL2`(二级)→ `categoryL1`(一级);`categoryGroup`(分类)是独立维度,不是一级类目,空 = 未分类。
- [ ] 终点列 `NULL` = 不设上限,不要当 0 处理。
- [ ] 档位判定顺序:爆款 → 热卖款 → 动销款 → 低动销款,从高到低取第一个命中区间。
- [ ] 阈值数据取 `ScaleRow` 表;Config 表上的 `scaleLabel`/阈值列为预留未使用。
- [ ] 停用(`INACTIVE`)配置历史仍在 Config 表里,统计按 `status` 过滤。
- [ ] 商品/档案行上的类目、分类是写入时快照,改配置不回刷历史数据。
- [ ] 页面写操作会双写两表(规模行 + 类目主表同步),外部只读不写就不会产生不一致。

View File

@ -0,0 +1,99 @@
# 内容视频流程看板取数与筛选逻辑
> 用途:给需要接入「新品-视频进度(内容部)」(钉钉内容视频流程)数据的开发人员使用。本文描述当前代码和数据库的真实口径(2026-10-08 首版)。
>
> 重要:本文不包含数据库连接串、账号、密码。该页面数据**不存在本系统 PostgreSQL**,全部来自企划 MySQL 的同步快照。
## 1. 数据源与责任边界
- **页面直接数据源**:企划 MySQL(连接变量 `JST_PLANNING_DB_*`,生产为生产服务器本机实例)的 `planning_database` 库,快照表 `oa_video_flow_board_snapshot`。
- **原始数据源**:钉钉 OA 审批镜像库的 `ding_Workflow` / `ding_WorkflowScheme` / `ding_WorkflowTask` 三张表,按流程编码过滤:
- 内容视频流程 `PROC-AE9FF3E1-A442-4E6A-85CF-0F2B86409673`(登记在 `oa-board.constants.ts` 的 `OA_BOARD_PROCESS_CODES.video`)。
- **同步方式**:`scripts/sync-oa-video-flow-board.ts --apply`(即 `npm run sync:oa-video-flow:apply`,服务 `oa-video-flow-board.service.ts`)全量拉取该流程全部实例与表单字段,按"一条流程实例 = 一行"写入快照表,并用 `sync_version` 清理已消失的实例。生产由 systemd 定时器(`install-oa-board-sync-timer.sh` 安装的 `furniture-oa-board-sync.timer`)每 15 分钟在主看板、品牌策划看板之后顺序执行。
- **责任边界**:镜像库与钉钉侧的数据质量不由本系统负责;本系统保证"镜像库 → 快照 → 页面"这一段。下游一律**只读快照表**,不要直连 ding_ 三张源表(源表结构随钉钉变化,无稳定性承诺)。
- **与新品主链的关系**:内容视频流程**不参与** OA 新品看板主链的项目聚合、总状态与时效矩阵(其表单无开款编号/关联单号,仅有 SPU)。本表是独立流程维度的进度数据。
### 1.1 代码依据
- 同步与快照读写的唯一实现:`src/server/services/oa-video-flow-board.service.ts`(建表 DDL 在 `ensureSnapshotTable`,自动建表)。
- 源库连接复用:`oa-board-sync.service.ts` 导出的 `openOaBoardSourceMysqlConnection` / `openOaBoardSourceSqlServer`(跟随 `OA_BOARD_SOURCE_DRIVER`)。
- 页面接口:`GET /api/intake/oa-board/video-flow`(参数 `keyword` / `demandType` / `brand` / `status` / `page` / `pageSize`,需登录态)。
- 页面组件:`src/app/intake/oa-board/OaBoardVideoFlowPanel.tsx`(Tab 参数 `tab=video-flow`)。
## 2. 物理表(MySQL · planning_database)
| 物理表 | 用途 | 键与索引 |
| --- | --- | --- |
| `oa_video_flow_board_snapshot` | 最新快照,一行 = 一条内容视频流程实例 | `row_key` 唯一;`spu` / `status` / `stage_label` / `demand_type` / `brand` / `created_date` 有索引 |
同步任务自动建表(`CREATE TABLE IF NOT EXISTS`),引擎 InnoDB,字符集 utf8mb4。
## 3. 字段字典
| 字段 | 类型 | 含义 | 取值规则 |
| --- | --- | --- | --- |
| `row_key` | `VARCHAR(191)` 唯一 | 钉钉流程实例 ID(processInstanceId),幂等拉取键 | 源表主键,稳定不变 |
| `business_id` | `VARCHAR(128)` | 流程发起编号 | 形如 `202610071758000088167` |
| `title` | `VARCHAR(255)` | 流程标题 | `XX提交的内容视频和图片申请流程` |
| `submitter_name` | `VARCHAR(255)` | 提交人姓名 | 从标题提取;`originator_user_id` 存钉钉用户 ID 兜底 |
| `status` | `VARCHAR(32)` | 流程状态 | `RUNNING` / `COMPLETED` / `TERMINATED` |
| `demand_department` | `VARCHAR(255)` | 需求部门 | 表单字段「需求部门」 |
| `demand_type` | `VARCHAR(255)` | 需求类型 | 如 淘系-逛逛视频 / 抖音-千川素材视频 / 安装视频 / AI视频 等 13 种 |
| `task_type` | `VARCHAR(255)` | 任务类型 | 如 布景拍摄 / 二次剪辑 |
| `install_video_category` | `VARCHAR(255)` | 安装视频分类 | 仅安装视频类需求有值(如 B档) |
| `brand` | `VARCHAR(255)` | 品牌 | 表单字段「品牌」 |
| `scene_category` | `VARCHAR(255)` | 场景类别 | 如 卧室 / 客厅 / 玄关 |
| `spu` | `VARCHAR(64)` | SPU | 大写规范化;约 3% 行无值(账号维护等非产品需求) |
| `product_id` | `VARCHAR(128)` | 商品ID | 表单字段「商品ID」,可能为"暂无" |
| `sample_barcodes` | `TEXT` | 样品条码 | 表单「样品明细」表格中商品编码列,多值分号分隔 |
| `content_owner` | `VARCHAR(255)` | 内容部负责人 | 表单字段 |
| `planning_supervisor` | `VARCHAR(255)` | 品牌策划主管 | 表单字段 |
| `script_director` | `VARCHAR(255)` | 编导 | 流程推进到该环节时才填,未填为 NULL |
| `cameraman` | `VARCHAR(255)` | 摄像 | 同上 |
| `editor` | `VARCHAR(255)` | 剪辑 | 同上 |
| `retoucher` | `VARCHAR(255)` | 修图 | 可选环节 |
| `cover` | `VARCHAR(255)` | 封面 | 同上 |
| `stage_label` | `VARCHAR(64)` | 当前阶段 | 推断规则见第 4 节 |
| `acceptance_result` | `VARCHAR(255)` | 验收结果 | 表单「运营/策划/客服验收结果」 |
| `score` | `VARCHAR(64)` | 评分 | 表单「评分」 |
| `created_date` | `DATETIME` | 流程发起时间(北京时间) | 源表 createAt |
| `finished_date` | `DATETIME` | 流程完成时间 | RUNNING 为 NULL |
| `duration_days` | `DECIMAL(10,2)` | 总时效(天) | 发起 → 完成;未完成计至同步时刻 |
| `last_source_updated_at` | `DATETIME` | 源表最近更新时间 | 增量拉取判据 |
| `sync_version` | `VARCHAR(64)` | 本次同步批次标识 | 全表同批;用于清理已消失实例 |
| `synced_at` | `TIMESTAMP` | 本行最近同步时间 | 数据新鲜度判据(`MAX(synced_at)`) |
## 4. 阶段(stage_label)推断规则
源表只有 activityId 没有节点名,首版按表单字段的推进顺序推断(钉钉流程设计中角色字段按环节顺序逐步填写):
```text
status = COMPLETED → 已完结
status = TERMINATED → 已终止
验收结果或评分已填 → 验收/评分
否则取已填角色的最后一个:
编导 → 编导制作 → 摄像 → 摄影拍摄 → 剪辑 → 剪辑制作
→ 修图/封面 → 封面/修图制作
一个角色都没填 → 需求发起
```
若后续拿到钉钉流程节点名清单,可升级为 activityId 级精确映射(预留位:`oa-board.constants.ts` 的 `OA_BOARD_ACTIVITY_MAPPING_SEED.video` 当前为空数组)。
## 5. 接入示例(一律只读)
```sql
-- 按 SPU 拉该产品的全部视频流程
SELECT * FROM oa_video_flow_board_snapshot WHERE spu = 'SJ0159';
-- 只看进行中的
SELECT * FROM oa_video_flow_board_snapshot WHERE status = 'RUNNING';
-- 增量拉取(幂等键 row_key)
SELECT * FROM oa_video_flow_board_snapshot
WHERE last_source_updated_at > :上次拉取时间;
-- 取数前先看新鲜度(生产最多滞后 15 分钟)
SELECT MAX(synced_at) FROM oa_video_flow_board_snapshot;
```
关联口径:与产品数据对齐用 `spu` 或 `sample_barcodes` 中的条码;本表**没有开款编号**,不要与主看板的 `OPENING:` 维度直接关联。

View File

@ -0,0 +1,188 @@
# BOM 成本预估 v2(agent 方案)设计文档
> 日期:2026-09-16
> 状态:设计稿,待评审
> 前置阅读:`docs/项目总览.md`、`src/server/services/bom-costing/agent/`(已建成的引擎)
> 关联会话结论:1688 以图搜图锚定为过渡方案(双重失败已实测证伪),终局为 agent 估算 + 公司数据
## 1. 一句话定位
**粘贴竞品链接 → 几分钟后得到一张诚实的成本预估卡。**
原始需求从未变过。v2 的一切设计决策都回到这句话:数字 + 构成故事 + 诚实的区间,工序全部退到幕后。
## 2. v1 的教训(为什么 v2 长这样)
| v1 的问题 | 根因 | v2 的对策 |
|---|---|---|
| 工序暴露给用户(逐图泳道、五阶段时间线、重算模式、复核流) | 工程美德当成了产品 | 一根进度条 + 一张卡;审计链存在但默认隐藏 |
| 27 个 SKU 全量核算(用户等十几分钟才见结果) | 首结果时间没有被设计 | 首批 3 个精算先出结果,其余按需点算,结果缓存 |
| 十几分钟等待 | 按 SKU 串行/并发全量跑 | case 级一次运行,2-3 分钟 |
| 复杂度随精度承诺膨胀 | 用流程堆精度 | 用数据+校准逼近精度(±10% 是"尽量"的校准目标) |
| 1688 锚定顶替估算 | 上线压力下的权宜 | 1688 永远是后台交叉验证信号 |
**必须保留的教训**:用户会锚定数字。区间 + 置信度 + 不确定点是答案的一部分,不是脚注——这是"给一个数"和"给一个诚实的数"的差别。
## 3. 设计总纲(五条,裁决一切分歧)
1. **预估,不是核算**:输出是带区间与置信度的假设生成器,供决策筛选,永不承诺报价级精度。
2. **agent 主导分析,代码承载确定性**:看什么图、怎么拆结构、查什么价格由 agent 决定;一切数值计算(汇总、区间合成、损耗)由确定性代码完成。模型永不算数、永不编价。
3. **价格只认公司数据**:历史 BOM 镜像是唯一定价依据(检索→分类中位→cohort→损耗四级回退);1688 行情仅作后台交叉验证,永不成为展示价。
4. **前端来源中性(不可触碰)**:界面永不出现 1688 字样/报价/链接;一切序列化出口经过 `src/lib/bom-costing/public-estimate-text.ts`。
5. **实测闸门**:agent 流程未经回测与真实案例对比验证(偏差分布达标)前,不替换任何线上行为。
## 4. 用户形态
### 4.1 默认入口:预估卡 + 全量 SKU 按需核算
**覆盖率是需求,等待时间是体验问题**——所有 SKU 都要能出结果,但采用"先出一批、其余按需点算"的渐进交付:
```
粘贴链接 → [进度条:抓取中 → 分析中 → 出预估](2-3 分钟)→
┌────────────────────────────────────────┐
│ 源氏木语 白蜡木实木衣柜 │
│ 预计出厂成本 ¥260 – 380(首批 3 个 SKU)│
│ 置信度:中等 │
│ 板材 ¥180± · 五金 ¥25± · 加工+人工 ¥60± · 包材 ¥15± │
│ 依据:8 张商品图 + 公司历史 BOM 12 条 │
│ 不确定:板厚未确认;铰链数量按两门推定 │
└────────────────────────────────────────┘
SKU 列表(27 个):
[¥260.29 已核算] (3) 原木色 0.9米两门 ← 首批自动精算
[¥375.48 已核算] (7) 原木色 1.0米 ← 首批自动精算
[¥406.77 已核算] (18) 原木色 1.96米 ← 首批自动精算
[点击核算] (4) 原木色 0.9米/高2.2米 ← 用户点击后单独运行
[点击核算] ... ← 已算结果缓存,不重跑
────────────────────────────────────────
商品图片(保留现有形式:主图/SKU图/详情图 分类浏览 + 大图预览)
```
- **首批自动精算**:默认 3 个代表性 SKU(跨度原则:最小/最大/中间,或按材质/结构差异选),2-3 分钟出卡——用户立刻有结果。
- **其余 SKU 按需点算**:未核算的 SKU 显示占位按钮,用户点击触发单 SKU agent 运行(复用现有 per-SKU runner,1-2 分钟/个);**结果持久缓存**,重复点击不重跑。
- 进度状态只有三个:抓取中 / 分析中 / 出预估(含失败态与重试按钮)。
- **渐进展开**:点构成 → BOM 行明细;点行 → 证据(哪张图、哪条镜像参考行)。审计链存在,默认折叠。
- **商品图片保留现有展示形式**(主图/SKU 图/详情图分类 + 逐图状态 + 大图预览):用户需要直接查看竞品链接内容,图片是判断依据,不是工程噪声。
### 4.2 深度模式(保留现有工作台)
需要全量 27 SKU 拆解、人工核对、证据溯源的用户,从预估卡一键进入现有工作台。v2 验证前工作台不动;验证后它降级为二级入口,不再是默认。
## 5. 架构:case 级 agent 运行
### 5.1 运行编排(新,核心增量)
交付模型:**首批自动精算 + 其余按需点算**。现有 per-SKU runner 是按需点算的执行体,case 级编排负责首批选择与结果汇总。
```
阶段一:runBomCostingAgentEstimateForCase(caseId)(首批)
1. 取案例简报(标题/渠道/已解析品类 skill)+ 归档素材 + SKU 清单(含尺寸)
2. 代码侧选首批 SKU(默认 3 个:最小/最大/中间,或按材质/结构差异;
单 SKU 商品即为它自己)
3. 逐个运行现有 per-SKU agent 循环(结构行 + 查价)
4. 代码侧汇总出 EstimateCard:卡面五行/区间/置信度 + SKU 列表状态
(已核算 3 / 待核算 24)
阶段二:按需点算(用户驱动)
用户点击未核算 SKU → 单 SKU agent 运行(现有 runner)→
结果持久缓存 → 卡面/SKU 列表实时更新
```
- 首批预算:≤24 轮 / ≤32 次模型调用 / ≤8 分钟(per-SKU 预算 × 3 + 编排开销)。
- **结果缓存**:per-SKU 结果按 (caseId, skuId, 引擎版本) 持久化;点击已核算 SKU 直接展示缓存,不重跑。SKU 重算 = 显式刷新动作(换引擎版本或用户主动重跑)。
- 失败语义:首批中单个 SKU 失败 → 该 SKU 标记失败可单独重试,不拖垮整卡;结构成功但价格饥饿 → 出结果但区间放宽 + 不确定点说明(PARTIAL 是合法产物)。
- 并发上限与幂等:同一 SKU 的点算请求可去重合并(进行中重复点击返回同一运行)。
### 5.2 输出契约:EstimateCard(case 级)
```ts
interface BomEstimateCard {
caseId: string
title: string
skus: Array<{ // 全量 SKU 清单 + 状态
externalSkuId: string
name: string
status: 'ESTIMATED' | 'PENDING' | 'FAILED' | 'RUNNING'
estimate?: BomCostingAgentEstimate // 已核算时:行/区间/置信度/evidence
cachedAt?: string
}>
firstBatchSkuIds: string[] // 首批自动精算的 SKU
breakdown: Array<{ // 卡面五行(由已核算 SKU 的行汇总;随点算增长)
category: 'BOARD'|'STEEL'|'ACCESSORY'|'PACKAGING'|'PROCESSING'|'LABOR'
total: number | null // null = 该组无价,卡面显示"待补"
}>
totalRange: { min: number; max: number } // 当前已核算 SKU 的区间包络
confidence: number // 已核算 SKU 的最小置信度
openQuestions: string[] // 卡面"不确定"(已核算 SKU 的并集)
usage: { rounds; modelCalls; elapsedMs }
}
```
原则:明细(行级)保留在各 SKU 的 estimate 里供展开;卡面只消费 breakdown/totalRange/confidence/openQuestions,并随按需点算增量更新。
### 5.3 价格供给(核心依赖,最大缺口)
首测"定价饥饿"的根治项——把 v1 场景服务的回退链包装成 agent 工具:
| 优先级 | 工具 | 包装自 |
|---|---|---|
| P0 | 尺寸感知镜像检索(分类/材质词/尺寸/参考 SKU 多级过滤) | `findReferenceCandidates` |
| P0 | 分类画像中位价 | `fillCategoryProfileFallbacks` |
| P1 | 同类 cohort 报价 | `resolveCohortQuote` |
| P1 | 历史损耗率 | `applyHistoricalLossFactorFallbacks` |
| P2 | AI 补估(置信度上限 0.75 + 标记复核) | 现有 estimator |
工具输出纪律:摘要级(无供应商/店铺/链接),带 sourceRowId 供 evidence 引用,单位明确。**1688 工具维持现状**(只透出状态/参考价/区间/可比数四个中性字段)。
### 5.4 数据与知识资产接入(全部保留,形态不变)
| 资产 | v2 中的角色 | 接入形态 |
|---|---|---|
| 历史 BOM 镜像(每日 MSSQL 同步) | 唯一价格依据 | 工具(5.3) |
| 品类 Skill(5 个)+ 结构先验 | agent 脑中的专家清单:必备组件、证据纪律、校验规则 | 提示词注入(`renderBomCostingAgentSkillKnowledge`,已建成) |
| 材质目录 | 材质身份唯一权威 | 工具(已接线) |
| 商品抓取/归档/抠图 | 看图取证的素材 | 工具 + mission 组装(已接线;抠图模型待切 remove-background) |
| 1688 行情 | 后台交叉验证信号 | 工具(已接线,中性摘要) |
### 5.5 校准闭环(±10% "尽量"的实现方式)
- **回测**:历史 BOM 镜像抽 20-50 个真实 SKU,同一规则/agent 重算 vs 真实成本,产出误差分布(复用 `backtest:bom-cost-reference` 基建)。
- **shadow 对比**:agent 结果与现行引擎结果对同一案例并排落库(工程域),持续记录偏差(`report:bom-cost-shadow-divergence` 同款模式)。
- **阈值语义**:±10% 是回测循环追的校准目标(按品类分段看分布),不是单案例门禁。偏差大 → 调工具/知识/提示词 → 再回测。
- 未过门槛 → agent 流程保持旁路,线上仍是现行方案。
## 6. 前端形态
- **新入口**:`/bom-costing` 默认视图替换为预估卡流(粘贴框 → 卡 → SKU 列表 → 图片浏览)。
- **商品图片**:保留现有展示形式(主图/SKU 图/详情图分类 tab + 大图预览 + 逐图状态),用户需要直接查看竞品内容;它从"处理进度件"回归"内容主体"。
- **SKU 列表**:已核算显示价格,未核算显示"点击核算"按钮(运行中显示进度态);点击结果写缓存并即时更新卡面。
- **组件**:新 `BomEstimateCard`(平台组件体系 + 白曜石规范),状态只有 加载/成功/失败(可重试)。
- **现有工作台**:不动,作为深度模式从卡进入。
- **移动端**:卡、SKU 列表与图片浏览响应式。
- 红线复检:卡面文案经过 `publicEstimateWarningText` 清洗;evidence 展开层同样不得出现来源敏感词。
## 7. 落地顺序(每步可独立验证)
| 步骤 | 内容 | 验收 |
|---|---|---|
| ① | 价格回退工具(P0 两个) | 对已知案例重跑,未定价行显著下降 |
| ② | 首批编排 + EstimateCard 契约 + 按需点算与结果缓存 | 单案例 2-8 分钟出首批卡;点击未核算 SKU 增量出结果且缓存生效 |
| ③ | 预估卡前端(含 SKU 列表与现有图片浏览)+ 三态进度 | 真人走通 粘贴→出卡→点算其他 SKU→看图核对 全流程 |
| ④ | 回测 + shadow 接入,按品类出基线报告 | 误差分布报告(对照 ±10% 目标) |
| ⑤ | 实测样本扩到 5-10 案例,按品类定门槛 | 达标 → 翻转默认入口;未达标 → 继续迭代 |
## 8. 红线(重申)
1. 前端永不出现 1688 相关内容;一切文本出口过脱敏契约。
2. agent 引擎在过实测闸门前保持旁路(不替换线上行为)。
3. 区间 + 置信度 + 不确定点是答案的一部分,任何展示形态不得省略。
4. 单价只来自工具结果;总额只由代码汇总。
5. 现有工作台在 v2 过门槛前不删不改语义。
## 9. 当前资产清单(截至本文日期)
已建成:agent 契约/工具层/循环/网关适配/skill 知识渲染/per-SKU runner(27 测试)、镜像与材质只读仓储、1688 中性摘要工具、板材开料工具、shadow 报表、trial 试跑脚本、生产真实案例(源氏木语 27 SKU)对照数据。
待建:价格回退工具(5.3 表)、case 级编排、EstimateCard 契约与前端、回测接入。
外部依赖:1688 新 key(当前过期,影响搜图与 A/B 实测,不影响 ①② 开发)。

View File

@ -78,9 +78,13 @@
# 是否启用 OA 看板钉钉待办
DINGTALK_TODO_ENABLED=false
# 钉钉应用基础配置
DINGTALK_CLIENT_ID=
DINGTALK_CLIENT_SECRET=
# 钉钉开放平台 API 应用配置(待办、审批等服务端调用)
DINGTALK_API_CLIENT_ID=
DINGTALK_API_CLIENT_SECRET=
# 钉钉网页登录使用独立 OAuth 应用配置,不与 API 应用混用
DINGTALK_LOGIN_CLIENT_ID=
DINGTALK_LOGIN_CLIENT_SECRET=
DINGTALK_CALLBACK_ORIGIN=
# 待办跳转回系统的基础地址

View File

@ -1,6 +1,6 @@
# 供应链产品全量档案字段来源对照表
更新时间:2026-08-10
更新时间:2026-10-16
## 1. 合并与追踪规则
@ -70,7 +70,7 @@
| `sales7d` | 7日日销 | | | `rpt.pm_stockMonitor.sevenAvgSale` | 按条码匹配最新记录 |
| `sales15d` | 15日日销 | | | `rpt.pm_stockMonitor.fifteenAvgSale` | 按条码匹配最新记录 |
| `purchasePrice` | 采购价 | `rpt.jst_sku.purchase_price` | | | 按档案条码精确匹配 `jst_sku.sku_id`;匹配不到时留空 |
| `costPrice` | 成本价(聚水潭) | `rpt.jst_sku.cost_price` | | | 按档案条码精确匹配 `jst_sku.sku_id`;匹配不到时留空 |
| `costPrice` | 成本价(聚水潭) | `rpt.jst_sku.cost_price` | | | 按档案条码精确匹配 `jst_sku.sku_id`;匹配不到时留空。DF 条码自 2026-10-16 起同样取本来源(原 `rpt.dws_sn_sales_full_info_di.goods_cost` 为金额口径,已停用) |
| `costPriceSecondary` | 成本价(新品) | | `external.product_catalog_wide_v1.cost_price` | | 按档案条码精确匹配 `barcode_business_key`,只读取 `catalog_source = INTAKE_READY` 的新品行;匹配不到或来源为空时留空 |
## 3. JST_SKU 第一批字段覆盖率

View File

@ -0,0 +1,289 @@
# 物流到货签收时长看板 · 订单明细每日快照
## 为什么要做
看板的全部数据来自远端只读报表视图 `rpt.jst_order_express_details`(外加 `jst_qmcust_orderlist_item` 条码明细和 `SupplyChainProductArchive` 产品档案富化)。
该视图是远端对象,一次扫描通常以分钟计,页面每次刷新、翻页、下钻、导出都要重新扫一遍,看板因此很难"高效运转"。
做法:每天把看板口径的订单明细物化到本地 Postgres,看板优先读本地快照,只有快照覆盖不到时才回落实时数据源。
## 数据与表
### 条码/供应商/采购的匹配来源(两套互补的订单表)
报表库把订单明细拆成了两套**互不重叠**的表,只看一套会漏掉近一半订单的条码:
| 表 | 覆盖平台 | 规模(实测) |
| --- | --- | --- |
| `rpt.jst_qmcust_orderlist` / `rpt.jst_qmcust_orderlist_item` | 淘宝天猫、阿里巴巴(1688)、淘工厂、天猫供销 | 408 万 / 576 万行 |
| `rpt.jst_Orders` / `rpt.jst_OrderItem` | 京东商城、京东厂家直送、拼多多、头条放心购(抖音)、得物、唯品会、线下、微信视频号、小红书 | 124 万 / 175 万行 |
看板取条码时会**同时查两张明细表并用 `INNER_ORDER_ID`(即 `o_id`)合并**(`listItemRows`,两表按订单号各发一批查询后合并结果)。修复前只查 `jst_qmcust_orderlist_item`,导致京东/拼多多/抖音/得物/唯品会的订单条码全空;修复后新生成的快照条码匹配率为 **100%**(此前整体 52.3%)。
> 注:`rpt.jst_salesOutboundInfo`(销售出库单头)可以读取,但只有 `o_id / so_id / l_id / shop_name / io_id / qty` 等出库单头字段,没有 SKU 列,不能单独提供条码。
纯新增两张表(迁移 `prisma/migrations/20260928120000_add_logistics_arrival_signing_duration_snapshot`):
| 表 | 作用 |
| --- | --- |
| `LogisticsArrivalSigningDurationSnapshotRun` | 批次元数据:`snapshotDate`、`windowFrom`、`windowTo`、`status`(RUNNING/COMPLETED/FAILED)、`rowCount`、`message` |
| `LogisticsArrivalSigningDurationSnapshot` | 明细行:与看板 09 订单明细一一对应,含条码/供应商/采购/`productItems` 等快照时点已固化的富化结果 |
要点:
- 只有 `status = COMPLETED` 的批次对读路径可见,写入中途或失败的批次不会被读到。
- 行唯一键 `(snapshotDate, rowKey)`,`rowKey` 与下钻键一致(内部单号 + 线上订单号 + 物流单号 + 签收时间),重复执行用 `skipDuplicates` 幂等。
- `(snapshotDate, signTime)` 有索引,按签收日期区间取数走索引。
## 生成任务
```bash
# 默认:快照日期=当天(Asia/Shanghai),窗口=最近 62 天(覆盖本月与上月),保留最近 2 批
npm run sync:logistics-arrival-signing-duration-snapshot -- \
--apply --confirm=sync-logistics-arrival-signing-duration-snapshot \
--expected-database-identity="<本地库身份>" \
--expected-source-database-identity="<远端报表身份>"
```
可调参数:`--snapshot-date=YYYY-MM-DD`、`--window-days`、`--chunk-days`(默认 7,按天分批抓取以控制内存)、`--batch-size`(默认 2000)、`--retention`、`--force`(当天批次已完成时强制重建)。
编排逻辑在 `src/server/services/logistics-arrival-signing-duration-snapshot.service.ts`:
1. 当天批次已 `COMPLETED` 且未 `--force` → 直接跳过;
2. 标记 `RUNNING` 并清空当天旧行;
3. 按 `chunk-days` 分片读远端并写入本地,同时记下真正抓到的最新签收时间 `dataThrough`;
4. 全部成功才标记 `COMPLETED`(连同 `dataThrough`),随后按 `retention` 清理更早批次;任一分片失败则标记 `FAILED` 并保留错误信息,读路径会自动继续用上一批快照;
5. 收尾关闭远端连接池(一次性任务必须关闭,否则进程挂在事件循环上)。
生产定时(systemd timer,默认北京时间 03:00):
```bash
sudo bash scripts/install-logistics-arrival-signing-duration-snapshot-timer.sh
```
首次安装会生成 `$BASE/shared/logistics-arrival-signing-duration-snapshot.env` 模板,需要填两个身份校验值(缺省为空会拒绝写入)。可用环境变量覆盖:`LOGISTICS_SNAPSHOT_CALENDAR`、`LOGISTICS_SNAPSHOT_WINDOW_DAYS`、`LOGISTICS_SNAPSHOT_RETENTION`。
## 读路径规则
`src/server/repositories/logistics-arrival-signing-duration.repository.ts` 的每个读方法都先判断快照是否可用:
- 取最近一批 `COMPLETED` 快照;请求区间**完全落在快照的实际覆盖范围**内才读快照(否则回落实时视图,避免返回不完整结果)。覆盖上限 = `min(windowTo - 1, dataThrough 当天)`:
- `windowTo` 是跑批当天,当天还没签收完,所以只能信任到它前一天;
- 再用批次记录的 `dataThrough`(该批真实最大签收时间)兜底,避免数据源滞后时把不完整的一天当成完整。
- 例:一批 10-08 生成、数据只到 10-08 05:30 的快照,对 `10-01~10-08` 不再命中,看板/导出自动改用实时源,不会少算 10-08。
- 命中快照时返回值带 `source: 'snapshot'` 与 `snapshotDate`,页面头部显示"快照数据 · 日期";未命中为 `source: 'live'`,显示原来的"数据更新 · 时间";
- 快照表还没迁移或还没有任何批次时,`findLatestCompletedRun()` 返回 null(并吞掉异常),整条链路自动等价于改造前行为。
因此在以下情况仍会走实时数据源(较慢,但结果正确):请求区间超出窗口、任务当天尚未跑完或失败、本地还没生成过快照。
## 指标口径(整区间精确 vs 样本)
命中快照时,**整块看板都是整区间精确值**,与 2,000 条样本彻底解耦:
| 看板块 | 精确口径实现 |
| --- | --- |
| 经营总览(6 个 KPI) | `aggregateDashboard()` 一次扫描聚合 |
| 履约环节拆解(5 环节平均/中位/P90) | 同上,`percentile_cont` 直接给出分位数 |
| 发货→签收时效分布 | 同上,4 个区间计数 |
| 付款→签收 / 付款→发货 多维经营分析(01–08) | `aggregatePayToSignAnalysis()` / `aggregatePayToSendAnalysis()`,按签收日期 + 采购/供应商/条码(`jsonb_array_elements` 展开 `productItems`)+ 物流公司/发货仓/平台/店铺分组 |
| 发货仓 / 物流公司 超时排名 | `aggregateThresholdRankings()` |
| 09 订单明细下钻 | `listDrilldownRows()`:按维度条件下钻,**列出全量匹配订单**并给出真实总数与页数 |
口径一致性由三点保证:筛选条件统一由 `snapshotFilterConditions()` 派生(Prisma 与原始 SQL 共用);比率/取整统一走 lib 的 `buildLogisticsArrivalSigningDurationOverview`、`buildPayToSignMetrics`、`buildPayToSendMetrics`;排序统一走 `comparePayToSignGroups` / `comparePayToSendGroups` / `compareLogisticsArrivalSigningDurationThresholdRows`。每个聚合块都用"把整区间明细喂给样本路径同样函数"的方式做过逐字段比对(MATCH)。
### 看板顺序与联动链路
### 履约环节拆解:目标值与看板宽度
### 经营总览:平均到货时长卡片与异常下钻
- 签收订单后面新增 **付款→签收平均到货时长** 卡片:主值是该环节平均时长,附行给出 **本月目标**(上月平均 × 98%)与 **目标达成率**,与履约环节拆解同一套口径。
- **缺少关键时间 / 时间异常** 两张卡片可点击:点击后用 `{ kind: 'anomaly', value: 'missing' | 'invalid' }` 条件下钻,09 订单明细列出对应订单;再点一次取消。判定口径与总览一致——缺少关键时间 = 四个时间任一为空;时间异常 = 四个时间齐全但至少一个环节时长算不出来(时间顺序异常)。
- **自动滚动只保留给经营总览的异常卡片**(卡片上写着"点击看明细"):点它们会把 09 订单明细滚到视口顶部(`detailBoardRef` + `scrollIntoView`,按"下钻签名"去重,不会在翻页时反复滚动)。**经营对象维度(平台/店铺/发货仓/物流/采购/供应商/商品/签收日期)的点选不再自动滚动**——那些看板是连着看的,一滚到底就没法接着看其他维度、还得手动往上拉;下钻筛选本身照常生效,09 明细就在原位置更新。
- 为保证"卡片数字 = 点击后的明细条数",**命中快照时经营总览始终按整区间精确统计**(`aggregateDashboard(..., includeStages=false)`,只算总览不算分位数);超出精确上限的大区间里,只有履约环节拆解/时效分布/经营分析/超时排名退回样本,页面脚注会写明。
### 经营总览视觉层次与看板工具条
- 经营总览从"7 张卡一张网格"改为**两组网格**:5 张履约指标卡一排(`--sc-kpi-cols`,标准 5 / 紧凑 3 / 展开 2),2 张数据质量卡单独成排(`--sc-kpi-alert-cols`,标准 2 / 展开 1)。7 张卡在常见宽度下不再是 5+2、第二排右侧大片留白。
- 卡片排版统一:标签 12px 用 `--sc-muted`,数值 24px 且 `font-variant-numeric: tabular-nums`,用 `margin-top: auto` 贴底、副标题保留 2 行最小高度(`min-height: 2.9em`)。同一行内标签换行不影响数值对齐,卡片等高、数字列对齐。
- 绿色强调卡(付款→签收平均到货时长、付款→签收履约率)改为"浅绿底 + 左侧 2px 竖条"(`inset 2px 0 0`),不再整卡大面积着色;点击态的竖条与边框跟着状态色走。
- 缺关键时间 / 时间异常改为横向"告警条":左侧图标 + 名称/说明,数值贴右,用低饱和暖色表达"异常"语义;点击下钻、`is-active` 选中态、`aria-pressed` 与 `aria-label`(含条数)都保留。
- 修复看板工具条(导出 / 尺寸切换)样式不生效的根因:`BoardTools` 是本文件内的**独立组件**,styled-jsx 按组件注入作用域类,主组件里的 `.sc-logistics-icon-button` / `.sc-logistics-board-tools` / `.sc-logistics-board-size-label` 规则永远匹配不到它的 DOM,四个看板右上角一直是"裸图标"。现在样式放进 `BoardTools` 自己的 `<style jsx>`(深色排名表头用 `:global(.sc-logistics-ranking-board-header)` 适配),并删除主样式块里的死规则。
- 每个环节多出一列 **目标**:`本月目标 = 上月同环节平均时长 × 98%`(`LOGISTICS_ARRIVAL_SIGNING_DURATION_TARGET_IMPROVEMENT_RATIO`)。基准月取所选区间结束日期所在月的上一个月(跨年正确),基准值由 `aggregateStageAverages()` 用同一套筛选条件跑上个月区间得到;当前平均不高于目标时目标值标绿表示达标,鼠标悬停可看到"目标 = 上月平均 × 98%"的算式。
- 目标后面还有 **目标达成率**:该环节时长不超过目标的订单占比。精确口径由 `aggregateStageTargetHits()` 按目标值统计命中订单数,样本口径由 `buildLogisticsArrivalSigningDurationStageAchievement()` 在样本上计算;无目标或无可计算订单时显示 `-`,鼠标悬停显示"命中 / 可计算"明细。环节行的平均/中位/P90/目标/达成率都只显示数值,单位(小时)只在表头语义里体现。
- 环节行按 `128px / 弹性条 / 74 / 74 / 86 / 92` 收紧列宽,列表支持横向滚动,避免 P90、目标被截断。
- 履约环节拆解与发货→签收时效分布之间加了**可拖动的分隔条**(`role="separator"`,支持左右方向键微调),比例默认 62% 并记在本地,可随时左右拖动调整两个看板的宽度。
经营对象问题排名各看板按"上级 → 下级"顺序排列,并逐级联动:
```
01 签收日期维度 → 02 平台维度 → 03 店铺维度 → 04 发货仓维度 → 05 物流维度
→ 06 采购维度 → 07 供应商维度 → 08 商品维度 → 09 订单明细
```
点击任意一级对象,都会保留更上级条件、丢弃更下级条件,并把这条链路作为筛选条件重新取数:
- 整区间口径:链路以 `cascade`(JSON 数组,`LogisticsArrivalSigningDurationDrilldownFilter[]`)随 dashboard 请求下发,服务端在 `aggregatePayToSignAnalysis` / `aggregatePayToSendAnalysis` / `aggregateThresholdRankings` 的 WHERE 上追加 `cascadeSqlWhere`,因此下级看板与 09 明细都只展示对应数据;
- 样本口径(实时源或超出精确上限):前端用 `logisticsArrivalSigningDurationRowMatchesCascade` 收窄样本后重算;
- 店铺维度分组键是"平台 + 店铺",因此平台级联动能精确到该平台下的店铺;
- 联动刷新是原位刷新(只有页面筛选变化才显示整块加载态),排名看板标题会显示当前链路与"联动加载中…"提示;
- 经营总览、履约环节拆解、时效分布属于页级口径,不随联动链路变化。
### 经营对象的目标与目标达成率
02–08 经营对象表(平台 / 店铺 / 发货仓 / 物流 / 采购 / 供应商 / 商品)在原有指标后面追加 **目标** 与 **目标达成率** 两列;01 签收日期维度的「指标 × 日期」矩阵与总计不追加(按日算"上月同日目标"没有业务含义)。
- **目标 = 该对象上月同环节平均时长 × 98%**,达成率 = `100% − (平均 − 目标) / 目标 × 100%`,与履约环节拆解同一套口径,只是精确到每个经营对象——所以每个对象的目标值不同(例如 08 月各采购平均不同,9 月目标 52.7 / 56.5 / 60.5 …),达成的判断是"相对自己上个月是否改善 2%"。
- 数值口径与展示一致:平均、目标都按 1 位小数取值后再算达成率,便于和表里看到的数字对账;没有上月基线的对象(新采购、新店铺、当月新条码)显示 `-`。
- 取数:`aggregateObjectBaselines()`(快照仓库)在上月区间一次扫描算出 7 个维度 × 付款→签收 / 付款→发货 两个环节的平均时长,随 dashboard 载荷以 `objectBaselines` 下发;前端用 `withObjectAnalysisTargets()` 按分组键(`JSON.stringify(keyParts)`,与样本路径同一套键)合并,样本口径与整区间口径共用。
- 这条聚合约 2–4 秒,因此按「快照日期 + 基准月 + 关键词/排除词」在服务端缓存 10 分钟(这些平均值只在每日快照重建时变化),并与看板其余查询并行执行;查询失败只让这两列显示 `-`,不会把整块看板拖回实时源。
- 「经营对象问题排名」导出的 CSV 同步带上这两列。
### 看板分页
> 页面顶部的「经营管理 / 物流时效 + 看板标题 + 物流签收明细口径 + 明细条数/更新时间」页头已去掉(用户口径:无需展示,标签页标题已经说明是哪个看板);快照/缓存状态在数据源异常时的黄色提示条里仍会给出。
### 批量关键词查询
筛选区的「关键词」支持批量输入(与排除关键词同一套拆分规则):
- 按中文逗号 `,`、英文逗号 `,`、换行拆分,自动去空、去重,最多 100 个、单个最长 120 字符(原始输入上限 4000 字符);
- **多个关键词之间是"或"**:命中任一字段即算匹配。整区间(快照)口径覆盖:内部单号、线上订单号、物流单号、物流公司、店铺、平台、发货仓、收货地址,以及快照时点固化的**条码、商品名称、供应商、采购**(`KEYWORD_FIELDS`)——所以能直接按采购/供应商/条码检索;
- 走实时源时(区间超出快照窗口)只覆盖前 8 个报表字段,条码/采购/供应商这三类需要本地快照支持,这是两套口径的已知差异;
- 解析规则由 `splitSearchKeywords()` 统一提供,服务端缓存键按关键词集合(排序后)计算,输入顺序不同不会重复缓存。
- 输入框与其它筛选控件**等高**(34px 单行高度,多行时框内滚动),"已识别 N 个关键词"显示在标签行右侧并在空间不足时省略,不再把这一格撑成两行高。
筛选区的三个按钮各管一件事,点下去**立即按新条件查询**(互不重复):
| 按钮 | 签收日期 | 关键词 | 排除关键词 | 结果 |
| --- | --- | --- | --- | --- |
| **重置** | 清空(输入框留空) | 清空 | 清空 | 没有任何筛选条件;空日期由服务端兜底为本月 1 号~昨日(`dateRangeCleared` 让输入框保持空白,不被兜底值回填) |
| **恢复默认** | 回到本月 1 号~昨日(显式填回输入框) | 保留 | 恢复系统默认配置(全部勾选) | 默认口径 |
| **清空排除** | 回到本月 1 号~昨日(显式填回输入框) | 保留 | 置空 | 默认区间 + 不排除任何关键词 |
三者过去都只是"改输入、等用户点查询"(重置只清日期与关键词、另两个只改排除词),结果看起来一样容易混;现在各自的语义、界面状态与结果都区分开了,按钮 hover 有对应说明,筛选栏底部也有一行图例。空日期提交时服务端仍按默认区间查询(跨天且当日快照未生成时可能落实时源,较慢但结果正确)。
**默认签收日期区间是「本月 1 号 ~ 昨日」**(由 `normalizeLogisticsArrivalSigningDurationFilters()` 生成):当日签收数据仍在滚动,且每日快照只覆盖到昨日,用"到昨日"作默认值可以让首屏直接命中快照(整区间精确 + 秒级),不再因为区间含当天而回落到实时源。每月 1 号时会自动退化为"昨日当天"(本月 1 号还没到昨日)。
筛选控件(签收日期、关键词输入框,以及排除关键词选择器的摘要)都是**受控并与服务端下发的筛选条件同步**:从菜单重新进入(无参数 URL)、重置后查询、浏览器前进/后退时,控件会刷成当前真正生效的条件。这一点很重要——否则会出现"数据已经是默认区间,输入框还停在上次条件"的错觉。
### 排除关键词选择器
筛选区的「排除关键词」是**按字段分组**的勾选式选择器(`ExcludeKeywordPicker.tsx`)。关键词只能配置在它要匹配的那个字段上,**不跨字段混用**——这是这一版最重要的口径修正。
**字段与匹配规则**
| 字段(配置分组) | 数据列 | 匹配方式 |
| --- | --- | --- |
| 发货仓 | `warehouseName` | 该字段值包含关键词(不区分大小写),命中即排除 |
| 物流公司 | `logisticCompanyName` | 同上 |
| 订单标签 | `orderLabels`(JSON 数组文本) | 同上 |
| 买家留言 | `buyerMessage` | 同上 |
| 卖家备注 | `remark` | 同上 |
配置与筛选都以「字段 → 关键词」存储(`LogisticsArrivalSigningDurationExcludeKeywords`)。SQL 侧对每个关键词只生成一条 `NOT (该字段 ILIKE '%词%')`;旧版"命中 5 个字段里任意一个就排除"的口径已废弃——那正是「物流」会连带命中订单标签里的 `商家指定物流`、`物流探查` 和买家留言里"物流单号"的根因。
**选择器交互**
- **按字段分组**:发货仓 / 物流公司 / 订单标签 / 买家留言 / 卖家备注,每组标题显示"共 N 项 · 已勾选 M 项",右侧索引条按字段跳转(字段没有候选词时不显示该组);
- **默认全选**:候选词 = 该字段的默认排除词 ∪ 当前已排除词(旧链接里带的自定义词不会丢),进入看板时全部处于"排除"状态;取消勾选 = 本次不排除该项,确定后立即以当前表单条件重新查询(`requestSubmit()` 在受控值落到 DOM 后再触发);一个都不勾时以 `excludeKeywordsMode=clear` 提交,避免回落到默认排除词;
- 选择器内含:搜索、`全部 / 已选项(n)` 页签、`全选 / 反选 / 清空`(作用于当前可见列表,全选带半选态)、`紧凑模式`(四列更密);
- **新增候选词仅系统管理员可操作**:底部先选字段、再输入关键词(回车或点「添加」),新词并入**该字段**的默认排除词并立即落库(`PUT …/settings`,服务端 `assertCanManage` 同样校验管理员身份),提示"已保存到「物流公司」默认排除关键词:…";非管理员只看到"需要新增请联系系统管理员";
- 页面原先独立的「默认排除关键词」设置卡片已删除,维护入口统一收敛到选择器;`清空排除`(不排除任何词)与 `恢复默认`(回到全部默认排除词)两个按钮保留,只改输入、点查询生效。
**编码与兼容**
- 筛选栏摘要按字段给数量(如"已排除 14 项:发货仓 3 · 物流公司 5 · 订单标签 2 · 买家留言 2 · 卖家备注 2"),URL 用 `excludeKeywords=字段:词,词;字段:词` 的紧凑写法;
- **老链接的平铺写法按当前配置归位**:`excludeKeywords=词1,词2` 会被逐个对应到配置里它所属的字段(大小写不敏感,`bbff` 会归一到配置里的 `BBFF`),配置里已经没有的词说明它没有字段、直接忽略——**不会再把一个词摊到 5 个字段上**(那会让选择器每组都列出全部关键词)。只有当配置为空、任何词都对应不上时才退回旧口径,避免老链接突然变成"不排除任何词"。
- 历史配置(数组)在 `normalizeExcludeKeywords()` 里仍按旧口径挂到全部 5 个字段(那是当年真实的匹配语义);但配置一旦以新结构保存,后续解析都会走"按字段归位"。
**当前初设配置**(2026-09-30 落库):发货仓=`自动发货仓 / 常州简尚家居有限公司 / 京东云仓`;物流公司=`京喜 / 物流 / 安能 / 自提 / 中铁`;订单标签=`配件 / 1688一件代发`;买家留言=`BBFF / SSDD`;卖家备注=`BBFF / SSDD`。2026-09-01~09-02 实测:不排除 26,296 行 → 按字段排除后 17,506 行(旧口径同样 14 个词是 16,552 行,多出的 954 行正是"标签里带物流、物流公司里没有"的那些)。
经营对象问题排名里的每个看板都有独立翻页,不再用"显示更多"或无限滚动,也不会因为对象太多被截断:
- 01 签收日期维度:按日期列翻页(默认 10 列/页,可切 10/20/50/100);
- 02–08 采购/供应商/条码/物流公司/发货仓/平台/店铺:按对象行翻页(默认 20 行/页);
- 发货仓 / 物流公司 超时排名:按对象行翻页(默认 20 行/页)。
- 09 订单明细:翻页控件就在看板内部(不再挂在页面底部);未下钻时按 URL 的 `page/pageSize` 走服务端分页(每页 25/50/100/200,命中快照时显示真实总数"共 N 条筛选结果"),下钻时用本地分页翻全量匹配明细。
分页条统一由 `BoardPagination.tsx` 渲染(含"共 N 条 · 第 x / y 页"、上/下一页与每页条数),行集合变化(例如切换采购后重新筛选供应商)时自动回到第 1 页。
**每页条数切换的坑(已修)**:下拉框原先在 `onChange` 里同时调了 `onPageSizeChange(新值)` 和 `onPageChange(1)`。服务端分页(09 订单明细未下钻时)这两次都会触发 `router.push`,而后一次带着**旧的** `pageSize` 构造 URL 并覆盖前一次,表现为"订单明细每页条数选了没反应、又跳回 50"。现在 `BoardPagination` 只通知"每页条数变了",回到第 1 页由调用方负责:服务端分页用 `href(1, { ...filters, pageSize })`,本地分页(各经营对象表、发货仓/物流公司排名、下钻后的 09 明细)在 `setState` 里带 `page: 1`。
店铺维度表在"店铺"列前额外展示"平台"列:点击平台即把该表筛成该平台的店铺(标题变成"店铺维度 · 天猫",再次点击取消),点击店铺名仍走 09 明细下钻。店铺分组的业务键是"平台 + 店铺",避免同名店铺跨平台互相覆盖;平台列在样本路径(JS 归组)与整区间路径(SQL `GROUPING SETS ((platform, shop))`)都做了同样的处理,并逐字段比对过一致性。
### 规模上限与样本回落
SQL 聚合成本与区间行数近似线性(约 5 万行/秒)。当**区间原始行数超过 `LOGISTICS_ARRIVAL_SIGNING_DURATION_EXACT_SCOPE_MAX_ROWS`(11 万条,约 11 天)**时,`getDashboard` 会退回区间内均匀抽取的 2,000 条样本(`listDashboardSampleRows`,5 层并行、按签收时间分层,不再是"取最近一段"),并在底部注释写明原因与"缩小签收日期范围即可得到精确值"。
返回值上的 `dataScope`(`exact` / `sample`)与 `dataScopeReason`(`live-source` / `range-too-large`)驱动卡片副标题、底部注释与 CSV 导出的口径说明。
实测(663k 行快照,含 13 个默认排除关键词):
| 区间 | 原始行数 | 口径 | 首屏耗时 |
| --- | --- | --- | --- |
| 1 天 | 约 1.3 万 | 整区间精确 | 0.55s |
| 2 天 | 约 2.6 万 | 整区间精确 | 1.7s |
| 5 天 | 约 6.5 万 | 整区间精确 | 3.1s |
| 本月 | 约 30 万 | 样本(超上限) | 3.8–5.0s |
服务端 60s 内重复访问直接命中缓存。要把大区间也做成亚秒精确,方向是在快照写入时就把 4 个时间戳、5 个环节时长落成列,并把多维指标的计数改为位掩码 `GROUP BY`(一次扫描产出全部桶计数),现有文档结论不变、只替换实现。
### 查询性能优化(2026-10)
上线后"每次查询生成太慢"的根因有三个,都在快照读路径上:
1. **整表顺序扫描**:聚合查询原先 `SELECT` 全列(`rowKey`、`productItems`、十几个文本列),
Postgres 只能对 900MB 的堆表做 `Seq Scan`(28 天区间实测 `read=106409` 页、6.3 秒)。
现在按用途收窄列集合——`FILTERED_TIMELINE_SELECT`(只取 4 个时间戳)用于整区间总览/环节目标,
`FILTERED_DIMENSION_SELECT`(时间戳 + 4 个维度列)用于签收日期/平台/店铺/发货仓/物流/超时排名——
并新增覆盖索引 `LogisticsArrivalSigningDurationSnapshot_cover_idx`
(`(snapshotDate, signTime) INCLUDE (payTime, sendTime, fetchTime, warehouseName, platformName, shopName, logisticCompanyName)`),
扫描变成 **Index Only Scan**(同样的 28 天区间 26ms、`Heap Fetches: 0`)。
2. **有序集聚合把整行拖进排序**:`percentile_cont(0.5/0.9) WITHIN GROUP` 原先和十几个计数写在同一条语句里,
输入行 14 列宽,PG 18 会把它整行送进排序(两个分位数单独就占 4.3 秒,环节分位数叠在一起甚至到分钟级)。
现在分位数**按列拆成独立语句**、只投影一个浮点列,并与计数语句并行下发(`Promise.all`)。
3. **CTE 表达式被重复内联**:`measured`/`durations` 的 `CASE + EXTRACT` 会在每一处引用被重新计算,
同一条聚合里重复十几次。改为 `AS MATERIALIZED` 后只算一次(计数语句从 2.3s 降到 ~0.5s)。
另外把 **环节目标(上月平均 × 98%)** 也接入 10 分钟内存缓存(此前每次刷新都重跑上月聚合)。
优化前后(本机 Postgres 18、快照 648,689 行、冷启动、无缓存):
| 场景 | 优化前 | 优化后 |
| --- | --- | --- |
| 仓库层 `getDashboard`(28 天,样本口径) | 7.3s | **2.3s** |
| 仓库层 `getDashboard`(10 天) | 4.4s | **0.4s** |
| 仓库层 `getDashboard`(2 天,精确口径) | 1.7s | **0.3s** |
| 浏览器首屏出数(冷启动,含 5.6MB 载荷 + 渲染) | ~7–10s | **2.7s** |
正确性用 2 天区间做过逐项对账:SQL 聚合与"取全量明细在 JS 里现算"在订单数、平均/中位/P90、
异常计数、达成率、时效分布、5 个环节的 可计算单量/平均/中位/P90 上完全一致。
时间戳在 SQL 里用 `NULLIF(BTRIM(col),'')::timestamp` 直接转型:快照写入的就是远端 `CONVERT(varchar(19), …, 120)` 结果,格式固定;早期版本用正则逐行校验,整区间聚合约慢一个数量级。
## 运维排查
```sql
-- 批次状态
SELECT "snapshotDate", status, "windowFrom", "windowTo", "rowCount", message, "finishedAt"
FROM "LogisticsArrivalSigningDurationSnapshotRun" ORDER BY "snapshotDate" DESC;
-- 当天行数
SELECT count(*) FROM "LogisticsArrivalSigningDurationSnapshot" WHERE "snapshotDate" = CURRENT_DATE;
```
- 页面长时间显示"数据更新"而不是"快照数据" → 先看批次是不是 `FAILED`,或窗口是否覆盖了页面的签收日期区间。
- 批次 `RUNNING` 卡住(进程被杀)→ 该批次不会发布;直接重跑或加 `--force` 即可(重跑会先清空当天行)。
- 需要重建历史窗口 → `--window-days=` 调大 + `--force`。
## 部署步骤(生产)
1. 部署新版本代码;
2. 执行迁移 `prisma/migrations/20260928120000_add_logistics_arrival_signing_duration_snapshot`(需按生产迁移流程授权、先备份);
3. 跑一次手工同步生成第一批快照(`--apply`),确认批次为 `COMPLETED`、行数与窗口符合预期;
4. 安装 timer 并核对 `systemctl list-timers`;
5. 打开看板确认头部显示"快照数据 · 日期"。
注意:Prisma Client 重新生成过,运行中的 dev/prod 进程需要重启才能拿到新模型。

View File

@ -1,207 +0,0 @@
# 苹果风格视觉设计标准 (Apple-Style Visual Design Standards)
> **版本**: 2.0 (2026-01-29)
> **状态**: 补充参考标准;当前新平台第一视觉标准以 `白曜石工坊统一视觉标准.md` 为准
> **优先级**: 如本文件与白曜石工坊标准冲突,优先执行白曜石工坊标准
> **目标**: 打造优雅、统一、沉浸式的用户体验,完全去除"模板化"和"AI味",接近苹果官网的视觉水准。
---
## 目录
1. [核心设计哲学](#1-核心设计哲学)
2. [详细视觉规范](#2-详细视觉规范)
* [色彩系统](#21-色彩系统)
* [排版系统](#22-排版系统)
* [布局与间距](#23-布局与间距)
* [材质与光影](#24-材质与光影)
3. [交互与动画](#3-交互与动画)
4. [去AI味专项指南](#4-去ai味专项指南)
5. [技术架构与文件结构](#5-技术架构与文件结构)
6. [组件开发规范](#6-组件开发规范)
---
## 1. 核心设计哲学
### 1.1 内容至上 (Content is King)
* **原则**:界面不仅是容器,更是内容的舞台。UI 应该"退后",让 3D 模型、产品图片和关键数据"向前"。
* **执行**:
* 背景色主要使用白色或极淡的灰色;当前产品不再提供深浅色主题切换。
* **拒绝**无意义的装饰性边框、分割线和阴影。只有在区分层级必要时才使用。
* **拒绝**均等分割的网格布局("AI味"典型特征)。使用非对称布局(如:左侧 2/3 展示 3D 模型,右侧 1/3 展示控制面板)。
### 1.2 物理感与材质 (Materiality & Physics)
* **原则**:数字界面应模拟真实世界的物理属性。
* **执行**:
* **Liquid Glass (毛玻璃)**:仅在覆盖内容时使用(如导航栏、侧边栏、浮动面板)。标准:`backdrop-filter: blur(20px) saturate(180%)`。
* **Spring Animation (弹簧动画)**:交互元素(按钮按压、弹窗出现)必须使用弹簧物理模拟,而不是简单的线性过渡。
* **光影**:光源应统一来自正上方。阴影应柔和、弥散,模拟环境光遮蔽 (Ambient Occlusion)。
### 1.3 优雅的排版 (Elegant Typography)
* **原则**:排版是信息层级的核心。
* **执行**:
* **动态字间距 (Dynamic Tracking)**:大标题收紧 (-0.4px),正文标准,小字加宽 (+0.06px)。
* **层次分明**:使用字重 (Weight) 和颜色 (Color) 区分层级,而不是单纯靠字号。
---
## 2. 详细视觉规范
### 2.1 色彩系统
**设计令牌位置**: `src/lib/design-tokens/colors.ts`
不要使用纯灰 (`#808080`),总是混入微量的蓝色或暖色调。
| 用途 | Light Mode | Dark Mode | 说明 |
| :--- | :--- | :--- | :--- |
| **背景** | `#FFFFFF` | `#000000` | 纯净背景,最大化对比度 |
| **次级背景** | `#F5F5F7` | `#1C1C1E` | 用于分组区块,**比系统灰(#F2F2F7)更淡、更优雅** |
| **主文本** | `#1D1D1F` | `#F5F5F7` | 接近纯黑但不是纯黑 (Off-black) |
| **次文本** | `#86868b` | `#86868b` | 经典的苹果灰,用于说明文字 |
| **强调色** | `#008D74` | `#00B896` | 品牌绿,仅用于链接和主按钮 |
| **分割线** | `rgba(0,0,0,0.1)` | `rgba(255,255,255,0.1)` | 极细,极淡 |
### 2.2 排版系统
**设计令牌位置**: `src/lib/design-tokens/typography.ts`
我们使用**合法的开源/系统字体栈**,严格避免版权风险:
* **西文**: `-apple-system`, `BlinkMacSystemFont`, `Inter`, `Segoe UI`
* **中文**: `-apple-system`, `Noto Sans CJK SC`, `Source Han Sans SC` (思源黑体), `Microsoft YaHei`
* **代码**: `JetBrains Mono`, `Fira Code`
| 样式 | 字号 (px) | 行高 (px) | 字间距 (px) | 用途 |
| :--- | :--- | :--- | :--- | :--- |
| **Large Title** | 34 | 41 | -0.4 | 页面主标题 |
| **Title 1** | 28 | 34 | -0.36 | 章节标题 |
| **Title 2** | 22 | 28 | -0.26 | 子章节标题 |
| **Headline** | 17 | 22 | -0.41 | 强调文本 (Semibold) |
| **Body** | 17 | 25 | -0.41 | 正文内容 |
| **Caption 1** | 12 | 16 | 0 | 说明文字 |
### 2.3 布局与间距
**设计令牌位置**: `src/lib/design-tokens/spacing.ts`
* **基础网格**: 4px
* **大留白原则**:
* 模块间距至少 `48px` 或 `64px`。
* 卡片内部 Padding 至少 `24px`。
* **流体布局**:
* 容器宽度随视口变化,最大宽度限制 (Max-width: 1440px)。
* **全宽突破**: Hero Banner 等关键元素应占据全宽。
### 2.4 材质与光影
* **Liquid Glass**:
* Light: `rgba(255, 255, 255, 0.72)` + Blur 20px
* Dark: `rgba(22, 22, 23, 0.72)` + Blur 20px
* **阴影 (Shadows)**:
* Subtle: `0 2px 8px rgba(0, 0, 0, 0.04)` (静态元素)
* Standard: `0 4px 12px rgba(0, 0, 0, 0.08)` (悬浮/交互)
* Elevated: `0 8px 24px rgba(0, 0, 0, 0.12)` (模态框)
---
## 3. 交互与动画
**设计令牌位置**: `src/lib/design-tokens/animations.ts`
所有交互必须有物理反馈。
* **按钮按压**: Scale 0.98, Duration 100ms
* **微交互 (Hover)**: Scale 1.02, Duration 200ms
* **页面过渡**: Duration 500ms, Curve `cubic-bezier(0.16, 1, 0.3, 1)`
* **Spring 参数**: 使用 `spring(mass: 1, stiffness: 100, damping: 15)` 模拟真实回弹。
---
## 4. 去AI味专项指南
"AI味" = 死板、重复、缺乏人情味、过度依赖默认组件。
### 4.1 拒绝"Lorem Ipsum"
* ❌ "Product Name 1", "This is a description."
* ✅ "Eames Lounge Chair", "由模压胶合板和皮革制成,经典的设计符号。"
* **要求**:所有占位文本必须是**真实场景数据**。
### 4.2 拒绝"均等分布"
* ❌ 4个功能点,每个占 25% 宽度,图标+标题+描述,完全一样。
* ✅ 1个核心功能占 60% 宽度配大图,其余 3 个功能以列表形式在侧边展示。打破对称。
### 4.3 拒绝"默认图标"
* ❌ 使用实心块状图标。
* ✅ 优先使用文字。如果必须用图标,使用细线条风格 (Stroke width 1.5px-2px)。
### 4.4 拒绝"彩虹渐变"
* ❌ 高饱和度彩虹渐变背景。
* ✅ 纯色、微渐变或高质量实拍图/3D渲染图。
---
## 5. 技术架构与文件结构
本项目基于 **Next.js 16 + Tailwind CSS + React 19** 构建。
### 5.1 目录结构
```
src/
├── lib/
│ └── design-tokens/ # 核心视觉配置
│ ├── typography.ts # 字体定义
│ ├── colors.ts # 颜色定义
│ ├── spacing.ts # 间距定义
│ └── animations.ts # 动画定义
├── components/
│ └── ui/ # 基础原子组件
│ ├── Button.tsx # 遵循规范的按钮
│ ├── Card.tsx # 遵循规范的卡片
│ ├── Typography.tsx # 文本组件封装
│ └── ...
└── app/
└── globals.css # CSS 变量映射 (自动从 tokens 生成)
```
### 5.2 迁移指南
如果你在代码中看到旧的 CSS 类(如 `text-gray-500`),请替换为语义化的设计令牌类:
* ❌ `text-gray-500` -> ✅ `text-(--color-label-secondary)` (或封装好的 Tailwind 类)
* ❌ `bg-white` -> ✅ `bg-(--color-background-primary)`
* ❌ `shadow-lg` -> ✅ `shadow-apple-lg`
---
## 6. 组件开发规范
### 6.1 Button
* **形状**: 高度 36px/44px,Pill shape (999px) 或 Rounded (8px)。
* **质感**: Primary (纯色无边框), Secondary (浅灰背景)。禁止复杂渐变。
### 6.2 Card
* **圆角**: 统一 `20px` 或 `24px`。
* **边框**: Light Mode 下通常无边框或极淡 `1px` 边框。
* **内容**: 内部 Padding 至少 `24px`。
### 6.3 Input
* **样式**: 移除默认边框,使用浅灰背景 (`#F5F5F7`),聚焦时出现 Focus Ring。
* **高度**: 至少 `44px`。
### 6.4 Slider
* **组件来源**: 统一使用 `src/components/ui/Slider.tsx`,禁止直接使用原生 `input[type="range"]`。
* **轨道样式**: 高度 `6px`,圆角 `999px`,未填充色 `#D4DCE6`,填充色使用品牌色 `#008D74`(深色场景可切换 `#00B896`)。
* **滑块样式**: 尺寸 `16px`,白色描边 `2px`,投影 `0 1px 3px rgba(0, 0, 0, 0.25)`。
* **状态规范**: 禁用态降低不透明度并禁用交互光效;拖动态允许轻微放大(建议 `scale(1.08)`)。
* **可访问性**: 必须提供 `aria-label`,并保持键盘可调节。
### 6.5 响应式
* 使用 `useBreakpoint` 或 Tailwind 的响应式前缀 (`md:`, `lg:`)。
* 支持 iPhone 刘海屏安全区域 (`safe-area-inset`).
---
*遵循此标准,我们将构建出一个不仅好用,而且令人愉悦的家具展示系统。*

View File

@ -0,0 +1,124 @@
# 限价与破价判定规则(业务确认版)
> 适用范围:限价管理 · 破价订单审核(`/supply-chain/management/custom-table` → 破价订单审核)
> 本文件为业务口径的唯一来源;规则变更请改本文件并注明生效时间。
## 一、限价与判定口径
1. **判定基准**:以「商家实收」为准;`商家实收 < 限价 × 数量` 即判定破价。
2. **实付款与平台承担**:实付款 + 平台承担(红包 / 消费券 / 平台券)= 商家实收;凭证必须同时包含「实付款」和「数量」。
3. **出资方字典**
- 商家承担:店铺优惠(官方立减、单品直降、店铺券)、政府补贴
- 平台承担:红包、消费券、平台券、平台优惠、新品礼金
- 买家承担:淘金币
- 待定:首单礼金(等与运营确认后补录)
4. **限价维度**:按**货号**限价——同一货号下所有规格/条码共用同一套限价。
5. **限价时间版本**:按订单付款时点生效的限价判定(限价调整前后分别取当时版本),不做"用最新限价回算历史单"。
6. **成本变动规则(限价维护侧)**:成本价上涨 5% 以内不动限价;成本价下调不变限价。
7. **非负责人限价**:2026-10-07 00:00 前的 ID 价格不判定破价(此后按规则判定)。
8. **老链接分界**:上架时间早于 2026-10-07 00:00 的链接(老链接)破价申诉通过;之后的按限价流程结束日期判定(上架时间需早于流程结束日期),并核对店铺款式编码 / 商品编码。
9. **负责人触发规则**:非运营发起的流程,前两个月全公司运营同价(不归属个人);两个月后按销量触发负责人(销量高者为负责人)。
10. **数据快照**:破价单在生成时保存金额快照;退款/售后状态单独记录,退款不等于免责(仍需判断是否已调价)。
11. **离职人员**:订单负责人已离职 → 自动标记「人员已离职」,不判定、不计乐捐。
12. **是否有检测报告**:取素材平台「检测报告」接口(`/api/external/certificate-reports`,按 **SPU** 精确匹配,只认有报告文件的记录),进程内缓存 10 分钟;
接口未配置或读取失败时该列显示为空、筛选候选值为空。配置项:`MATERIAL_LIBRARY_CERTIFICATE_REPORT_API_BASE_URL/API_KEY`,
不填则复用素材平台 `MATERIAL_LIBRARY_WHITE_BACKGROUND_API_*`。
13. **手工换货单**:聚水潭订单明细出现「被替换行(`item_status=Replaced`、数量归 0)」+「换入行(`item_ext_data` 含 `item_source=手工换货/手工添加`)」时按换货单处理:
- 判定按**换出行原编码**的限价执行(客户在平台上购买的那个编码);
- 成交数量与商家实收取**换入行**(换货不改变订单成交金额);换入行金额抓不到时回退订单主表商家实收 → 订单主表买家实付,都取不到才按 0(由运营申诉说明);
- 换货行配对成功后按原编码生成一条,不再按换入编码单独生成,避免同一订单重复生成两条破价单;
- 配对优先按 `sku_uuid` 精确匹配,剩余行仅在一对一时顺序配对;复杂结构(多对多)暂不强行配对,相关行按各自编码现状处理。
## 二、申诉与审核
1. **申诉类型**(4 组)
- 链接时间:老链接,上架时间早于限价流程(凭证须含链接 ID + 创建时间)/ 京喜换主体 · 老店转新店
- 优惠 / 活动:平台优惠 · 红包 · 券(非商家承担)/ 参与秒杀 · 百补 · 大促等平台活动(凭证须含活动开始与截止时间,且活动价未破价)/ 拼多多多件折扣(99折)
- 流程申请:已走新品限价及限价调整(必填流程号,系统到「新品限价及限价调整」流程校验,查不到不允许提交)
- 其他原因(须在备注中详细说明)
2. **申诉范围**:链接时间 / 流程申请类支持**链接级一次性申诉**(默认勾选,覆盖该链接下全部破价订单,后续新单自动继承);优惠 / 活动类按订单申诉。
3. **审核要点仅审核人可见**;申诉人只看到凭证要求与示例图。
4. **凭证校验**:必填校验 + 识别提示(识别结果仅提示,人工一键打回);后续增加图片查重(hash / 相似度)与关键字段(订单号、链接 ID)一致性校验。
5. **乐捐**:驳回计 ¥10/单(离职不计);配套审计链(数据快照 + 申诉材料 + 审核记录),扣款执行交财务确认,保留异议通道。
6. **举证期限**:待与运营确认后设置(避免事后无法追溯凭证)。
## 三、权限(由管理员统一管理)
路径:用户管理后台 → 权限配置 → 经营管理。限价管理相关的查看 / 导出 / 申诉都由管理员在这里开通。
| 能力 | 权限节点 | 动作 |
| --- | --- | --- |
| 页面与数据查看(限价明细、破价订单审核) | 限价管理 | 可见 |
| 导出(导出明细 / 导出明细(含图片)/ 破价订单导出 / 乐捐明细) | 限价管理 | 下载 |
| 申诉 · 审核 · 生成 · 导入 | 限价管理 | 操作 |
| 规则配置(页面可见 + 保存参数) | 限价管理 · 规则配置 | 可见 / 操作 |
- 没有「下载」权限时页面不展示导出入口,服务端导出接口同样鉴权(下载中心任务按「限价管理 · 下载」校验)。
- 没有「操作」权限时:不显示申诉/审核按钮,服务端也会拒绝申诉、审核、生成、导入。
- 申诉保留一条兜底:破价单的负责人本人,即使没有「操作」权限,也可以对本人负责的订单发起申诉(审核仍需「操作」)。
- 没有「规则配置」权限时,规则配置页签不显示、接口不可调用。
## 四、生成与运行
1. **按天增量**:限价逻辑上线后,破价订单**按天增量生成**(每天只生成最新一天的破价单并入库),**历史数据不再重复回算**;破价订单审核页每天展示最新破价单。
2. **数据来源**:聚水潭报表库(`jst_Orders` + `jst_OrderItem` / 天猫奇门 `jst_qmcust_orderlist` + `_item`),取 `seller_income_amount`、`buyer_paid_amount`、`qty`、`shop_i_id`、`shop_sku_id`、`pay_date`、退款状态,以及换货识别字段 `item_status`、`item_ext_data`(`item_source`、`sku_uuid`)。报表库查询较慢,逐日增量任务建议按天分片拉取。
3. **限价来源**:钉钉流程(仅取**已完结**),共 4 个模板,同步脚本 `scripts/sync-limit-workflows.mjs`:
- 「新品限价及限价调整」→ 货号取「货号(非商品编码)」,限价取「期望日常限价」;
其中的「调整限价明细表」子表(申请类型=调整原货号限价)只填「调整后限价」,该列同样作为限价取值
(2026-10-16 修复,此前这类调价会被漏掉);
- 「新品限价流程」→ 明细取系统产品限价页面(按流程链接 SPU 关联)。页面数据来自正式库
`external.product_catalog_wide_v1`(限价字段:`expected_daily_limit_price` / `non_spu_owner_limit_price`),
本地库该视图为空时退回本地 `IntakeNonIntegratedProduct(LimitRow)`;
- 「各中心特殊限价需求申请」→ 货号取「货号」,限价取「最低售价」;
- 「GB特殊限价需求申请」→ 货号取「货号」,限价取「期望日常限价」(2026-10-16 纳入)。
- 「产品限价页面」已确认(CONFIRMED)的限价全量纳入(2026-10-16):来源正式库
`external.product_catalog_wide_v1`,给没有限价流程的货号兜底(如 CY012801=268.6);
同货号与流程限价冲突时**以流程为准**,页面价只在流程没覆盖时生效。
- 「限价期初数据」(业务整理的存量限价 Excel,2026-10-16 纳入):放在期初数据目录
(`LIMIT_SYNC_INITIAL_DIR`,默认 `<app>/期初数据`;目录为空时用仓库自带
`scripts/data/limit-sync/期初数据/限价期初数据*.xlsx`),取目录里最新一份,
按「货号 + 负责人限价 + 更新日期」写入,作为**最低优先级**兜底(如 MKHDNZ0409260600468=279)。
优先级:**限价流程 > 产品限价页面(已确认)> 限价期初数据**;低优先级只在货号没有更高优先级限价时生效。
**例外(2026-10-16 确认)**:期初数据的「更新日期」**晚于**流程完成时间时,以期初数据为准(谁新用谁);
期初比流程旧则维持上面的优先级。
**非负责人限价一律按最新口径计算**(2026-10-16 确认):负责人限价 × 1.05,且不低于 +10 元;
期初数据表 / 产品限价页面里的历史「非负责人限价」(倍数 1%~15% 不统一)不采用,
限价明细页、导出文件、破价判定三处都用同一公式。
写入规则:同货号多流程取**审核通过时间最新**的一条;限价为 0 或空的明细剔除;「申请与SPU负责人同价 / 活动限价」不覆盖负责人限价,只作为价格授权;流程「货号」列若填的是商品编码,用商品编码匹配产品档案换算为货号;无货号时同样用商品编码匹配。页面限价的时间按**北京时间墙钟**存(与流程完成时间一致)。
4. **覆盖率**:暂不做未匹配清单与告警(后续优化)。
5. **正式站同步(服务器端)**:`scripts/sync-limit-workflows.mjs` 已改成服务器可运行,连接信息全部取
服务器 `shared/.env` 里的既有变量(`DATABASE_URL` / `JST_REPORT_DB_*` / `PROD_ARCHIVE_DB_*`),
不再依赖 Mac 本地路径:
```bash
# 手动跑一次(正式站目录下,root 或能读 shared/.env 的账号)
cd /opt/furniture-3d-system-prod/current/furniture-3d-system
sudo -E bash -lc 'set -a; . /opt/furniture-3d-system-prod/shared/.env; set +a; \
LIMIT_SYNC_OUT_DIR=/opt/furniture-3d-system-prod/shared/limit-workflow-sync/导出结果 \
LIMIT_SYNC_INITIAL_DIR=/opt/furniture-3d-system-prod/shared/limit-workflow-sync/期初数据 \
node scripts/run-limit-workflow-sync-production.cjs'
# 安装每日定时器(systemd,与营销日销同一套样板):每天北京时间 07:30
sudo bash scripts/install-limit-workflow-sync-timer.sh
systemctl list-timers --all 'furniture-limit-workflow-sync*' --no-pager
journalctl -u furniture-limit-workflow-sync.service -n 200 --no-pager
```
- 写库是**全量重建 + 单事务**:成功才提交;失败自动回滚,正式库保留上一版限价与授权。
- 输出目录 `LIMIT_SYNC_OUT_DIR`(默认 `<app>/导出结果`)里的 `限价流程_完整明细表.xlsx` 每日覆盖,
旧版本进 `历史版本/`,默认保留最近 20 份(`LIMIT_SYNC_HISTORY_KEEP`)。
- `--skip-excel` / `LIMIT_SYNC_SKIP_EXCEL=1` 可只写库不导 Excel。
- 期初数据更新:把新的 `限价期初数据*.xlsx` 放进 `shared/limit-workflow-sync/期初数据/` 即可,下次同步自动取最新。
- `SupplyChainBreachRuleConfig` 不需要灌数:接口读取时用代码内置默认值,首次在「规则配置」保存时落库。
## 五、待办
- [ ] 限价流程数据接入 + 筛选逻辑(生效时间版本、货号维度)
- [ ] 非负责人限价 10-07 规则、负责人触发规则(前两个月同价 / 两个月后按销量)落到判定引擎
- [ ] 按天增量任务(cron / worker)与失败告警
- [ ] 凭证查重(hash / 相似度)、关键字段一致性校验
- [ ] 举证期限、乐捐财务对接流程
- [ ] 首单礼金 / 新品礼金出资方确认后补录出资方字典

View File

@ -74,6 +74,7 @@ const contentSecurityPolicy = [
"base-uri 'self'",
"object-src 'none'",
"frame-ancestors 'none'",
`frame-src 'self' ${lodopScriptSources.join(' ')}`,
"form-action 'self'",
"img-src 'self' data: blob: http: https:",
"font-src 'self' data:",
@ -239,7 +240,7 @@ const nextConfig = {
},
// 服务器外部包配置(Next.js 16新配置)
serverExternalPackages: ['sharp', 'three', 'gsap', '@huggingface/transformers', 'onnxruntime-node'],
serverExternalPackages: ['sharp', 'three', 'gsap', '@huggingface/transformers', 'onnxruntime-node', 'mssql'],
// Turbopack配置(Next.js 16默认启用)
turbopack: {},

View File

@ -26,6 +26,7 @@
"bcryptjs": "^3.0.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"crypto-js": "^4.2.0",
"date-fns": "^4.1.0",
"dockerode": "^4.0.9",
"exceljs": "^4.4.0",
@ -42,7 +43,7 @@
"next": "^16.1.6",
"obj2gltf": "^3.2.0",
"pg": "^8.17.2",
"pinyin-pro": "^3.28.1",
"pinyin-pro": "^3.29.5",
"postcss": "^8.5.6",
"print-js": "^1.6.0",
"puppeteer": "^24.40.0",
@ -52,10 +53,12 @@
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-hook-form": "^7.54.2",
"server-only": "^0.0.1",
"sharp": "^0.34.5",
"ssh2": "^1.17.0",
"tailwind-merge": "^3.4.0",
"three": "^0.182.0",
"uuid": "^11.1.1",
"xlsx": "^0.18.5",
"zod": "^4.3.6",
"zustand": "^5.0.2"
@ -66,6 +69,7 @@
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/crypto-js": "^4.2.2",
"@types/dockerode": "^4.0.1",
"@types/ioredis": "^4.28.10",
"@types/jest": "^30.0.0",
@ -6145,6 +6149,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/crypto-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz",
"integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/docker-modem": {
"version": "3.0.6",
"resolved": "https://registry.npmmirror.com/@types/docker-modem/-/docker-modem-3.0.6.tgz",
@ -8873,6 +8884,13 @@
"node": ">= 8"
}
},
"node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"deprecated": "Active development of CryptoJS has been discontinued. This library is no longer maintained.",
"license": "MIT"
},
"node_modules/css-line-break": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
@ -9337,6 +9355,20 @@
"node": ">= 8.0"
}
},
"node_modules/dockerode/node_modules/uuid": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
"deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-2.1.0.tgz",
@ -15505,9 +15537,9 @@
}
},
"node_modules/pinyin-pro": {
"version": "3.28.1",
"resolved": "https://registry.npmmirror.com/pinyin-pro/-/pinyin-pro-3.28.1.tgz",
"integrity": "sha512-oqz8ulwRgtUXRi0vbqEfGNly19zpyCxYrjhkk5TibGcgSW6eNwS5woajCXRwqURi8Ehc2yOFTiB4uNoZ+NJOnA==",
"version": "3.29.5",
"resolved": "https://registry.npmjs.org/pinyin-pro/-/pinyin-pro-3.29.5.tgz",
"integrity": "sha512-Fk5YoRmtdHqJWqAuzYVG/NzvzeCbaG+ZEwbY6Nbps3QAIrvQQEtcm49frcyCM8i2b5najnzPDtYqrHRYTTiuwQ==",
"license": "MIT"
},
"node_modules/pirates": {
@ -16921,6 +16953,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/server-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz",
"integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==",
"license": "MIT"
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@ -18832,16 +18870,16 @@
}
},
"node_modules/uuid": {
"version": "10.0.0",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-10.0.0.tgz",
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz",
"integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
"uuid": "dist/esm/bin/uuid"
}
},
"node_modules/v8-to-istanbul": {

View File

@ -3,8 +3,9 @@
"version": "0.1.0",
"private": true,
"scripts": {
"worker:ai-image": "node scripts/run-ai-image-worker.mjs",
"predev": "npm run prisma:check-client-sync",
"dev": "next dev",
"dev": "next dev --webpack",
"build": "prisma generate && next build",
"start": "next start",
"lint": "eslint",
@ -17,11 +18,26 @@
"prisma:push": "node scripts/prisma-db-push-local.js",
"prisma:studio": "prisma studio",
"sync:supply-chain-product-archive": "tsx scripts/sync-supply-chain-product-archive.ts",
"sync:supply-chain-product-archive-daily": "tsx scripts/sync-supply-chain-product-archive-sales-performance-daily.ts",
"sync:supply-chain-product-archive-enrichment": "tsx scripts/sync-supply-chain-product-archive-enrichment.ts",
"backfill:product-archive-designer": "tsx scripts/backfill-product-archive-designer-from-jst-sku-info-manage.ts",
"backfill:product-archive-designer:apply": "tsx scripts/backfill-product-archive-designer-from-jst-sku-info-manage.ts --apply",
"backfill:product-archive-opshead": "tsx scripts/backfill-product-archive-opshead-from-jst-sku-info-manage.ts",
"backfill:product-archive-opshead:apply": "tsx scripts/backfill-product-archive-opshead-from-jst-sku-info-manage.ts --apply",
"backfill:product-archive-opshead:regular": "tsx scripts/backfill-product-archive-opshead-from-jst-sku-info-manage.ts --mode=regular",
"backfill:product-archive-opshead:regular:apply": "tsx scripts/backfill-product-archive-opshead-from-jst-sku-info-manage.ts --mode=regular --apply",
"backfill:product-archive-production-type": "tsx scripts/backfill-product-archive-production-type.ts",
"backfill:product-archive-production-type:apply": "tsx scripts/backfill-product-archive-production-type.ts --apply",
"sync:supply-chain-product-archive-sales-performance": "tsx scripts/sync-supply-chain-product-archive-sales-performance.ts",
"run:product-sabc": "tsx scripts/run-product-sabc.ts",
"compare:product-sabc-xlsx": "tsx scripts/compare-product-sabc-xlsx.ts",
"seed:local-product-sabc-xlsx": "tsx scripts/seed-local-product-sabc-xlsx.ts",
"sync:jst-sku-product-archive": "tsx scripts/sync-jst-sku-product-archive.ts",
"sync:bom-cost-reference": "tsx scripts/sync-bom-cost-reference.ts",
"backtest:bom-cost-reference": "tsx scripts/backtest-bom-cost-reference.ts",
"report:bom-cost-shadow-divergence": "tsx scripts/report-bom-cost-shadow-divergence.ts",
"report:bom-agent-baseline": "tsx scripts/report-bom-agent-baseline.ts",
"trial:bom-costing-agent": "tsx scripts/run-bom-costing-agent-trial.ts",
"import:yunshan-created-at": "tsx scripts/import-yunshan-product-created-at.ts",
"import:product-archive-drawing-media": "tsx scripts/import-product-archive-drawing-media.ts",
"db:verify-master-data": "node scripts/verify-master-data-foundation.js",
@ -59,6 +75,7 @@
"materials:sync-standard": "tsx scripts/sync-standard-finishes.ts",
"materials:sync-standard:apply": "tsx scripts/sync-standard-finishes.ts --apply",
"sync:jst-sku-stock": "tsx scripts/sync-jst-sku-stock-products.ts",
"sync:product-inventory-snapshot": "tsx scripts/sync-product-inventory-snapshot.ts",
"image-search:model:prepare": "tsx scripts/prepare-product-archive-image-search-model.ts",
"image-search:index": "tsx scripts/index-product-archive-images.ts",
"repair:barcode-print:historical": "tsx scripts/repair-barcode-print-source-rows.ts",
@ -66,12 +83,17 @@
"sync:oa-board:apply": "tsx scripts/sync-oa-board.ts --apply",
"sync:oa-brand-planning": "tsx scripts/sync-oa-brand-planning-board.ts",
"sync:oa-brand-planning:apply": "tsx scripts/sync-oa-brand-planning-board.ts --apply",
"sync:oa-video-flow": "tsx scripts/sync-oa-video-flow-board.ts",
"sync:oa-video-flow:apply": "tsx scripts/sync-oa-video-flow-board.ts --apply",
"sync:oa-art-flow-raw": "tsx scripts/sync-oa-art-flow-raw.ts",
"sync:oa-art-flow-raw:apply": "tsx scripts/sync-oa-art-flow-raw.ts --apply",
"cache:oa-board:local": "tsx scripts/cache-oa-board-local-preview.ts",
"import:oa-board-activity-mappings": "tsx scripts/import-oa-board-activity-mappings.ts",
"import:oa-board-node-rules": "tsx scripts/import-oa-board-node-rules.ts",
"report:oa-board-activities": "tsx scripts/report-oa-board-activities.ts",
"sync:employee-directory": "tsx scripts/sync-employee-directory.ts",
"sync:employee-directory:apply": "tsx scripts/sync-employee-directory.ts --apply",
"sync:dingtalk-directory:apply": "tsx scripts/sync-dingtalk-directory.ts --apply",
"sync:external-jsreport": "tsx scripts/sync-external-readonly-to-jsreport.ts",
"sync:external-jsreport:apply": "tsx scripts/sync-external-readonly-to-jsreport.ts --apply",
"db:verify-master-data:truth": "node scripts/verify-master-data-truth-sources.js",
@ -124,6 +146,7 @@
"bcryptjs": "^3.0.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"crypto-js": "^4.2.0",
"date-fns": "^4.1.0",
"dockerode": "^4.0.9",
"exceljs": "^4.4.0",
@ -140,7 +163,7 @@
"next": "^16.1.6",
"obj2gltf": "^3.2.0",
"pg": "^8.17.2",
"pinyin-pro": "^3.28.1",
"pinyin-pro": "^3.29.5",
"postcss": "^8.5.6",
"print-js": "^1.6.0",
"puppeteer": "^24.40.0",
@ -150,10 +173,12 @@
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-hook-form": "^7.54.2",
"server-only": "^0.0.1",
"sharp": "^0.34.5",
"ssh2": "^1.17.0",
"tailwind-merge": "^3.4.0",
"three": "^0.182.0",
"uuid": "^11.1.1",
"xlsx": "^0.18.5",
"zod": "^4.3.6",
"zustand": "^5.0.2"
@ -187,6 +212,7 @@
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/crypto-js": "^4.2.2",
"@types/dockerode": "^4.0.1",
"@types/ioredis": "^4.28.10",
"@types/jest": "^30.0.0",
@ -209,5 +235,20 @@
"tailwindcss": "^4.1.18",
"tsx": "^4.21.0",
"typescript": "^5"
},
"pnpm": {
"onlyBuiltDependencies": [
"@parcel/watcher",
"@prisma/engines",
"cpu-features",
"esbuild",
"onnxruntime-node",
"prisma",
"protobufjs",
"puppeteer",
"sharp",
"ssh2",
"unrs-resolver"
]
}
}

View File

@ -0,0 +1,92 @@
BEGIN;
SET LOCAL lock_timeout = '5s';
SET LOCAL statement_timeout = '30s';
DO $preflight$
DECLARE
actual_columns TEXT[];
expected_columns CONSTANT TEXT[] := ARRAY[
'id', 'storeName', 'operationOwner', 'operationDepartmentHead',
'operationDirector', 'profitCenter', 'subject', 'accountType', 'account',
'boundPhone', 'verificationContact', 'jstOrderService', 'accountGroupName',
'createdAt', 'updatedAt', 'passwordConfigured'
];
BEGIN
IF to_regclass('external_store_accounts.accounts_v1') IS NULL THEN
RETURN;
END IF;
SELECT array_agg(column_name ORDER BY ordinal_position)
INTO actual_columns
FROM information_schema.columns
WHERE table_schema = 'external_store_accounts'
AND table_name = 'accounts_v1';
IF actual_columns IS DISTINCT FROM expected_columns THEN
RAISE EXCEPTION 'External account contract needs preflight review';
END IF;
END $preflight$;
DO $migration$
DECLARE
original_owner OID;
original_acl ACLITEM[];
BEGIN
IF to_regclass('external_store_accounts.accounts_v1') IS NULL THEN
RETURN;
END IF;
SELECT relowner, relacl
INTO original_owner, original_acl
FROM pg_class
WHERE oid = 'external_store_accounts.accounts_v1'::regclass;
CREATE TEMP TABLE store_account_view_rows_before ON COMMIT DROP AS
SELECT to_jsonb(account_row) - 'passwordConfigured' AS row_value
FROM external_store_accounts.accounts_v1 account_row;
CREATE OR REPLACE VIEW external_store_accounts.accounts_v1 AS
SELECT
source.id,
source."storeName",
source."operationOwner",
source."operationDepartmentHead",
source."operationDirector",
source."profitCenter",
source.subject,
source."accountType",
source.account,
source."boundPhone",
source."verificationContact",
source."jstOrderService",
source."accountGroupName",
source."createdAt",
source."updatedAt",
(source."passwordCiphertext" <> '')::boolean AS "passwordConfigured"
FROM public."StoreAccount" source;
IF EXISTS (
(SELECT row_value FROM store_account_view_rows_before
EXCEPT ALL
SELECT to_jsonb(account_row) - 'passwordConfigured'
FROM external_store_accounts.accounts_v1 account_row)
UNION ALL
(SELECT to_jsonb(account_row) - 'passwordConfigured'
FROM external_store_accounts.accounts_v1 account_row
EXCEPT ALL
SELECT row_value FROM store_account_view_rows_before)
) THEN
RAISE EXCEPTION 'External account rows changed while fixing password status';
END IF;
IF EXISTS (
SELECT 1
FROM pg_class
WHERE oid = 'external_store_accounts.accounts_v1'::regclass
AND (relowner <> original_owner OR relacl IS DISTINCT FROM original_acl)
) THEN
RAISE EXCEPTION 'External account owner or ACL changed';
END IF;
END $migration$;
COMMIT;

View File

@ -0,0 +1,7 @@
-- Persist the Jianshang (JST public available) stock on the archive main table
-- so list filters, page rows and exports all read one source of truth.
ALTER TABLE "SupplyChainProductArchive"
ADD COLUMN "stockJianshang" TEXT NOT NULL DEFAULT '';
CREATE INDEX "SupplyChainProductArchive_stockJianshang_idx"
ON "SupplyChainProductArchive"("stockJianshang");

View File

@ -0,0 +1,43 @@
-- Product archive period-aware sales facts and sync audit.
CREATE TABLE "SupplyChainProductArchiveSalesPerformanceDaily" (
"barcode" VARCHAR(100) NOT NULL,
"statDate" DATE NOT NULL,
"shipAmount" DECIMAL(24,4) NOT NULL,
"returnAmount" DECIMAL(24,4) NOT NULL,
"realPerformance" DECIMAL(24,4) NOT NULL,
"shipQty" BIGINT NOT NULL,
"returnQty" BIGINT NOT NULL,
"clearSalesCost" DECIMAL(24,4) NOT NULL,
"sendFreight" DECIMAL(24,4) NOT NULL,
"platformCommission" DECIMAL(24,4) NOT NULL,
"xyCost" DECIMAL(24,4) NOT NULL,
"afterSaleAmount" DECIMAL(24,4) NOT NULL,
"pushFeeTotal" DECIMAL(24,4) NOT NULL,
"marketingFeeTotal" DECIMAL(24,4) NOT NULL,
"advertisementFeeTotal" DECIMAL(24,4) NOT NULL,
"sharedWarehousingSum" DECIMAL(24,4) NOT NULL,
"otherFee" DECIMAL(24,4) NOT NULL,
"operatingFee" DECIMAL(24,4) NOT NULL,
"sourceRowCount" INTEGER NOT NULL,
"syncedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "SupplyChainProductArchiveSalesPerformanceDaily_pkey" PRIMARY KEY ("barcode", "statDate")
);
CREATE INDEX "SupplyChainProductArchiveSalesPerformanceDaily_statDate_idx" ON "SupplyChainProductArchiveSalesPerformanceDaily"("statDate");
CREATE INDEX "SupplyChainProductArchiveSalesPerformanceDaily_syncedAt_idx" ON "SupplyChainProductArchiveSalesPerformanceDaily"("syncedAt");
CREATE TABLE "SupplyChainProductArchiveSalesSyncRun" (
"id" TEXT NOT NULL,
"startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"completedAt" TIMESTAMP(3),
"status" VARCHAR(24) NOT NULL,
"rangeStart" DATE NOT NULL,
"rangeEnd" DATE NOT NULL,
"sourceLatestDate" DATE,
"rowsRead" INTEGER NOT NULL DEFAULT 0,
"rowsWritten" INTEGER NOT NULL DEFAULT 0,
"unmatchedBarcodes" INTEGER NOT NULL DEFAULT 0,
"errorMessage" TEXT,
CONSTRAINT "SupplyChainProductArchiveSalesSyncRun_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "SupplyChainProductArchiveSalesSyncRun_startedAt_idx" ON "SupplyChainProductArchiveSalesSyncRun"("startedAt");
CREATE INDEX "SupplyChainProductArchiveSalesSyncRun_status_startedAt_idx" ON "SupplyChainProductArchiveSalesSyncRun"("status", "startedAt");

View File

@ -0,0 +1,19 @@
-- CreateTable
CREATE TABLE "HomeQuickLink" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT NOT NULL DEFAULT '',
"url" TEXT NOT NULL,
"iconKey" TEXT NOT NULL DEFAULT 'link',
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"enabled" BOOLEAN NOT NULL DEFAULT true,
"createdBy" TEXT NOT NULL,
"updatedBy" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "HomeQuickLink_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "HomeQuickLink_enabled_sortOrder_idx" ON "HomeQuickLink"("enabled", "sortOrder");

View File

@ -0,0 +1,6 @@
-- Keep all existing barcode/day facts and the mainline shipAmount definition.
-- NULL salesAmount explicitly marks history requiring a source backfill.
ALTER TABLE "SupplyChainProductArchiveSalesPerformanceDaily"
ADD COLUMN "skuOperator" TEXT NOT NULL DEFAULT '',
ADD COLUMN "unitCost" DECIMAL(24,4),
ADD COLUMN "salesAmount" DECIMAL(24,4);

View File

@ -0,0 +1,19 @@
-- Operations-maintained checklist per dropship (DF) SPU on the 1688 dropship
-- issues page. Read-only columns always come live from the product archive;
-- only the two operations-maintained fields live here.
CREATE TABLE "SupplyChainDropshipIssue" (
"id" TEXT NOT NULL,
"spu" VARCHAR(120) NOT NULL,
"invoiceType" VARCHAR(16) NOT NULL DEFAULT '',
"returnAddress" TEXT NOT NULL DEFAULT '',
"updatedByName" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SupplyChainDropshipIssue_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "SupplyChainDropshipIssue_spu_key" ON "SupplyChainDropshipIssue"("spu");
CREATE INDEX "SupplyChainDropshipIssue_updatedAt_idx" ON "SupplyChainDropshipIssue"("updatedAt");
ALTER TYPE "OperationLogAction" ADD VALUE IF NOT EXISTS 'SUPPLY_CHAIN_DROPSHIP_ISSUE_WRITE';

View File

@ -0,0 +1,15 @@
ALTER TABLE "FinishedProductProgressItem"
ADD COLUMN "sourceOpeningCode" TEXT,
ADD COLUMN "sourceProductId" TEXT,
ADD COLUMN "sourceSkuId" TEXT,
ADD COLUMN "sourceGroupKey" TEXT,
ADD COLUMN "sourceSupplierCode" TEXT,
ADD COLUMN "sourceImportedAt" TIMESTAMP(3),
ADD COLUMN "supplierCode" TEXT NOT NULL DEFAULT '';
CREATE UNIQUE INDEX "FinishedProductProgressItem_factory_source_key"
ON "FinishedProductProgressItem" ("sourceOpeningCode", "sourceProductId", "sourceSkuId");
CREATE INDEX "FinishedProductProgressItem_sourceGroupKey_idx"
ON "FinishedProductProgressItem" ("sourceGroupKey");
CREATE INDEX "FinishedProductProgressItem_sourceImportedAt_idx"
ON "FinishedProductProgressItem" ("sourceImportedAt");

View File

@ -0,0 +1,4 @@
-- 1688 dropship issues page: operations now maintain whether the SPU ships
-- assembled (整装, drawing not required) or knocked down (拆装, drawing required).
ALTER TABLE "SupplyChainDropshipIssue"
ADD COLUMN "assemblyType" VARCHAR(16) NOT NULL DEFAULT '';

View File

@ -0,0 +1,21 @@
-- CreateTable
CREATE TABLE IF NOT EXISTS "BomCostingAgentSkuEstimate" (
"id" TEXT NOT NULL,
"caseId" TEXT NOT NULL,
"skuId" TEXT NOT NULL,
"externalSkuId" TEXT NOT NULL DEFAULT '',
"engineVersion" TEXT NOT NULL,
"status" TEXT NOT NULL,
"estimate" JSONB NOT NULL,
"usage" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "BomCostingAgentSkuEstimate_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX IF NOT EXISTS "BomCostingAgentSkuEstimate_skuId_engineVersion_key" ON "BomCostingAgentSkuEstimate"("skuId", "engineVersion");
-- CreateIndex
CREATE INDEX IF NOT EXISTS "BomCostingAgentSkuEstimate_caseId_idx" ON "BomCostingAgentSkuEstimate"("caseId");

View File

@ -0,0 +1,22 @@
CREATE TABLE "SupplyChainManagementOwnerConfig" (
"id" TEXT NOT NULL,
"spu" TEXT NOT NULL,
"itemNumber" TEXT NOT NULL,
"productCode" TEXT NOT NULL,
"operationOwner" TEXT NOT NULL DEFAULT '',
"departmentOwner" TEXT NOT NULL DEFAULT '',
"generalOwner" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SupplyChainManagementOwnerConfig_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "SupplyChainManagementOwnerConfig_spu_itemNumber_productCode_key"
ON "SupplyChainManagementOwnerConfig"("spu", "itemNumber", "productCode");
CREATE INDEX "SupplyChainManagementOwnerConfig_spu_idx"
ON "SupplyChainManagementOwnerConfig"("spu");
CREATE INDEX "SupplyChainManagementOwnerConfig_itemNumber_idx"
ON "SupplyChainManagementOwnerConfig"("itemNumber");
CREATE INDEX "SupplyChainManagementOwnerConfig_productCode_idx"
ON "SupplyChainManagementOwnerConfig"("productCode");

View File

@ -0,0 +1,9 @@
DROP INDEX IF EXISTS "SupplyChainManagementOwnerConfig_spu_itemNumber_productCode_key";
DROP INDEX IF EXISTS "SupplyChainManagementOwnerConfig_spu_idx";
DROP INDEX IF EXISTS "SupplyChainManagementOwnerConfig_itemNumber_idx";
DROP INDEX IF EXISTS "SupplyChainManagementOwnerConfig_productCode_idx";
ALTER TABLE "SupplyChainManagementOwnerConfig"
DROP COLUMN IF EXISTS "spu",
DROP COLUMN IF EXISTS "itemNumber",
DROP COLUMN IF EXISTS "productCode";

View File

@ -0,0 +1,8 @@
ALTER TABLE "Department"
ADD COLUMN "directoryManagerDingtalkUserIds" TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[];
ALTER TABLE "EmployeeDirectory"
ADD COLUMN "managerDingtalkUserId" TEXT;
CREATE INDEX "EmployeeDirectory_managerDingtalkUserId_idx"
ON "EmployeeDirectory"("managerDingtalkUserId");

View File

@ -0,0 +1,10 @@
ALTER TABLE "SupplyChainManagementOwnerConfig"
ADD COLUMN "sourceEmployeeId" TEXT;
CREATE UNIQUE INDEX "SupplyChainManagementOwnerConfig_sourceEmployeeId_key"
ON "SupplyChainManagementOwnerConfig"("sourceEmployeeId");
ALTER TABLE "SupplyChainManagementOwnerConfig"
ADD CONSTRAINT "SupplyChainManagementOwnerConfig_sourceEmployeeId_fkey"
FOREIGN KEY ("sourceEmployeeId") REFERENCES "EmployeeDirectory"("id")
ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,6 @@
ALTER TABLE "SupplyChainManagementOwnerConfig"
ADD COLUMN "manualOverride" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "isHidden" BOOLEAN NOT NULL DEFAULT false;
CREATE INDEX "SupplyChainManagementOwnerConfig_isHidden_updatedAt_idx"
ON "SupplyChainManagementOwnerConfig"("isHidden", "updatedAt");

View File

@ -0,0 +1,3 @@
ALTER TABLE "SupplyChainManagementOwnerConfig"
ADD COLUMN "department" TEXT NOT NULL DEFAULT '',
ADD COLUMN "profitCenter" TEXT NOT NULL DEFAULT '';

View File

@ -0,0 +1,42 @@
CREATE TABLE "Dropship1688SourcingTask" (
"id" TEXT NOT NULL,
"sourceArchiveId" TEXT NOT NULL,
"sourceSpu" TEXT NOT NULL DEFAULT '',
"sourceBarcode" VARCHAR(100) NOT NULL,
"sourceCostPrice" DECIMAL(24,4),
"sourceSkuOperator" TEXT NOT NULL DEFAULT '',
"sourceDateFrom" DATE NOT NULL,
"sourceDateTo" DATE NOT NULL,
"purchaserUserId" TEXT NOT NULL,
"purchaserName" TEXT NOT NULL,
"supplierName" TEXT NOT NULL DEFAULT '',
"quoted_price_tax_included" DECIMAL(24,4),
"invoiceTaxPoint" DECIMAL(8,4),
"sourcingMode" TEXT NOT NULL DEFAULT '',
"jianshangBarcode" TEXT NOT NULL DEFAULT '',
"acceptedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Dropship1688SourcingTask_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "Dropship1688SourcingTask_sourceBarcode_purchaserUserId_key"
ON "Dropship1688SourcingTask"("sourceBarcode", "purchaserUserId");
CREATE INDEX "Dropship1688SourcingTask_sourceArchiveId_idx"
ON "Dropship1688SourcingTask"("sourceArchiveId");
CREATE INDEX "Dropship1688SourcingTask_purchaserName_idx"
ON "Dropship1688SourcingTask"("purchaserName");
CREATE INDEX "Dropship1688SourcingTask_sourceSpu_idx"
ON "Dropship1688SourcingTask"("sourceSpu");
CREATE INDEX "Dropship1688SourcingTask_sourceSkuOperator_idx"
ON "Dropship1688SourcingTask"("sourceSkuOperator");
CREATE INDEX "Dropship1688SourcingTask_purchaserUserId_acceptedAt_idx"
ON "Dropship1688SourcingTask"("purchaserUserId", "acceptedAt");
CREATE INDEX "Dropship1688SourcingTask_acceptedAt_idx"
ON "Dropship1688SourcingTask"("acceptedAt");

View File

@ -0,0 +1,3 @@
-- Store the quantity-one freight estimate separately from the existing total freight fact.
ALTER TABLE "SupplyChainProductArchiveSalesPerformanceDaily"
ADD COLUMN "estimatedFreight" DECIMAL(24,4);

View File

@ -0,0 +1,32 @@
ALTER TABLE "SupplyChainManagementOwnerConfig"
ADD COLUMN "secondaryDepartment" TEXT NOT NULL DEFAULT '';
WITH RECURSIVE department_tree AS (
SELECT "id", "name", "parentId", 0 AS depth
FROM "Department"
WHERE "externalId" = '1'
UNION ALL
SELECT child."id", child."name", child."parentId", parent.depth + 1
FROM "Department" child
INNER JOIN department_tree parent ON child."parentId" = parent."id"
), employee_ancestors AS (
SELECT employee."id" AS "employeeId", department."id" AS "departmentId", department."parentId"
FROM "EmployeeDirectory" employee
INNER JOIN "Department" department
ON department."externalId" = employee."primaryDepartmentExternalId"
UNION ALL
SELECT ancestor."employeeId", parent."id", parent."parentId"
FROM employee_ancestors ancestor
INNER JOIN "Department" parent ON parent."id" = ancestor."parentId"
)
UPDATE "SupplyChainManagementOwnerConfig" config
SET "secondaryDepartment" = department_tree."name"
FROM employee_ancestors
INNER JOIN department_tree
ON department_tree."id" = employee_ancestors."departmentId"
AND department_tree.depth = 3
WHERE config."sourceEmployeeId" = employee_ancestors."employeeId";

View File

@ -0,0 +1,67 @@
ALTER TABLE "SupplyChainProductArchive"
ADD COLUMN "sabcRating" VARCHAR(1) NOT NULL DEFAULT '',
ADD COLUMN "sabcUpdatedAt" TIMESTAMP(3);
CREATE TABLE "SupplyChainProductArchiveSabcResult" (
"id" TEXT NOT NULL,
"archiveId" TEXT NOT NULL,
"barcode" VARCHAR(100) NOT NULL,
"periodStart" DATE NOT NULL,
"periodEnd" DATE NOT NULL,
"eligible" BOOLEAN NOT NULL,
"sabcRating" VARCHAR(1) NOT NULL DEFAULT '',
"category" TEXT NOT NULL DEFAULT '',
"performanceScale" TEXT NOT NULL DEFAULT '',
"salesScale" TEXT NOT NULL DEFAULT '',
"finalScale" TEXT NOT NULL DEFAULT '',
"profitLabel" TEXT NOT NULL DEFAULT '',
"averageDailySales" DECIMAL(24,8),
"feeRatio" DECIMAL(20,8),
"grossMargin" DECIMAL(20,8),
"shipAmount" DECIMAL(24,4) NOT NULL,
"realPerformance" DECIMAL(24,4) NOT NULL,
"shipQty" BIGINT NOT NULL,
"grossProfit" DECIMAL(24,4) NOT NULL,
"afterSaleAmount" DECIMAL(24,4) NOT NULL,
"status" VARCHAR(24) NOT NULL DEFAULT 'CALCULATED',
"reason" TEXT NOT NULL DEFAULT '',
"calculatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "SupplyChainProductArchiveSabcResult_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "SupplyChainProductArchiveSabcRun" (
"id" TEXT NOT NULL,
"periodStart" DATE NOT NULL,
"periodEnd" DATE NOT NULL,
"mode" VARCHAR(16) NOT NULL,
"status" VARCHAR(24) NOT NULL,
"sourceRows" INTEGER NOT NULL DEFAULT 0,
"eligibleRows" INTEGER NOT NULL DEFAULT 0,
"taggedRows" INTEGER NOT NULL DEFAULT 0,
"comparedRows" INTEGER NOT NULL DEFAULT 0,
"mismatchRows" INTEGER NOT NULL DEFAULT 0,
"errorMessage" TEXT,
"startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"completedAt" TIMESTAMP(3),
CONSTRAINT "SupplyChainProductArchiveSabcRun_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "SupplyChainProductArchiveSabcResult_barcode_periodStart_periodEnd_key"
ON "SupplyChainProductArchiveSabcResult"("barcode", "periodStart", "periodEnd");
CREATE INDEX "SupplyChainProductArchiveSabcResult_periodStart_periodEnd_idx"
ON "SupplyChainProductArchiveSabcResult"("periodStart", "periodEnd");
CREATE INDEX "SupplyChainProductArchiveSabcResult_sabcRating_periodEnd_idx"
ON "SupplyChainProductArchiveSabcResult"("sabcRating", "periodEnd");
CREATE INDEX "SupplyChainProductArchiveSabcResult_archiveId_periodEnd_idx"
ON "SupplyChainProductArchiveSabcResult"("archiveId", "periodEnd");
CREATE UNIQUE INDEX "SupplyChainProductArchiveSabcRun_periodStart_periodEnd_mode_key"
ON "SupplyChainProductArchiveSabcRun"("periodStart", "periodEnd", "mode");
CREATE INDEX "SupplyChainProductArchiveSabcRun_status_startedAt_idx"
ON "SupplyChainProductArchiveSabcRun"("status", "startedAt");
ALTER TABLE "SupplyChainProductArchiveSabcResult"
ADD CONSTRAINT "SupplyChainProductArchiveSabcResult_archiveId_fkey"
FOREIGN KEY ("archiveId") REFERENCES "SupplyChainProductArchive"("id")
ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,2 @@
-- Add factory stock (scmStock from plm_productDiscontinuation) to the archive main table.
ALTER TABLE "SupplyChainProductArchive" ADD COLUMN "stockFactory" TEXT NOT NULL DEFAULT '';

View File

@ -0,0 +1,29 @@
-- 全局下载中心:后台导出/下载任务
CREATE TABLE "DownloadTask" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"userName" TEXT NOT NULL DEFAULT '',
"type" TEXT NOT NULL,
"title" TEXT NOT NULL DEFAULT '',
"paramsJson" TEXT NOT NULL DEFAULT '{}',
"status" TEXT NOT NULL DEFAULT 'pending',
"progress" INTEGER NOT NULL DEFAULT 0,
"fileName" TEXT NOT NULL DEFAULT '',
"filePath" TEXT NOT NULL DEFAULT '',
"fileSize" INTEGER NOT NULL DEFAULT 0,
"rowCount" INTEGER NOT NULL DEFAULT 0,
"error" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"startedAt" TIMESTAMP(3),
"finishedAt" TIMESTAMP(3),
CONSTRAINT "DownloadTask_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "DownloadTask_userId_createdAt_idx" ON "DownloadTask"("userId", "createdAt" DESC);
CREATE INDEX "DownloadTask_status_idx" ON "DownloadTask"("status");
ALTER TABLE "DownloadTask"
ADD CONSTRAINT "DownloadTask_status_check"
CHECK ("status" IN ('pending', 'processing', 'completed', 'failed'));

View File

@ -0,0 +1,41 @@
ALTER TABLE "WorkRecordScoreRule"
ADD COLUMN "workFrequency" TEXT NOT NULL DEFAULT '',
ADD COLUMN "initiativeType" TEXT NOT NULL DEFAULT '';
CREATE INDEX "WorkRecordScoreRule_workFrequency_sortOrder_idx"
ON "WorkRecordScoreRule"("workFrequency", "sortOrder");
CREATE INDEX "WorkRecordScoreRule_initiativeType_sortOrder_idx"
ON "WorkRecordScoreRule"("initiativeType", "sortOrder");
CREATE TABLE "WorkRecordBusinessOption" (
"id" TEXT NOT NULL,
"type" TEXT NOT NULL,
"value" TEXT NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "WorkRecordBusinessOption_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "WorkRecordBusinessOption_type_value_key"
ON "WorkRecordBusinessOption"("type", "value");
CREATE INDEX "WorkRecordBusinessOption_type_sortOrder_idx"
ON "WorkRecordBusinessOption"("type", "sortOrder");
CREATE INDEX "WorkRecordBusinessOption_type_isActive_sortOrder_idx"
ON "WorkRecordBusinessOption"("type", "isActive", "sortOrder");
INSERT INTO "WorkRecordBusinessOption" ("id", "type", "value", "sortOrder", "isActive", "createdAt", "updatedAt")
VALUES
('employee-hours-category-product', 'BUSINESS_CATEGORY', '产品开发', 1, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-category-design', 'BUSINESS_CATEGORY', '设计支持', 2, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-category-operation', 'BUSINESS_CATEGORY', '日常运营', 3, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-frequency-daily', 'WORK_FREQUENCY', '每日', 1, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-frequency-weekly', 'WORK_FREQUENCY', '每周', 2, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-frequency-monthly', 'WORK_FREQUENCY', '每月', 3, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-frequency-temporary', 'WORK_FREQUENCY', '临时', 4, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-initiative-active', 'INITIATIVE_TYPE', '主动', 1, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-initiative-passive', 'INITIATIVE_TYPE', '被动', 2, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);

View File

@ -0,0 +1,2 @@
ALTER TABLE "SupplyChainProductArchive"
ADD COLUMN "productAttribute" TEXT NOT NULL DEFAULT '';

View File

@ -0,0 +1,35 @@
CREATE TABLE "EmployeeHoursRecord" (
"id" TEXT NOT NULL,
"workDate" DATE NOT NULL,
"nickname" TEXT NOT NULL,
"businessContent" TEXT NOT NULL,
"businessNote" TEXT NOT NULL DEFAULT '',
"businessCategory" TEXT NOT NULL,
"workFrequency" TEXT NOT NULL,
"businessSource" TEXT NOT NULL,
"startTime" VARCHAR(16) NOT NULL,
"endTime" VARCHAR(16) NOT NULL,
"minutes" INTEGER NOT NULL,
"hours" DECIMAL(10,2) NOT NULL,
"estimatedScore" DECIMAL(12,4) NOT NULL,
"approvedScore" DECIMAL(12,4),
"department" TEXT NOT NULL DEFAULT '',
"createdById" TEXT NOT NULL,
"updatedById" TEXT NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "EmployeeHoursRecord_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "EmployeeHoursRecord_workDate_deletedAt_idx"
ON "EmployeeHoursRecord"("workDate", "deletedAt");
CREATE INDEX "EmployeeHoursRecord_nickname_workDate_idx"
ON "EmployeeHoursRecord"("nickname", "workDate");
CREATE INDEX "EmployeeHoursRecord_businessCategory_workDate_idx"
ON "EmployeeHoursRecord"("businessCategory", "workDate");
CREATE INDEX "EmployeeHoursRecord_department_workDate_idx"
ON "EmployeeHoursRecord"("department", "workDate");

View File

@ -0,0 +1,3 @@
-- 店铺状态(数字码:0=正常、6=已关闭、3=已出售;来源公司报表库 rpt.fys_shopInfo,每日定时同步)
-- NULL 表示尚未同步或在公司报表库中未匹配到对应店铺
ALTER TABLE "StoreAccount" ADD COLUMN "shopState" INTEGER;

View File

@ -0,0 +1,26 @@
-- 类目配置增加「分类」维度,并新增分类规模档位表
ALTER TABLE "SupplyChainProductArchiveCategoryConfig" ADD COLUMN "categoryGroup" TEXT NOT NULL DEFAULT '';
CREATE INDEX "SupplyChainProductArchiveCategoryConfig_categoryGroup_idx" ON "SupplyChainProductArchiveCategoryConfig"("categoryGroup");
-- CreateTable
CREATE TABLE "SupplyChainProductArchiveCategoryGroupTier" (
"id" TEXT NOT NULL,
"categoryGroup" TEXT NOT NULL,
"scaleLabel" TEXT NOT NULL,
"performanceStart" DECIMAL(14,2) NOT NULL,
"performanceEnd" DECIMAL(14,2),
"dailySalesStart" DECIMAL(12,2) NOT NULL,
"dailySalesEnd" DECIMAL(12,2),
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SupplyChainProductArchiveCategoryGroupTier_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "SupplyChainProductArchiveCategoryGroupTier_categoryGroup_scaleLabel_key" ON "SupplyChainProductArchiveCategoryGroupTier"("categoryGroup", "scaleLabel");
-- CreateIndex
CREATE INDEX "SupplyChainProductArchiveCategoryGroupTier_categoryGroup_idx" ON "SupplyChainProductArchiveCategoryGroupTier"("categoryGroup");

View File

@ -0,0 +1,6 @@
-- 类目配置增加规模标签与业绩/日销阈值字段(终点可空 = 不设上限)
ALTER TABLE "SupplyChainProductArchiveCategoryConfig" ADD COLUMN "scaleLabel" TEXT NOT NULL DEFAULT '';
ALTER TABLE "SupplyChainProductArchiveCategoryConfig" ADD COLUMN "performanceStart" DECIMAL(14,2);
ALTER TABLE "SupplyChainProductArchiveCategoryConfig" ADD COLUMN "performanceEnd" DECIMAL(14,2);
ALTER TABLE "SupplyChainProductArchiveCategoryConfig" ADD COLUMN "dailySalesStart" DECIMAL(12,2);
ALTER TABLE "SupplyChainProductArchiveCategoryConfig" ADD COLUMN "dailySalesEnd" DECIMAL(12,2);

View File

@ -0,0 +1,19 @@
-- 类目规模行表:一行 = 一个二级类目 × 一档规模标签
CREATE TABLE "SupplyChainProductArchiveCategoryScaleRow" (
"id" TEXT NOT NULL,
"categoryL2" TEXT NOT NULL,
"scaleLabel" TEXT NOT NULL,
"performanceStart" DECIMAL(14,2),
"performanceEnd" DECIMAL(14,2),
"dailySalesStart" DECIMAL(12,2),
"dailySalesEnd" DECIMAL(12,2),
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SupplyChainProductArchiveCategoryScaleRow_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "SupplyChainProductArchiveCategoryScaleRow_categoryL2_scaleLabel_key" ON "SupplyChainProductArchiveCategoryScaleRow"("categoryL2", "scaleLabel");
CREATE INDEX "SupplyChainProductArchiveCategoryScaleRow_categoryL2_idx" ON "SupplyChainProductArchiveCategoryScaleRow"("categoryL2");

View File

@ -0,0 +1,26 @@
CREATE TABLE "EmployeeHoursAccessGrant" (
"userId" TEXT NOT NULL,
"scope" TEXT NOT NULL DEFAULT 'SELF',
"updatedById" TEXT NOT NULL,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "EmployeeHoursAccessGrant_pkey" PRIMARY KEY ("userId"),
CONSTRAINT "EmployeeHoursAccessGrant_scope_check" CHECK ("scope" IN ('SELF', 'DEPARTMENT', 'ALL'))
);
CREATE TABLE "EmployeeHoursAuditLog" (
"id" TEXT NOT NULL,
"area" TEXT NOT NULL,
"action" TEXT NOT NULL,
"status" TEXT NOT NULL,
"actorId" TEXT NOT NULL,
"actorName" TEXT NOT NULL,
"targetId" TEXT,
"message" TEXT NOT NULL,
"details" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "EmployeeHoursAuditLog_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "EmployeeHoursAuditLog_area_createdAt_idx" ON "EmployeeHoursAuditLog"("area", "createdAt");
CREATE INDEX "EmployeeHoursAuditLog_actorId_createdAt_idx" ON "EmployeeHoursAuditLog"("actorId", "createdAt");
CREATE INDEX "EmployeeHoursAuditLog_status_createdAt_idx" ON "EmployeeHoursAuditLog"("status", "createdAt");

View File

@ -0,0 +1,15 @@
-- 合并为单表:类目规模行表自带一级类目与分类列,删除分类档位模板表
ALTER TABLE "SupplyChainProductArchiveCategoryScaleRow" ADD COLUMN "categoryL1" TEXT NOT NULL DEFAULT '';
ALTER TABLE "SupplyChainProductArchiveCategoryScaleRow" ADD COLUMN "categoryGroup" TEXT NOT NULL DEFAULT '';
-- 从类目主表回填一级类目与分类
UPDATE "SupplyChainProductArchiveCategoryScaleRow" s
SET "categoryL1" = c."categoryL1",
"categoryGroup" = c."categoryGroup"
FROM "SupplyChainProductArchiveCategoryConfig" c
WHERE s."categoryL2" = c."categoryL2";
CREATE INDEX "SupplyChainProductArchiveCategoryScaleRow_categoryGroup_idx" ON "SupplyChainProductArchiveCategoryScaleRow"("categoryGroup");
-- 删除分类档位模板表(其数据已按类目展开至规模行表)
DROP TABLE "SupplyChainProductArchiveCategoryGroupTier";

View File

@ -0,0 +1,2 @@
ALTER TABLE "EmployeeHoursRecord"
ADD COLUMN "attachment" JSONB;

View File

@ -0,0 +1,57 @@
-- 员工工时独立业务配置(与工作记录 WorkRecordBusinessOption / WorkRecordScoreRule 解耦,空白起始)
CREATE TABLE "EmployeeHoursBusinessOption" (
"id" TEXT NOT NULL,
"type" TEXT NOT NULL,
"value" TEXT NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "EmployeeHoursBusinessOption_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "EmployeeHoursBusinessOption_type_value_key"
ON "EmployeeHoursBusinessOption"("type", "value");
CREATE INDEX "EmployeeHoursBusinessOption_type_sortOrder_idx"
ON "EmployeeHoursBusinessOption"("type", "sortOrder");
CREATE INDEX "EmployeeHoursBusinessOption_type_isActive_sortOrder_idx"
ON "EmployeeHoursBusinessOption"("type", "isActive", "sortOrder");
-- 初始选项与作者环境对齐:业务来源=提前规划/临时任务,工作频率=每日/每周/每月/临时;业务分类留空由页面配置
INSERT INTO "EmployeeHoursBusinessOption" ("id", "type", "value", "sortOrder", "isActive", "createdAt", "updatedAt")
VALUES
('employee-hours-frequency-daily', 'WORK_FREQUENCY', '每日', 1, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-frequency-weekly', 'WORK_FREQUENCY', '每周', 2, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-frequency-monthly', 'WORK_FREQUENCY', '每月', 3, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-frequency-temporary', 'WORK_FREQUENCY', '临时', 4, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-initiative-planned', 'INITIATIVE_TYPE', '提前规划', 1, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('employee-hours-initiative-temporary-task', 'INITIATIVE_TYPE', '临时任务', 2, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
CREATE TABLE "EmployeeHoursScoreRule" (
"id" TEXT NOT NULL,
"primary" TEXT NOT NULL,
"secondary" TEXT NOT NULL DEFAULT '',
"workFrequency" TEXT NOT NULL DEFAULT '',
"initiativeType" TEXT NOT NULL DEFAULT '',
"unitScore" DECIMAL(12, 4),
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "EmployeeHoursScoreRule_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "EmployeeHoursScoreRule_primary_sortOrder_idx"
ON "EmployeeHoursScoreRule"("primary", "sortOrder");
CREATE INDEX "EmployeeHoursScoreRule_workFrequency_sortOrder_idx"
ON "EmployeeHoursScoreRule"("workFrequency", "sortOrder");
CREATE INDEX "EmployeeHoursScoreRule_initiativeType_sortOrder_idx"
ON "EmployeeHoursScoreRule"("initiativeType", "sortOrder");
CREATE INDEX "EmployeeHoursScoreRule_isActive_sortOrder_idx"
ON "EmployeeHoursScoreRule"("isActive", "sortOrder");

View File

@ -0,0 +1,33 @@
ALTER TABLE "SupplyChainProductArchiveCategoryConfig"
ADD COLUMN IF NOT EXISTS "categoryGroup" TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS "scaleLabel" TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS "performanceStart" DECIMAL(14,2),
ADD COLUMN IF NOT EXISTS "performanceEnd" DECIMAL(14,2),
ADD COLUMN IF NOT EXISTS "dailySalesStart" DECIMAL(12,2),
ADD COLUMN IF NOT EXISTS "dailySalesEnd" DECIMAL(12,2);
CREATE INDEX IF NOT EXISTS "SupplyChainProductArchiveCategoryConfig_categoryGroup_idx"
ON "SupplyChainProductArchiveCategoryConfig"("categoryGroup");
CREATE TABLE IF NOT EXISTS "SupplyChainProductArchiveCategoryScaleRow" (
"id" TEXT NOT NULL,
"categoryL1" TEXT NOT NULL DEFAULT '',
"categoryL2" TEXT NOT NULL,
"categoryGroup" TEXT NOT NULL DEFAULT '',
"scaleLabel" TEXT NOT NULL,
"performanceStart" DECIMAL(14,2) NOT NULL,
"performanceEnd" DECIMAL(14,2),
"dailySalesStart" DECIMAL(12,2) NOT NULL,
"dailySalesEnd" DECIMAL(12,2),
"sortOrder" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SupplyChainProductArchiveCategoryScaleRow_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "SupplyChainProductArchiveCategoryScaleRow_categoryL2_scaleLabel_key"
ON "SupplyChainProductArchiveCategoryScaleRow"("categoryL2", "scaleLabel");
CREATE INDEX IF NOT EXISTS "SupplyChainProductArchiveCategoryScaleRow_categoryL2_idx"
ON "SupplyChainProductArchiveCategoryScaleRow"("categoryL2");
CREATE INDEX IF NOT EXISTS "SupplyChainProductArchiveCategoryScaleRow_categoryGroup_idx"
ON "SupplyChainProductArchiveCategoryScaleRow"("categoryGroup");

View File

@ -0,0 +1,53 @@
-- 物流到货签收时长看板的订单明细日快照(纯新增表,仅本地 Postgres 物化,不改动既有表)。
CREATE TABLE "LogisticsArrivalSigningDurationSnapshotRun" (
"id" TEXT NOT NULL,
"snapshotDate" DATE NOT NULL,
"status" TEXT NOT NULL DEFAULT 'RUNNING',
"windowFrom" TEXT NOT NULL DEFAULT '',
"windowTo" TEXT NOT NULL DEFAULT '',
"rowCount" INTEGER NOT NULL DEFAULT 0,
"message" TEXT NOT NULL DEFAULT '',
"startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"finishedAt" TIMESTAMP(3),
CONSTRAINT "LogisticsArrivalSigningDurationSnapshotRun_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "LogisticsArrivalSigningDurationSnapshotRun_snapshotDate_key" ON "LogisticsArrivalSigningDurationSnapshotRun"("snapshotDate");
CREATE INDEX "LogisticsArrivalSigningDurationSnapshotRun_status_date_idx" ON "LogisticsArrivalSigningDurationSnapshotRun"("status", "snapshotDate");
CREATE TABLE "LogisticsArrivalSigningDurationSnapshot" (
"id" TEXT NOT NULL,
"snapshotDate" DATE NOT NULL,
"rowKey" TEXT NOT NULL,
"innerOrderId" TEXT NOT NULL,
"soId" TEXT NOT NULL DEFAULT '',
"logisticId" TEXT NOT NULL DEFAULT '',
"logisticCompanyName" TEXT NOT NULL DEFAULT '',
"logisticStatusName" TEXT NOT NULL DEFAULT '',
"logisticReturnFlag" TEXT NOT NULL DEFAULT '',
"orderLabels" TEXT NOT NULL DEFAULT '',
"remark" TEXT NOT NULL DEFAULT '',
"buyerMessage" TEXT NOT NULL DEFAULT '',
"shopName" TEXT NOT NULL DEFAULT '',
"platformName" TEXT NOT NULL DEFAULT '',
"warehouseName" TEXT NOT NULL DEFAULT '',
"receiverAddress" TEXT NOT NULL DEFAULT '',
"payTime" TEXT,
"sendTime" TEXT,
"fetchTime" TEXT,
"signTime" TEXT,
"barcode" TEXT NOT NULL DEFAULT '',
"productName" TEXT NOT NULL DEFAULT '',
"supplier" TEXT NOT NULL DEFAULT '',
"purchaser" TEXT NOT NULL DEFAULT '',
"productItems" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "LogisticsArrivalSigningDurationSnapshot_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "LogisticsArrivalSigningDurationSnapshot_date_row_key_key" ON "LogisticsArrivalSigningDurationSnapshot"("snapshotDate", "rowKey");
CREATE INDEX "LogisticsArrivalSigningDurationSnapshot_date_sign_time_idx" ON "LogisticsArrivalSigningDurationSnapshot"("snapshotDate", "signTime");

View File

@ -0,0 +1,3 @@
-- 产品全量档案新增「产品类型」:定厂「产品生产类型」优先,JST rpt.jst_sku.vc_name 兜底。
ALTER TABLE "SupplyChainProductArchive"
ADD COLUMN "productionType" TEXT NOT NULL DEFAULT '';

View File

@ -0,0 +1,5 @@
-- 商品 SABC 月度核算冻结:保存生成时的整行明细快照。
-- 生成后(每月 3 日自动、或管理员手动执行核算)页面只读取该快照,
-- 后续档案销售状态、人员字段等变化不会再改变已生成月份的数据。
ALTER TABLE "SupplyChainProductArchiveSabcResult"
ADD COLUMN IF NOT EXISTS "snapshot" JSONB;

View File

@ -0,0 +1,86 @@
-- 停产需求管理初版:需求单、条码明细行、停产原因分类配置(纯新增表,不改动既有表)。
CREATE TABLE "ProductionStopDemand" (
"id" TEXT NOT NULL,
"demandNo" TEXT NOT NULL,
"requesterId" TEXT NOT NULL,
"requesterName" TEXT NOT NULL,
"department" TEXT NOT NULL DEFAULT '',
"reasonCategory" TEXT NOT NULL,
"reasonDetail" TEXT NOT NULL DEFAULT '',
"status" TEXT NOT NULL DEFAULT 'SUBMITTED',
"submittedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProductionStopDemand_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "ProductionStopDemand_demandNo_key" ON "ProductionStopDemand"("demandNo");
CREATE INDEX "ProductionStopDemand_status_submittedAt_idx" ON "ProductionStopDemand"("status", "submittedAt");
CREATE INDEX "ProductionStopDemand_requesterId_submittedAt_idx" ON "ProductionStopDemand"("requesterId", "submittedAt");
CREATE TABLE "ProductionStopDemandItem" (
"id" TEXT NOT NULL,
"demandId" TEXT NOT NULL,
"barcode" TEXT NOT NULL,
"categoryL1" TEXT NOT NULL DEFAULT '',
"categoryL2" TEXT NOT NULL DEFAULT '',
"spu" TEXT NOT NULL DEFAULT '',
"sku" TEXT NOT NULL DEFAULT '',
"productName" TEXT NOT NULL DEFAULT '',
"productStatus" TEXT NOT NULL DEFAULT '',
"supplierName" TEXT NOT NULL DEFAULT '',
"purchaseOwner" TEXT NOT NULL DEFAULT '',
"brand" TEXT NOT NULL DEFAULT '',
"productTag" TEXT NOT NULL DEFAULT '',
"productLabel" TEXT NOT NULL DEFAULT '',
"barcodeCreatedAt" TIMESTAMP(3),
"postStopSkuStatus" TEXT NOT NULL DEFAULT '',
"sales30dWithBig" TEXT NOT NULL DEFAULT '',
"sales30dWithBigSku" TEXT NOT NULL DEFAULT '',
"opsDirector" TEXT NOT NULL DEFAULT '',
"topSalesOpsDirector" TEXT NOT NULL DEFAULT '',
"stopSuggestion" TEXT NOT NULL DEFAULT '',
"replaceType" TEXT NOT NULL DEFAULT '',
"replaceBarcode" TEXT NOT NULL DEFAULT '',
"imageChange" TEXT NOT NULL DEFAULT '',
"modificationPoint" TEXT NOT NULL DEFAULT '',
"responseStatus" TEXT NOT NULL DEFAULT 'PENDING',
"responderId" TEXT,
"responderName" TEXT NOT NULL DEFAULT '',
"responseNote" TEXT NOT NULL DEFAULT '',
"respondedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProductionStopDemandItem_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "ProductionStopDemandItem_demandId_barcode_key" ON "ProductionStopDemandItem"("demandId", "barcode");
CREATE INDEX "ProductionStopDemandItem_barcode_idx" ON "ProductionStopDemandItem"("barcode");
CREATE INDEX "ProductionStopDemandItem_sku_idx" ON "ProductionStopDemandItem"("sku");
CREATE INDEX "ProductionStopDemandItem_responseStatus_updatedAt_idx" ON "ProductionStopDemandItem"("responseStatus", "updatedAt");
CREATE INDEX "ProductionStopDemandItem_purchaseOwner_responseStatus_idx" ON "ProductionStopDemandItem"("purchaseOwner", "responseStatus");
ALTER TABLE "ProductionStopDemandItem" ADD CONSTRAINT "ProductionStopDemandItem_demandId_fkey" FOREIGN KEY ("demandId") REFERENCES "ProductionStopDemand"("id") ON DELETE CASCADE ON UPDATE CASCADE;
CREATE TABLE "ProductionStopReasonCategory" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProductionStopReasonCategory_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "ProductionStopReasonCategory_name_key" ON "ProductionStopReasonCategory"("name");
CREATE INDEX "ProductionStopReasonCategory_isActive_sortOrder_idx" ON "ProductionStopReasonCategory"("isActive", "sortOrder");

View File

@ -0,0 +1,2 @@
-- 停产需求明细删除支持写入操作日志(新增枚举值,不改动既有数据)。
ALTER TYPE "OperationLogAction" ADD VALUE 'PRODUCTION_STOP_DEMAND_DELETE';

View File

@ -0,0 +1,12 @@
-- 商品SABC打标「各部门数据分类」配置表:品牌范围、负责人归属例外、数据权限例外、目标参数。
CREATE TABLE IF NOT EXISTS "SupplyChainProductArchiveDepartmentSetting" (
"id" TEXT NOT NULL,
"key" VARCHAR(64) NOT NULL,
"value" JSONB NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "SupplyChainProductArchiveDepartmentSetting_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "SupplyChainProductArchiveDepartmentSetting_key_key"
ON "SupplyChainProductArchiveDepartmentSetting" ("key");

View File

@ -0,0 +1,16 @@
-- 商品SABC打标「各部门数据分类」月度快照:核算生成后落库,页面/导出直接读结果,不再每次重算。
CREATE TABLE IF NOT EXISTS "SupplyChainProductArchiveDepartmentSnapshot" (
"id" TEXT NOT NULL,
"periodStart" DATE NOT NULL,
"periodEnd" DATE NOT NULL,
"payload" JSONB NOT NULL,
"rowTotal" INTEGER NOT NULL DEFAULT 0,
"generatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "SupplyChainProductArchiveDepartmentSnapshot_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "SupplyChainProductArchiveDepartmentSnapshot_periodStart_periodEnd_key"
ON "SupplyChainProductArchiveDepartmentSnapshot" ("periodStart", "periodEnd");
CREATE INDEX IF NOT EXISTS "SupplyChainProductArchiveDepartmentSnapshot_generatedAt_idx"
ON "SupplyChainProductArchiveDepartmentSnapshot" ("generatedAt");

View File

@ -0,0 +1,172 @@
-- CreateTable
CREATE TABLE "InstallationDemoV2Drawing" (
"id" TEXT NOT NULL,
"spuCode" TEXT NOT NULL,
"drawingCode" TEXT NOT NULL,
"createdBy" TEXT NOT NULL,
"parts" JSONB,
"steps" JSONB,
"numbering" JSONB,
"toolIds" JSONB,
"uploadedModel" JSONB,
"explodeTemplate" JSONB,
"sourceDrawingId" TEXT,
"sourceCopiedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "InstallationDemoV2Drawing_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InstallationDemoV2NumberingConfig" (
"id" TEXT NOT NULL,
"drawingId" TEXT NOT NULL,
"parts" JSONB NOT NULL,
"view" JSONB,
"partsUpdatedAt" TIMESTAMP(3),
"viewUpdatedAt" TIMESTAMP(3),
"updatedById" TEXT,
"updatedByName" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "InstallationDemoV2NumberingConfig_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InstallationDemoV2NumberingSnapshot" (
"id" TEXT NOT NULL,
"drawingId" TEXT NOT NULL,
"modelUrl" TEXT NOT NULL,
"modelIdentity" TEXT NOT NULL,
"snapshotUrls" JSONB NOT NULL,
"snapshotLineArtUrls" JSONB NOT NULL,
"toolSnapshotUrls" JSONB NOT NULL,
"toolSnapshotLineArtUrls" JSONB NOT NULL,
"savedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "InstallationDemoV2NumberingSnapshot_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InstallationDemoV2NodeMaterialConfig" (
"id" TEXT NOT NULL,
"drawingId" TEXT NOT NULL,
"nodeMaterialConfigs" JSONB NOT NULL,
"updatedById" TEXT,
"updatedByName" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "InstallationDemoV2NodeMaterialConfig_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InstallationDemoV2DrawingCapture" (
"id" TEXT NOT NULL,
"drawingId" TEXT NOT NULL,
"manifest" JSONB NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "InstallationDemoV2DrawingCapture_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InstallationDemoV2DrawingTextAnnotation" (
"id" TEXT NOT NULL,
"drawingId" TEXT NOT NULL,
"annotationsByTargetKey" JSONB NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "InstallationDemoV2DrawingTextAnnotation_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InstallationDemoV2Tool" (
"id" TEXT NOT NULL,
"toolCode" TEXT NOT NULL,
"name" TEXT NOT NULL,
"modelUrl" TEXT NOT NULL,
"fileName" TEXT NOT NULL,
"mountOffset" JSONB,
"mountRotation" JSONB,
"motionType" TEXT NOT NULL DEFAULT 'spin',
"motionPivot" JSONB,
"motionAxis" JSONB,
"spinTurns" DOUBLE PRECISION NOT NULL DEFAULT 3,
"strikeDistanceRatio" DOUBLE PRECISION NOT NULL DEFAULT 0.2,
"articulationConfig" JSONB,
"sourceToolId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "InstallationDemoV2Tool_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InstallationDemoV2Share" (
"id" TEXT NOT NULL,
"token" TEXT NOT NULL,
"drawingId" TEXT NOT NULL,
"createdById" TEXT NOT NULL,
"snapshot" JSONB NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"expiresAt" TIMESTAMP(3),
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "InstallationDemoV2Share_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "InstallationDemoV2Drawing_spuCode_drawingCode_key" ON "InstallationDemoV2Drawing"("spuCode", "drawingCode");
-- CreateIndex
CREATE UNIQUE INDEX "InstallationDemoV2NumberingConfig_drawingId_key" ON "InstallationDemoV2NumberingConfig"("drawingId");
-- CreateIndex
CREATE UNIQUE INDEX "InstallationDemoV2NumberingSnapshot_drawingId_key" ON "InstallationDemoV2NumberingSnapshot"("drawingId");
-- CreateIndex
CREATE UNIQUE INDEX "InstallationDemoV2NodeMaterialConfig_drawingId_key" ON "InstallationDemoV2NodeMaterialConfig"("drawingId");
-- CreateIndex
CREATE UNIQUE INDEX "InstallationDemoV2DrawingCapture_drawingId_key" ON "InstallationDemoV2DrawingCapture"("drawingId");
-- CreateIndex
CREATE UNIQUE INDEX "InstallationDemoV2DrawingTextAnnotation_drawingId_key" ON "InstallationDemoV2DrawingTextAnnotation"("drawingId");
-- CreateIndex
CREATE UNIQUE INDEX "InstallationDemoV2Tool_toolCode_key" ON "InstallationDemoV2Tool"("toolCode");
-- CreateIndex
CREATE UNIQUE INDEX "InstallationDemoV2Tool_sourceToolId_key" ON "InstallationDemoV2Tool"("sourceToolId");
-- CreateIndex
CREATE UNIQUE INDEX "InstallationDemoV2Share_token_key" ON "InstallationDemoV2Share"("token");
-- CreateIndex
CREATE UNIQUE INDEX "InstallationDemoV2Share_createdById_drawingId_key" ON "InstallationDemoV2Share"("createdById", "drawingId");
-- AddForeignKey
ALTER TABLE "InstallationDemoV2NumberingConfig" ADD CONSTRAINT "InstallationDemoV2NumberingConfig_drawingId_fkey" FOREIGN KEY ("drawingId") REFERENCES "InstallationDemoV2Drawing"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "InstallationDemoV2NumberingSnapshot" ADD CONSTRAINT "InstallationDemoV2NumberingSnapshot_drawingId_fkey" FOREIGN KEY ("drawingId") REFERENCES "InstallationDemoV2Drawing"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "InstallationDemoV2NodeMaterialConfig" ADD CONSTRAINT "InstallationDemoV2NodeMaterialConfig_drawingId_fkey" FOREIGN KEY ("drawingId") REFERENCES "InstallationDemoV2Drawing"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "InstallationDemoV2DrawingCapture" ADD CONSTRAINT "InstallationDemoV2DrawingCapture_drawingId_fkey" FOREIGN KEY ("drawingId") REFERENCES "InstallationDemoV2Drawing"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "InstallationDemoV2DrawingTextAnnotation" ADD CONSTRAINT "InstallationDemoV2DrawingTextAnnotation_drawingId_fkey" FOREIGN KEY ("drawingId") REFERENCES "InstallationDemoV2Drawing"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "InstallationDemoV2Share" ADD CONSTRAINT "InstallationDemoV2Share_drawingId_fkey" FOREIGN KEY ("drawingId") REFERENCES "InstallationDemoV2Drawing"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,9 @@
-- 物流到货签收时长看板的热路径聚合(整区间总览、环节目标、经营对象分组、超时排名)
-- 只需要 4 个时间戳 + 4 个维度列。此前查询 SELECT 全列,Postgres 只能对 900MB 的堆表做
-- Seq Scan(28 天区间实测 6.3 秒),加覆盖索引后可以走 Index Only Scan。
CREATE INDEX IF NOT EXISTS "LogisticsArrivalSigningDurationSnapshot_cover_idx"
ON "LogisticsArrivalSigningDurationSnapshot" ("snapshotDate", "signTime")
INCLUDE ("payTime", "sendTime", "fetchTime", "warehouseName", "platformName", "shopName", "logisticCompanyName");
-- 让计划器立刻用上新索引。
ANALYZE "LogisticsArrivalSigningDurationSnapshot";

View File

@ -0,0 +1,3 @@
-- Add modelCode for V2 three-level catalog (SPU -> model -> drawing)
ALTER TABLE "InstallationDemoV2Drawing" ADD COLUMN "modelCode" TEXT;

View File

@ -0,0 +1,87 @@
-- 停产需求「是否同意停产」多人确认:确认人配置、确认票、需求侧确认状态与截止时间。
ALTER TABLE "ProductionStopDemand"
ADD COLUMN "confirmationDeadline" TIMESTAMP(3),
ADD COLUMN "confirmationStatus" TEXT NOT NULL DEFAULT 'PENDING',
ADD COLUMN "confirmationAt" TIMESTAMP(3),
ADD COLUMN "confirmationReasons" TEXT NOT NULL DEFAULT '',
ADD COLUMN "confirmationPushedAt" TIMESTAMP(3),
ADD COLUMN "confirmationPushMessage" TEXT NOT NULL DEFAULT '';
-- 原「采购反馈」改为需求级「是否同意停产」多人确认,移除按明细的反馈字段。
DROP INDEX "ProductionStopDemand_status_submittedAt_idx";
ALTER TABLE "ProductionStopDemand" DROP COLUMN "status";
DROP INDEX "ProductionStopDemandItem_responseStatus_updatedAt_idx";
DROP INDEX "ProductionStopDemandItem_purchaseOwner_responseStatus_idx";
ALTER TABLE "ProductionStopDemandItem"
DROP COLUMN "responseStatus",
DROP COLUMN "responderId",
DROP COLUMN "responderName",
DROP COLUMN "responseNote",
DROP COLUMN "respondedAt";
CREATE INDEX "ProductionStopDemand_submittedAt_idx" ON "ProductionStopDemand"("submittedAt");
CREATE INDEX "ProductionStopDemandItem_purchaseOwner_idx" ON "ProductionStopDemandItem"("purchaseOwner");
CREATE INDEX "ProductionStopDemandItem_updatedAt_idx" ON "ProductionStopDemandItem"("updatedAt");
CREATE INDEX "ProductionStopDemand_confirmationStatus_confirmationDeadline_idx" ON "ProductionStopDemand"("confirmationStatus", "confirmationDeadline");
-- 历史需求按「当天 15:00 前提交当天 16:00 截止,否则次日 16:00 截止」补齐确认截止时间。
-- 注意:submittedAt / confirmationDeadline 是 timestamp(无时区),Prisma 按 UTC 读写,
-- 因此这里显式按 UTC 解释存储值,结果也写回 UTC,不依赖数据库会话时区。
UPDATE "ProductionStopDemand"
SET "confirmationDeadline" = (
CASE
WHEN (("submittedAt" AT TIME ZONE 'UTC') AT TIME ZONE 'Asia/Shanghai')::time < TIME '15:00'
THEN date_trunc('day', ("submittedAt" AT TIME ZONE 'UTC') AT TIME ZONE 'Asia/Shanghai')
+ INTERVAL '16 hours'
ELSE date_trunc('day', ("submittedAt" AT TIME ZONE 'UTC') AT TIME ZONE 'Asia/Shanghai')
+ INTERVAL '1 day 16 hours'
END
) AT TIME ZONE 'Asia/Shanghai' AT TIME ZONE 'UTC'
WHERE "confirmationDeadline" IS NULL;
CREATE TABLE "ProductionStopConfirmationApprover" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"userName" TEXT NOT NULL,
"dingtalkUnionId" TEXT NOT NULL DEFAULT '',
"isActive" BOOLEAN NOT NULL DEFAULT true,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProductionStopConfirmationApprover_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "ProductionStopConfirmationApprover_userId_key" ON "ProductionStopConfirmationApprover"("userId");
CREATE INDEX "ProductionStopConfirmationApprover_isActive_sortOrder_idx" ON "ProductionStopConfirmationApprover"("isActive", "sortOrder");
CREATE TABLE "ProductionStopDemandConfirmationVote" (
"id" TEXT NOT NULL,
"demandId" TEXT NOT NULL,
"approverId" TEXT NOT NULL,
"approverUserId" TEXT NOT NULL,
"approverName" TEXT NOT NULL,
"decision" TEXT NOT NULL DEFAULT 'AGREED',
"reason" TEXT NOT NULL DEFAULT '',
"source" TEXT NOT NULL DEFAULT 'USER',
"respondedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProductionStopDemandConfirmationVote_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "ProductionStopDemandConfirmationVote_demandId_approverId_key" ON "ProductionStopDemandConfirmationVote"("demandId", "approverId");
CREATE INDEX "ProductionStopDemandConfirmationVote_demandId_decision_idx" ON "ProductionStopDemandConfirmationVote"("demandId", "decision");
CREATE INDEX "ProductionStopDemandConfirmationVote_respondedAt_idx" ON "ProductionStopDemandConfirmationVote"("respondedAt");
ALTER TABLE "ProductionStopDemandConfirmationVote" ADD CONSTRAINT "ProductionStopDemandConfirmationVote_demandId_fkey" FOREIGN KEY ("demandId") REFERENCES "ProductionStopDemand"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ProductionStopDemandConfirmationVote" ADD CONSTRAINT "ProductionStopDemandConfirmationVote_approverId_fkey" FOREIGN KEY ("approverId") REFERENCES "ProductionStopConfirmationApprover"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,2 @@
-- 停产需求修改停产原因支持写入操作日志(新增枚举值,不改动既有数据)。
ALTER TYPE "OperationLogAction" ADD VALUE 'PRODUCTION_STOP_DEMAND_UPDATE';

View File

@ -0,0 +1,32 @@
-- 停产需求状态(待确认/需求驳回/已确认待走流程/已走流程待审批/流程审批通过)与流程负责人名单。
ALTER TABLE "ProductionStopDemand"
ADD COLUMN "demandStatus" TEXT NOT NULL DEFAULT 'PENDING_CONFIRMATION',
ADD COLUMN "demandStatusAt" TIMESTAMP(3),
ADD COLUMN "demandStatusNote" TEXT NOT NULL DEFAULT '';
CREATE INDEX "ProductionStopDemand_demandStatus_updatedAt_idx" ON "ProductionStopDemand"("demandStatus", "updatedAt");
CREATE TABLE "ProductionStopFlowOwner" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"userName" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProductionStopFlowOwner_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "ProductionStopFlowOwner_userId_key" ON "ProductionStopFlowOwner"("userId");
CREATE INDEX "ProductionStopFlowOwner_isActive_sortOrder_idx" ON "ProductionStopFlowOwner"("isActive", "sortOrder");
-- 历史需求按当前确认结果回填需求状态(确认通过→已确认待走流程;确认驳回→需求驳回)。
UPDATE "ProductionStopDemand"
SET "demandStatus" = CASE
WHEN "confirmationStatus" = 'AGREED' THEN 'CONFIRMED_WAIT_FLOW'
WHEN "confirmationStatus" = 'REJECTED' THEN 'REJECTED'
ELSE 'PENDING_CONFIRMATION'
END,
"demandStatusAt" = COALESCE("confirmationAt", "createdAt");

View File

@ -0,0 +1,3 @@
-- 按条码拆单后,用批次号把同一次提交的需求单串起来(用于批量确认与推送聚合)。
ALTER TABLE "ProductionStopDemand" ADD COLUMN "batchNo" TEXT NOT NULL DEFAULT '';
CREATE INDEX "ProductionStopDemand_batchNo_idx" ON "ProductionStopDemand"("batchNo");

View File

@ -0,0 +1,16 @@
-- 盘停建议配置:人工批量维护「条码 → 盘停建议」,提单时按条码自动带出。
CREATE TABLE "ProductionStopSuggestionConfig" (
"id" TEXT NOT NULL,
"barcode" TEXT NOT NULL,
"suggestion" TEXT NOT NULL DEFAULT '',
"updatedById" TEXT NOT NULL DEFAULT '',
"updatedByName" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProductionStopSuggestionConfig_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "ProductionStopSuggestionConfig_barcode_key" ON "ProductionStopSuggestionConfig"("barcode");
CREATE INDEX "ProductionStopSuggestionConfig_updatedAt_idx" ON "ProductionStopSuggestionConfig"("updatedAt");

View File

@ -0,0 +1,25 @@
-- 简尚「替换款管理」换货规则表(商品SABC打标「同款商品编码」依赖)。
-- 历史遗留:该表一直没进 migrations(本地用 db push 建过),正式环境用 migrate deploy 时缺失,
-- 导致同款商品编码全部为空。这里补上建表语句,生产/本地都可重复执行。
CREATE TABLE IF NOT EXISTS "SupplyChainProductArchiveReplacementRule" (
"id" TEXT NOT NULL,
"sourceId" INTEGER NOT NULL,
"sku" TEXT NOT NULL,
"replaceSku" TEXT NOT NULL DEFAULT '',
"replaceType" INTEGER NOT NULL DEFAULT 0,
"isSwapping" INTEGER NOT NULL DEFAULT 0,
"differentPoints" TEXT NOT NULL DEFAULT '',
"creator" TEXT NOT NULL DEFAULT '',
"sourceCreatedAt" TIMESTAMP(6),
"syncedAt" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "SupplyChainProductArchiveReplacementRule_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "SupplyChainProductArchiveReplacementRule_sourceId_key"
ON "SupplyChainProductArchiveReplacementRule" ("sourceId");
CREATE INDEX IF NOT EXISTS "SupplyChainProductArchiveReplacementRule_sku_idx"
ON "SupplyChainProductArchiveReplacementRule" ("sku");
CREATE INDEX IF NOT EXISTS "SupplyChainProductArchiveReplacementRule_replaceSku_idx"
ON "SupplyChainProductArchiveReplacementRule" ("replaceSku");

View File

@ -0,0 +1,17 @@
-- Retire the independent installation demo V2 experiment.
-- Keep the original migrations unchanged and remove only V2-owned tables.
-- Drop dependents first; do not cascade into unrelated objects.
BEGIN;
SET LOCAL lock_timeout = '5s';
DROP TABLE IF EXISTS "InstallationDemoV2NumberingConfig";
DROP TABLE IF EXISTS "InstallationDemoV2NumberingSnapshot";
DROP TABLE IF EXISTS "InstallationDemoV2NodeMaterialConfig";
DROP TABLE IF EXISTS "InstallationDemoV2DrawingCapture";
DROP TABLE IF EXISTS "InstallationDemoV2DrawingTextAnnotation";
DROP TABLE IF EXISTS "InstallationDemoV2Share";
DROP TABLE IF EXISTS "InstallationDemoV2Tool";
DROP TABLE IF EXISTS "InstallationDemoV2Drawing";
COMMIT;

View File

@ -0,0 +1,25 @@
ALTER TABLE "SupplyChainProductArchive"
ADD COLUMN "sales30d" TEXT NOT NULL DEFAULT '0.00',
ADD COLUMN "sales30dWithBig" TEXT NOT NULL DEFAULT '0.00';
CREATE TABLE "ProductArchiveMarketingSalesState" (
"id" TEXT NOT NULL,
"state" JSONB NOT NULL,
"snapshot" JSONB NOT NULL,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProductArchiveMarketingSalesState_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "ProductArchiveMarketingSalesCheck" (
"id" TEXT NOT NULL,
"checkedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"status" TEXT NOT NULL,
"fingerprint" TEXT NOT NULL DEFAULT '',
"sourceDate" TEXT NOT NULL DEFAULT '',
"rowCount" INTEGER NOT NULL DEFAULT 0,
"skuCount" INTEGER NOT NULL DEFAULT 0,
"changedRows" INTEGER NOT NULL DEFAULT 0,
"errorCode" TEXT NOT NULL DEFAULT '',
CONSTRAINT "ProductArchiveMarketingSalesCheck_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "ProductArchiveMarketingSalesCheck_checkedAt_idx" ON "ProductArchiveMarketingSalesCheck"("checkedAt");

View File

@ -0,0 +1,6 @@
-- 记录每批快照实际覆盖到的最大签收时间('YYYY-MM-DD HH:MM:SS')。
-- 快照任务把 windowTo 写成"运行当天",但数据只到运行那一刻;读路径此前只比对声明窗口,
-- 于是当天跑批后看板/导出会把尚未签收完的当天误判为已覆盖(少算当天、导出缺单)。
-- 新增 dataThrough 后,覆盖判定取 min(windowTo - 1, dataThrough 当天),数据没到就回落实时源。
ALTER TABLE "LogisticsArrivalSigningDurationSnapshotRun"
ADD COLUMN IF NOT EXISTS "dataThrough" TEXT NOT NULL DEFAULT '';

View File

@ -0,0 +1,85 @@
BEGIN;
-- Use the same lock as permission configuration writes. Retain every old row.
SELECT pg_advisory_xact_lock(2026081501);
LOCK TABLE "SupplyChainPermissionOverride" IN SHARE ROW EXCLUSIVE MODE;
WITH mappings("source", "target") AS (
VALUES
('businessManagement', 'ownerConfig'),
('businessManagement', 'limitManagement'),
('businessManagement', 'productionStopDemands'),
('businessManagement', 'managementBlankPage1'),
('businessManagement', 'managementBlankPage2'),
('newProductSampleProgress', 'finishedProductProgress')
)
INSERT INTO "SupplyChainPermissionOverride"
("id", "targetKey", "subjectKey", "action", "enabled", "createdBy", "updatedBy", "createdAt", "updatedAt")
SELECT
'scp-' || md5(old."id" || ':' || mappings."target"),
mappings."target", old."subjectKey", old."action", old."enabled",
old."createdBy", old."updatedBy", old."createdAt", old."updatedAt"
FROM "SupplyChainPermissionOverride" AS old
JOIN mappings ON old."targetKey" = mappings."source"
ON CONFLICT ("targetKey", "subjectKey", "action") DO NOTHING;
-- Logistics previously allowed exporting whenever effective view was true.
-- Preserve that ability once, then enforce its independent download permission.
-- Personal download=false used to suppress editing, not the view-based export.
-- Leave inherited personal view/download unset in that case and copy edit=false.
WITH legacy AS (
SELECT
"subjectKey",
bool_or("enabled") FILTER (WHERE "action" = 'view') AS v,
bool_or("enabled") FILTER (WHERE "action" = 'download') AS d,
bool_or("enabled") FILTER (WHERE "action" = 'edit') AS e,
min("id") AS "sourceId",
max("createdBy") AS "createdBy", max("updatedBy") AS "updatedBy",
min("createdAt") AS "createdAt", max("updatedAt") AS "updatedAt"
FROM "SupplyChainPermissionOverride"
WHERE "targetKey" = 'businessManagement'
GROUP BY "subjectKey"
), access AS (
SELECT *,
CASE
WHEN "subjectKey" LIKE 'user@%' THEN
CASE WHEN v IS NOT NULL THEN v
WHEN d IS DISTINCT FROM FALSE AND (d IS TRUE OR e IS TRUE) THEN TRUE
ELSE NULL END
WHEN v IS FALSE THEN FALSE
WHEN d IS FALSE THEN v IS TRUE
ELSE v IS TRUE OR d IS TRUE OR e IS TRUE
END AS "newView",
CASE
WHEN "subjectKey" LIKE 'user@%' THEN
CASE WHEN v IS FALSE THEN FALSE
WHEN v IS TRUE THEN TRUE
WHEN d IS DISTINCT FROM FALSE AND (d IS TRUE OR e IS TRUE) THEN TRUE
ELSE NULL END
WHEN v IS FALSE THEN FALSE
WHEN d IS FALSE THEN v IS TRUE
ELSE v IS TRUE OR d IS TRUE OR e IS TRUE
END AS "newDownload",
CASE
WHEN "subjectKey" LIKE 'user@%' THEN
CASE WHEN v IS FALSE OR d IS FALSE THEN FALSE ELSE e END
ELSE v IS DISTINCT FROM FALSE AND d IS DISTINCT FROM FALSE AND e IS TRUE
END AS "newEdit"
FROM legacy
)
INSERT INTO "SupplyChainPermissionOverride"
("id", "targetKey", "subjectKey", "action", "enabled", "createdBy", "updatedBy", "createdAt", "updatedAt")
SELECT
'scp-' || md5(access."sourceId" || ':logisticsArrivalSigningDuration:' || actions."action"),
'logisticsArrivalSigningDuration', access."subjectKey", actions."action", actions."enabled",
access."createdBy", access."updatedBy", access."createdAt", access."updatedAt"
FROM access
CROSS JOIN LATERAL (VALUES
('view', access."newView"),
('download', access."newDownload"),
('edit', access."newEdit")
) AS actions("action", "enabled")
WHERE actions."enabled" IS NOT NULL
ON CONFLICT ("targetKey", "subjectKey", "action") DO NOTHING;
COMMIT;

View File

@ -0,0 +1,15 @@
CREATE TABLE "DiyScheme" (
"id" TEXT NOT NULL,
"code" TEXT NOT NULL,
"name" TEXT NOT NULL,
"parentCode" TEXT,
"createdBy" TEXT NOT NULL,
"snapshot" JSONB NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "DiyScheme_pkey" PRIMARY KEY ("id"),
CONSTRAINT "DiyScheme_name_not_blank" CHECK (length(btrim("name")) BETWEEN 1 AND 80),
CONSTRAINT "DiyScheme_snapshot_object" CHECK (jsonb_typeof("snapshot") = 'object')
);
CREATE UNIQUE INDEX "DiyScheme_code_key" ON "DiyScheme"("code");
CREATE INDEX "DiyScheme_createdBy_createdAt_id_idx" ON "DiyScheme"("createdBy", "createdAt", "id");
ALTER TABLE "DiyScheme" ADD CONSTRAINT "DiyScheme_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,43 @@
CREATE TABLE "DiyCatalog" (
"spu" TEXT PRIMARY KEY, "name" TEXT NOT NULL, "version" INTEGER NOT NULL DEFAULT 1,
"sourceHash" TEXT NOT NULL, "importedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE "DiyAssetFile" (
"id" TEXT PRIMARY KEY, "spu" TEXT NOT NULL REFERENCES "DiyCatalog"("spu") ON DELETE RESTRICT ON UPDATE CASCADE,
"storageKey" TEXT NOT NULL UNIQUE, "url" TEXT NOT NULL, "sourcePath" TEXT NOT NULL,
"sha256" TEXT NOT NULL, "bytes" INTEGER NOT NULL CHECK ("bytes" > 0),
"contentType" TEXT NOT NULL, "kind" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE "DiyCabinetStyle" (
"code" TEXT PRIMARY KEY, "spu" TEXT NOT NULL REFERENCES "DiyCatalog"("spu") ON DELETE RESTRICT ON UPDATE CASCADE,
"name" TEXT NOT NULL, "displaySize" TEXT NOT NULL,
"width" DOUBLE PRECISION NOT NULL CHECK ("width" > 0),
"height" DOUBLE PRECISION NOT NULL CHECK ("height" > 0),
"depth" DOUBLE PRECISION NOT NULL CHECK ("depth" > 0),
"cellCount" INTEGER NOT NULL CHECK ("cellCount" > 0), "panelCount" INTEGER NOT NULL CHECK ("panelCount" > 0),
"panels" JSONB NOT NULL, "cells" JSONB NOT NULL, "sortOrder" INTEGER NOT NULL
);
CREATE TABLE "DiyCabinetVariant" (
"itemCode" TEXT PRIMARY KEY, "styleCode" TEXT NOT NULL REFERENCES "DiyCabinetStyle"("code") ON DELETE RESTRICT ON UPDATE CASCADE,
"name" TEXT NOT NULL, "barcodes" TEXT[] NOT NULL,
"factoryModel" TEXT NOT NULL, "factoryCode" TEXT NOT NULL, "orderQty" INTEGER NOT NULL, "sortOrder" INTEGER NOT NULL,
"thumbnailFileId" TEXT NOT NULL REFERENCES "DiyAssetFile"("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE TABLE "DiyDoorPattern" (
"itemCode" TEXT PRIMARY KEY, "spu" TEXT NOT NULL REFERENCES "DiyCatalog"("spu") ON DELETE RESTRICT ON UPDATE CASCADE,
"name" TEXT NOT NULL, "barcodes" TEXT[] NOT NULL,
"factoryModel" TEXT NOT NULL, "factoryCode" TEXT NOT NULL, "orderQty" INTEGER NOT NULL, "sortOrder" INTEGER NOT NULL,
"imageFileId" TEXT NOT NULL REFERENCES "DiyAssetFile"("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE TABLE "DiyMaterial" (
"itemCode" TEXT PRIMARY KEY, "spu" TEXT NOT NULL REFERENCES "DiyCatalog"("spu") ON DELETE RESTRICT ON UPDATE CASCADE,
"name" TEXT NOT NULL, "kind" TEXT NOT NULL CHECK ("kind" IN ('main', 'shelf')), "colorHex" TEXT NOT NULL,
"barcodes" TEXT[] NOT NULL, "factoryModel" TEXT NOT NULL, "factoryCode" TEXT NOT NULL, "sortOrder" INTEGER NOT NULL,
"textureFileId" TEXT REFERENCES "DiyAssetFile"("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE INDEX "DiyAssetFile_spu_idx" ON "DiyAssetFile"("spu");
CREATE INDEX "DiyCabinetStyle_spu_sortOrder_idx" ON "DiyCabinetStyle"("spu", "sortOrder");
CREATE UNIQUE INDEX "DiyCabinetVariant_styleCode_name_key" ON "DiyCabinetVariant"("styleCode", "name");
CREATE INDEX "DiyDoorPattern_spu_sortOrder_idx" ON "DiyDoorPattern"("spu", "sortOrder");
CREATE UNIQUE INDEX "DiyMaterial_spu_name_key" ON "DiyMaterial"("spu", "name");

View File

@ -0,0 +1,18 @@
ALTER TABLE "DiyCabinetStyle"
ADD COLUMN "modelFileId" TEXT,
ADD COLUMN "mainNodeNames" TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
ADD COLUMN "shelfNodeNames" TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
ADD COLUMN "modelRotationY" DOUBLE PRECISION NOT NULL DEFAULT 0;
ALTER TABLE "DiyCabinetStyle" ADD CONSTRAINT "DiyCabinetStyle_modelFileId_fkey"
FOREIGN KEY ("modelFileId") REFERENCES "DiyAssetFile"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "DiyCabinetVariant"
ADD COLUMN "mainMaterialCode" TEXT,
ADD COLUMN "shelfMaterialCode" TEXT;
ALTER TABLE "DiyCabinetVariant" ADD CONSTRAINT "DiyCabinetVariant_mainMaterialCode_fkey"
FOREIGN KEY ("mainMaterialCode") REFERENCES "DiyMaterial"("itemCode") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "DiyCabinetVariant" ADD CONSTRAINT "DiyCabinetVariant_shelfMaterialCode_fkey"
FOREIGN KEY ("shelfMaterialCode") REFERENCES "DiyMaterial"("itemCode") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "DiyDoorPattern"
ADD COLUMN "width" DOUBLE PRECISION,
ADD COLUMN "height" DOUBLE PRECISION,
ADD COLUMN "thickness" DOUBLE PRECISION;

View File

@ -0,0 +1,85 @@
-- 限价管理 · 破价订单审核
CREATE TABLE "SupplyChainBreachOrder" (
"id" TEXT NOT NULL,
"platform" TEXT NOT NULL DEFAULT '',
"shopName" TEXT NOT NULL DEFAULT '',
"soId" TEXT NOT NULL DEFAULT '',
"oId" TEXT NOT NULL DEFAULT '',
"payDate" TIMESTAMP(3),
"itemCode" TEXT NOT NULL DEFAULT '',
"shopItemCode" TEXT NOT NULL DEFAULT '',
"shopStyleCode" TEXT NOT NULL DEFAULT '',
"linkGroupKey" TEXT NOT NULL DEFAULT '',
"qty" INTEGER NOT NULL DEFAULT 1,
"buyerPaidAmount" DECIMAL(18,4) NOT NULL DEFAULT 0,
"platformBearAmount" DECIMAL(18,4) NOT NULL DEFAULT 0,
"sellerIncomeAmount" DECIMAL(18,4) NOT NULL DEFAULT 0,
"limitUnitPrice" DECIMAL(18,4),
"limitTotal" DECIMAL(18,4),
"gapAmount" DECIMAL(18,4),
"ownerName" TEXT NOT NULL DEFAULT '',
"profitCenter" TEXT NOT NULL DEFAULT '',
"orderStatus" TEXT NOT NULL DEFAULT '',
"refundStatus" TEXT NOT NULL DEFAULT '',
"listedAt" TIMESTAMP(3),
"flowEndDate" TIMESTAMP(3),
"status" TEXT NOT NULL DEFAULT 'PENDING_APPEAL',
"appealType" TEXT NOT NULL DEFAULT '',
"appealScope" TEXT NOT NULL DEFAULT 'ORDER',
"processNo" TEXT NOT NULL DEFAULT '',
"appealRound" INTEGER NOT NULL DEFAULT 1,
"previousAppeal" JSON,
"appealDesc" TEXT NOT NULL DEFAULT '',
"appealImages" JSON NOT NULL DEFAULT '[]',
"appliedFromLink" BOOLEAN NOT NULL DEFAULT false,
"autoSuggested" TEXT NOT NULL DEFAULT '',
"autoSuggestedNote" TEXT NOT NULL DEFAULT '',
"reviewedBy" TEXT NOT NULL DEFAULT '',
"reviewedAt" TIMESTAMP(3),
"reviewDecision" TEXT NOT NULL DEFAULT '',
"reviewReason" TEXT NOT NULL DEFAULT '',
"reviewNote" TEXT NOT NULL DEFAULT '',
"fineAmount" DECIMAL(10,2) NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SupplyChainBreachOrder_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "SupplyChainBreachOrder_oId_itemCode_key" ON "SupplyChainBreachOrder"("oId", "itemCode");
CREATE INDEX "SupplyChainBreachOrder_status_payDate_idx" ON "SupplyChainBreachOrder"("status", "payDate" DESC);
CREATE INDEX "SupplyChainBreachOrder_linkGroupKey_idx" ON "SupplyChainBreachOrder"("linkGroupKey");
CREATE INDEX "SupplyChainBreachOrder_ownerName_status_idx" ON "SupplyChainBreachOrder"("ownerName", "status");
CREATE INDEX "SupplyChainBreachOrder_shopName_itemCode_idx" ON "SupplyChainBreachOrder"("shopName", "itemCode");
CREATE TABLE "SupplyChainBreachOrderLog" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"action" TEXT NOT NULL,
"operatorId" TEXT NOT NULL DEFAULT '',
"operatorName" TEXT NOT NULL DEFAULT '',
"detail" JSON NOT NULL DEFAULT '{}',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "SupplyChainBreachOrderLog_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "SupplyChainBreachOrderLog_orderId_createdAt_idx" ON "SupplyChainBreachOrderLog"("orderId", "createdAt");
CREATE TABLE "SupplyChainLinkLimitPrice" (
"id" TEXT NOT NULL,
"shopName" TEXT NOT NULL,
"itemCode" TEXT NOT NULL,
"spu" TEXT NOT NULL DEFAULT '',
"limitUnitPrice" DECIMAL(18,4) NOT NULL,
"listedAt" TIMESTAMP(3),
"flowEndDate" TIMESTAMP(3),
"source" TEXT NOT NULL DEFAULT 'MANUAL',
"ownerName" TEXT NOT NULL DEFAULT '',
"updatedById" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SupplyChainLinkLimitPrice_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "SupplyChainLinkLimitPrice_shopName_itemCode_key" ON "SupplyChainLinkLimitPrice"("shopName", "itemCode");
CREATE INDEX "SupplyChainLinkLimitPrice_itemCode_idx" ON "SupplyChainLinkLimitPrice"("itemCode");

View File

@ -0,0 +1,10 @@
-- Uploaded models have a file reference rather than generated panel geometry.
-- A cabinet without door openings is a valid model-only cabinet.
ALTER TABLE "DiyCabinetStyle"
DROP CONSTRAINT "DiyCabinetStyle_cellCount_check",
ADD CONSTRAINT "DiyCabinetStyle_cellCount_check"
CHECK ("cellCount" >= 0 AND jsonb_typeof(cells) = 'array' AND jsonb_array_length(cells) = "cellCount"),
DROP CONSTRAINT "DiyCabinetStyle_panelCount_check",
ADD CONSTRAINT "DiyCabinetStyle_panelCount_check"
CHECK ("panelCount" >= 0 AND jsonb_typeof(panels) = 'array' AND jsonb_array_length(panels) = "panelCount"
AND ("panelCount" > 0 OR "modelFileId" IS NOT NULL));

View File

@ -0,0 +1,9 @@
CREATE TABLE "SupplyChainBreachRuleConfig" (
"id" TEXT NOT NULL DEFAULT 'default',
"data" JSON NOT NULL DEFAULT '{}',
"updatedById" TEXT NOT NULL DEFAULT '',
"updatedByName" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SupplyChainBreachRuleConfig_pkey" PRIMARY KEY ("id")
);

View File

@ -0,0 +1,2 @@
-- 「图片」列:提单时按条码从产品全量档案库快照图片地址,列表在条码前展示缩略图。
ALTER TABLE "ProductionStopDemandItem" ADD COLUMN "image" TEXT NOT NULL DEFAULT '';

View File

@ -0,0 +1,10 @@
ALTER TABLE "DiyMaterial"
ADD COLUMN "deletedAt" TIMESTAMP(3),
ADD COLUMN "deletedBy" TEXT;
ALTER TABLE "DiyDoorPattern"
ADD COLUMN "deletedAt" TIMESTAMP(3),
ADD COLUMN "deletedBy" TEXT;
CREATE INDEX "DiyMaterial_deletedAt_idx" ON "DiyMaterial"("deletedAt");
CREATE INDEX "DiyDoorPattern_deletedAt_idx" ON "DiyDoorPattern"("deletedAt");

View File

@ -0,0 +1,25 @@
CREATE TABLE "NativeImageGenerationJob" (
"id" TEXT NOT NULL,
"createdBy" TEXT NOT NULL,
"providerConfigId" TEXT NOT NULL,
"providerFingerprint" TEXT NOT NULL,
"requestHash" TEXT NOT NULL,
"request" JSONB NOT NULL,
"completion" JSONB NOT NULL,
"progress" JSONB NOT NULL DEFAULT '[]',
"status" TEXT NOT NULL DEFAULT 'QUEUED',
"retryCount" INTEGER NOT NULL DEFAULT 0,
"nextRunAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"leaseOwner" TEXT,
"leaseUntil" TIMESTAMP(3),
"errorMessage" TEXT,
"finishedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "NativeImageGenerationJob_pkey" PRIMARY KEY ("id"),
CONSTRAINT "NativeImageGenerationJob_status_valid" CHECK ("status" IN ('QUEUED', 'RUNNING', 'FINALIZING', 'SUCCEEDED', 'FAILED', 'BLOCKED')),
CONSTRAINT "NativeImageGenerationJob_retry_valid" CHECK ("retryCount" >= 0),
CONSTRAINT "NativeImageGenerationJob_identity_valid" CHECK (length(btrim("id")) > 0 AND length(btrim("createdBy")) > 0)
);
CREATE INDEX "NativeImageGenerationJob_status_nextRunAt_leaseUntil_idx" ON "NativeImageGenerationJob"("status", "nextRunAt", "leaseUntil");
CREATE INDEX "NativeImageGenerationJob_createdBy_createdAt_idx" ON "NativeImageGenerationJob"("createdBy", "createdAt");

View File

@ -0,0 +1,37 @@
-- 限价流程同步:货号负责人限价(来源:限价流程的期望日常限价,按货号维度)
CREATE TABLE "SupplyChainStyleLimitPrice" (
"id" TEXT NOT NULL,
"styleCode" TEXT NOT NULL,
"ownerLimitPrice" DECIMAL(18,4),
"processInstanceId" TEXT NOT NULL DEFAULT '',
"processNo" TEXT NOT NULL DEFAULT '',
"applyType" TEXT NOT NULL DEFAULT '',
"initiator" TEXT NOT NULL DEFAULT '',
"approvedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SupplyChainStyleLimitPrice_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "SupplyChainStyleLimitPrice_styleCode_key" ON "SupplyChainStyleLimitPrice"("styleCode");
-- 限价流程同步:价格授权(同价申请 / 活动限价;供破价判定豁免与超期判定)
CREATE TABLE "SupplyChainLimitFlowAuth" (
"id" TEXT NOT NULL,
"processInstanceId" TEXT NOT NULL,
"processNo" TEXT NOT NULL DEFAULT '',
"applyType" TEXT NOT NULL DEFAULT '',
"styleCode" TEXT NOT NULL DEFAULT '',
"spu" TEXT NOT NULL DEFAULT '',
"approvedAt" TIMESTAMP(3),
"startDate" TIMESTAMP(3),
"endDate" TIMESTAMP(3),
"adjustedLimitPrice" DECIMAL(18,4),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SupplyChainLimitFlowAuth_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "SupplyChainLimitFlowAuth_processInstanceId_styleCode_applyType_key" ON "SupplyChainLimitFlowAuth"("processInstanceId", "styleCode", "applyType");
CREATE INDEX "SupplyChainLimitFlowAuth_styleCode_idx" ON "SupplyChainLimitFlowAuth"("styleCode");
CREATE INDEX "SupplyChainLimitFlowAuth_applyType_idx" ON "SupplyChainLimitFlowAuth"("applyType");

View File

@ -0,0 +1,19 @@
-- 产品全量档案字段权限(黑名单):主体对字段的屏蔽条目,只存被屏蔽的字段。
-- CreateTable
CREATE TABLE "ProductArchiveFieldPermissionBlock" (
"id" TEXT NOT NULL,
"subjectKey" TEXT NOT NULL,
"fieldKey" TEXT NOT NULL,
"createdBy" TEXT,
"updatedBy" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProductArchiveFieldPermissionBlock_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "ProductArchiveFieldPermissionBlock_subjectKey_fieldKey_key" ON "ProductArchiveFieldPermissionBlock"("subjectKey", "fieldKey");
-- CreateIndex
CREATE INDEX "ProductArchiveFieldPermissionBlock_subjectKey_idx" ON "ProductArchiveFieldPermissionBlock"("subjectKey");

View File

@ -0,0 +1,20 @@
-- Preserve explicit empty personal overrides separately from following a position.
CREATE TABLE "ProductArchiveFieldPermissionSubject" (
"subjectKey" TEXT NOT NULL,
"createdBy" TEXT,
"updatedBy" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProductArchiveFieldPermissionSubject_pkey" PRIMARY KEY ("subjectKey")
);
-- Keep every existing blocked field and its subject.
INSERT INTO "ProductArchiveFieldPermissionSubject" ("subjectKey", "createdAt", "updatedAt")
SELECT "subjectKey", MIN("createdAt"), MAX("updatedAt")
FROM "ProductArchiveFieldPermissionBlock"
GROUP BY "subjectKey";
ALTER TABLE "ProductArchiveFieldPermissionBlock"
ADD CONSTRAINT "ProductArchiveFieldPermissionBlock_subjectKey_fkey"
FOREIGN KEY ("subjectKey") REFERENCES "ProductArchiveFieldPermissionSubject"("subjectKey")
ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,13 @@
-- 仓库属性人工覆盖:数据源 remark2 异常时在本系统修正展示值,不影响聚水潭原始数据。
CREATE TABLE "SupplyChainProductArchiveWarehouseAttributeOverride" (
"warehouseName" TEXT NOT NULL,
"attribute" TEXT NOT NULL DEFAULT '',
"updatedById" TEXT,
"updatedByName" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SupplyChainProductArchiveWarehouseAttributeOverride_pkey" PRIMARY KEY ("warehouseName")
);
CREATE INDEX "SupplyChainProductArchiveWarehouseAttributeO_updatedAt_idx" ON "SupplyChainProductArchiveWarehouseAttributeOverride"("updatedAt");

View File

@ -0,0 +1,13 @@
CREATE TABLE "IntakeProductPhotoUploadGrant" (
"id" TEXT NOT NULL,
"productId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"grantedBy" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "IntakeProductPhotoUploadGrant_pkey" PRIMARY KEY ("id"),
CONSTRAINT "IntakeProductPhotoUploadGrant_productId_fkey" FOREIGN KEY ("productId") REFERENCES "IntakeNonIntegratedProduct"("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "IntakeProductPhotoUploadGrant_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "IntakeProductPhotoUploadGrant_grantedBy_fkey" FOREIGN KEY ("grantedBy") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE UNIQUE INDEX "IntakeProductPhotoUploadGrant_productId_userId_key" ON "IntakeProductPhotoUploadGrant"("productId", "userId");
CREATE INDEX "IntakeProductPhotoUploadGrant_userId_idx" ON "IntakeProductPhotoUploadGrant"("userId");

View File

@ -0,0 +1,2 @@
-- 产品全量档案新增「安装方式」列:取值来源产品开发(产品定厂 SKU installMethod,整装/拆装)。
ALTER TABLE "SupplyChainProductArchive" ADD COLUMN "installMethod" TEXT NOT NULL DEFAULT '';

View File

@ -0,0 +1,48 @@
-- 商品库存快照:条码 × 实际仓库粒度,由 15 分钟定时任务全量替换写入;失败保留上一份快照。
CREATE TABLE "ProductInventorySnapshot" (
"id" TEXT NOT NULL,
"barcode" TEXT NOT NULL,
"wmsCoId" INTEGER NOT NULL,
"productName" TEXT NOT NULL DEFAULT '',
"category" TEXT NOT NULL DEFAULT '',
"sku" TEXT NOT NULL DEFAULT '',
"productionStatus" TEXT NOT NULL DEFAULT '',
"supplier" TEXT NOT NULL DEFAULT '',
"purchaser" TEXT NOT NULL DEFAULT '',
"stockQty" INTEGER NOT NULL DEFAULT 0,
"orderLockQty" INTEGER NOT NULL DEFAULT 0,
"availableQty" INTEGER NOT NULL DEFAULT 0,
"cloudAvailableQty" INTEGER NOT NULL DEFAULT 0,
"warehouseName" TEXT NOT NULL DEFAULT '',
"warehouseAttribute" TEXT NOT NULL DEFAULT '',
"warehouseOwnership" TEXT NOT NULL DEFAULT '',
"warehouseHead" TEXT NOT NULL DEFAULT '',
"syncedAt" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ProductInventorySnapshot_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "ProductInventorySnapshot_barcode_wmsCoId_key" ON "ProductInventorySnapshot"("barcode", "wmsCoId");
CREATE INDEX "ProductInventorySnapshot_warehouseName_idx" ON "ProductInventorySnapshot"("warehouseName");
CREATE INDEX "ProductInventorySnapshot_syncedAt_idx" ON "ProductInventorySnapshot"("syncedAt");
-- 同步运行状态:每次定时任务一行,记录 RUNNING / COMPLETED / FAILED。
CREATE TABLE "ProductInventorySnapshotRun" (
"id" TEXT NOT NULL,
"startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"completedAt" TIMESTAMP(3),
"status" VARCHAR(24) NOT NULL,
"rowsRead" INTEGER NOT NULL DEFAULT 0,
"rowsWritten" INTEGER NOT NULL DEFAULT 0,
"syncedAt" TIMESTAMP(3),
"errorMessage" TEXT,
CONSTRAINT "ProductInventorySnapshotRun_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "ProductInventorySnapshotRun_startedAt_idx" ON "ProductInventorySnapshotRun"("startedAt");
CREATE INDEX "ProductInventorySnapshotRun_status_startedAt_idx" ON "ProductInventorySnapshotRun"("status", "startedAt");

View File

@ -0,0 +1,17 @@
-- 通知中心待办同步状态(停产确认 / 待走流程两类)。
CREATE TABLE "ProductionStopNotificationSync" (
"id" TEXT NOT NULL,
"kind" TEXT NOT NULL,
"recipientKey" TEXT NOT NULL,
"recipientName" TEXT NOT NULL DEFAULT '',
"pendingCount" INTEGER NOT NULL DEFAULT 0,
"snapshot" TEXT NOT NULL DEFAULT '',
"lastSentAt" TIMESTAMP(3),
"completedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProductionStopNotificationSync_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "ProductionStopNotificationSync_kind_recipientKey_key"
ON "ProductionStopNotificationSync"("kind", "recipientKey");

View File

@ -0,0 +1,11 @@
-- 含大单近30日日销的数值快照列:用于 >、>=、<、<= 数值筛选与数值排序。
ALTER TABLE "ProductionStopDemandItem" ADD COLUMN "sales30dWithBigValue" DECIMAL(18,4);
ALTER TABLE "ProductionStopDemandItem" ADD COLUMN "sales30dWithBigSkuValue" DECIMAL(18,4);
UPDATE "ProductionStopDemandItem"
SET "sales30dWithBigValue" = "sales30dWithBig"::numeric
WHERE "sales30dWithBig" ~ '^-?[0-9]+(\.[0-9]+)?$';
UPDATE "ProductionStopDemandItem"
SET "sales30dWithBigSkuValue" = "sales30dWithBigSku"::numeric
WHERE "sales30dWithBigSku" ~ '^-?[0-9]+(\.[0-9]+)?$';

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Some files were not shown because too many files have changed in this diff Show More