Compare commits

...

2826 Commits

Author SHA1 Message Date
WuYongJie
83a3138fe9 fix(intake): use configured vision model for reference cost estimates 2026-10-28 17:06:19 +08:00
居居
45387c9003 fix(limit-management): include freight in limit exports 2026-10-28 16:36:28 +08:00
WuYongJie
a9f2917c5b style(intake): align SKU toolbar actions by task 2026-10-28 16:16:29 +08:00
WuYongJie
77665c85b5 feat(intake): add product notification delivery history 2026-10-28 16:03:41 +08:00
居居
3f7710ef84 feat(limit-management): add regional and national freight averages 2026-10-28 15:56:14 +08:00
居居
281e52bb95 feat(supply-chain): 完善快递维护与AI运费比价 2026-10-28 15:56:14 +08:00
WuYongJie
2775b4ff83 feat(intake): import SKU templates with automatic coding 2026-10-28 14:36:34 +08:00
WuYongJie
e58f8ba96d fix(limit-management): restore mysql2 query call compatible with locked 3.15.3 typings
75adc3f7 imported QueryValues from mysql2/promise, which only exists in newer mysql2 typings than the package-lock version (3.15.3), breaking tsc on a clean install. Restore the previous equivalent call form (options object + values argument), preserving runtime behavior exactly.
2026-10-28 14:04:01 +08:00
居居
062666f530 feat(limit-management): consolidate limit and freight maintenance 2026-10-28 13:59:30 +08:00
居居
ce0aa8f387 fix(supply-chain): 修复运费配置权限并过滤过期费率 2026-10-28 13:59:30 +08:00
居居
5c5fef8b8e feat(supply-chain): 新增运费配置查询页面 2026-10-28 13:59:30 +08:00
WuYongJie
8f1cb2ac02 refactor(users): align product permissions with four navigation levels 2026-10-28 10:28:26 +08:00
WuYongJie
500da7be24 fix(market-analysis): add account and role access control 2026-10-28 09:42:24 +08:00
WuYongJie
1ddf0a34ce fix(intake): honor photo upload grants across products 2026-10-28 09:04:00 +08:00
居居
d3468b3756 feat(clearance): 排除福利品和空白分类 2026-10-27 17:25:40 +08:00
居居
11c91796e3 fix(clearance): 扣减虚拟云仓库存 2026-10-27 17:25:40 +08:00
WuYongJie
dcb61cc13f feat(intake): improve installation pushes and admin replacement 2026-10-27 17:13:37 +08:00
居居
6eb3ac0d53 feat(clearance): 简尚无库存时标记入库时间 2026-10-27 15:39:03 +08:00
居居
47fde040ee fix(clearance): 日销保留两位小数 2026-10-27 15:39:03 +08:00
居居
14b6157411 feat(clearance): 调整加权日销计算逻辑 2026-10-27 15:39:03 +08:00
居居
51e9ac3642 fix(clearance): 修正两清协议库存统计标记 2026-10-27 15:39:03 +08:00
居居
c7d1965df1 fix(clearance): 页面库存明细负数显示为零 2026-10-27 15:39:03 +08:00
居居
74613ce107 fix(clearance): 负库存参与最终库存汇总 2026-10-27 15:39:03 +08:00
居居
003b0e780c fix(clearance): 库存负数生成时归零 2026-10-27 15:39:03 +08:00
居居
638d8f591f fix(clearance): 生成时使用实时商品状态 2026-10-27 15:39:03 +08:00
WuYongJie
40debae10c fix(store-account): prevent accidental dismissal of create dialog 2026-10-27 15:24:13 +08:00
WuYongJie
cc208a400a fix(store-account): sync personnel profit centers and show save errors 2026-10-27 14:39:13 +08:00
WuYongJie
b026c8c840 fix(intake): 更新新增产品必填项 2026-10-27 14:05:21 +08:00
居居
6b85379ebb fix(clearance): 注册清货菜单权限节点
(cherry picked from commit 136350a02efa09bb4684da8cf6a6e442c198ed93)
2026-10-27 09:40:30 +08:00
居居
6d341c4196 fix(discontinuation): 将每日任务调整为十点三十分 2026-10-27 09:23:54 +08:00
WuYongJie
7fe6939daa Merge remote-tracking branch 'origin/main' of D:\ChanPing\furniture-3d-system-Wu into HEAD 2026-10-26 18:03:03 +08:00
WuYongJie
afb2d2b153 fix(clearance): restore applied migration content to keep checksum stable
5ea75eac added IF NOT EXISTS to migration 20261020090000 so dev databases can replay it. That migration is already applied on production with a recorded checksum, so main must keep the original file content or prisma migrate deploy fails with a checksum mismatch. The IF NOT EXISTS tweak is only needed in dev environments; production already has the table.
2026-10-26 17:55:47 +08:00
居居
af9381c2df feat(clearance): 接入采购入库与分利润中心发货统计
- 最近入库时间按采购单完结时间或预计完结时间取最新值
- 入库天数按清货池生成日展示整数
- 发货数量按任务利润中心匹配发货店铺后汇总
- 发货数据限定当前清货月份和有效确认出库单
2026-10-26 17:55:47 +08:00
居居
5375e21d92 feat(supply-chain): 上线清货管理与月度评分
- 新增清货池、任务审核确认、任务推送与复盘分数体系
- 新增优先级/固定任务/折扣限制/分数档位配置
- 新增清货数据迁移、同步脚本、权限和钉钉组合异常通知
- 补充清货页面、接口、服务、仓储及自动化测试
2026-10-26 17:55:47 +08:00
apple
64ab4a9065 fix(modular-processing): remove retired status rule column 2026-10-26 17:55:35 +08:00
WuYongJie
769e3d519c fix(intake): standardize category display and validate create prefill 2026-10-26 17:50:21 +08:00
WuYongJie
3ac409dd24 Merge remote-tracking branch 'origin/main' into release-discontinuation 2026-10-26 16:46:05 +08:00
WuYongJie
75ad922a5f fix(supply-chain): trigger ERP export dropdown on hover 2026-10-26 16:45:32 +08:00
apple
454ea9f564 feat(modular-processing): add modular processing order management 2026-10-26 16:41:43 +08:00
WuYongJie
3e7f2c4dd1 fix(supply-chain): click ERP goods controls inside their frame 2026-10-26 16:32:49 +08:00
WuYongJie
e0b99c7331 fix(supply-chain): recognize authenticated ERP with hidden goods menu 2026-10-26 16:24:26 +08:00
WuYongJie
5abc141b03 test(supply-chain): complete typed ERP page mocks 2026-10-26 16:24:26 +08:00
WuYongJie
17c10b0863 fix(intake): follow current product purchase owner permissions 2026-10-26 16:17:59 +08:00
WuYongJie
3e487baa0f fix(supply-chain): handle actual ERP agreement and QR login state 2026-10-26 16:07:07 +08:00
居居
5905923527 fix(deploy): 发布时安全写入聚水潭登录凭据 2026-10-26 16:06:28 +08:00
居居
c9cd224684 feat(supply-chain): 支持聚水潭登录态失效后自动登录 2026-10-26 16:06:28 +08:00
居居
5874d57f00 test(supply-chain): 锁定首次导出成功即停止重试 2026-10-26 16:06:28 +08:00
居居
b7cf1e25ae feat(supply-chain): 单次导出重试三次后再分批 2026-10-26 16:06:28 +08:00
WuYongJie
b65051cf8a fix(supply-chain): load Next env through native ESM default import 2026-10-26 14:32:32 +08:00
WuYongJie
f08ac565cd fix(supply-chain): continue historical monitoring on empty notice dates
(cherry picked from commit c137661049b733449be4b21983ed511d1e0773e5)
2026-10-26 14:19:43 +08:00
居居
bd44175734 fix(supply-chain): 加强每日同步异常与并发保护
(cherry picked from commit 2b985b657f1faa4d26fabbe746a47c0ad8d50923)
2026-10-26 14:19:43 +08:00
居居
4350394e81 fix(supply-chain): 恢复完整链接导出并阻止快照覆盖
(cherry picked from commit e4b15fa61527c5dbef753676274265f2b9e57d80)
2026-10-26 14:19:42 +08:00
居居
05df5da4e0 feat(supply-chain): 提供服务器每日调度与重跑保护
(cherry picked from commit 1eb6ebd387a84e2a57b51f9ed798fc67238e24bb)
2026-10-26 14:19:42 +08:00
WuYongJie
121e213fc1 fix(market-analysis): use unified workbench layout 2026-10-26 11:56:19 +08:00
WuYongJie
b152c821af fix(supply-chain): 恢复前置仓列设置拖拽排序 2026-10-26 11:34:51 +08:00
apple
bba34df1ab feat(supply-chain): 过滤下单表中的双零明细 2026-10-26 11:14:13 +08:00
apple
20b09c6b12 fix(supply-chain): 同步更新下单重量汇总 2026-10-26 11:14:13 +08:00
apple
35c226a783 feat(supply-chain): 新增前置仓明细列设置 2026-10-26 11:14:12 +08:00
apple
f232e1adab feat(supply-chain): 精简前置仓页面并新增下单重量 2026-10-26 11:14:12 +08:00
apple
733b98792e fix(supply-chain): 导入后立即覆盖并展示反馈数据 2026-10-26 11:14:12 +08:00
apple
572888f688 feat(supply-chain): 新增批量清空字段弹窗 2026-10-26 11:14:11 +08:00
apple
b6937fc9cf fix(supply-chain): 刷新数据时恢复最终下单量计算值 2026-10-26 11:14:11 +08:00
apple
461e6866d4 fix(supply-chain): 限制采购账号并锁定下单表最终下单量 2026-10-26 11:14:11 +08:00
apple
57c366b2a0 feat(supply-chain): 支持最终下单量人工编辑 2026-10-26 11:14:10 +08:00
apple
58d217cbc9 fix(supply-chain): 将前置仓下单表调整为进销存子页面 2026-10-26 11:14:10 +08:00
apple
ea76449f3c feat(supply-chain): 新增前置仓下单表快照推送 2026-10-26 11:14:10 +08:00
apple
d9f3b5ae1e feat(supply-chain): 支持前置仓批量筛选与反馈默认值 2026-10-26 11:14:09 +08:00
apple
5aa915f477 feat(supply-chain): 限制采购视角与导出字段 2026-10-26 11:14:09 +08:00
WuYongJie
6c874a67db feat(market-analysis): integrate and modularize selection dashboard 2026-10-26 10:59:31 +08:00
居居
3d5c23ace9 feat(supply-chain): 使用聚水潭最新替换规则快照 2026-10-26 09:50:02 +08:00
居居
2702a501f4 fix(supply-chain): 重复商品按最新PLM记录取剩余库存 2026-10-26 09:50:02 +08:00
居居
b57346a5ae feat(supply-chain): 接入聚水潭替换规则并展开条码 2026-10-26 09:50:02 +08:00
居居
ffabb6af88 fix(supply-chain): 替换规则支持同货号并展开条码 2026-10-26 09:50:02 +08:00
居居
5d541e92dc fix(supply-chain): 默认一次性查询聚水潭链接 2026-10-26 09:50:01 +08:00
居居
38d6642833 fix(supply-chain): 分批同步聚水潭链接并增强重试 2026-10-26 09:50:01 +08:00
居居
3b5560adb6 feat(supply-chain): 调整停产下架查看与负责人数据范围 2026-10-26 09:50:01 +08:00
居居
8781c40461 feat(supply-chain): 配置停产下架页签权限与运营数据范围 2026-10-26 09:50:01 +08:00
居居
00227f524d feat(supply-chain): 完成停产下架管理与监控 2026-10-26 09:50:01 +08:00
WuYongJie
c6310a9912 fix(product-archive): retire historical owner refresh and prioritize intake 2026-10-26 09:43:37 +08:00
apple
1b66b87d19 feat(production-stop-demands): 增加分页页大小并放宽原因分类权限 2026-10-24 15:03:08 +08:00
WuYongJie
ab408b90a0 feat(intake): 产品定厂仓库必填改为样包测量门禁
产品定厂确认不再强制仓库必填,允许空仓库确认并进入条码打印;样包测量确认前校验定厂仓库已填,未填提示「定厂仓库未填写」。产品开发待办规则口径同步调整。
2026-10-24 14:22:43 +08:00
WuYongJie
1646e6c4ee feat(supply-chain): 导出字段弹窗分组级全选/全不选切换 2026-10-24 14:03:13 +08:00
WuYongJie
5fc195d9fe feat(supply-chain): 产品全量档案导出字段勾选与图片URL导出修复 2026-10-24 13:40:30 +08:00
WuYongJie
c0e7a8d606 feat(supply-chain): add authenticated SPU return address push API 2026-10-24 13:30:41 +08:00
apple
5feb69feff feat(production-stop-demands): 完善流程校验、删除权限与分析视图 2026-10-24 13:14:38 +08:00
WuYongJie
83fb538d8c fix(product-archive): preserve existing images when sync source is empty 2026-10-24 11:31:23 +08:00
WuYongJie
26a6a28aa0 fix(supply-chain): include no-sales SPUs in all dropship issues
(cherry picked from commit 9d32b57e970905fdee7b3194ceb9df4894eb517e)
(cherry picked from commit ac885932d082a371ddb16d982a8acdea6dc28588)
2026-10-24 11:21:19 +08:00
WuYongJie
3d7c519b3c fix(intake): 产品开发待办节点确认回退批次级确认记录
待办规则此前只读产品级 documentWorkspaceConfirmations/documentWorkspaceApprovedAt,与产品开发明细页面读取批次级 skuBatchWorkflow* 的兜底口径不一致,历史数据两份记录不同步时(如 GBSFY-LRSF0091 样包测量批次级已确认、产品级为 false)会每天误报节点未完成。规则层 nodeApproved/approvedAt 增加批次级记录回退(任一批次已确认且带可解析确认时间即视为节点已确认,激活锚点同取批次级时间),取数组装传入批次级确认映射。生产真实数据对照:消除 14 条误报候选(样包测量 6、基础信息相关 8),恢复 8 条此前漏推的提醒。
2026-10-24 11:18:39 +08:00
WuYongJie
e5d4db5127 feat(ai-render): manage independent SPU SKU models and deletion 2026-10-24 10:25:24 +08:00
WuYongJie
005b8cf535 feat(navigation): rename AI tools and add Yiyidian link 2026-10-24 10:25:23 +08:00
WuYongJie
4f076c3b67 fix(distribution): separate daily job lock from archive writers 2026-10-24 10:05:48 +08:00
WuYongJie
6b221e0db4 perf(distribution): use indexed scope checks for dependency groups 2026-10-24 09:49:25 +08:00
WuYongJie
87cf63745d perf(distribution): publish incremental snapshots with durable checkpoints 2026-10-24 09:18:01 +08:00
WuYongJie
4b16861dd1 fix(diy): place dimension annotations outside the cabinet 2026-10-23 17:29:27 +08:00
WuYongJie
69599e1d60 feat(diy): allow barcode overrides with admin audit history 2026-10-23 16:03:32 +08:00
WuYongJie
3730b8cbd8 feat(mobile): simplify product and supply workspaces and unify navigation 2026-10-23 14:44:14 +08:00
WuYongJie
3c4706019c feat(diy): add brand barcode summaries and product dimensions 2026-10-23 14:08:55 +08:00
apple
fbcc5bf26a fix(supply-chain): 前置仓默认展示满足选品数据 2026-10-23 14:00:11 +08:00
WuYongJie
3b715e79b9 feat(production-stop-demands): 新增停产需求明细导出(全字段/自选字段)
- 导出使用停产需求管理的「下载」权限,未授权时不显示导出入口

- 支持全字段导出和点选字段导出当前筛选结果

- 新增导出接口与共享筛选参数解析,单次最多 20000 条
2026-10-23 11:51:24 +08:00
WuYongJie
530b9631fa fix(intake): keep date filter hints and values consistent across browser locales 2026-10-23 11:32:30 +08:00
WuYongJie
e1fba9a533 fix(intake): compact filters and unify workspace typography 2026-10-23 10:19:56 +08:00
WuYongJie
b04b196155 fix(print): stabilize Sofia carton label size and preview 2026-10-23 10:07:46 +08:00
WuYongJie
6c494c72e0 fix(supply-chain): complete front warehouse import reports and local layout 2026-10-23 09:31:42 +08:00
apple
a68d80e59e fix(supply-chain): 修复前置仓移仓与导入反馈 2026-10-23 09:31:42 +08:00
apple
902cb3613f feat(supply-chain): 新增前置仓进销存管理 2026-10-23 09:31:42 +08:00
WuYongJie
3321f0fcdd fix(supply-chain): refine distribution count display 2026-10-23 09:13:30 +08:00
WuYongJie
11c9ea93ba fix(workbench): unify account operation logs and show scoped records 2026-10-22 16:17:12 +08:00
WuYongJie
840ef0c319 feat(supply-chain): export all unselected supplier products
Isolated integration of c9370e6c53f64e0078d839d75f5a5ae7a0efb33a; approved task supplier-recommended-unselected-export-20261022.
2026-10-22 15:36:50 +08:00
WuYongJie
304514ee13 fix(supply-chain): remove excess inventory table bottom spacing 2026-10-22 14:10:32 +08:00
WuYongJie
2a94cebb14 perf(supply-chain): publish paged distribution summaries and online snapshot exports 2026-10-22 12:44:45 +08:00
WuYongJie
08e1a4764f test(workbench): align account tools with accepted product layout
(cherry picked from commit e665ed6c53543ca3447162ce1082f119dd680f46)
(cherry picked from commit b8792bd18857b0391c1fb6659f61cab59f2f2cc2)
2026-10-22 11:10:10 +08:00
WuYongJie
f5f27f44b4 feat(workbench): unify workspace navigation, surfaces and tab scrolling
(cherry picked from commit 8beecc67ea3df76bbb97cca01400bc67c5dbd08c)
(cherry picked from commit 65428ffa0df6a04d7997af46acd6600698e2dd28)
2026-10-22 11:03:33 +08:00
WuYongJie
dc6c3a74ee fix(product-archive): classify DF products as dropship without warehouse fallback 2026-10-22 10:42:47 +08:00
WuYongJie
142bafaa34 feat(intake): allow editing quality notification content 2026-10-22 09:09:21 +08:00
WuYongJie
bf6cdf8ef3 perf(product-archive): batch source reads and writes with resumable sync 2026-10-22 08:57:56 +08:00
WuYongJie
bc17baba42 fix(supply-chain): protect the 81 limit-file manual prices
Preserve manual prices only for the exact 586cf718 batch and the limit-file provenance marker. Keep other styles and sources on the existing upstream sync rules. Retain timestamp precision and explain manual precedence in sync exports. Add focused scope and preservation regression checks.
2026-10-21 18:22:16 +08:00
居居
975efc6dfb feat(supply-chain): 按限价文件更新大货和品牌商品限价
命中 81 个货号并统一人工指定为 49,组合关系与组合逻辑不变。
2026-10-21 18:18:43 +08:00
WuYongJie
a6f097da3d fix(supply-chain): restore original SJ0380 limit prices
Compensate the authorized 48186a9f / 925f5b2e price-49 release using its pre-release production snapshot. Restore 74 prices and approval sources, remove the added manual sync protection, retain original migration history and all later distribution features. No limit upload interface or API.
2026-10-21 18:01:53 +08:00
WuYongJie
7f190d3e77 feat(supply-chain): publish distribution snapshots, combo coverage and archive links 2026-10-21 17:21:59 +08:00
WuYongJie
925f5b2e6b fix(supply-chain): pin SJ0380 limit prices to 49
Apply the final authorized net change from 17a56eca and 48186a9f21fda9c0ddeb6baed2be3846ca0297ac. Includes the 74-code data migration and manual price sync protection. The temporary upload UI, API, schema and migration are excluded.
2026-10-21 17:07:05 +08:00
WuYongJie
772ee85cb4 fix(intake): include business roles in owner candidates
(cherry picked from commit 3c7d69250efe05da878cfbe7ae92c85b44fd8b50)
(cherry picked from commit eb560236a941f7a1426b638ccf17b149c1199716)
2026-10-21 16:10:19 +08:00
WuYongJie
7b142eb04a fix(supply-chain): normalize marketing sales barcode matching
Normalize case and surrounding spaces during source aggregation and archive matching, while preserving stored barcode values and existing daily sales calculations.
2026-10-21 15:19:01 +08:00
WuYongJie
599273a46e feat(home): default to centered brand and workspace capsules 2026-10-21 15:07:18 +08:00
WuYongJie
170ca52dac fix(supply-chain): retry transient product archive enrichment reads
Reconnect and retry failed source reads after 30 seconds, 2 minutes and 5 minutes. Keep the current batch cursor, avoid repeated archive and audit writes, wait for concurrent reads before cleanup, and preserve permanent errors and the existing failure notification.
2026-10-21 14:14:07 +08:00
WuYongJie
50c2d44571 fix(production-stop-demands): 恢复盘停建议配置主窗口操作反馈
- 查询、新增、删除、导出、导入的错误/成功提示恢复到配置主窗口

- 新增/删除/导出的成功提示改为刷新后写入,避免被查询清空

- 保留独立批量导入弹窗

(cherry picked from commit 6226a4076d6352323f6342754d539970e7282e24)
2026-10-21 11:50:22 +08:00
WuYongJie
244a9f8b85 fix(production-stop-demands): 修复盘停建议大批量导入并完善配置权限
- 盘停建议导入上限提升到 5MB / 20000 行,修复数千行被拒与静默截断

- 修复条码解析把「保留:」并入条码的问题,前后端口径一致

- 批量 upsert 改为单条 SQL,5867 行导入约 0.1 秒

- 批量导入界面改为独立弹窗

- 原因分类配置仅系统管理员可维护

(cherry picked from commit f314d7802090c18ccc2b94d2e23529a4ffb89bfa)
2026-10-21 11:50:21 +08:00
居居
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
df9c0ad67f 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:27:26 +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
5754172787 fix(product-development-todo): route missing purchaser reminders and use plain text 2026-09-08 10:31:22 +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
7139f40c8d 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:02:51 +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
WuYongJie
22cede9ed0 merge(main): publish finance store password reveal 2026-09-08 06:22:23 +08:00
c1dd4e666e Merge pull request 'feat(store-accounts): 财务专用按需密码读取、审计与安全状态' (#17) from codex/wuyongjie-store-accounts-finance-reveal into codex/xuxiake-mainline 2026-09-08 06:21:56 +08:00
WuYongJie
494a1cf60f feat(store-accounts): add audited finance password reveal 2026-09-08 05:59:19 +08:00
WuYongJie
f5a8ec1c05 feat(intake): add product factory workbook export and import by sku code
the product factory workspace gains export/import template buttons (purchase/merchandiser already hold node download grants): the export brings out the sku code, set count and every factory fillable field, splitting set count 2 rows into their A/B package codes; the import matches rows by sku code, enforces a header layout identical to the exported template, validates supplier names against the supplier directory, warehouses against the warehouse directory, enum fields against system options and number/date formats, collapses or splits A/B packages when the imported set count changes, and rejects confirmed rows - any row error aborts the whole import with per-row line numbers and reasons. Size, color and material columns are reference only.
2026-09-07 17:14:07 +08:00
WuYongJie
e9f83b16ee feat(intake): add product factory workbook export and import by sku code
the product factory workspace gains export/import template buttons (purchase/merchandiser already hold node download grants): the export brings out the sku code, set count and every factory fillable field, splitting set count 2 rows into their A/B package codes; the import matches rows by sku code, enforces a header layout identical to the exported template, validates supplier names against the supplier directory, warehouses against the warehouse directory, enum fields against system options and number/date formats, collapses or splits A/B packages when the imported set count changes, and rejects confirmed rows - any row error aborts the whole import with per-row line numbers and reasons. Size, color and material columns are reference only.
2026-09-07 17:12:59 +08:00
WuYongJie
416e22754a 供应商新品推荐:新增采购数据看板与PK榜
看板双视图(明细数据/采购看板,仅采购与管理可见看板);按月聚合采购推荐SKU/涉及工厂/被选款/被选率;路径行明细支持工厂勾选后跳转明细数据;被选款数量卡片跳转已选明细;PK榜爬山形态铺满全屏,管理员可配置主题/总金额/层级门槛与金额/规则说明(存 SystemConfig,无迁移)。
2026-09-07 16:40:49 +08:00
WuYongJie
d4d26c43bf 供应商新品推荐:新增采购数据看板与PK榜
看板双视图(明细数据/采购看板,仅采购与管理可见看板);按月聚合采购推荐SKU/涉及工厂/被选款/被选率;路径行明细支持工厂勾选后跳转明细数据;被选款数量卡片跳转已选明细;PK榜爬山形态铺满全屏,管理员可配置主题/总金额/层级门槛与金额/规则说明(存 SystemConfig,无迁移)。
2026-09-07 16:39:38 +08:00
WuYongJie
8fb872a3f9 feat(intake): open sample measurement and pricing template access with spec name column
measurement and pricing nodes now default downloads for pricing/purchase/merchandiser subjects so non-admin owners see the export/import template buttons; both workbook exports add a spec name reference column after the barcode (imports stay barcode-keyed and ignore it); the pricing node also grants merchandiser download parity with purchase owners.
2026-09-07 11:48:18 +08:00
WuYongJie
f8ba4f1e25 feat(intake): open sample measurement and pricing template access with spec name column
measurement and pricing nodes now default downloads for pricing/purchase/merchandiser subjects so non-admin owners see the export/import template buttons; both workbook exports add a spec name reference column after the barcode (imports stay barcode-keyed and ignore it); the pricing node also grants merchandiser download parity with purchase owners.
2026-09-07 11:47:40 +08:00
WuYongJie
39d7fc02f6 feat(finished-product-progress): auto-import factory-confirmed SKUs on sample completion 2026-09-07 11:17:21 +08:00
WuYongJie
8ea6a54eaa feat(finished-product-progress): auto-import factory-confirmed SKUs on sample completion 2026-09-07 11:15:01 +08:00
WuYongJie
667438447a fix(intake): move import rowNumber off the shared sku export row type
the optional numeric rowNumber on NonIntegratedSkuExportRow broke the dynamic template column writes (string vs never) in the production build type check; import rows now use a dedicated NonIntegratedSkuImportRow type that extends the export row with a required rowNumber.
2026-09-05 12:26:25 +08:00
WuYongJie
092036786c fix(barcode-print): restore Sofia certificate row pitch 2026-09-05 11:55:52 +08:00
WuYongJie
4a574041a9 fix(barcode-print): restore Sofia certificate row pitch 2026-09-05 11:53:55 +08:00
WuYongJie
2dbb214006 fix(intake): move import rowNumber off the shared sku export row type
the optional numeric rowNumber on NonIntegratedSkuExportRow broke the dynamic template column writes (string vs never) in the production build type check; import rows now use a dedicated NonIntegratedSkuImportRow type that extends the export row with a required rowNumber.
2026-09-04 17:59:40 +08:00
WuYongJie
e926a5a990 fix(intake): replace unsafe cast in sku export empty row builder
adding the optional rowNumber field to NonIntegratedSkuExportRow made the Object.fromEntries cast insufficient for the production build type check; build the empty import row explicitly instead.
2026-09-04 17:49:57 +08:00
WuYongJie
4f6510271f fix(intake): replace unsafe cast in sku export empty row builder
adding the optional rowNumber field to NonIntegratedSkuExportRow made the Object.fromEntries cast insufficient for the production build type check; build the empty import row explicitly instead.
2026-09-04 17:49:31 +08:00
WuYongJie
fccf9485b7 feat(intake): validate sku import skuCode spu prefix
standard sku workbook import now rejects rows whose skuCode does not start with the product spu in uppercase (lowercase codes are reported as needing uppercase); rows with an empty skuCode still pass and get auto-generated on save; the whole import is rejected with per-row line numbers and reasons. barcodes are skipped since they derive from the sku code.
2026-09-04 17:40:11 +08:00
WuYongJie
f4712cd1a2 feat(intake): validate sku import skuCode spu prefix
standard sku workbook import now rejects rows whose skuCode does not start with the product spu in uppercase (lowercase codes are reported as needing uppercase); rows with an empty skuCode still pass and get auto-generated on save; the whole import is rejected with per-row line numbers and reasons. barcodes are skipped since they derive from the sku code.
2026-09-04 17:39:35 +08:00
WuYongJie
e27e4fe67e feat(sample-progress): add daily due-date reminder notifications 2026-09-04 16:15:44 +08:00
WuYongJie
4843e16124 feat(sample-progress): add daily due-date reminder notifications 2026-09-04 16:14:51 +08:00
WuYongJie
f41e87373b fix(barcode-print): normalize Sofia certificate row spacing 2026-09-04 15:27:09 +08:00
WuYongJie
d32988dde6 fix(barcode-print): normalize Sofia certificate row spacing 2026-09-04 15:26:23 +08:00
WuYongJie
b30e5e5850 fix(barcode-print): tune Sofia certificate field positions 2026-09-04 12:27:39 +08:00
WuYongJie
bbb35a44f3 fix(barcode-print): tune Sofia certificate field positions 2026-09-04 12:25:32 +08:00
WuYongJie
56f3a7fe3f fix(supply-chain): show feedback as timed toast 2026-09-04 11:27:55 +08:00
WuYongJie
d3dee58b2b fix(supply-chain): default batch selection operator 2026-09-04 11:27:55 +08:00
WuYongJie
3df9bf08b1 fix(supply-chain): show feedback as timed toast 2026-09-04 11:25:31 +08:00
WuYongJie
d08a244fcb fix(supply-chain): default batch selection operator 2026-09-04 11:23:54 +08:00
WuYongJie
b488e6084c fix(finished-product-progress): enforce first-due-date protection on import updates
Import rows must not bypass the confirmed-first-due-date protection. Drop the allowDueDateChanges flag from the import update path so first-due-date changes are rejected and weekly due-date changes still require a reason, matching manual edits. Legacy rows without an order-placed date keep their free-correction path.
2026-09-04 10:59:29 +08:00
WuYongJie
99f004a593 fix(finished-product-progress): enforce first-due-date protection on import updates
Import rows must not bypass the confirmed-first-due-date protection. Drop the allowDueDateChanges flag from the import update path so first-due-date changes are rejected and weekly due-date changes still require a reason, matching manual edits. Legacy rows without an order-placed date keep their free-correction path.
2026-09-04 10:55:55 +08:00
WuYongJie
a252c00ac2 fix: align sofia certificate print fields 2026-09-04 10:32:41 +08:00
WuYongJie
13dd73069e fix: align sofia certificate print fields 2026-09-04 10:28:58 +08:00
WuYongJie
592b53f0ac merge(main): 发布可切换抠图配置与异步智能抠图 2026-09-03 18:48:20 +08:00
WuYongJie
16d6069123 merge: 发布可切换抠图配置与异步智能抠图 2026-09-03 18:48:04 +08:00
WuYongJie
a5cca36d69 fix(bom-costing): 接入供应商异步智能抠图模型 2026-09-03 18:44:51 +08:00
WuYongJie
966a8340e4 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-estimate-agent-phase0 2026-09-03 18:27:47 +08:00
WuYongJie
6b3760c6e7 feat(bom-costing): 支持可切换的抠图接口配置 2026-09-03 18:27:22 +08:00
WuYongJie
92bd812a17 feat(supply-chain): add supplier recommendation notice discard pool 2026-09-03 17:55:00 +08:00
WuYongJie
3456d1f465 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-09-03 17:53:56 +08:00
WuYongJie
ffcf242738 feat(supply-chain): add supplier recommendation notice discard pool 2026-09-03 17:51:56 +08:00
WuYongJie
24b5d5872f merge(main): 发布 BOM 商品详情图排列修正 2026-09-03 17:46:23 +08:00
WuYongJie
b335ef7719 merge: 修正 BOM 商品详情图排列 2026-09-03 17:46:11 +08:00
WuYongJie
394927d843 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-estimate-agent-phase0 2026-09-03 17:45:59 +08:00
WuYongJie
f4adeb3808 fix(bom-costing): 纵向完整展示商品详情图 2026-09-03 17:45:38 +08:00
WuYongJie
452c41bc4d merge(main): 发布 BOM 商品图片布局修正 2026-09-03 17:24:23 +08:00
WuYongJie
cac0452672 merge: 修正 BOM 商品图片布局 2026-09-03 17:24:09 +08:00
WuYongJie
0c65f7af2b Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-estimate-agent-phase0 2026-09-03 17:23:54 +08:00
WuYongJie
4a9e5d99be fix(bom-costing): 按指定位置重排商品图片 2026-09-03 17:23:03 +08:00
WuYongJie
04d6219417 merge(main): 发布 BOM 商品抠图与图片分类 2026-09-03 17:06:12 +08:00
WuYongJie
30ab1b4b35 merge: 发布 BOM 商品抠图与图片分类 2026-09-03 17:05:49 +08:00
WuYongJie
b5d05f2779 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-estimate-agent-phase0 2026-09-03 17:04:34 +08:00
WuYongJie
4ce03262c0 feat(bom-costing): 接入商品抠图并完善图片浏览 2026-09-03 17:04:05 +08:00
WuYongJie
4014f53b9e Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into HEAD 2026-09-03 15:52:13 +08:00
WuYongJie
3e92e7242a Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-09-03 15:51:17 +08:00
WuYongJie
c10e787fcf fix(users): fall back permission subjects to business roles when department misses dedicated lines
Department dedicated lines stay authoritative; when no department path matches any line, resolve subjects from account business roles (OPERATIONS->operation, PURCHASING->purchase, STRATEGY->brandCenter, etc). Personal overrides still apply last. Fixes operations and four purchasing accounts being downgraded to plain viewers.
2026-09-03 15:51:09 +08:00
WuYongJie
17a0491a4a merge(main): 发布 BOM 九折预估与极简工作台
# Conflicts:
#	furniture-3d-system/prisma/schema.prisma
#	furniture-3d-system/src/app/intake/sample-progress/components/SampleProgressTableRow.tsx
#	furniture-3d-system/src/app/supply-chain/database/store-account-management/StoreAccountManagementView.tsx
#	furniture-3d-system/src/lib/store-account-management.ts
#	furniture-3d-system/src/server/integrations/notification-center/client.test.ts
#	furniture-3d-system/src/server/integrations/notification-center/client.ts
#	furniture-3d-system/src/server/integrations/notification-center/types.ts
#	furniture-3d-system/src/server/repositories/__tests__/store-account-management.repository.test.ts
#	furniture-3d-system/src/server/repositories/store-account-management.repository.ts
#	furniture-3d-system/src/server/services/__tests__/notification-delivery.service.test.ts
#	furniture-3d-system/src/server/services/__tests__/store-account-notification.service.test.ts
#	furniture-3d-system/src/server/services/notification-delivery.service.ts
#	furniture-3d-system/src/server/services/sample-progress.service.test.ts
#	furniture-3d-system/src/server/services/store-account-notification.service.ts
2026-09-03 15:44:35 +08:00
WuYongJie
1e1f667530 merge: 发布 BOM 九折预估与极简工作台 2026-09-03 15:41:55 +08:00
WuYongJie
33a13a8abb Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-estimate-agent-phase0 2026-09-03 15:38:14 +08:00
WuYongJie
4ceedf50ee feat(bom-costing): 固定九折预估并精简工作台 2026-09-03 15:37:34 +08:00
WuYongJie
41a6bbff48 chore: enforce operation log retention policy 2026-09-03 15:36:36 +08:00
WuYongJie
856522552f chore: enforce operation log retention policy 2026-09-03 15:33:22 +08:00
WuYongJie
c30aed3987 feat(supply-chain): enforce import required fields and move sample request column for supplier recommended products
- import rejects rows missing required fields and reports the row number with
  missing labels; drop-shipping fee is required only when shipping mode includes
  代发; date is system-filled on import and not required
- move 申请寄样 after 代发运费 across export, import template, table columns
  and the editor drawer
- gate the download-template button behind edit permission (the template belongs
  to the purchaser import workflow)
- add selection endpoint tests
2026-09-03 15:08:51 +08:00
WuYongJie
205db8234c feat(supply-chain): enforce import required fields and move sample request column for supplier recommended products
- import rejects rows missing required fields and reports the row number with
  missing labels; drop-shipping fee is required only when shipping mode includes
  代发; date is system-filled on import and not required
- move 申请寄样 after 代发运费 across export, import template, table columns
  and the editor drawer
- gate the download-template button behind edit permission (the template belongs
  to the purchaser import workflow)
- add selection endpoint tests
2026-09-03 15:07:09 +08:00
WuYongJie
1edbc110d4 feat(users): show last login status in account management list
账号管理表格在账号状态与操作之间新增最后登录列:从未登录灰色、今天 HH:mm、昨天、N 天前、日期,超 30 天橙色标记闲置账号;组织树接口对管理员已返回 lastLoginAt,本次仅前端改动。
2026-09-03 14:21:23 +08:00
WuYongJie
455025f546 feat(users): show last login status in account management list
账号管理表格在账号状态与操作之间新增最后登录列:从未登录灰色、今天 HH:mm、昨天、N 天前、日期,超 30 天橙色标记闲置账号;组织树接口对管理员已返回 lastLoginAt,本次仅前端改动。
2026-09-03 14:21:06 +08:00
WuYongJie
58d5e0ffdd feat: add operation log scrolling and pagination 2026-09-03 13:53:21 +08:00
WuYongJie
583e3240ce feat: add operation log scrolling and pagination 2026-09-03 13:44:06 +08:00
WuYongJie
88ddd67736 style(finished-product-progress): soften contract group separator to 1px light gray 2026-09-03 13:32:09 +08:00
WuYongJie
259f4b8e7e style(finished-product-progress): soften contract group separator to 1px light gray 2026-09-03 13:29:48 +08:00
WuYongJie
bb561d2482 feat(mobile): streamline mobile navigation and compact intake workspace UI 2026-09-03 13:20:42 +08:00
WuYongJie
8f5fde8d4e feat(mobile): streamline mobile navigation and compact intake workspace UI 2026-09-03 13:18:39 +08:00
WuYongJie
925868fb65 feat(intake): allow designers to delete their own product records
product intake list delete button now also resolves for designers (DESIGN business role or design department paths), restricted to records they created; admins keep full delete scope; server DELETE route and client row button share the same record-level predicate; delete stays soft with operation logs.
2026-09-03 13:06:01 +08:00
WuYongJie
3556b48c4a feat(intake): allow designers to delete their own product records
product intake list delete button now also resolves for designers (DESIGN business role or design department paths), restricted to records they created; admins keep full delete scope; server DELETE route and client row button share the same record-level predicate; delete stays soft with operation logs.
2026-09-03 13:03:49 +08:00
WuYongJie
74ac36f34d feat(finished-product-progress): add filtered export with embedded images
Add an export action that collects all rows matching the current filters (or everything when unfiltered), fetches each row's image from object storage, and builds an ExcelJS workbook with the images embedded in the image column. The workbook headers match the import template so the file round-trips through the existing import flow. Export progress, image fetch failures and completion counts are surfaced in a status panel.
2026-09-03 12:07:58 +08:00
WuYongJie
b129acdc48 feat(finished-product-progress): add filtered export with embedded images
Add an export action that collects all rows matching the current filters (or everything when unfiltered), fetches each row's image from object storage, and builds an ExcelJS workbook with the images embedded in the image column. The workbook headers match the import template so the file round-trips through the existing import flow. Export progress, image fetch failures and completion counts are surfaced in a status panel.
2026-09-03 12:06:29 +08:00
WuYongJie
ee9a7cb88e feat(supply-chain): add operations selection pools and selected-pool export to supplier recommended products
- selection pools: all/unselected/selected switch on the same page, batch select with
  operator search-confirm (status, operator and time recorded with change logs), batch return
- selected rows stamped with a red 已选款 seal on card/thumbnail/detail images and locked
  in the master pool to avoid accidental re-selection
- operations-role users (or admins) can select/return; service re-validates the operator
- selected-pool export: export endpoint follows all active filters, embeds product images
  into the image column (webp converted via sharp to 320px png, fallback to url text),
  includes selection status/operator/time columns, gated by download permission
2026-09-03 11:46:34 +08:00
WuYongJie
970cd3f234 feat(supply-chain): add operations selection pools and selected-pool export to supplier recommended products
- selection pools: all/unselected/selected switch on the same page, batch select with
  operator search-confirm (status, operator and time recorded with change logs), batch return
- selected rows stamped with a red 已选款 seal on card/thumbnail/detail images and locked
  in the master pool to avoid accidental re-selection
- operations-role users (or admins) can select/return; service re-validates the operator
- selected-pool export: export endpoint follows all active filters, embeds product images
  into the image column (webp converted via sharp to 320px png, fallback to url text),
  includes selection status/operator/time columns, gated by download permission
2026-09-03 11:44:41 +08:00
WuYongJie
7f15c5b756 fix finished product progress order dates 2026-09-03 10:01:26 +08:00
WuYongJie
25f5796dd0 fix finished product progress order dates 2026-09-03 09:54:33 +08:00
WuYongJie
ce34f56c39 feat(users): merchandiser inherits purchase permission subject
采购-跟单(merchandiser,含跟单组与雁兰/槐米特例)解析权限主体时追加采购负责人主体:给采购负责人开通的节点权限与供应链权限对跟单自动生效,无需重复开通;核价与质检主体保持独立。
2026-09-02 16:21:01 +08:00
WuYongJie
7ed6c061c4 feat(users): merchandiser inherits purchase permission subject
采购-跟单(merchandiser,含跟单组与雁兰/槐米特例)解析权限主体时追加采购负责人主体:给采购负责人开通的节点权限与供应链权限对跟单自动生效,无需重复开通;核价与质检主体保持独立。
2026-09-02 16:19:57 +08:00
WuYongJie
15f3331d58 feat(supply-chain): support clipboard paste image upload in supplier recommended product editor
- paste a screenshot anywhere in the drawer to upload and fill the storage url
- add file picker button, uploading state, error message and preview thumbnail
2026-09-02 15:51:02 +08:00
WuYongJie
cc0f5e65f4 feat(supply-chain): add category, card view and unified controls to supplier recommended products
- add category field with migration, import template column and multi-select filter (IN query)
- card view aligned with product selection dashboard style; table/card toggle persisted per browser
- purchaser/shipping-mode filters upgraded to searchable multi-select
- table actions as icon-only buttons with a view entry; date filter opens the picker on click
- column settings: reset to defaults and name search; page styles moved to a plain css file
2026-09-02 15:51:02 +08:00
WuYongJie
607b030034 feat(supply-chain): support clipboard paste image upload in supplier recommended product editor
- paste a screenshot anywhere in the drawer to upload and fill the storage url
- add file picker button, uploading state, error message and preview thumbnail
2026-09-02 15:44:14 +08:00
WuYongJie
021d7eab2b feat(supply-chain): add category, card view and unified controls to supplier recommended products
- add category field with migration, import template column and multi-select filter (IN query)
- card view aligned with product selection dashboard style; table/card toggle persisted per browser
- purchaser/shipping-mode filters upgraded to searchable multi-select
- table actions as icon-only buttons with a view entry; date filter opens the picker on click
- column settings: reset to defaults and name search; page styles moved to a plain css file
2026-09-02 15:44:13 +08:00
WuYongJie
adfecb4c67 feat(intake): add sample measurement and product pricing workbook import/export
sample measurement: export template with image/barcode/L-W-H-weight columns (all batches) and import backfill by barcode into draft rows with per-row skip reasons; product pricing: export template with image/barcode/cost-price and import backfill cost price only; both reuse the product limit workbook pattern, keep edits in draft until the existing confirm/push flow, no API or schema changes.
2026-09-02 15:19:48 +08:00
WuYongJie
5aedfea85c feat(intake): add sample measurement and product pricing workbook import/export
sample measurement: export template with image/barcode/L-W-H-weight columns (all batches) and import backfill by barcode into draft rows with per-row skip reasons; product pricing: export template with image/barcode/cost-price and import backfill cost price only; both reuse the product limit workbook pattern, keep edits in draft until the existing confirm/push flow, no API or schema changes.
2026-09-02 15:19:04 +08:00
WuYongJie
58966bba20 feat(finished-product-progress): align column settings drawer with unified standard
Replace the two-column popover with a right-docked drawer: single-column list with drag handles, checkboxes and lock markers; add column-name search, hide-all and reset-default buttons, and persist unlocked column order locally. Locked columns keep their default positions (trailing sticky actions column).
2026-09-02 14:43:01 +08:00
WuYongJie
7740433ae7 feat(finished-product-progress): align column settings drawer with unified standard
Replace the two-column popover with a right-docked drawer: single-column list with drag handles, checkboxes and lock markers; add column-name search, hide-all and reset-default buttons, and persist unlocked column order locally. Locked columns keep their default positions (trailing sticky actions column).
2026-09-02 14:41:19 +08:00
WuYongJie
5c43428a47 feat: route store account notices to DingTalk group 2026-09-02 13:57:51 +08:00
WuYongJie
7f078abb7b feat: route store account notices to DingTalk group 2026-09-02 13:55:00 +08:00
WuYongJie
940e94882e fix(finished-product-progress): allow negative in-transit quantity for over-delivery
Remove the Math.max(0,...) clamps in the in-transit calculation and delivery-record recompute, and switch the input schema inTransitQuantity validator to a dedicated negative-tolerant one. Ordered and delivered quantities stay non-negative.
2026-09-02 12:22:17 +08:00
WuYongJie
7126984caa feat(finished-product-progress): support clipboard paste image upload in editor drawer 2026-09-02 12:22:17 +08:00
WuYongJie
43b867ef55 feat(finished-product-progress): add contract group separators and alternating band backgrounds 2026-09-02 12:22:17 +08:00
WuYongJie
9c459e7195 fix(finished-product-progress): allow negative in-transit quantity for over-delivery
Remove the Math.max(0,...) clamps in the in-transit calculation and delivery-record recompute, and switch the input schema inTransitQuantity validator to a dedicated negative-tolerant one. Ordered and delivered quantities stay non-negative.
2026-09-02 12:19:07 +08:00
WuYongJie
e4df1b2576 feat(finished-product-progress): support clipboard paste image upload in editor drawer 2026-09-02 12:18:26 +08:00
WuYongJie
b2fc4dde6d feat(finished-product-progress): add contract group separators and alternating band backgrounds 2026-09-02 12:18:11 +08:00
WuYongJie
138dc0d644 feat(supply-chain): add finished product select all 2026-09-02 09:23:04 +08:00
WuYongJie
f91554c7dc feat(supply-chain): add finished product select all 2026-09-02 09:17:39 +08:00
WuYongJie
6ec0258001 Merge branch 'release-finished-product-progress' 2026-09-01 18:32:15 +08:00
WuYongJie
9c66706b67 release finished product progress import and management 2026-09-01 18:32:06 +08:00
WuYongJie
f89bc61af4 fix finished product progress import and management 2026-09-01 18:27:04 +08:00
WuYongJie
3c69088433 fix: map store account subject company header 2026-09-01 17:53:56 +08:00
WuYongJie
e60b3b1e60 fix: map store account subject company header 2026-09-01 17:47:45 +08:00
WuYongJie
d06e45b0c8 fix(supply-chain): support supplier recommendation import images 2026-09-01 17:44:32 +08:00
WuYongJie
03eca07f9f fix(supply-chain): support supplier recommendation import images 2026-09-01 17:38:57 +08:00
WuYongJie
242ed201ba feat(store-accounts): add profit center options 2026-09-01 16:04:51 +08:00
WuYongJie
6b8661dcfd feat(store-accounts): add profit center options 2026-09-01 16:02:49 +08:00
WuYongJie
b3f85d5f5a fix(work-record): persist monthly archive rows with stable ids 2026-09-01 14:33:15 +08:00
WuYongJie
dc6c45848a fix(work-record): persist monthly archive rows with stable ids 2026-09-01 14:31:58 +08:00
WuYongJie
eead468d4d fix(users): use standalone permission navigation 2026-09-01 13:37:19 +08:00
WuYongJie
fb51614a86 fix(users): use standalone permission navigation 2026-09-01 13:35:49 +08:00
WuYongJie
e7996abc6a feat(supply-chain): improve supplier recommended product management 2026-09-01 13:08:04 +08:00
WuYongJie
474f245ba7 feat(supply-chain): add supplier recommended products 2026-09-01 13:07:55 +08:00
WuYongJie
03f308261e feat(supply-chain): improve supplier recommended product management 2026-09-01 13:01:55 +08:00
WuYongJie
934b073eea fix(supply-chain): unify list pagination spacing 2026-09-01 12:37:41 +08:00
WuYongJie
9a3c5f9157 fix(supply-chain): unify list pagination spacing 2026-09-01 12:35:29 +08:00
WuYongJie
6e140e1445 fix(work-record): complete monthly archive release 2026-09-01 12:24:38 +08:00
WuYongJie
0b12ec28b9 feat(work-record): add monthly archive editing permissions 2026-09-01 12:08:25 +08:00
WuYongJie
2b5f49fc51 feat(work-record): add monthly archive editing permissions 2026-09-01 11:59:22 +08:00
WuYongJie
7b7e7d7e28 fix(oa-board): prevent production unified table redirect 2026-09-01 11:12:35 +08:00
WuYongJie
b01579ca99 fix(oa-board): prevent production unified table redirect 2026-09-01 11:10:59 +08:00
WuYongJie
d5f2b68d88 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration
# Conflicts:
#	furniture-3d-system/agents/script-monitor/README.md
#	furniture-3d-system/agents/script-monitor/agent.mjs
#	furniture-3d-system/agents/script-monitor/config.example.json
#	furniture-3d-system/src/app/api/supply-chain/store-account-management/[id]/bank-account/route.ts
#	furniture-3d-system/src/app/api/supply-chain/store-account-management/profiles/import/route.ts
#	furniture-3d-system/src/app/api/supply-chain/store-account-management/profiles/route.ts
#	furniture-3d-system/src/app/api/supply-chain/store-account-management/profiles/template/route.ts
#	furniture-3d-system/src/app/intake/sample-progress/components/SampleProgressTableRow.tsx
#	furniture-3d-system/src/app/supply-chain/database/store-account-management/StoreAccountManagementView.tsx
#	furniture-3d-system/src/app/supply-chain/script-monitor/ScriptMonitorView.test.tsx
#	furniture-3d-system/src/app/supply-chain/script-monitor/ScriptMonitorView.tsx
#	furniture-3d-system/src/app/supply-chain/script-monitor/page.tsx
#	furniture-3d-system/src/lib/store-account-management.ts
#	furniture-3d-system/src/lib/supply-chain-script-monitor.ts
#	furniture-3d-system/src/server/dto/script-monitor.dto.ts
#	furniture-3d-system/src/server/integrations/notification-center/client.test.ts
#	furniture-3d-system/src/server/integrations/notification-center/client.ts
#	furniture-3d-system/src/server/integrations/notification-center/types.ts
#	furniture-3d-system/src/server/repositories/__tests__/store-account-management.repository.test.ts
#	furniture-3d-system/src/server/repositories/script-monitor.repository.ts
#	furniture-3d-system/src/server/repositories/store-account-management.repository.ts
#	furniture-3d-system/src/server/services/__tests__/notification-delivery.service.test.ts
#	furniture-3d-system/src/server/services/__tests__/store-account-management-export.service.test.ts
#	furniture-3d-system/src/server/services/__tests__/store-account-notification.service.test.ts
#	furniture-3d-system/src/server/services/notification-delivery.service.ts
#	furniture-3d-system/src/server/services/sample-progress.service.test.ts
#	furniture-3d-system/src/server/services/script-monitor.service.test.ts
#	furniture-3d-system/src/server/services/script-monitor.service.ts
#	furniture-3d-system/src/server/services/store-account-management-export.service.ts
#	furniture-3d-system/src/server/services/store-account-notification.service.ts
2026-09-01 11:09:26 +08:00
WuYongJie
a2afbc40bb chore: reconcile worktree snapshot before origin merge (local branch only) 2026-09-01 11:07:53 +08:00
WuYongJie
facc1f0955 feat(supply-chain): restrict store account password visibility 2026-09-01 10:36:27 +08:00
WuYongJie
e38f8e813b feat(supply-chain): restrict store account password visibility 2026-09-01 10:33:49 +08:00
WuYongJie
70e268034e fix(intake): keep default workspace tab available
Cherry-picked as an isolated mainline release from 22227e7cadf00b45056c429f96de489af7c6767c.
2026-09-01 10:11:07 +08:00
WuYongJie
508e0a40d5 fix(intake): keep default workspace tab available 2026-09-01 10:08:05 +08:00
WuYongJie
c0562fdddc fix(intake): keep default workspace tab available 2026-09-01 10:02:57 +08:00
WuYongJie
22227e7cad fix(auth): preserve custom permission role in API context 2026-08-31 18:07:46 +08:00
WuYongJie
7089ada0a0 fix(auth): preserve custom permission role in API context 2026-08-31 18:05:56 +08:00
WuYongJie
9bb36756d1 fix(auth): preserve custom permission role in session 2026-08-31 17:43:30 +08:00
jingchuan
f2a554a330 fix(oa-board): mark XP2026081900000414 terminated 2026-08-31 17:39:04 +08:00
WuYongJie
0aaacb4bff fix(auth): preserve custom permission role in session 2026-08-31 17:38:38 +08:00
WuYongJie
bf7b71ab00 fix(auth): preserve custom permission role in session 2026-08-31 17:36:25 +08:00
WuYongJie
4ecad009b9 cherry-pick: publish approved supply chain updates 2026-08-31 17:17:23 +08:00
WuYongJie
e43bf78c73 feat: publish approved supply chain updates 2026-08-31 17:15:56 +08:00
WuYongJie
fecfd9053e fix: 完善店铺账号通知与筛选体验 2026-08-31 16:34:36 +08:00
WuYongJie
7c6be91a91 fix: 完善店铺账号通知与筛选体验 2026-08-31 16:30:58 +08:00
WuYongJie
7d246e1d5b fix(product-archive): include full export client hook
Add the client hook omitted from the isolated main integration.
2026-08-31 16:02:37 +08:00
WuYongJie
b974d83986 feat(product-archive): add full export and column filters
Applied only the product archive feature patch from 232eaa87 onto origin/main.
2026-08-31 15:52:20 +08:00
WuYongJie
232eaa8768 feat(product-archive): add full export and column filters
Cherry-picked feature patch from c2289ca0 onto latest codex/xuxiake-mainline without including unrelated working-tree changes.
2026-08-31 15:49:13 +08:00
WuYongJie
c2289ca074 feat(product-archive): add full export and column filters 2026-08-31 15:42:07 +08:00
WuYongJie
68916e2791 feat: store account management updates 2026-08-31 13:53:44 +08:00
WuYongJie
72262acfa0 feat: complete store account management updates 2026-08-31 13:52:18 +08:00
WuYongJie
4950135cfc fix: normalize notification line breaks 2026-08-31 12:19:26 +08:00
WuYongJie
d40b6ad197 fix: normalize notification line breaks 2026-08-31 12:15:44 +08:00
WuYongJie
315ede4f3f fix: normalize notification line breaks 2026-08-31 12:13:36 +08:00
WuYongJie
f3b0124d4c feat(intake): add supplier filter and fixed development toolbar 2026-08-31 11:19:57 +08:00
WuYongJie
fb53234811 feat(intake): add supplier filter and fixed development toolbar 2026-08-31 11:17:30 +08:00
WuYongJie
9e51faa192 feat: notify on store account creation 2026-08-31 10:39:08 +08:00
WuYongJie
916930a714 feat: notify on store account creation 2026-08-31 10:38:33 +08:00
WuYongJie
7c17626339 feat: notify on store account creation 2026-08-31 10:27:20 +08:00
WuYongJie
368107c17b release: publish approved sample and finished progress updates 2026-08-31 10:24:00 +08:00
WuYongJie
865be95b6e feat: submit approved sample and finished progress updates 2026-08-31 10:21:29 +08:00
WuYongJie
3efe408a27 merge: release product development base SPU grouping 2026-08-29 16:52:10 +08:00
WuYongJie
b1505b4d63 feat(intake): group product development list by base SPU 2026-08-29 16:14:03 +08:00
WuYongJie
e1b7683bfd fix(supply-chain): include finished product delivery service 2026-08-29 15:35:59 +08:00
WuYongJie
a69bf1997b feat(supply-chain): add multi-delivery tracking to finished product progress 2026-08-29 15:25:36 +08:00
WuYongJie
622fc9d527 feat(supply-chain): refine finished product progress import 2026-08-29 15:17:19 +08:00
WuYongJie
3409a9db50 feat(supply-chain): add multi-delivery tracking to finished product progress 2026-08-29 15:07:12 +08:00
WuYongJie
0f55175d9b fix: respect configured HR SQL Server schema
Use HR_SQLSERVER_SCHEMA for default employee directory queries so production can read the rpt source tables.
2026-08-29 14:07:33 +08:00
WuYongJie
0811bea57c fix: respect configured HR SQL Server schema
Use HR_SQLSERVER_SCHEMA for default employee directory queries so production can read the rpt source tables.
2026-08-29 14:06:54 +08:00
WuYongJie
0ff6f87757 feat(supply-chain): add supplier recommended products 2026-08-29 13:31:45 +08:00
WuYongJie
089845e7ee merge: integrate employee directory account protection 2026-08-29 13:25:24 +08:00
WuYongJie
ab5ea15e80 fix: protect external accounts during employee directory sync 2026-08-29 13:23:26 +08:00
WuYongJie
1347a34d14 feat(supply-chain): refine finished product progress import 2026-08-29 13:08:12 +08:00
WuYongJie
6a32957bf5 fix: protect external accounts during employee directory sync 2026-08-29 12:04:57 +08:00
WuYongJie
9bf54d95d3 fix: protect external accounts during employee directory sync 2026-08-29 12:01:18 +08:00
WuYongJie
dc16d2b75c merge: release script monitor machine deletion 2026-08-28 20:48:38 +08:00
WuYongJie
cd73be673d feat(script-monitor): allow deleting monitored machines 2026-08-28 20:47:47 +08:00
WuYongJie
85c31677c5 merge: release script monitor operations and store account completion
# Conflicts:
#	furniture-3d-system/prisma/schema.prisma
#	furniture-3d-system/src/app/api/supply-chain/store-account-management/[id]/bank-account/route.ts
#	furniture-3d-system/src/app/api/supply-chain/store-account-management/profiles/import/route.ts
#	furniture-3d-system/src/app/api/supply-chain/store-account-management/profiles/route.ts
#	furniture-3d-system/src/app/api/supply-chain/store-account-management/profiles/template/route.ts
2026-08-28 18:28:11 +08:00
WuYongJie
59634f8516 merge: integrate latest shared mainline 2026-08-28 18:23:01 +08:00
WuYongJie
d8495e98ef fix(store-account): complete unified profile workflow 2026-08-28 18:17:01 +08:00
WuYongJie
ea363984a7 merge: release approved store account updates 2026-08-28 17:49:53 +08:00
WuYongJie
47df269785 release: approve store account updates and users build fix 2026-08-28 17:49:53 +08:00
WuYongJie
19e39e0b8c fix: remove invalid users page export 2026-08-28 17:46:29 +08:00
WuYongJie
68c48086df feat: extend store account profiles and bank account 2026-08-28 17:46:14 +08:00
WuYongJie
e34c80cdd2 feat(supply-chain): add script monitor operations 2026-08-28 17:38:51 +08:00
WuYongJie
73b3049e01 fix(store-account): export bank account crypto helpers 2026-08-28 16:31:43 +08:00
WuYongJie
978b609325 merge: release accepted five features and latest long-lived changes 2026-08-28 16:12:43 +08:00
WuYongJie
6e9d0e474f merge: release five accepted features 2026-08-28 16:09:04 +08:00
WuYongJie
240ea4b01a feat(supply-chain): unify store profile entry 2026-08-28 16:07:35 +08:00
WuYongJie
422c23d3e5 release: merge latest development with script monitoring 2026-08-28 15:51:55 +08:00
WuYongJie
f974e7d4b4 feat(intake): bulk maintain barcode print imports 2026-08-28 15:43:15 +08:00
WuYongJie
b27399d908 feat(supply-chain): maintain supplier information by barcode 2026-08-28 15:43:15 +08:00
WuYongJie
cc7e45a225 feat(intake): add fuzzy SKU material import 2026-08-28 15:43:14 +08:00
WuYongJie
0a466dcb2b feat(installation): add hinge articulation motion 2026-08-28 15:43:14 +08:00
WuYongJie
4492c0eb8f feat(supply-chain): add finished product progress detail 2026-08-28 15:43:13 +08:00
WuYongJie
0f336e9d1e feat(supply-chain): add script monitoring center 2026-08-28 15:41:35 +08:00
WuYongJie
4d34a387a9 feat(supply-chain): add store account exports and permissions 2026-08-28 09:41:13 +08:00
WuYongJie
571b840911 feat(supply-chain): add store account exports and permissions 2026-08-28 09:39:08 +08:00
WuYongJie
78cfa85232 release: integrate approved product and notification updates
Includes 13268ee5, 01a9d1e2, 48e496b0, bf85258d.
Store account fix e48229ab is already integrated by parent d8141a63.
2026-08-27 16:39:54 +08:00
WuYongJie
d8141a63a9 fix(supply-chain): allow incomplete store account imports
Isolated main integration of e48229ab.
2026-08-27 16:34:50 +08:00
WuYongJie
bf85258d3c feat(notifications): add product development reminders 2026-08-27 15:13:22 +08:00
WuYongJie
e48229ab22 fix(supply-chain): allow incomplete store account imports 2026-08-27 15:13:21 +08:00
WuYongJie
48e496b01b fix(intake): preserve confirmation after factory edits 2026-08-27 15:13:20 +08:00
WuYongJie
01a9d1e2c8 fix(intake): default product type to all 2026-08-27 15:13:20 +08:00
WuYongJie
13268ee5b5 feat(intake): default sku material compliance 2026-08-27 15:13:19 +08:00
WuYongJie
55c99cbc4d feat(sample-progress): enhance detail collaboration 2026-08-27 13:53:01 +08:00
WuYongJie
023c4c07d2 feat(sample-progress): enhance detail collaboration 2026-08-27 13:52:56 +08:00
WuYongJie
37074cf25b feat(supply-chain): stream store account import progress 2026-08-27 13:14:23 +08:00
WuYongJie
1619213039 feat(supply-chain): stream store account import progress 2026-08-27 13:11:11 +08:00
WuYongJie
5da6c34758 feat(installation): auto-explode unnamed assemblies 2026-08-26 18:29:15 +08:00
WuYongJie
879a73e480 feat(supply-chain): add product, supplier, and store management 2026-08-26 18:28:55 +08:00
WuYongJie
629ad42104 feat(installation): auto-explode unnamed assemblies 2026-08-26 18:19:24 +08:00
WuYongJie
a34dee6825 feat(supply-chain): add product, supplier, and store management 2026-08-26 18:18:41 +08:00
WuYongJie
8b8a41f2d7 fix(intake): preserve sku identities on delete 2026-08-26 16:49:42 +08:00
WuYongJie
3f5cf79cc2 fix(intake): preserve sku identities on delete 2026-08-26 16:48:22 +08:00
WuYongJie
c093c32e1d fix(intake): preserve sku identities on delete 2026-08-26 16:46:57 +08:00
WuYongJie
07d6d0d03f fix(intake): isolate basic info confirmation by batch 2026-08-26 16:25:41 +08:00
WuYongJie
71e746b5f5 fix(intake): isolate basic info confirmation by batch 2026-08-26 16:23:46 +08:00
WuYongJie
9761465d45 fix(intake): isolate basic info confirmation by batch 2026-08-26 16:20:04 +08:00
WuYongJie
aa5d983b7d merge: release SKU batch workbook isolation 2026-08-26 13:19:46 +08:00
WuYongJie
d0ddbb6ccf fix(intake): isolate SKU batch workbook imports 2026-08-26 13:17:16 +08:00
WuYongJie
77b3b3c800 merge: release daily Jushuitan fixed virtual inventory 2026-08-25 14:27:28 +08:00
WuYongJie
2cd254fee5 fix(jushuitan): schedule fixed virtual inventory daily 2026-08-25 14:26:02 +08:00
WuYongJie
ffc9c1a727 fix(jushuitan): schedule fixed virtual inventory daily 2026-08-25 14:07:36 +08:00
jingchuan
b23e4122f2 feat(supply-chain): organize sample timing by plan tabs 2026-08-25 13:04:30 +08:00
WuYongJie
42e4503ce6 test(auth): cover sample editor permission scope 2026-08-25 11:12:55 +08:00
jingchuan
336b47a5fc fix: classify sample timing by review product type 2026-08-25 10:35:32 +08:00
jingchuan
ecf4d4f743 fix: pass supply chain scope through sample editor 2026-08-24 18:00:47 +08:00
jingchuan
5984f82c75 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/jingchuan-sample-dimensions-preview-20260824 2026-08-24 17:57:37 +08:00
jingchuan
2e33b9b1c0 feat: refine sample timing and procurement analysis 2026-08-24 17:56:55 +08:00
WuYongJie
73ffcb17bb feat(users): create suppliers from warehouse directory 2026-08-24 17:43:52 +08:00
WuYongJie
8365286651 feat(auth): enforce permission contract 2026-08-24 17:43:52 +08:00
WuYongJie
b497c2d061 fix(intake): preserve basic info confirmation after factory sync 2026-08-24 16:42:03 +08:00
WuYongJie
10c32b2c97 fix(intake): preserve basic info confirmation after factory sync 2026-08-24 16:40:58 +08:00
WuYongJie
bfbe044005 feat(sample-progress): move editing into drawer 2026-08-24 10:20:59 +08:00
WuYongJie
0fa4481719 fix(intake): project factory model for barcode printing 2026-08-24 09:21:11 +08:00
WuYongJie
b0a9aa0c4b feat(intake): sync factory-confirmed SKUs to barcode printing 2026-08-21 18:32:51 +08:00
jingchuan
fcdf25ed38 fix(supply-chain): align flow detail dates with status 2026-08-21 17:34:26 +08:00
jingchuan
e8531fde1d feat(supply-chain): focus sample owner load on inflight tasks 2026-08-21 17:16:39 +08:00
jingchuan
64f1b2732b merge: synchronize latest production mainline before termination fix 2026-08-21 16:44:51 +08:00
jingchuan
9a68b886ac merge: synchronize latest shared mainline before termination fix 2026-08-21 16:44:43 +08:00
jingchuan
9bb14e391d fix(oa-board): preserve terminated opening status 2026-08-21 16:44:20 +08:00
jingchuan
d252924a21 feat(supply-chain): add sample performance analysis 2026-08-21 16:27:39 +08:00
WuYongJie
19f9affe5c 供应链:打样进度明细移入采购管理 2026-08-21 15:20:29 +08:00
jingchuan
b15c292f12 merge: synchronize shared mainline before OA binding release 2026-08-21 13:36:48 +08:00
jingchuan
03c97d234b merge: persist OA historical order binding 2026-08-21 13:33:18 +08:00
WuYongJie
b1cebf0707 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration
# Conflicts:
#	furniture-3d-system/src/app/supply-chain/database/product-archive/ProductArchiveView.tsx
#	furniture-3d-system/src/lib/permissions.ts
#	furniture-3d-system/src/lib/workspace-route-permissions.ts
#	furniture-3d-system/src/server/core/types.ts
#	furniture-3d-system/src/server/repositories/user.repository.ts
#	furniture-3d-system/src/server/services/auth.service.ts
2026-08-21 13:11:21 +08:00
WuYongJie
58fa0c7133 test(users): cover permission role activation 2026-08-21 11:47:16 +08:00
WuYongJie
2631849409 test(users): cover permission role activation 2026-08-21 11:46:33 +08:00
WuYongJie
e5985cf4cd feat(users): add administrator account filter 2026-08-21 11:33:32 +08:00
WuYongJie
bd651338dc feat(users): add administrator account filter 2026-08-21 11:30:21 +08:00
WuYongJie
022298db92 refactor(supply-chain): clarify sync and UI ownership
Integrate the reviewed supply-chain structure governance scope without unpublished image-search changes.
2026-08-21 11:12:34 +08:00
WuYongJie
d0c11dd979 refactor(supply-chain): clarify sync and UI ownership
Integrate the reviewed supply-chain structure governance scope without unpublished image-search changes.
2026-08-21 11:12:34 +08:00
WuYongJie
a26f53aaba refactor(supply-chain): clarify sync and UI ownership 2026-08-21 10:54:30 +08:00
jingchuan
1075eea0aa fix(oa-board): bind historical order approval 2026-08-21 10:45:26 +08:00
WuYongJie
b15329bec8 feat(users): sync permissions with real navigation 2026-08-21 10:34:45 +08:00
WuYongJie
9faa52d91d feat(users): sync permissions with real navigation 2026-08-21 10:34:45 +08:00
WuYongJie
c8c06c88e9 Merge origin/main into shared development mainline 2026-08-21 10:14:29 +08:00
WuYongJie
4fe426ce56 fix(intake): prevent modular barcode prefix clipping 2026-08-21 09:38:44 +08:00
WuYongJie
e8990d7e63 fix(intake): prevent modular barcode prefix clipping 2026-08-21 09:36:44 +08:00
WuYongJie
075f37f31f feat(users): redesign account and role permissions 2026-08-20 17:47:21 +08:00
WuYongJie
095bae1be8 feat(users): redesign account and role permissions 2026-08-20 17:40:21 +08:00
WuYongJie
1e035f10a4 feat(supply-chain): sync clearance stock 2026-08-20 16:34:46 +08:00
WuYongJie
f5d7cfb2df feat(supply-chain): sync clearance stock 2026-08-20 16:32:55 +08:00
jingchuan
26ce017921 feat(supply-chain): streamline terminated task details 2026-08-20 15:20:22 +08:00
WuYongJie
10ae34fc4a fix(intake): split modular barcode suffix 2026-08-20 15:12:22 +08:00
WuYongJie
482858405c feat(supply-chain): read JST public available stock 2026-08-20 15:12:22 +08:00
WuYongJie
aae05326ab fix(intake): split modular barcode suffix 2026-08-20 15:09:12 +08:00
WuYongJie
b20ffaa30f feat(supply-chain): read JST public available stock 2026-08-20 15:09:12 +08:00
WuYongJie
07516303d4 fix(intake): fit long certificate product names 2026-08-20 13:55:35 +08:00
WuYongJie
61c66362bb fix(intake): fit long certificate product names 2026-08-20 13:51:46 +08:00
jingchuan
633b7a37dd feat(supply-chain): filter termination feedback by creator 2026-08-20 11:00:41 +08:00
jingchuan
fa32c6bec4 fix(supply-chain): archive missing termination instances 2026-08-20 10:37:22 +08:00
jingchuan
22744b28c8 feat(supply-chain): archive termination reasons 2026-08-20 10:28:25 +08:00
jingchuan
2897f3b0c6 fix(supply-chain): refine terminated task drilldowns 2026-08-19 17:58:54 +08:00
jingchuan
718c6a8886 merge: add terminated task feedback 2026-08-19 16:59:54 +08:00
jingchuan
32fedbe898 feat(supply-chain): add terminated task feedback 2026-08-19 16:58:13 +08:00
WuYongJie
0bf579a6f0 merge: add admin barcode data export 2026-08-19 15:03:41 +08:00
WuYongJie
1dc2c04b20 feat(intake): add admin barcode data export 2026-08-19 15:02:48 +08:00
WuYongJie
e645a71283 merge: align permission nodes and add pricing role 2026-08-19 14:50:38 +08:00
WuYongJie
9a8c25fe24 fix(users): align permission nodes and add pricing role 2026-08-19 14:48:09 +08:00
WuYongJie
8ee0dd21fe feat(users): refine permission management 2026-08-19 13:50:13 +08:00
WuYongJie
c0aab147bf feat(users): refine permission management 2026-08-19 13:44:38 +08:00
jingchuan
52ac60cf29 merge: fix near-overtime display precision 2026-08-19 13:26:14 +08:00
jingchuan
6178d09e31 fix(supply-chain): round near-overtime remaining days 2026-08-19 13:25:45 +08:00
jingchuan
7e772dcfa8 merge: publish timing hover summaries 2026-08-19 13:06:33 +08:00
jingchuan
1f9293cc42 feat(supply-chain): refine timing hover summaries 2026-08-19 13:04:46 +08:00
jingchuan
e2b711c0df merge: include material in procurement timing 2026-08-19 12:01:08 +08:00
jingchuan
94f754c80b fix(supply-chain): include material in procurement timing 2026-08-19 11:11:02 +08:00
WuYongJie
2d1516e621 merge: add product archive scrollbar wheel 2026-08-19 11:06:41 +08:00
WuYongJie
68051dd0bf feat(supply-chain): add product archive scrollbar wheel 2026-08-19 11:05:41 +08:00
WuYongJie
a8184ae024 merge: fix installation demo snapshot resave 2026-08-19 10:40:36 +08:00
WuYongJie
efbf1c1a41 fix(installation-demo): preserve existing snapshots on resave 2026-08-19 10:39:33 +08:00
WuYongJie
66dced1c3f merge: add fee ratio and operating profit to product archive 2026-08-19 10:03:04 +08:00
WuYongJie
6024abe7aa feat(supply-chain): add fee ratio and operating profit 2026-08-19 10:01:21 +08:00
jingchuan
b16320c352 merge: publish supply-chain new product timing drilldown 2026-08-19 09:39:26 +08:00
jingchuan
8cfd3f8292 fix(supply-chain): preserve new product overview entry 2026-08-19 09:37:15 +08:00
jingchuan
053a603308 feat(supply-chain): add running timing department drilldown 2026-08-19 09:34:18 +08:00
WuYongJie
90eeba280d Merge sample progress detail rebuild into main
Includes only f8555a49: the approved sample progress detail table and its API, persistence, navigation, history, and migration changes.
2026-08-19 09:00:52 +08:00
WuYongJie
f8555a4946 feat: rebuild sample progress detail
Implement the complete sample progress detail table from the approved production baseline, including source-derived timing fields, editable sampling fields, review links, change history, pagination and supply-chain navigation updates.
2026-08-19 09:00:13 +08:00
jingchuan
b840de8d55 merge: supply-chain timing departments into main 2026-08-18 19:12:05 +08:00
jingchuan
641a162675 merge: completed timing department analysis 2026-08-18 19:06:19 +08:00
jingchuan
d507aa08f7 feat(supply-chain): drill into department overtime flows 2026-08-18 19:02:08 +08:00
jingchuan
90e58ecaf7 feat(supply-chain): add completed timing department metrics 2026-08-18 18:49:20 +08:00
WuYongJie
f0730ec03d fix: stabilize installation shares and restore supply-chain home navigation 2026-08-18 18:17:02 +08:00
WuYongJie
a51936ed98 Merge remote-tracking branch 'origin/main' into codex/wuyongjie-bom-source-retry-release 2026-08-18 17:01:56 +08:00
WuYongJie
91026fccd7 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-source-retry-release 2026-08-18 16:55:27 +08:00
jingchuan
45f8aaf449 Merge shared timing image fix into main 2026-08-18 16:52:17 +08:00
jingchuan
6b6564e59a Merge timing detail image preview fix 2026-08-18 16:52:01 +08:00
jingchuan
58aa36720b fix(supply-chain): enlarge timing detail images 2026-08-18 16:50:26 +08:00
WuYongJie
5e20f8178f Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-source-retry-release 2026-08-18 16:48:10 +08:00
WuYongJie
239cae7b5a Merge branch 'codex/wuyongjie-bom-source-retry' into codex/wuyongjie-bom-source-retry-integration 2026-08-18 16:43:22 +08:00
WuYongJie
f2599a5a37 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-source-retry 2026-08-18 16:41:22 +08:00
WuYongJie
2db8e39666 fix(oa): keep page module exports Next-compatible 2026-08-18 16:39:30 +08:00
WuYongJie
e267426f5e Merge BOM-only session redirect fix 2026-08-18 16:33:51 +08:00
WuYongJie
cfe9ce4e7b fix(auth): preserve BOM-only session source 2026-08-18 16:32:58 +08:00
WuYongJie
e9cd4e312a fix(auth): preserve BOM-only session source 2026-08-18 16:32:30 +08:00
WuYongJie
8d09d19206 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-source-retry 2026-08-18 16:16:48 +08:00
WuYongJie
d3b4d56934 fix(bom): clarify source read failures and allow retry 2026-08-18 16:16:42 +08:00
WuYongJie
f5cc1764cc merge: release workspace switcher 2026-08-18 16:09:25 +08:00
WuYongJie
6f10780bc5 feat(auth): add BOM-costing-only account access 2026-08-18 15:59:08 +08:00
WuYongJie
d264efc2e7 feat(auth): add BOM-costing-only account access 2026-08-18 15:57:39 +08:00
WuYongJie
63b37b564a feat(auth): add BOM-costing-only account access 2026-08-18 15:57:05 +08:00
WuYongJie
3ba90b83e0 feat(navigation): add workspace switcher 2026-08-18 15:56:01 +08:00
WuYongJie
1ffee0d6cc merge: release installation demo part material workflow 2026-08-18 15:19:19 +08:00
WuYongJie
ec5875367a feat(installation-demo): add part material editing workflow 2026-08-18 15:16:20 +08:00
jingchuan
d6e78edaf2 Merge shared supply-chain timing overview into main 2026-08-18 15:02:00 +08:00
jingchuan
75f7b3ac7f Merge supply-chain new product timing overview 2026-08-18 15:01:20 +08:00
jingchuan
df3a8a7894 Merge latest shared mainline before supply-chain timing release 2026-08-18 14:59:34 +08:00
jingchuan
d8d254598b feat(supply-chain): refine timing metrics and detail imagery 2026-08-18 14:57:41 +08:00
jingchuan
b83a95df06 feat(supply-chain): split completed and running timing 2026-08-18 12:52:54 +08:00
jingchuan
20c6433273 fix(supply-chain): use single-select opening brand metric 2026-08-18 09:13:48 +08:00
WuYongJie
033c1058e9 merge: release BOM completion feedback 2026-08-18 00:33:11 +08:00
WuYongJie
86b60103c1 merge: align BOM completion feedback 2026-08-18 00:32:56 +08:00
WuYongJie
51f4c97489 fix(bom-costing): align completed history status 2026-08-18 00:27:14 +08:00
WuYongJie
4a8cbcdcb3 fix(bom-costing): refresh completed run feedback 2026-08-18 00:18:20 +08:00
WuYongJie
c30b094194 merge: synchronize BOM estimate result-first flow 2026-08-17 23:39:56 +08:00
WuYongJie
2be5f59220 merge: preserve BOM estimates after structure review 2026-08-17 23:39:17 +08:00
WuYongJie
c80c06d2f7 fix(bom-costing): preserve estimates after structure review 2026-08-17 23:22:50 +08:00
WuYongJie
4c03c35e8d Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-estimate-release-20260817 2026-08-17 21:21:18 +08:00
WuYongJie
c7da99562d Merge remote-tracking branch 'origin/codex/wuyongjie-bom-1688-realtime-cost' into codex/wuyongjie-bom-estimate-integration-20260817 2026-08-17 21:21:12 +08:00
WuYongJie
f08d4ff5c4 fix(bom-costing): read stored images through object storage 2026-08-17 21:20:56 +08:00
WuYongJie
fece43291b Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-estimate-release-20260817 2026-08-17 21:14:11 +08:00
WuYongJie
e674843455 Merge remote-tracking branch 'origin/codex/wuyongjie-bom-1688-realtime-cost' into codex/wuyongjie-bom-estimate-integration-20260817 2026-08-17 21:13:50 +08:00
WuYongJie
c038bcedbe fix(bom-costing): fetch stored images for realtime estimates 2026-08-17 21:13:23 +08:00
WuYongJie
f64fac7a68 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-estimate-release-20260817 2026-08-17 20:59:43 +08:00
WuYongJie
6f0fdf3c71 Merge remote-tracking branch 'origin/codex/wuyongjie-bom-1688-realtime-cost' into codex/wuyongjie-bom-estimate-integration-20260817 2026-08-17 20:58:59 +08:00
WuYongJie
7f67bd858c Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-1688-realtime-cost 2026-08-17 20:54:52 +08:00
WuYongJie
d3a1049b55 feat(bom-costing): complete unified cost estimates 2026-08-17 20:54:45 +08:00
jingchuan
62e7ea28e4 fix(supply-chain): recognize configured node stage sla 2026-08-17 19:33:38 +08:00
jingchuan
e5e7c3bd07 fix(supply-chain): map review-stage sla 2026-08-17 19:26:58 +08:00
jingchuan
9a17715a21 fix(supply-chain): correct new product overview metrics 2026-08-17 19:19:14 +08:00
jingchuan
41b33c731c fix(oa-board): restore original workflow dashboard entry 2026-08-17 19:00:16 +08:00
jingchuan
156b431dda fix(intake): preserve existing new product dashboard 2026-08-17 18:53:46 +08:00
WuYongJie
fa48bcc48d Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-1688-realtime-cost 2026-08-17 18:48:08 +08:00
WuYongJie
4e892573df fix(bom-costing): sign 1688 image search requests 2026-08-17 18:48:02 +08:00
jingchuan
5ac98a6b82 fix(supply-chain): show product overview in sidebar 2026-08-17 18:46:47 +08:00
jingchuan
5441d1786d feat: add executive overview to supply chain new products 2026-08-17 18:36:57 +08:00
WuYongJie
5cfbcaa5fe feat(installation-demo): refine printable drawing workflow 2026-08-17 14:55:22 +08:00
jingchuan
330cf30ac9 feat: expose new product management dashboard 2026-08-17 13:44:30 +08:00
jingchuan
5af369ef3d feat: add new product management executive dashboard 2026-08-17 13:36:46 +08:00
WuYongJie
0c782238af Merge product archive sales performance into main 2026-08-17 12:09:00 +08:00
WuYongJie
5d60abd57b feat(supply-chain): sync product archive sales performance 2026-08-17 12:07:57 +08:00
jingchuan
ab66b20893 Merge OA latest child flow selection into main 2026-08-17 11:40:00 +08:00
jingchuan
dded0237d0 Merge OA latest child flow selection 2026-08-17 11:39:18 +08:00
jingchuan
fb4fa3efca fix(oa-board): prefer latest restarted child flow 2026-08-17 11:38:37 +08:00
WuYongJie
6a3bd0fa22 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-1688-realtime-cost 2026-08-17 10:29:00 +08:00
jingchuan
9929f6a3c6 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' 2026-08-17 09:14:56 +08:00
jingchuan
04379751f6 Merge remote-tracking branch 'origin/codex/jingchuan-unified-table-stage-fix' into codex/xuxiake-mainline 2026-08-17 09:14:41 +08:00
jingchuan
8c2776ec1d Rename sample progress detail view 2026-08-17 09:14:25 +08:00
jingchuan
54548071ad Merge remote-tracking branch 'origin/codex/xuxiake-mainline' 2026-08-16 20:33:41 +08:00
jingchuan
e24adfa492 Merge remote-tracking branch 'origin/codex/jingchuan-unified-table-stage-fix' into codex/xuxiake-mainline 2026-08-16 20:33:28 +08:00
jingchuan
54af0add08 Rename unified table to new product progress summary 2026-08-16 20:33:08 +08:00
jingchuan
d8dd85d380 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' 2026-08-16 20:21:21 +08:00
jingchuan
867de78fe7 Merge remote-tracking branch 'origin/codex/jingchuan-unified-table-stage-fix' into codex/xuxiake-mainline 2026-08-16 20:21:06 +08:00
jingchuan
cd4b6f7023 fix(supply-chain): move timing view toggle to table header 2026-08-16 20:19:52 +08:00
jingchuan
ec687f4788 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' 2026-08-16 19:57:21 +08:00
jingchuan
e0fb0f22a4 Merge remote-tracking branch 'origin/codex/jingchuan-unified-table-stage-fix' into codex/xuxiake-mainline 2026-08-16 19:57:10 +08:00
jingchuan
816d2ff544 fix(supply-chain): correct unified table stage groups 2026-08-16 19:56:54 +08:00
jingchuan
59a1449d73 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' 2026-08-16 17:26:06 +08:00
jingchuan
7b5fea54c5 fix(supply-chain): refine unified table layout 2026-08-16 17:25:54 +08:00
jingchuan
bd33cfc936 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' 2026-08-16 16:35:05 +08:00
jingchuan
b69f1c8eb6 Merge branch 'codex/jingchuan-unified-table-groups' into codex/xuxiake-mainline 2026-08-16 16:34:48 +08:00
jingchuan
016280c1a3 feat(supply-chain): group unified table columns 2026-08-15 18:28:54 +08:00
jingchuan
f78f8c1cf4 Merge supply chain new product views 2026-08-15 17:41:08 +08:00
jingchuan
7a1d05b7db Merge sample progress and supply chain new product views
# Conflicts:
#	furniture-3d-system/src/app/supply-chain/config/supply-chain-menu.test.ts
2026-08-15 17:40:03 +08:00
jingchuan
ceda595797 feat(supply-chain): add new product data views 2026-08-15 17:27:50 +08:00
WuYongJie
5082213a11 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-1688-realtime-cost 2026-08-15 17:07:11 +08:00
WuYongJie
5b7435f218 Merge supply chain business permissions 2026-08-15 17:06:57 +08:00
WuYongJie
57b502e1c3 feat(bom-costing): prioritize realtime 1688 procurement estimates 2026-08-15 17:06:46 +08:00
WuYongJie
65c1cd6d93 Merge supply chain business permissions 2026-08-15 17:05:53 +08:00
WuYongJie
465a92612f feat(supply-chain): add business permission controls 2026-08-15 17:05:19 +08:00
WuYongJie
8ffbea63ce Merge installation demo drawing generator 2026-08-15 16:56:38 +08:00
WuYongJie
8b1cb357ea Merge installation demo drawing generator 2026-08-15 16:55:43 +08:00
WuYongJie
d711742300 feat(installation-demo): add printable installation drawings 2026-08-15 16:55:19 +08:00
WuYongJie
9142d24612 Merge development mainline for BOM GPT reasoning fix 2026-08-15 16:11:05 +08:00
WuYongJie
72f2519e3a Merge BOM GPT reasoning fix 2026-08-15 16:10:34 +08:00
WuYongJie
d0eb2d4bd4 fix(bom-costing): use supported GPT-5 reasoning effort 2026-08-15 16:09:56 +08:00
WuYongJie
2972a8fb7a Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-ai-release-20260815 2026-08-15 15:50:51 +08:00
WuYongJie
d2c6f0749a Merge remote-tracking branch 'origin/codex/wuyongjie-bom-ai-estimation' into codex/wuyongjie-bom-ai-integration-20260815 2026-08-15 15:46:40 +08:00
WuYongJie
a0029c7dec feat(bom-costing): estimate unresolved lines with multimodal model 2026-08-15 15:42:33 +08:00
WuYongJie
07008eeeb7 fix(intake): keep patent workspace on full product source 2026-08-14 15:05:27 +08:00
WuYongJie
16b43cba11 feat(intake): paginate product patent ledger 2026-08-14 14:09:23 +08:00
WuYongJie
c3566c4b83 Merge remote-tracking branch 'origin/main' into codex/local-3001-integration 2026-08-14 13:08:16 +08:00
WuYongJie
026ea66c62 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/local-3001-integration 2026-08-14 13:06:32 +08:00
WuYongJie
4511c0ad51 fix: include selected patent applications in detail 2026-08-14 12:10:33 +08:00
WuYongJie
c0f9c147fa merge: release flip shoe cabinet costing fix 2026-08-14 11:49:25 +08:00
WuYongJie
ecce8a1a28 merge: fix flip shoe cabinet costing 2026-08-14 11:41:34 +08:00
WuYongJie
7c53b42199 fix(bom-costing): classify flip shoe cabinets correctly 2026-08-14 11:37:57 +08:00
WuYongJie
8016482127 merge(main): release product progress completion filters 2026-08-14 10:09:36 +08:00
WuYongJie
413debe66b merge(intake): add product progress completion filters 2026-08-14 10:08:29 +08:00
WuYongJie
f8d877c88c feat(intake): filter product progress by completion time 2026-08-14 10:07:34 +08:00
WuYongJie
c6c2790354 merge(main): retire sku operator archive source 2026-08-14 09:42:25 +08:00
WuYongJie
b2e672139e refactor(supply-chain): retire sku operator source 2026-08-14 09:40:23 +08:00
WuYongJie
0d6d080687 refactor(supply-chain): retire sku operator source 2026-08-14 09:39:20 +08:00
WuYongJie
ec38c6e64a merge(main): release on-demand product archive images 2026-08-14 08:55:33 +08:00
WuYongJie
d786515831 fix(supply-chain): cache material images on SPU lookup 2026-08-14 08:54:21 +08:00
WuYongJie
032b609f1b fix(supply-chain): cache material images on SPU lookup 2026-08-14 08:53:05 +08:00
WuYongJie
502d85decb merge(main): release archive group header alignment 2026-08-13 14:53:46 +08:00
jingchuan
88fbdbacd2 fix(sample-progress): match flow detail typography and filter defaults 2026-08-13 14:51:25 +08:00
WuYongJie
42ad3299a3 fix(supply-chain): center archive group headers 2026-08-13 14:45:24 +08:00
WuYongJie
bdb21cd824 fix(supply-chain): center archive group headers 2026-08-13 14:39:27 +08:00
jingchuan
a5c0a4f83d fix(sample-progress): preserve selected calendar dates 2026-08-13 14:37:36 +08:00
jingchuan
7371676f6f fix(sample-progress): restrict deletion and optional sample fields 2026-08-13 14:11:01 +08:00
WuYongJie
6acbc75a09 merge(main): release BOM responsive inspector and sample progress 2026-08-13 14:03:11 +08:00
WuYongJie
7b46654cb7 merge(sample-progress): preserve latest production flow 2026-08-13 14:01:36 +08:00
jingchuan
2ab42d68e1 fix(sample-progress): compact header and filter overdue items 2026-08-13 13:55:46 +08:00
WuYongJie
2199376f11 Merge branch 'codex/jingchuan-sample-progress-main' into main 2026-08-13 13:42:14 +08:00
WuYongJie
4192f27b17 Merge branch 'codex/xuxiake-mainline' into main 2026-08-13 13:40:10 +08:00
WuYongJie
ea834d8dea Merge branch 'codex/wuyongjie-bom-responsive-inspector' into codex/xuxiake-mainline 2026-08-13 13:39:34 +08:00
jingchuan
f34d938e2e fix(sample-progress): use fixed scrollable table workspace 2026-08-13 13:38:02 +08:00
WuYongJie
f6b28ecd63 fix(bom-costing): keep recalculation controls in view 2026-08-13 13:30:06 +08:00
jingchuan
ade0751701 fix(sample-progress): calculate dwell time and anomalies 2026-08-13 13:25:38 +08:00
WuYongJie
2ba8ab0f05 Merge commit '62017377' into codex/wuyongjie-promote-bom-optional-20260813 2026-08-13 12:38:47 +08:00
WuYongJie
62017377ad fix(bom): preserve resolved primary material 2026-08-13 12:36:47 +08:00
WuYongJie
2905c42455 Merge remote-tracking branch 'origin/codex/jingchuan-sample-progress-main' into codex/wuyongjie-promote-bom-optional-20260813 2026-08-13 12:15:27 +08:00
jingchuan
ce808f8c07 feat(sample-progress): allow no-sample status 2026-08-13 12:14:43 +08:00
WuYongJie
915450a748 Merge remote-tracking branch 'origin/codex/jingchuan-sample-progress-main' into codex/wuyongjie-promote-bom-optional-20260813 2026-08-13 12:11:31 +08:00
WuYongJie
adae1ac0f1 Merge remote-tracking branch 'origin/codex/wuyongjie-bom-category-skills-v7' into codex/wuyongjie-promote-bom-optional-20260813 2026-08-13 12:11:10 +08:00
jingchuan
db844e6126 fix(sample-progress): derive sample status from OA stage 2026-08-13 12:09:44 +08:00
WuYongJie
cdb807dcd5 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-category-skills-v7 2026-08-13 12:08:42 +08:00
WuYongJie
97ade48cf8 fix(bom): preserve trusted slot geometry 2026-08-13 12:08:13 +08:00
WuYongJie
af97ea7074 merge: sync product archive operators 2026-08-13 12:02:14 +08:00
WuYongJie
138155b04a feat(supply-chain): sync product archive operators 2026-08-13 12:01:02 +08:00
WuYongJie
609e675659 merge: preserve latest sample progress stage alignment 2026-08-13 11:02:08 +08:00
WuYongJie
218a7d91a6 merge: preserve deployed sample progress updates 2026-08-13 11:00:32 +08:00
jingchuan
5318a30346 fix(sample-progress): align board with current OA stage 2026-08-13 10:57:13 +08:00
WuYongJie
c21b384050 merge: add storage rack and table v2 skills 2026-08-13 10:55:04 +08:00
WuYongJie
acd22bbffa feat(bom): add storage rack and table v2 skills 2026-08-13 10:54:26 +08:00
jingchuan
1433428cf7 fix(sample-progress): show current OA sample stage count 2026-08-13 10:38:22 +08:00
WuYongJie
5c05df5855 fix(bom): preserve sku material and module geometry 2026-08-13 09:56:21 +08:00
WuYongJie
35384031c6 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-category-skills-v7 2026-08-13 09:28:12 +08:00
WuYongJie
fee8bdb0a8 fix(bom): harden category skill costing contracts 2026-08-13 09:27:59 +08:00
jingchuan
e2d739d1d4 fix(sample-progress): count only active managed samples 2026-08-13 09:08:33 +08:00
WuYongJie
f12b68ff56 Merge remote-tracking branch 'origin/codex/wuyongjie-bom-category-skills-v7' into codex/wuyongjie-promote-bom-optional-20260813 2026-08-13 06:32:43 +08:00
WuYongJie
869f0e6922 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-category-skills-v7 2026-08-13 06:30:22 +08:00
WuYongJie
76421163c7 fix(bom-costing): enforce exclusive sku structure 2026-08-13 06:30:01 +08:00
WuYongJie
439b427259 Merge remote-tracking branch 'origin/codex/wuyongjie-bom-category-skills-v7' into codex/wuyongjie-promote-bom-optional-20260813 2026-08-13 04:49:02 +08:00
WuYongJie
6f8fc02b2f Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-category-skills-v7 2026-08-13 04:46:13 +08:00
WuYongJie
b12d1eeeb3 fix(bom-costing): preserve sku evidence and size scaling 2026-08-13 04:45:31 +08:00
WuYongJie
0c9491a370 merge: promote BOM optional SKU structure fix 2026-08-13 02:52:34 +08:00
WuYongJie
37430946d8 fix(bom-costing): isolate optional SKU structures 2026-08-13 02:51:12 +08:00
WuYongJie
eb0a6ad499 fix(bom-costing): restore layered category estimates 2026-08-13 01:18:19 +08:00
WuYongJie
de048075ef Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-category-skills-v7
# Conflicts:
#	furniture-3d-system/src/server/services/__tests__/intake-non-integrated-product.service.test.ts
#	furniture-3d-system/src/server/services/intake-non-integrated-product.service.ts
2026-08-12 21:06:18 +08:00
WuYongJie
c87266c2c7 fix(intake): bound virtual inventory processing 2026-08-12 20:51:03 +08:00
WuYongJie
a51ec8a162 fix(bom-costing): guard fact snapshot fingerprints 2026-08-12 20:49:07 +08:00
WuYongJie
14915ef1f2 fix(supply-chain): resolve new-product images by barcode 2026-08-12 18:10:08 +08:00
jingchuan
7a25001d6b fix(sample-progress): retain reached cancelled sample nodes 2026-08-12 17:48:52 +08:00
jingchuan
fd0354f1d0 fix(sample-progress): sync all OA flow statuses 2026-08-12 17:34:26 +08:00
WuYongJie
ae9ae0fcae Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wu-product-development-barcode-print-20260812 2026-08-12 17:32:37 +08:00
WuYongJie
a36b5ede46 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wu-product-development-barcode-print-20260812 2026-08-12 17:29:44 +08:00
WuYongJie
e2a725ed58 feat(barcode-print): add barcode QR to Sofia carton label 2026-08-12 17:29:12 +08:00
WuYongJie
0b4f903325 fix(intake): require directory selections for suppliers and warehouses 2026-08-12 17:22:41 +08:00
jingchuan
8d7ab94cac fix(sample-progress): sync cancelled and terminated flows 2026-08-12 16:54:33 +08:00
jingchuan
27b16a9ce6 fix(sample-progress): refine board fields and unique codes 2026-08-12 16:20:53 +08:00
jingchuan
09dd4a1730 fix(sample-progress): normalize compact delivery dates 2026-08-12 16:04:59 +08:00
jingchuan
fc4763a8c9 fix(sample-progress): use configured prisma client for import 2026-08-12 16:03:17 +08:00
jingchuan
0b2bc3ce5a Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/jingchuan-sample-progress-main 2026-08-12 15:46:52 +08:00
jingchuan
73005fdd9b feat(sample-progress): import sampling data and completion time 2026-08-12 15:43:53 +08:00
jingchuan
c2e75dda3e fix(sample-progress): sort by OA arrival time 2026-08-12 14:54:15 +08:00
jingchuan
ac146f8879 Merge remote-tracking branch 'origin/main' into codex/jingchuan-sample-progress-main 2026-08-12 14:43:59 +08:00
jingchuan
4f1372668f fix(sample-progress): simplify summary and expand designer 2026-08-12 14:43:29 +08:00
WuYongJie
344e1b108a Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-installation-demo-integration-20260812 2026-08-12 14:25:56 +08:00
WuYongJie
a17844604c Merge commit '082d5cfc' into codex/xuxiake-installation-demo-integration-20260812 2026-08-12 14:24:23 +08:00
WuYongJie
082d5cfcee feat(installation-demo): complete sharing catalog and tool motion 2026-08-12 14:17:40 +08:00
WuYongJie
bdc8f393a8 fix(bom-costing): extend structure progress transaction 2026-08-12 14:16:31 +08:00
WuYongJie
76230f9f34 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-category-skills-v7 2026-08-12 13:09:36 +08:00
WuYongJie
17dd830976 fix(bom-costing): harden category skill costing closure 2026-08-12 13:09:26 +08:00
WuYongJie
70d0e4e5d9 fix(supply-chain): move price type to data group 2026-08-12 11:24:06 +08:00
jingchuan
432cd9fc4f feat(sample-progress): enrich OA tracking board 2026-08-12 11:07:49 +08:00
WuYongJie
944b5b0a58 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-category-skills-v7 2026-08-11 18:33:33 +08:00
WuYongJie
7040fd2d03 feat(bom-costing): add category skill engine v7 2026-08-11 18:33:04 +08:00
jingchuan
a040b015b5 fix(oa-board): align export default statuses 2026-08-11 18:08:32 +08:00
jingchuan
ad9afabc49 fix(sample-progress): exclude OA drafts from overdue metrics 2026-08-11 17:45:23 +08:00
jingchuan
2c201674a4 Merge remote-tracking branch 'origin/main' into codex/jingchuan-sample-progress-main 2026-08-11 17:36:57 +08:00
jingchuan
ba9adb0db4 fix(sample-progress): sync OA data and embed board tab 2026-08-11 17:36:57 +08:00
WuYongJie
7558939482 feat(supply-chain): sync supplier regions 2026-08-11 17:19:29 +08:00
WuYongJie
31ef235c80 fix(intake): keep opening name out of SKU specs 2026-08-11 17:02:31 +08:00
jingchuan
37851f0f04 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/jingchuan-sample-progress-main 2026-08-11 16:32:03 +08:00
jingchuan
20a450c4bd feat(intake): reorder OA tabs and add sample flow links 2026-08-11 16:30:00 +08:00
WuYongJie
0ece1c38dc fix(supply-chain): populate warehouse config and pin archive actions 2026-08-11 16:14:36 +08:00
WuYongJie
179040b1ab Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-package-set-count-fix-20260811-1548 2026-08-11 16:12:21 +08:00
WuYongJie
6388b8f37a fix(intake): keep package set counts consistent 2026-08-11 16:05:00 +08:00
jingchuan
ac9deefe85 merge: release sample progress board 2026-08-11 16:02:00 +08:00
jingchuan
1fa15cef79 merge: add sample progress board 2026-08-11 16:00:59 +08:00
jingchuan
23c20714dd feat(sample-progress): integrate board into OA workspace 2026-08-11 16:00:17 +08:00
jingchuan
c031c36f8f feat(sample-progress): add sampling progress board module 2026-08-11 15:49:58 +08:00
WuYongJie
aa646a795a fix(supply-chain): isolate product archive column settings 2026-08-11 14:59:06 +08:00
jingchuan
6a19e30527 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/jingchuan-brand-shipment-main-merge 2026-08-11 14:56:35 +08:00
jingchuan
43d8d43e16 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/jingchuan-brand-shipment-main-merge 2026-08-11 14:56:23 +08:00
jingchuan
b2fb4165b5 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/jingchuan-brand-shipment-todo 2026-08-11 14:54:22 +08:00
jingchuan
bdf20c2064 feat(oa-board): remind brand planning before shipment 2026-08-11 14:49:28 +08:00
WuYongJie
c86367c6b0 fix(intake): keep product type filters readable 2026-08-11 14:33:00 +08:00
jingchuan
4dcbed4c10 feat(oa-board): track unconfirmed completion followups 2026-08-11 13:46:32 +08:00
WuYongJie
199228241a fix(intake): save product tags and support all product types 2026-08-11 13:42:33 +08:00
WuYongJie
63ca46c030 fix(intake): save product tags and support all product types 2026-08-11 13:38:28 +08:00
WuYongJie
10ce1696dc fix(supply-chain): sync product development tags 2026-08-11 13:01:54 +08:00
WuYongJie
728495143e fix(supply-chain): sync product development tags 2026-08-11 12:52:30 +08:00
WuYongJie
20fee3b7ba merge: fix barcode certificate QR codes 2026-08-11 12:31:55 +08:00
WuYongJie
5c547dd25f fix(barcode-print): sync certificate QR codes 2026-08-11 12:31:52 +08:00
jingchuan
981df46e3f feat(oa-board): add monthly flow analysis 2026-08-11 12:06:51 +08:00
jingchuan
b24328f4f8 feat(oa-board): add daily stage report snapshots 2026-08-11 11:46:58 +08:00
WuYongJie
83a4d456b5 merge: release supply chain archive persistence 2026-08-11 10:46:55 +08:00
WuYongJie
be247f5a4b Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/tmp-supply-chain-mainline-20260811 2026-08-11 10:40:13 +08:00
WuYongJie
b47f745b83 feat(supply-chain): persist archive enrichment and image cache 2026-08-11 10:31:12 +08:00
jingchuan
35b9c5e080 feat(oa-board): read DingTalk source from JSReport replica 2026-08-11 10:17:18 +08:00
WuYongJie
759cba00d7 Merge remote-tracking branch 'origin/codex/wu-product-pricing-inventory-status' into codex/tmp-main-merge-product-pricing-20260811 2026-08-11 09:06:39 +08:00
WuYongJie
6858161f3f fix: show fixed virtual inventory task status 2026-08-11 08:58:37 +08:00
WuYongJie
7bfb69405d release: honor unitless internal dimensions 2026-08-11 05:59:32 +08:00
WuYongJie
814a7ed4be merge: honor unitless internal dimensions 2026-08-11 05:59:14 +08:00
WuYongJie
9383233130 fix(bom): honor unitless internal dimensions 2026-08-11 05:58:26 +08:00
WuYongJie
27ed15f55a release: isolate oversized board packing 2026-08-11 05:42:04 +08:00
WuYongJie
7b95e5af33 merge: isolate oversized board packing 2026-08-11 05:41:14 +08:00
WuYongJie
5bd554f762 fix(bom): isolate oversized board packing 2026-08-11 05:40:24 +08:00
WuYongJie
c1985a2396 release: area-scaled BOM fallback v6 2026-08-11 05:10:27 +08:00
WuYongJie
c538e175bc merge: area-scaled BOM fallback v6 2026-08-11 05:10:21 +08:00
WuYongJie
24a5043d15 fix(bom): scale fallback costs by board area 2026-08-11 05:10:02 +08:00
WuYongJie
0ba596a7c7 release: deterministic BOM blueprint v6 2026-08-11 04:39:10 +08:00
WuYongJie
bcf04b7438 merge: deterministic BOM blueprint v6 2026-08-11 04:38:52 +08:00
WuYongJie
d5e1f2af99 fix(bom): stabilize structure blueprint costing 2026-08-11 04:38:14 +08:00
WuYongJie
42095c6dab release: BOM tuple synthesis v6 2026-08-11 03:28:38 +08:00
WuYongJie
67ce313c9a merge: BOM tuple synthesis v6 2026-08-11 03:28:32 +08:00
WuYongJie
4f1571d508 fix(bom): compact sku synthesis into tuples 2026-08-11 03:27:15 +08:00
WuYongJie
871fd38bb5 release: BOM non-stream synthesis v6 2026-08-11 02:50:38 +08:00
WuYongJie
3c8d7f0801 merge: BOM non-stream synthesis v6 2026-08-11 02:50:17 +08:00
WuYongJie
7518041829 fix(bom-costing): avoid stalled synthesis streams 2026-08-11 02:49:47 +08:00
WuYongJie
164192bae7 release: BOM compact structure synthesis v6 2026-08-11 02:21:31 +08:00
WuYongJie
b9fd690e5d merge: BOM compact structure synthesis v6 2026-08-11 02:21:08 +08:00
WuYongJie
efbb08b7a5 fix(bom-costing): compact structure synthesis 2026-08-11 02:20:39 +08:00
WuYongJie
ebf0eca3b7 merge: publish complete BOM JSON stream handling 2026-08-11 01:25:16 +08:00
WuYongJie
5416bed96e merge: accept complete BOM JSON streams 2026-08-11 01:24:53 +08:00
WuYongJie
e945eff091 fix(bom-costing): accept completed json streams 2026-08-11 01:24:10 +08:00
WuYongJie
aefff98cad merge: publish BOM finish-reason stream fix 2026-08-11 00:38:32 +08:00
WuYongJie
be26f6d795 merge: finish completed BOM response streams 2026-08-11 00:38:25 +08:00
WuYongJie
f1a7b9c941 fix(bom-costing): finish completed response streams 2026-08-11 00:37:57 +08:00
WuYongJie
67a68067a0 merge: publish scoped BOM structure synthesis 2026-08-11 00:07:57 +08:00
WuYongJie
707feb213a merge: scope BOM structure synthesis requests 2026-08-11 00:06:57 +08:00
WuYongJie
2afdbb9ca7 fix(bom-costing): scope structure synthesis requests 2026-08-11 00:06:32 +08:00
WuYongJie
f2df3e53dc merge: publish BOM stream completion fix 2026-08-10 23:32:51 +08:00
WuYongJie
075fb38096 merge: fix completed BOM model streams 2026-08-10 23:32:14 +08:00
WuYongJie
dbf48330bd fix(bom-costing): stop completed model streams promptly 2026-08-10 23:27:39 +08:00
WuYongJie
7b220f824e merge: publish BOM structure blueprint v6 2026-08-10 22:16:31 +08:00
WuYongJie
e4554d86cf merge: BOM structure blueprint v6 2026-08-10 22:15:08 +08:00
WuYongJie
7a84b8fde0 feat(bom-costing): add v6 structure blueprint and batch cutting 2026-08-10 22:12:59 +08:00
jingchuan
fe48bbb181 feat(oa-board): add private daily report 2026-08-10 18:23:51 +08:00
WuYongJie
50317a3c0f merge: supply chain mainline into main 2026-08-10 17:28:10 +08:00
jingchuan
127730c5cf fix(oa-board): add manual termination overrides 2026-08-10 17:26:47 +08:00
WuYongJie
cbbf2fbb21 feat(supply-chain): map archive prices by barcode 2026-08-10 17:21:51 +08:00
WuYongJie
b9ec3511ac feat(supply-chain): add interactive archive dashboard 2026-08-10 16:25:16 +08:00
jingchuan
f55bddb017 fix(oa-board): honor manual termination overrides 2026-08-10 16:18:35 +08:00
jingchuan
9a1e588af3 fix(oa-board): terminate withdrawn XP2026070100000242 2026-08-10 16:17:11 +08:00
WuYongJie
c1ba901f7a feat(supply-chain): map warehouse attributes from JST 2026-08-10 16:05:13 +08:00
WuYongJie
cf74985e56 merge: publish BOM thickness evidence priority 2026-08-10 15:49:20 +08:00
WuYongJie
80c4dcc91b merge: release BOM thickness evidence priority 2026-08-10 15:48:48 +08:00
WuYongJie
c632402f2d Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-v5-runtime-fix 2026-08-10 15:44:46 +08:00
WuYongJie
2e3168bb99 fix(bom-costing): prefer product-wide explicit thickness facts 2026-08-10 15:42:16 +08:00
WuYongJie
d630a73433 fix(bom-costing): classify observed and estimated board thickness 2026-08-10 15:26:41 +08:00
WuYongJie
d90e6754b5 fix(bom-costing): bound history memory and price material substitutions 2026-08-10 14:54:09 +08:00
WuYongJie
cdf95640ae Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wu-product-development-type-filter-main-release-20260810 2026-08-10 14:38:59 +08:00
WuYongJie
e7799331e0 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wu-product-development-type-filter 2026-08-10 14:37:50 +08:00
WuYongJie
dab6002b5c feat(intake): add product type filter 2026-08-10 14:37:34 +08:00
WuYongJie
b3d947fc16 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-release-main-bom-v5-20260810 2026-08-10 14:00:30 +08:00
WuYongJie
b0a9cd029f Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-release-bom-v5-20260810 2026-08-10 13:57:52 +08:00
WuYongJie
a783e4c94b feat(supply-chain): map stock monitor daily sales 2026-08-10 13:55:58 +08:00
WuYongJie
2ca9d51339 Merge remote-tracking branch 'origin/codex/wuyongjie-bom-cost-v5-acceptance-fix' into codex/wuyongjie-release-bom-v5-20260810 2026-08-10 13:50:45 +08:00
WuYongJie
218941d97b fix(bom-costing): constrain history to product structure 2026-08-10 13:50:20 +08:00
WuYongJie
ff771b7bba feat(supply-chain): configure warehouse archive fields 2026-08-10 13:16:22 +08:00
WuYongJie
b010bd1355 merge: keep BOM estimates when synthesis is unavailable 2026-08-10 13:12:49 +08:00
WuYongJie
2dbe903c64 fix(bom-costing): price from saved facts on synthesis outage 2026-08-10 13:12:26 +08:00
WuYongJie
2f0c9a37e6 merge: improve BOM v5 production estimates 2026-08-10 12:59:54 +08:00
WuYongJie
22403f5a45 fix(bom-costing): select comparable whole BOM profiles 2026-08-10 12:59:27 +08:00
WuYongJie
d8a9be3d00 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-release-main-bom-v5-20260810
# Conflicts:
#	furniture-3d-system/src/server/services/__tests__/oa-board-sync.service.test.ts
#	furniture-3d-system/src/server/services/oa-board-sync.service.ts
2026-08-10 12:24:37 +08:00
WuYongJie
80179df9fc Merge remote-tracking branch 'origin/codex/wuyongjie-bom-cost-dynamic-20260807' into codex/wuyongjie-release-bom-v5-20260810 2026-08-10 12:18:10 +08:00
WuYongJie
c353757d6b Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-cost-dynamic-20260807 2026-08-10 12:14:30 +08:00
WuYongJie
9f368104b5 fix(oa-board): pass pending pricing stage to metrics 2026-08-10 12:14:11 +08:00
WuYongJie
d0b9b22879 feat(jushuitan): add fixed virtual inventory queue 2026-08-10 12:08:51 +08:00
WuYongJie
81b1453db5 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-cost-dynamic-20260807 2026-08-10 12:08:06 +08:00
WuYongJie
d511fd9b9e feat(jushuitan): add fixed virtual inventory queue 2026-08-10 12:08:03 +08:00
WuYongJie
3ea61a2f07 feat(bom-costing): add layered v5 estimates 2026-08-10 12:08:01 +08:00
jingchuan
e869d32594 fix(oa-board): track pending pricing SLA 2026-08-10 11:45:10 +08:00
jingchuan
3c5c38e72b fix(oa-board): track pending pricing SLA 2026-08-10 11:34:35 +08:00
WuYongJie
8dda70ecdd Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-cost-dynamic-20260807 2026-08-10 11:04:17 +08:00
WuYongJie
b9a398830f perf(supply-chain): batch product archive image loading 2026-08-10 11:02:52 +08:00
WuYongJie
772c6813ed fix(bom-costing): restore detail slices and correct cost semantics 2026-08-10 10:43:00 +08:00
WuYongJie
ead86d523e feat(bom-costing): add auditable dynamic company cost engine 2026-08-09 14:23:45 +08:00
WuYongJie
a77c7a3296 merge: supply chain archive updates 2026-08-07 18:10:20 +08:00
WuYongJie
b87bb1ccb4 feat(supply-chain): add stock monitor fields 2026-08-07 18:09:56 +08:00
WuYongJie
c3f9ecbafd fix(supply-chain): backfill archive source images 2026-08-07 17:44:28 +08:00
WuYongJie
8b53afa468 fix(supply-chain): backfill archive source images 2026-08-07 17:43:34 +08:00
WuYongJie
28901d34b4 feat(supply-chain): enrich archive supplier profiles 2026-08-07 17:42:56 +08:00
WuYongJie
3c1983352a merge: release drawing and video archive media 2026-08-07 16:17:19 +08:00
WuYongJie
a69da95217 feat(supply-chain): import archive drawing and video media 2026-08-07 16:14:51 +08:00
WuYongJie
eaf7b9ecca feat(supply-chain): resolve archive images from material library 2026-08-07 15:56:36 +08:00
WuYongJie
e3047c8e6e feat(supply-chain): persist archive views and add multiselect filters 2026-08-07 14:38:18 +08:00
WuYongJie
e7f07a451c feat(supply-chain): add full JST archive sync 2026-08-07 13:02:22 +08:00
WuYongJie
b1566874e1 feat(supply-chain): add full JST archive sync 2026-08-07 13:01:47 +08:00
WuYongJie
2e87027672 fix(db): make JST archive source migration idempotent 2026-08-07 12:38:21 +08:00
WuYongJie
2a483ea415 fix(db): make JST archive source migration idempotent 2026-08-07 12:38:02 +08:00
WuYongJie
a1972a0103 merge: release supply chain product archive
# Conflicts:
#	furniture-3d-system/src/app/intake/oa-board/OaBoardClient.tsx
#	furniture-3d-system/src/server/services/__tests__/oa-board.service.test.ts
#	furniture-3d-system/src/server/services/oa-board.service.ts
2026-08-07 12:22:11 +08:00
WuYongJie
4b5affacd0 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/tmp-supply-chain-archive-snapshot-20260804 2026-08-07 12:18:20 +08:00
WuYongJie
2bd31cc06b feat(supply-chain): complete product archive data pipeline 2026-08-07 12:17:47 +08:00
WuYongJie
1910c2581e fix(intake): expose CJ0267 installation demo 2026-08-07 11:55:13 +08:00
WuYongJie
501f561eae fix(intake): expose CJ0267 installation demo 2026-08-07 11:53:32 +08:00
jingchuan
16a70d2920 feat(oa-board): track production stage overtime 2026-08-06 18:44:01 +08:00
jingchuan
ddb153f0d7 fix(oa-board): track pending first-order SLA 2026-08-06 17:39:54 +08:00
jingchuan
41251e2223 fix(oa-board): separate overall and stage overtime 2026-08-06 17:02:04 +08:00
WuYongJie
f7885fa6ec fix(work-record): prepend newly added records
(cherry picked from commit 8a999c29dcc9d07202c6b685f58c8fb386ed04a7)
2026-08-06 16:31:18 +08:00
WuYongJie
8a999c29dc fix(work-record): prepend newly added records 2026-08-06 16:29:28 +08:00
jingchuan
f047d6b418 fix(oa-board): allow partial timing metrics 2026-08-06 15:55:04 +08:00
jingchuan
7914b07996 fix(oa-board): unify stage overtime followups 2026-08-06 15:45:23 +08:00
WuYongJie
5b0f98264c fix(deploy): align intake sku schema before release 2026-08-06 15:17:20 +08:00
WuYongJie
929b0eacd3 merge: 索菲亚合格证/箱贴模板修复 2026-08-06 14:33:43 +08:00
WuYongJie
4d24976ab9 fix(barcode-print): 索菲亚合格证/箱贴模板修复 + 顾家布局微调
索菲亚相关:
- barcodePrintSofiaCartonLayout 新增索菲亚箱贴布局
- barcodePrintCertificate 证书渲染逻辑调整
- BarcodePrintCertificateEditDialog 编辑弹窗重构

顾家相关:
- barcodePrintGujiaCertificateLayout 布局微调

纯前端/打印逻辑,不涉及数据库结构,可安全部署。
2026-08-06 14:33:24 +08:00
WuYongJie
a62ed2b6b7 merge: sync latest xuxiake mainline 2026-08-06 14:16:52 +08:00
WuYongJie
2ccb52e75b fix(intake): restore structured sku sales status normalization 2026-08-06 13:54:37 +08:00
WuYongJie
dc236236be docs: document product archive wide view contract 2026-08-06 13:21:41 +08:00
WuYongJie
3ba508e8bb docs: document product archive wide view contract 2026-08-06 13:20:47 +08:00
jingchuan
100d2d05fa fix(oa-board): keep total overtime separate from stage sla 2026-08-06 13:06:09 +08:00
WuYongJie
21572d3377 fix: calibrate Sofia carton certificate print layout 2026-08-06 12:58:37 +08:00
WuYongJie
09d0e169bc feat(supply-chain): unify archive source with product catalog wide table 2026-08-06 12:40:00 +08:00
WuYongJie
373aa0a753 feat(supply-chain): unify archive source with product catalog wide table 2026-08-06 12:29:36 +08:00
WuYongJie
ef1ad7999c feat(supply-chain): update product archive column headers 2026-08-06 12:11:17 +08:00
jingchuan
f60dbfa159 fix(oa-board): correct duration and sku flow matching 2026-08-06 10:53:56 +08:00
jingchuan
4efb7f4083 fix(oa-board): prefer active reassigned tasks 2026-08-06 09:59:50 +08:00
jingchuan
d3280f8fc5 fix(oa-board): refresh brand planning snapshot with timer 2026-08-06 09:39:36 +08:00
WuYongJie
c8b8fccc5b Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/release-bom-mincost-main-20260805 2026-08-05 21:07:34 +08:00
WuYongJie
1f001701a6 Merge branch 'codex/wuyongjie-bom-min-cost-20260805' into codex/release-bom-mincost-xuxiake-20260805 2026-08-05 21:06:58 +08:00
WuYongJie
db262e18db feat(bom): cross-check sibling sku facts 2026-08-05 21:04:13 +08:00
WuYongJie
2239d35564 fix(bom): estimate costs from saved sku facts 2026-08-05 20:56:48 +08:00
WuYongJie
5ea48578ec Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-main-release-20260805 2026-08-05 20:05:50 +08:00
WuYongJie
5a2438595e Merge branch 'codex/wuyongjie-bom-recalc-rootfix-20260805' into codex/xuxiake-mainline 2026-08-05 20:04:55 +08:00
WuYongJie
87a7e75d21 fix(bom): assemble exact sku structures from image facts 2026-08-05 19:56:34 +08:00
WuYongJie
d41ee6a61a fix(bom): bound gpt synthesis by real sku 2026-08-05 19:26:10 +08:00
jingchuan
25f4354823 fix(oa-board): use stage SLA for overtime 2026-08-05 17:53:15 +08:00
jingchuan
317eaa8655 fix(oa-board): allow historical overtime followup editing 2026-08-05 17:53:15 +08:00
jingchuan
d531a62361 fix(oa-board): retain historical overtime followups 2026-08-05 17:53:15 +08:00
WuYongJie
2a01b4929b Merge remote-tracking branch 'origin/main' into codex/wuyongjie-bom-recalc-rootfix-20260805 2026-08-05 16:31:02 +08:00
WuYongJie
cc01261d96 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-recalc-rootfix-20260805 2026-08-05 16:28:53 +08:00
WuYongJie
6fbc44f0d1 fix(bom): tolerate partial image analysis failures 2026-08-05 16:27:07 +08:00
jingchuan
495a639c8d fix(oa-board): expose historical overtime followups 2026-08-05 16:23:44 +08:00
jingchuan
8e5c6f91ba fix(oa-board): expose historical overtime followups 2026-08-05 16:22:58 +08:00
WuYongJie
c08f5661b4 merge: sync management workspace access 2026-08-05 16:19:49 +08:00
WuYongJie
656a1d1d9e merge: add management workspace access 2026-08-05 16:19:01 +08:00
WuYongJie
8f14eebe15 feat: add management workspace access 2026-08-05 16:16:33 +08:00
WuYongJie
82b478a1fe fix(bom): clarify actual sku recalculation scope 2026-08-05 15:42:13 +08:00
WuYongJie
d6214a2c40 fix(bom): recover malformed image facts and clarify recalc 2026-08-05 15:39:12 +08:00
WuYongJie
2c13dd0312 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/release-bom-live-stream-main-20260805 2026-08-05 14:25:35 +08:00
WuYongJie
06665326b2 Merge branch 'codex/wuyongjie-bom-live-stream-workbench-clean-20260805' into codex/release-bom-live-stream-xuxiake-20260805 2026-08-05 14:23:59 +08:00
WuYongJie
bc7be2e2ad feat(bom): surface live image analysis results 2026-08-05 14:19:38 +08:00
WuYongJie
52e8d8baef merge: release OpenSSH deploy key support to production main 2026-08-05 13:31:36 +08:00
WuYongJie
d12ecc74f2 merge: bound identity-key deployment connections 2026-08-05 13:31:14 +08:00
WuYongJie
5e0f34ecb7 fix(deploy): bound OpenSSH identity connections 2026-08-05 13:31:13 +08:00
WuYongJie
e2c22b260f merge: promote OpenSSH deploy key support to shared mainline 2026-08-05 13:27:13 +08:00
WuYongJie
fc74d265cd fix(deploy): support OpenSSH identity files 2026-08-05 13:26:49 +08:00
WuYongJie
8350e9f5fc merge: release product progress completion fix 2026-08-05 12:53:51 +08:00
WuYongJie
024be4307a merge: complete product progress after SKU entry 2026-08-05 12:52:55 +08:00
WuYongJie
a863754ae6 fix(product-progress): complete historical entered SPUs 2026-08-05 12:51:57 +08:00
WuYongJie
cd4ec9a23a feat(supply-chain): add product archive dashboard 2026-08-05 12:45:28 +08:00
WuYongJie
e5b97d7750 feat(supply-chain): add product archive dashboard 2026-08-05 12:43:51 +08:00
WuYongJie
f6460b1eda feat(supply-chain): add product archive dashboard 2026-08-05 12:43:13 +08:00
WuYongJie
c5ddcb428c merge: release BOM quality safeguards to production main 2026-08-05 11:45:39 +08:00
WuYongJie
68914fceec merge: promote BOM quality safeguards to shared mainline 2026-08-05 11:45:11 +08:00
WuYongJie
3d18a89e55 fix(bom): guard historical and invalid costing results 2026-08-05 11:42:28 +08:00
WuYongJie
f1dc44376a merge: retain current production BOM safeguards 2026-08-05 11:19:42 +08:00
WuYongJie
02c869cf70 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wu-product-development-main-merge-20260805 2026-08-05 11:04:43 +08:00
WuYongJie
8b9474262c Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wu-product-development-release-20260805
# Conflicts:
#	furniture-3d-system/src/app/operation-logs/OperationLogClient.tsx
2026-08-05 11:02:16 +08:00
WuYongJie
f456c56d57 feat(intake): consolidate product development progress 2026-08-05 11:00:42 +08:00
WuYongJie
9a713e9a98 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-supply-chain-archive-release-20260804 2026-08-05 10:50:57 +08:00
WuYongJie
1a90b2e7b3 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-supply-chain-archive-release-20260804 2026-08-05 10:50:29 +08:00
WuYongJie
bc573566eb feat(operation-logs): separate workspace administrator views 2026-08-05 10:50:09 +08:00
WuYongJie
3cbbe8eb3d merge: release bounded BOM worker execution 2026-08-05 10:49:46 +08:00
WuYongJie
3136604a53 merge: bound BOM worker execution time 2026-08-05 10:49:17 +08:00
WuYongJie
d17d308be1 merge: sync shared mainline before BOM timeout fix 2026-08-05 10:47:55 +08:00
WuYongJie
115a8b8042 fix(bom): bound worker and model execution time 2026-08-05 10:47:49 +08:00
jingchuan
8cd5764428 fix(oa-board): allow historical overtime followup editing 2026-08-05 10:45:36 +08:00
WuYongJie
5b537b8f9c feat(intake): consolidate product development progress 2026-08-05 10:42:54 +08:00
WuYongJie
af596560b6 merge: skip unused onnx cuda runtime 2026-08-05 09:59:18 +08:00
WuYongJie
adaa1ed8ac fix(deploy): skip unused onnx cuda runtime 2026-08-05 09:58:43 +08:00
WuYongJie
9636cc9733 merge: release real BOM SKU verification 2026-08-05 09:51:45 +08:00
WuYongJie
86290edf6a merge: preserve real BOM SKU rows 2026-08-05 09:51:20 +08:00
WuYongJie
26063e1d16 fix(bom): verify real sku rows before costing 2026-08-05 09:50:42 +08:00
WuYongJie
461e10a3ab merge: release supply chain product archive 2026-08-05 09:06:03 +08:00
WuYongJie
2cb7a35c26 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-supply-chain-archive-release-20260804
# Conflicts:
#	scripts/deploy-yydd-prod-from-git.ps1
2026-08-05 09:02:28 +08:00
WuYongJie
368fc1953a feat(supply-chain): integrate product archive pipeline 2026-08-05 09:00:49 +08:00
WuYongJie
3d382146f4 merge: release batched BOM result persistence 2026-08-05 03:07:44 +08:00
WuYongJie
03cc085514 merge: batch BOM result persistence 2026-08-05 03:07:33 +08:00
WuYongJie
1e41401ec3 fix(bom): batch large costing result persistence 2026-08-05 03:07:18 +08:00
WuYongJie
78fe9d6ded merge: release BOM tabletop area costing 2026-08-05 02:57:04 +08:00
WuYongJie
0d82963ffa merge: fix BOM tabletop area costing 2026-08-05 02:56:50 +08:00
WuYongJie
8c56a3a164 fix(bom): use sku dimensions for tabletop area costing 2026-08-05 02:56:32 +08:00
WuYongJie
5972bf4db4 merge: recover transient BOM upstream bad requests 2026-08-05 02:13:42 +08:00
WuYongJie
eaef521816 merge: recover transient BOM upstream bad requests 2026-08-05 02:13:31 +08:00
WuYongJie
faf313b940 fix(bom-costing): recover transient upstream bad requests 2026-08-05 02:13:18 +08:00
WuYongJie
dccc745d51 merge: auto-recover empty BOM model responses 2026-08-05 01:50:32 +08:00
WuYongJie
97b9359235 merge: auto-recover empty BOM model responses 2026-08-05 01:50:21 +08:00
WuYongJie
00b8070699 fix(bom-costing): auto-recover empty model responses 2026-08-05 01:49:55 +08:00
WuYongJie
76e60f2465 merge: reset BOM recovery budget after progress 2026-08-05 01:01:41 +08:00
WuYongJie
d5da0a0390 merge: reset BOM recovery budget after progress 2026-08-05 01:01:20 +08:00
WuYongJie
131e83ce77 fix(bom-costing): reset recovery budget after progress 2026-08-05 01:00:07 +08:00
WuYongJie
71a8d06c97 merge: default omitted BOM model confidence 2026-08-05 00:28:47 +08:00
WuYongJie
c6feb67947 merge: default omitted BOM model confidence 2026-08-05 00:28:41 +08:00
WuYongJie
96a3abda1d fix(bom-costing): default omitted model confidence conservatively 2026-08-05 00:28:24 +08:00
WuYongJie
300753f56c merge: auto-resume malformed BOM model responses 2026-08-05 00:21:51 +08:00
WuYongJie
09743ffc3f merge: auto-resume malformed BOM model responses 2026-08-05 00:21:46 +08:00
WuYongJie
0e20ab41c5 fix(bom-costing): auto-resume malformed model responses 2026-08-05 00:21:26 +08:00
WuYongJie
4ea787faf1 merge: auto-resume transient BOM model failures 2026-08-05 00:07:44 +08:00
WuYongJie
74ec257d96 merge: auto-resume transient BOM model failures 2026-08-05 00:07:31 +08:00
WuYongJie
e5e5f62504 fix(bom-costing): auto-resume transient model failures 2026-08-05 00:05:35 +08:00
WuYongJie
29b6992314 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/release-bom-rearchive-main-20260804 2026-08-04 23:45:44 +08:00
WuYongJie
0a3bd1969c Merge branch 'codex/wuyongjie-bom-rearchive-batch-20260804' into codex/release-bom-rearchive-xuxiake-20260804 2026-08-04 23:45:26 +08:00
WuYongJie
ad43342687 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-rearchive-batch-20260804 2026-08-04 23:45:12 +08:00
WuYongJie
22b02adda9 fix(bom-costing): split network timeouts immediately 2026-08-04 23:44:58 +08:00
WuYongJie
68d59842c5 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/release-bom-rearchive-main-20260804 2026-08-04 22:47:35 +08:00
WuYongJie
c1e14b23a8 Merge branch 'codex/wuyongjie-bom-rearchive-batch-20260804' into codex/release-bom-rearchive-xuxiake-20260804 2026-08-04 22:47:22 +08:00
WuYongJie
86f0e56739 fix(bom-costing): checkpoint completed synthesis batches 2026-08-04 22:46:49 +08:00
WuYongJie
4dae247b96 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/release-bom-rearchive-main-20260804 2026-08-04 22:04:25 +08:00
WuYongJie
f881fdda30 Merge branch 'codex/wuyongjie-bom-rearchive-batch-20260804' into codex/release-bom-rearchive-xuxiake-20260804 2026-08-04 22:04:07 +08:00
WuYongJie
86702e95ff Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-rearchive-batch-20260804 2026-08-04 22:03:54 +08:00
WuYongJie
6e296c6c61 fix(bom-costing): time out stalled response streams 2026-08-04 22:03:28 +08:00
WuYongJie
9c9c57ba5c merge: release immediate BOM timeout splitting 2026-08-04 21:37:52 +08:00
WuYongJie
2420cec963 merge: split timed out BOM synthesis immediately 2026-08-04 21:37:34 +08:00
WuYongJie
0f9caa09ca merge: synchronize compact BOM synthesis release 2026-08-04 21:37:13 +08:00
WuYongJie
56988d753d fix(bom-costing): split timed out synthesis immediately 2026-08-04 21:36:55 +08:00
WuYongJie
146b3992ab merge: release compact BOM synthesis facts 2026-08-04 21:23:12 +08:00
WuYongJie
6ac92f6274 merge: compact BOM synthesis facts 2026-08-04 21:22:55 +08:00
WuYongJie
e0ca7775a4 merge: synchronize stalled BOM recovery release 2026-08-04 21:22:39 +08:00
WuYongJie
634a2dad9d perf(bom-costing): compact product synthesis facts 2026-08-04 21:22:19 +08:00
WuYongJie
3b16f69efe merge: release stalled BOM synthesis batch recovery 2026-08-04 21:00:07 +08:00
WuYongJie
a61a10ffb9 merge: split stalled BOM synthesis batches 2026-08-04 20:59:49 +08:00
WuYongJie
b32fc52427 merge: synchronize BOM retry safeguards 2026-08-04 20:59:31 +08:00
WuYongJie
acf3dc8fc2 fix(bom-costing): split stalled synthesis batches 2026-08-04 20:59:00 +08:00
WuYongJie
89db71a377 merge: release BOM synthesis retry safeguards 2026-08-04 20:30:56 +08:00
WuYongJie
71558003a7 merge: retry incomplete BOM synthesis batches 2026-08-04 20:30:35 +08:00
WuYongJie
7211a2fc78 fix(bom-costing): retry incomplete synthesis batches 2026-08-04 20:30:07 +08:00
WuYongJie
eccbc1bc68 merge: release scoped BOM synthesis facts 2026-08-04 20:14:34 +08:00
WuYongJie
f6a54980df merge: scope BOM synthesis facts by SKU batch 2026-08-04 20:14:17 +08:00
WuYongJie
b23d0b44f6 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-rearchive-batch-20260804 2026-08-04 20:14:02 +08:00
WuYongJie
99c6833b0c fix(bom-costing): scope synthesis facts to SKU batch 2026-08-04 20:13:48 +08:00
WuYongJie
c12194f333 merge: release bounded BOM synthesis reasoning 2026-08-04 20:05:02 +08:00
WuYongJie
179ec64a8c merge: bound BOM synthesis reasoning 2026-08-04 20:04:46 +08:00
WuYongJie
07cdfbb20c Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-rearchive-batch-20260804 2026-08-04 20:04:25 +08:00
WuYongJie
b6b4edeaf5 fix(bom-costing): bound GPT synthesis reasoning 2026-08-04 20:04:04 +08:00
WuYongJie
0b0e014d5e merge: release hardened BOM SKU batch processing 2026-08-04 19:49:29 +08:00
WuYongJie
10a23cb5b8 merge: harden BOM SKU batch processing 2026-08-04 19:49:12 +08:00
WuYongJie
d5b73444e5 fix(bom-costing): tolerate unknown measures in sku batches 2026-08-04 19:48:22 +08:00
WuYongJie
2e51bad245 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/release-bom-rearchive-main-20260804 2026-08-04 19:16:17 +08:00
WuYongJie
041b60a563 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-rearchive-batch-20260804 2026-08-04 19:15:15 +08:00
WuYongJie
143aa7fa3f fix(bom-costing): bound concurrent sku synthesis 2026-08-04 19:14:41 +08:00
WuYongJie
1b6e743dd0 merge: release 600 SKU BOM catalog support 2026-08-04 18:44:25 +08:00
WuYongJie
4841ca549b merge: support complete 600 SKU BOM catalogs 2026-08-04 18:43:59 +08:00
WuYongJie
f0687f2d5d fix(bom-costing): support complete 600 sku catalogs 2026-08-04 18:43:33 +08:00
WuYongJie
6ba383076a merge: release BOM synthesis progress serialization 2026-08-04 18:34:01 +08:00
WuYongJie
d2b123066b merge: serialize BOM synthesis progress events 2026-08-04 18:33:35 +08:00
WuYongJie
47f19da2e2 fix(bom-costing): serialize synthesis progress events 2026-08-04 18:32:48 +08:00
WuYongJie
b192e0359a merge: release BOM source recovery and batch synthesis 2026-08-04 18:23:45 +08:00
WuYongJie
a11aa15017 merge: recover BOM changed sources and batch synthesis 2026-08-04 18:22:35 +08:00
WuYongJie
45a501406b fix(bom-costing): recover changed sources and batch sku synthesis 2026-08-04 18:21:44 +08:00
WuYongJie
26906c0585 feat(supply-chain): snapshot product archive workspace 2026-08-04 18:20:12 +08:00
WuYongJie
899ec8b9f4 feat(supply-chain): snapshot product archive workspace 2026-08-04 18:19:44 +08:00
WuYongJie
ab2b64c0d4 merge: release BOM SKU intake repair 2026-08-04 17:49:56 +08:00
WuYongJie
6371d9f248 merge: harden BOM SKU intake and hints 2026-08-04 17:49:20 +08:00
WuYongJie
a78900ce64 fix(bom-costing): harden SKU intake and simplify hints 2026-08-04 17:48:48 +08:00
WuYongJie
71cd22a12a Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/release-bom-costing-promote-main-20260804 2026-08-04 17:14:40 +08:00
WuYongJie
c4628fb8c4 Merge remote-tracking branch 'origin/codex/release-bom-costing-main-20260804' into codex/release-bom-costing-xuxiake-20260804 2026-08-04 17:13:45 +08:00
WuYongJie
a7c371073b Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/release-bom-costing-xuxiake-20260804 2026-08-04 17:13:44 +08:00
WuYongJie
2899a1bcd4 fix(bom-costing): bound synthesis workload 2026-08-04 17:13:03 +08:00
WuYongJie
37cb0fd876 Merge remote-tracking branch 'origin/main' into codex/release-bom-costing-main-20260804 2026-08-04 16:53:10 +08:00
WuYongJie
a115a4a73c fix(bom-costing): retry transient synthesis failures 2026-08-04 16:52:40 +08:00
WuYongJie
8f8e03734c merge: guard intake basic info before factory confirmation 2026-08-04 16:45:07 +08:00
WuYongJie
747a201196 fix(intake): guard basic info confirmation before factory 2026-08-04 16:44:30 +08:00
WuYongJie
b3fd61dadb fix(bom-costing): bound GPT-5 reasoning time 2026-08-04 16:43:59 +08:00
WuYongJie
608da97538 Merge remote-tracking branch 'origin/main' into codex/release-bom-costing-main-20260804 2026-08-04 16:24:36 +08:00
WuYongJie
a36d7ab1a8 fix(bom-costing): recover interrupted worker jobs 2026-08-04 16:24:31 +08:00
jingchuan
6187aecab2 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/main-oa-history-followup-release 2026-08-04 15:40:51 +08:00
jingchuan
02d3345763 fix(oa-board): retain historical overtime followups 2026-08-04 15:40:12 +08:00
WuYongJie
532e4724da merge: synchronize BOM synthesis resilience 2026-08-04 15:35:15 +08:00
WuYongJie
81f3c10ca3 merge: extend BOM synthesis resilience 2026-08-04 15:34:54 +08:00
WuYongJie
e5a5662303 fix(bom-costing): extend synthesis resilience 2026-08-04 15:34:30 +08:00
WuYongJie
16b22dcd40 merge: release BOM costing recovery 2026-08-04 15:13:48 +08:00
WuYongJie
e6580c9b26 merge: release BOM costing recovery 2026-08-04 15:13:08 +08:00
WuYongJie
425e5e444d fix(bom-costing): preserve viable cost estimates 2026-08-04 14:43:29 +08:00
jingchuan
3553211fc5 merge: block duplicate todo recovery 2026-08-03 17:52:08 +08:00
jingchuan
5848dcd71f merge: block duplicate todo recovery 2026-08-03 17:51:29 +08:00
jingchuan
5c370b3f57 fix(oa-board): block duplicate todo recovery 2026-08-03 17:51:05 +08:00
jingchuan
699fb65e10 merge: retain DingTalk todo ID on update 2026-08-03 17:40:48 +08:00
jingchuan
b80792d753 merge: retain DingTalk todo ID on update 2026-08-03 17:40:34 +08:00
jingchuan
6d92aa52ae fix(oa-board): retain todo id after dingtalk update 2026-08-03 17:40:17 +08:00
jingchuan
59e44086b1 merge: schedule OA overtime todo dispatch 2026-08-03 17:29:35 +08:00
jingchuan
a2040410e8 merge: enable scheduled OA overtime todo dispatch 2026-08-03 17:28:45 +08:00
jingchuan
5d01a3b2fd fix(oa-board): run overdue todo scan after snapshot sync 2026-08-03 17:26:56 +08:00
WuYongJie
9882dede2c merge: keep one intake workspace tab per page 2026-08-03 17:03:09 +08:00
WuYongJie
59dfb2a1b1 fix: keep one tab per intake workspace page 2026-08-03 17:01:47 +08:00
WuYongJie
e5f603ca8e merge: portal permissions and external barcode scope 2026-08-03 16:35:54 +08:00
WuYongJie
29c70537fb Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-supply-chain-permissions-20260803 2026-08-03 16:30:19 +08:00
WuYongJie
c58f642574 fix: scope external barcode print records 2026-08-03 16:28:40 +08:00
WuYongJie
076075a3a8 fix: stabilize intake and supply-chain tab navigation 2026-08-03 16:28:39 +08:00
WuYongJie
7acd668918 feat: move product permission controls to user management 2026-08-03 16:28:26 +08:00
WuYongJie
a50f81eaed feat: add portal and workspace access guards 2026-08-03 16:28:16 +08:00
WuYongJie
19c3409b09 Merge remote-tracking branch 'origin/main' into codex/wuyongjie-bom-unified-progress-20260803 2026-08-03 16:21:56 +08:00
WuYongJie
5a40b422f9 fix(bom-costing): 自动形成预估成本并精简工作台 2026-08-03 16:21:14 +08:00
WuYongJie
546cea38d9 fix: 对齐顾家合格证2026模板 2026-08-03 13:53:37 +08:00
WuYongJie
e3da1628ce fix: 删除首页门户return后的死代码(UTF8安全), 修复TS构建错误 2026-08-03 13:06:56 +08:00
WuYongJie
fee09e5116 fix: 删除首页门户 return 后的死代码,修复 TS 构建错误 2026-08-03 12:59:08 +08:00
WuYongJie
0bf9003485 fix: HomeClient editingProduct 可能为 null 的 TS 构建错误 2026-08-03 12:50:52 +08:00
WuYongJie
97c73a4e0c merge: 首页门户化 + 供应链中心框架 2026-08-03 12:43:57 +08:00
WuYongJie
51e44d1eaf feat: 首页门户化 + 供应链中心框架
首页改门户选择页:
- HomeClient.workspace 加门户短路,渲染 HomePortal(两个工作台入口)
- HomePortal:产品开发(→/intake) + 供应链中心(→/supply-chain) 两大入口卡
- 顶部导航:首页时只显示「首页」,进入产品开发后恢复全部功能导航
- 入口图标:产品开发用 Package,供应链用 Truck
- 时间问候语(早午晚好+用户名)

供应链中心(src/app/supply-chain/):
- 独立布局:窄边栏(76px) + hover二级菜单浮层 + 多标签页 + 顶栏搜索
- 复用主项目顶栏(保留首页导航),供应链路由时品牌切换为「由壹点「供应链中心」」
- 全局菜单搜索:模糊匹配 + 拼音全拼/首字母 + 键盘导航(↑↓Enter Esc)
- 4个页面:工作台(问候语)、产品全量档案、日常事项(空壳)、产品变价(空壳)
- 产品全量档案:多级表头(5分组81字段) + 列设置抽屉(勾选显隐+拖拽排序+重置) + localStorage持久化 + 浮动分页

视觉:白曜石规范(浅色精密、克制通透、石墨灰强调色)
基于 origin/main,不含其他线程改动
2026-08-03 12:39:02 +08:00
WuYongJie
5904e96494 merge: 发布 BOM 并发分析与进度可视化修复 2026-08-03 12:15:40 +08:00
WuYongJie
62d9578e1b merge: 合入 BOM 并发分析与进度可视化修复 2026-08-03 12:13:07 +08:00
WuYongJie
4f7acbdafa fix(bom-costing): 修复多模态结果解析并展示并发进度 2026-08-03 12:11:43 +08:00
WuYongJie
668fa3c219 merge: 发布 BOM 多模态事实传输契约修复 2026-08-03 09:25:04 +08:00
WuYongJie
a0f701f24d merge: 合入 BOM 多模态事实传输契约修复 2026-08-03 09:22:34 +08:00
WuYongJie
ecb6549341 fix(bom-costing): 拆分多模态事实传输契约 2026-08-02 01:03:10 +08:00
WuYongJie
dc46e09888 merge: 发布 BOM 多模态事实类型契约修复 2026-08-02 00:17:46 +08:00
WuYongJie
2261b3f663 merge: 合入 BOM 多模态事实类型契约修复 2026-08-02 00:16:55 +08:00
WuYongJie
45b5cb05ba fix(bom-costing): 统一多模态事实类型契约 2026-08-02 00:15:27 +08:00
WuYongJie
229a65b8aa merge: 发布 BOM 多模态输出契约根因修复 2026-08-01 23:24:34 +08:00
WuYongJie
9faa24fe9c merge: 合入 BOM 多模态输出契约根因修复 2026-08-01 23:23:38 +08:00
WuYongJie
62c89a81c8 fix(bom-costing): 重构多模态输出契约边界 2026-08-01 23:11:14 +08:00
WuYongJie
2e891c9a95 feat(bom-costing): 使用 GSAP 重构核算动效 2026-08-01 21:57:03 +08:00
WuYongJie
70a2d8eb55 fix(bom-costing): 修正多模态事实结果契约 2026-08-01 19:59:57 +08:00
WuYongJie
d9ebf7d9cb Merge remote-tracking branch 'origin/main' into codex/wuyongjie-bom-failure-root-20260801 2026-08-01 18:38:11 +08:00
WuYongJie
665151aa22 fix(bom-costing): 修复多模态核算失败链路 2026-08-01 18:32:49 +08:00
jingchuan
3289809a95 feat(oa-board): split current and historical timeout analysis 2026-08-01 09:22:22 +08:00
jingchuan
d9672a9231 Merge branch 'codex/xuxiake-mainline' of http://git.1080p.hk/WuYongJie/furniture-3d-system-Wu into codex/xuxiake-mainline-todo-unionid-20260729 2026-07-31 18:08:29 +08:00
jingchuan
c88e63978c fix(oa-board): retain historical timeout followups 2026-07-31 18:07:35 +08:00
WuYongJie
939810a702 fix(barcode-print): restore Gujia certificate field alignment 2026-07-31 17:36:18 +08:00
WuYongJie
d9052de583 fix(barcode-print): restore Gujia certificate field alignment 2026-07-31 17:34:57 +08:00
jingchuan
19ce18fa0b fix(oa-board): align stage overtime followups 2026-07-31 15:44:02 +08:00
jingchuan
adc97204ff fix(oa-board): fall back to current stage SLA alerts 2026-07-31 13:11:08 +08:00
WuYongJie
d3915c4cd3 merge: 同步徐霞客主线最新 OA 修复 2026-07-31 12:04:31 +08:00
jingchuan
781d92ea71 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline-todo-unionid-20260729 2026-07-31 12:00:13 +08:00
jingchuan
f4150e8e2c fix(oa-board): disable unconfirmed sample courier SLA 2026-07-31 11:59:47 +08:00
WuYongJie
9233a9013d merge: 同步最新生产主线后发布 BOM 改动 2026-07-31 11:40:21 +08:00
WuYongJie
4cc6a63267 merge: barcode print database pagination 2026-07-31 11:37:25 +08:00
WuYongJie
c598da5ef8 fix(barcode-print): page records at database 2026-07-31 11:36:37 +08:00
WuYongJie
e7de41ce8d merge: 同步最新徐霞客主线 2026-07-31 11:34:28 +08:00
WuYongJie
2d3d5f2ef0 feat(bom-costing): 按业务类型开放并留档人工核算 2026-07-31 11:33:31 +08:00
jingchuan
46a7f25619 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline-todo-unionid-20260729 2026-07-31 11:24:49 +08:00
jingchuan
64738ea4d6 fix(oa-board): unify current-node overtime alerts 2026-07-31 11:23:15 +08:00
WuYongJie
1d4f176337 merge: align Gujia certificate fields 2026-07-31 10:32:37 +08:00
WuYongJie
8d04698637 fix(intake): align Gujia certificate fields 2026-07-31 10:29:10 +08:00
WuYongJie
59b960d5ce merge: release barcode pagination and supplier brand SKU visibility 2026-07-30 23:37:06 +08:00
WuYongJie
783e7038b0 fix(barcode-print): page imports and expose supplier brand SKUs 2026-07-30 23:36:14 +08:00
WuYongJie
0264b4c63c merge: 发布 BOM 全量图片事实分析 2026-07-30 19:08:20 +08:00
WuYongJie
d9a7ac208a merge: BOM 全量图片事实分析 2026-07-30 19:05:35 +08:00
WuYongJie
ed424431a7 feat(bom-costing): 持久化全量图片事实分析 2026-07-30 19:01:56 +08:00
WuYongJie
e0cad57074 merge: barcode print append import and supplier scope 2026-07-30 17:28:09 +08:00
WuYongJie
d68f49620d fix(barcode-print): append imports and scope suppliers by name 2026-07-30 17:14:49 +08:00
WuYongJie
6af723ac40 feat(barcode-print): 条码打印后端真分页 + 供应商隐藏空字段
后端分页:
- BarcodePrintService 新增 listRowsPaged,take/skip 只查当前页 + count 取总数
- 筛选条件下沉到后端 where(货号/供应商查独立列,条码/型号等查 updates JSONB)
- GET /api/intake/barcode-print 接收 page/pageSize/筛选参数

前端真分页:
- 从全量拉取+内存切片 改为 带参数请求后端+只存当前页
- 翻页/搜索/重置/导入/删除 均触发后端重新请求
- 本地实测:打开 18.5s -> 0.4s

供应商隐藏空字段:
- tableConfig 新增供应商可见列集合 + getVisibleBarcodePrintColumns
- BarcodePrintTable 加 isSupplier prop,供应商隐藏 24 个始终为空的内部字段
- 验证:管理员仍看全部 46 列,供应商看 20 列

注意:本提交的 listRowsPaged 依赖 normalizedSupplierName 列
(schema.prisma + 迁移 20260730150000,由另一线程改动,未含在本提交)。
单独部署会因缺列报 500,需与另一线程的 schema/迁移一并合并。
2026-07-30 17:04:53 +08:00
WuYongJie
400bdaf1ef merge: release AI render catalog scope 2026-07-30 10:32:27 +08:00
WuYongJie
3a0f7079fc merge: scope AI render catalog to product development 2026-07-30 10:30:54 +08:00
WuYongJie
0a2f6b971b fix(ai-render): scope catalog to product development 2026-07-30 10:28:45 +08:00
WuYongJie
fc61e25d73 merge: release BOM costing cost truth 2026-07-29 20:02:49 +08:00
WuYongJie
10409ae69d Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-cost-truth-20260729 2026-07-29 19:56:50 +08:00
WuYongJie
91a5ce30ce fix(bom-costing): align material and packaging cost truth 2026-07-29 19:56:27 +08:00
jingchuan
a0bf3a21c7 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline-todo-unionid-20260729 2026-07-29 18:32:09 +08:00
jingchuan
2c178b5b49 fix(oa-board): use union ids for dingtalk todos 2026-07-29 18:31:29 +08:00
WuYongJie
03585c7872 merge: release intake AI render asset linking 2026-07-29 17:54:30 +08:00
WuYongJie
f8fe78e2cb merge: link intake products with AI render assets 2026-07-29 17:42:56 +08:00
WuYongJie
449eef2a19 feat(ai-render): link intake models and generated assets 2026-07-29 17:41:04 +08:00
jingchuan
e9d60543b0 fix(oa-board): dedupe dingtalk owner identities 2026-07-29 17:18:26 +08:00
jingchuan
e53d5555f6 fix(oa-board): dedupe dingtalk owner identities 2026-07-29 17:04:12 +08:00
WuYongJie
c3036152f2 merge: restrict barcode import permissions 2026-07-29 16:59:13 +08:00
WuYongJie
2697fb9f1d merge: restrict barcode import permissions 2026-07-29 16:57:56 +08:00
WuYongJie
d435d1f535 fix: restrict barcode import management permissions 2026-07-29 16:56:34 +08:00
WuYongJie
49fde0f5c0 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-main-work-record-score-deploy-20260729 2026-07-29 16:01:48 +08:00
WuYongJie
c1151b6af5 Merge remote-tracking branch 'origin/codex/xuxiake-work-record-score-management-20260729' into codex/xuxiake-mainline-work-record-score-submit-20260729 2026-07-29 16:00:48 +08:00
WuYongJie
0259d685d2 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-work-record-score-management-20260729 2026-07-29 15:59:53 +08:00
WuYongJie
679e726a14 feat(work-record): manage score rules 2026-07-29 15:59:41 +08:00
WuYongJie
03430313b5 merge: 同步 BOM 核算商品决策布局 2026-07-29 15:07:36 +08:00
WuYongJie
ec98f2b33c merge: 发布 BOM 核算商品决策布局 2026-07-29 15:07:08 +08:00
WuYongJie
0c9c2fccc8 feat(bom-costing): 重构竞品核算结果工作台 2026-07-29 15:06:16 +08:00
jingchuan
60f252571a Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline-submit-20260704 2026-07-29 13:49:38 +08:00
WuYongJie
eafa63f962 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-restore-prod-ssh-main-20260729 2026-07-29 13:47:29 +08:00
jingchuan
c1f1c319c6 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline-submit-20260704 2026-07-29 13:47:14 +08:00
WuYongJie
f496d8aac9 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-restore-prod-ssh-20260729 2026-07-29 13:45:57 +08:00
WuYongJie
27789d37e8 revert(deploy): 恢复生产 SSH 直连入口 2026-07-29 13:45:50 +08:00
jingchuan
9ed6399c32 feat(oa-board): scaffold dingtalk todo dispatch 2026-07-29 13:45:41 +08:00
WuYongJie
a85c41383d merge: release square mobile nine-view previews 2026-07-29 13:39:15 +08:00
WuYongJie
654df39b1e fix(intake): square mobile nine-view previews 2026-07-29 13:38:30 +08:00
WuYongJie
8f1cfa68ce merge: release BOM streaming and workspace layout 2026-07-29 13:02:34 +08:00
WuYongJie
b7227f3ddc Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-524-layout-20260729 2026-07-29 13:00:37 +08:00
WuYongJie
2d866aa5e4 fix(bom-costing): 修复长请求并压缩图片工作区 2026-07-29 13:00:20 +08:00
WuYongJie
04aa9b3004 merge: release mobile intake detail adaptation 2026-07-29 12:26:55 +08:00
WuYongJie
97f7018c03 fix(intake): adapt product detail links for mobile 2026-07-29 12:26:18 +08:00
WuYongJie
b18418ff40 merge: sync production management jump path 2026-07-29 11:44:36 +08:00
WuYongJie
bdc8cdab8c merge: 生产管理跳板通道 2026-07-29 11:44:12 +08:00
WuYongJie
8d92e137cb ops(deploy): 切换生产管理跳板通道 2026-07-29 11:43:47 +08:00
WuYongJie
c437177609 merge: sync OA category normalization 2026-07-29 11:16:27 +08:00
WuYongJie
e6a4fdf21a fix(intake): normalize OA residential categories 2026-07-29 11:14:27 +08:00
WuYongJie
5dedc6ed72 merge: 同步 BOM SKU 尺寸契约修复 2026-07-29 11:10:53 +08:00
WuYongJie
e41966d1fa merge: BOM SKU 尺寸契约修复 2026-07-29 11:10:08 +08:00
WuYongJie
bfdefd20d3 fix(bom-costing): 统一 SKU 尺寸输出契约 2026-07-29 11:07:10 +08:00
WuYongJie
7aa0e67063 merge: sync top navigation layout 2026-07-29 10:56:48 +08:00
WuYongJie
a8dff278e0 fix: keep workbench navigation on one line 2026-07-29 10:48:15 +08:00
WuYongJie
11ad175bae merge: 同步 BOM 核算过程可视化 2026-07-29 10:36:12 +08:00
WuYongJie
30989452a9 merge: BOM 核算过程可视化 2026-07-29 10:35:20 +08:00
WuYongJie
b541acca8e feat(bom-costing): 增强核算过程可视化 2026-07-29 10:34:07 +08:00
WuYongJie
5f5a8f2036 merge: 同步 BOM 核算历史续跑 2026-07-28 21:13:17 +08:00
WuYongJie
7c632aba6f merge: BOM 核算历史续跑 2026-07-28 21:12:42 +08:00
WuYongJie
80b65bc7f3 fix(bom-costing): 收敛模型输入并支持历史续跑 2026-07-28 21:11:57 +08:00
WuYongJie
2093f809c7 merge: installation demo share preview parity 2026-07-28 19:03:25 +08:00
WuYongJie
155a6579aa fix: align installation demo share preview runtime 2026-07-28 18:59:14 +08:00
WuYongJie
3a7fbe81fb merge: 同步 BOM 核算异步工作台 2026-07-28 18:52:48 +08:00
WuYongJie
cab376d91c merge: BOM 核算异步工作台 2026-07-28 18:52:11 +08:00
WuYongJie
999969a78d Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-async-workbench 2026-07-28 18:50:59 +08:00
WuYongJie
156bff395d feat(bom-costing): 增加异步核算与资源缓存 2026-07-28 18:50:47 +08:00
WuYongJie
7f201fe08e merge: supplier barcode-only access 2026-07-28 17:39:50 +08:00
WuYongJie
7f0561df35 merge: supplier barcode-only access 2026-07-28 17:39:17 +08:00
WuYongJie
10be42a9c8 fix(auth): restrict supplier accounts to barcode printing 2026-07-28 17:38:23 +08:00
WuYongJie
63562013af Merge branch 'codex/xuxiake-mainline' into main 2026-07-28 17:01:33 +08:00
WuYongJie
6e6e0f5e4b Merge branch 'codex/wuyongjie-bom-real-provider' into codex/xuxiake-mainline 2026-07-28 17:01:01 +08:00
WuYongJie
5311fc2d51 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-real-provider 2026-07-28 16:56:04 +08:00
WuYongJie
61a0c9e301 feat(bom-costing): 接入竞品详情与多模态核算 2026-07-28 16:55:59 +08:00
WuYongJie
175b5b7bb5 fix(intake): add barcode print page jump 2026-07-28 16:26:09 +08:00
jingchuan
c5000a5950 Merge branch 'codex/xuxiake-oa-flow-status-20260728' into HEAD 2026-07-28 16:21:33 +08:00
jingchuan
1b6ea9dcbe fix(oa-board): finalize component development flows 2026-07-28 16:20:36 +08:00
WuYongJie
78f5e8c570 fix(intake): wrap long Gujia barcode suffixes 2026-07-28 15:56:39 +08:00
WuYongJie
a423bddc63 merge: sync main before release notes deployment 2026-07-28 11:32:16 +08:00
WuYongJie
c927c831c5 feat: add release notes and sku warehouse batch initialization 2026-07-28 11:29:32 +08:00
WuYongJie
b2cf27ef68 merge: apply shared display refinements 2026-07-28 11:22:05 +08:00
WuYongJie
be9cdb8ed8 fix(installation-demo): refine shared display stages 2026-07-28 11:20:13 +08:00
WuYongJie
a36521f07c merge: apply carried-group screw playback fix 2026-07-28 10:52:47 +08:00
WuYongJie
7695bacfe2 fix(installation-demo): skip screw drive for carried groups 2026-07-28 10:49:08 +08:00
WuYongJie
84510b2f57 merge: 同步 AI 配置模型列表与共享主线 2026-07-28 10:17:56 +08:00
WuYongJie
e3d37c4391 merge: AI 配置读取可用模型列表 2026-07-28 10:17:34 +08:00
WuYongJie
edab790d0d Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-ai-model-selector 2026-07-28 10:13:50 +08:00
WuYongJie
fd76a06942 fix(ai-config): 从服务端读取可用模型列表 2026-07-28 10:13:37 +08:00
WuYongJie
89acc2cd4b merge: publish installation demo playback refinements 2026-07-28 09:59:43 +08:00
WuYongJie
494bd0b04b merge: installation demo playback refinements 2026-07-28 09:56:36 +08:00
WuYongJie
b4e6bb865c feat(installation-demo): refine authored playback guidance 2026-07-28 09:55:52 +08:00
WuYongJie
22d54be408 merge: improve work record editing workflow 2026-07-27 14:37:57 +08:00
WuYongJie
117abf7df0 fix(work-record): improve table editing workflow 2026-07-27 14:37:20 +08:00
WuYongJie
0c73cdddc2 merge: save installation snapshots under production CSP 2026-07-27 14:20:13 +08:00
WuYongJie
2773368df1 merge: save installation snapshots under production CSP 2026-07-27 14:19:45 +08:00
WuYongJie
f30c1aa17f fix(installation-demo): save snapshots under production CSP 2026-07-27 14:19:14 +08:00
WuYongJie
fa9c4738e0 merge: release installation demo snapshot persistence 2026-07-27 12:00:25 +08:00
WuYongJie
4a9e8460c0 merge: persist installation demo snapshots 2026-07-27 11:59:48 +08:00
WuYongJie
cea36979d9 fix(installation-demo): persist numbering snapshots 2026-07-27 11:59:26 +08:00
WuYongJie
eb69c3473e merge: release installation demo tool loading fix 2026-07-27 10:53:20 +08:00
WuYongJie
d6e3be000b fix(installation-demo): stabilize tool model loading 2026-07-27 10:51:50 +08:00
WuYongJie
951f62832a merge: 同步徐霞客主线协作规则 2026-07-27 10:32:12 +08:00
WuYongJie
7e2f20bba9 merge: 回流主逻辑优先协作规则 2026-07-27 10:32:05 +08:00
WuYongJie
675237e16d docs(agents): 强化主逻辑优先规则 2026-07-27 10:31:43 +08:00
WuYongJie
f6f6ea4493 Merge remote-tracking branch 'origin/main' into codex/wuyongjie-bom-costing-ui-polish-20260723 2026-07-27 10:23:31 +08:00
WuYongJie
d48a905077 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-costing-ui-polish-20260723 2026-07-27 10:23:26 +08:00
WuYongJie
e909cbdc94 refactor(bom-costing): 收紧空态工作台布局 2026-07-27 10:22:59 +08:00
WuYongJie
4e59043761 fix(installation-demo): recover screw-drive tool attachments 2026-07-27 09:00:45 +08:00
WuYongJie
8752d7c5ef merge: 同步主线后发布安装演示工作台
# Conflicts:
#	furniture-3d-system/prisma/schema.prisma
#	furniture-3d-system/src/app/installation-demo-workbench/InstallationDemoWorkbenchClient.tsx
#	furniture-3d-system/src/app/installation-demo-workbench/components/InstallationDetailsPanel.tsx
#	furniture-3d-system/src/app/installation-demo-workbench/components/InstallationInspector.tsx
#	furniture-3d-system/src/app/installation-demo-workbench/components/InstallationPartsInventoryBoard.tsx
#	furniture-3d-system/src/app/installation-demo-workbench/components/InstallationPreviewStage.tsx
#	furniture-3d-system/src/app/installation-demo-workbench/components/InstallationToolsInventoryBoard.tsx
#	furniture-3d-system/src/app/installation-demo-workbench/components/PartCatalogSection.tsx
#	furniture-3d-system/src/app/installation-demo-workbench/hooks/useInstallationLocalTransformPreview.ts
#	furniture-3d-system/src/app/installation-demo-workbench/hooks/useInstallationPreviewController.ts
#	furniture-3d-system/src/app/installation-demo-workbench/lib/installationNodeMaterialStorage.ts
#	furniture-3d-system/src/app/installation-demo-workbench/lib/installationNumberingSnapshotStorage.ts
2026-07-24 18:05:06 +08:00
WuYongJie
43ed761fae test: 补充安装步骤选择回归覆盖 2026-07-24 17:58:39 +08:00
WuYongJie
fc22c24fa0 feat: 完善安装演示工作台工具链 2026-07-24 17:58:02 +08:00
WuYongJie
ddd0556030 merge: release work record column resize 2026-07-24 17:36:17 +08:00
WuYongJie
de2c1e1011 fix(work-record): add resizable table columns 2026-07-24 17:35:49 +08:00
WuYongJie
2e43240f51 merge: release installation demo workbench 2026-07-24 15:45:51 +08:00
WuYongJie
bc6b5249c4 chore(installation-demo): fix source whitespace 2026-07-24 15:39:21 +08:00
WuYongJie
1973664e0a chore(installation-demo): normalize source line endings 2026-07-24 15:38:26 +08:00
WuYongJie
4025cf6538 merge: retain installation demo playback refinements 2026-07-24 15:32:33 +08:00
WuYongJie
a33b249e28 feat(installation-demo): refine playback and shared preview 2026-07-24 15:32:22 +08:00
WuYongJie
a8e48929ea feat(installation-demo): add toolbox authoring support 2026-07-24 15:30:39 +08:00
jingchuan
adc7fbd7b8 Merge branch 'codex/xuxiake-mainline-submit-20260704' into HEAD 2026-07-24 14:51:03 +08:00
jingchuan
2869c9a27c fix(oa-board): enforce brand planning field sources 2026-07-24 14:48:52 +08:00
WuYongJie
9ed61a8a14 fix(work-record): normalize underscore import labels 2026-07-24 14:03:59 +08:00
WuYongJie
67a0efc0a5 fix(work-record): normalize imported labels and dates 2026-07-24 13:17:16 +08:00
WuYongJie
63e3ab1504 fix(intake): 直连报表库获取供应商目录 2026-07-24 10:54:23 +08:00
WuYongJie
cb0f69d6c0 fix(work-record): preserve workspace tab labels 2026-07-24 09:30:01 +08:00
WuYongJie
32df0f8c84 feat(work-record): add designer work record workspace 2026-07-23 20:36:09 +08:00
WuYongJie
baea745394 merge: release BOM costing workbench 2026-07-23 14:38:42 +08:00
WuYongJie
026fab35c9 merge: add BOM costing workbench 2026-07-23 14:38:05 +08:00
WuYongJie
fabc7ebdb4 feat(bom-costing): add competitor costing workbench 2026-07-23 14:37:12 +08:00
WuYongJie
b24fcf26ca feat(operation-logs): record intake detail events 2026-07-23 10:20:35 +08:00
jingchuan
018feadda3 merge: scope OA brand planning board to new workflows 2026-07-22 18:48:54 +08:00
jingchuan
b1c8ef2e48 fix(oa-board): scope brand planning rows to new workflows 2026-07-22 18:48:21 +08:00
jingchuan
b5506df5d1 merge: add OA brand planning board 2026-07-22 18:32:10 +08:00
jingchuan
3150fea488 feat(oa-board): add brand planning board snapshot 2026-07-22 18:18:57 +08:00
WuYongJie
735a0edd1c merge: add operation-log admin navigation 2026-07-22 16:33:40 +08:00
WuYongJie
615b0013f4 fix(operation-logs): add admin navigation entry 2026-07-22 16:32:34 +08:00
WuYongJie
0a85348fbd fix(jushuitan): map package count to package type 2026-07-22 15:38:24 +08:00
WuYongJie
79618eb594 merge: release external intake visibility restriction 2026-07-22 13:43:07 +08:00
WuYongJie
36f3f6e910 merge: restrict external intake record visibility 2026-07-22 13:41:01 +08:00
WuYongJie
058272ecbf fix(intake): hide external entry records from internal users 2026-07-22 13:39:14 +08:00
WuYongJie
2c15734c11 merge: admin operation monitoring into main 2026-07-22 12:31:31 +08:00
WuYongJie
b870272eb5 merge: admin operation monitoring into mainline 2026-07-22 12:30:33 +08:00
WuYongJie
e18773d3aa feat(operation-logs): add admin operation monitoring 2026-07-22 12:29:58 +08:00
WuYongJie
ab954f3505 feat(external): 结构化产品定厂字段并扩展宽表 2026-07-22 11:58:54 +08:00
WuYongJie
53ded89075 feat(jushuitan): retry virtual inventory push 2026-07-22 10:44:47 +08:00
WuYongJie
28ea27f119 fix(intake): unify purchase owner authority 2026-07-21 20:42:54 +08:00
WuYongJie
5e722abde4 fix: OA预填改为系统已有值优先,OA只在系统没值时才填 2026-07-21 15:06:05 +08:00
WuYongJie
f4edd3e780 fix: 仓库模糊匹配——输入不完整仓库名自动纠正 2026-07-21 09:24:28 +08:00
WuYongJie
c18b54a92c fix: 非保留类目一级分类统一改为住宅家具 2026-07-20 14:56:51 +08:00
WuYongJie
7bd796b04a fix: 更新favicon.ico为新logo 2026-07-20 13:16:35 +08:00
WuYongJie
fe2c7f8249 Merge branch 'main' of http://git.1080p.hk/WuYongJie/furniture-3d-system-Wu 2026-07-20 13:07:28 +08:00
WuYongJie
edb1f30451 fix: 更新logo为最新版本 2026-07-20 13:06:44 +08:00
jingchuan
7a9ec16300 merge: measure first order from material completion 2026-07-18 14:36:50 +08:00
jingchuan
1c38d05b1d fix(oa-board): measure first order from material completion 2026-07-18 14:36:09 +08:00
jingchuan
df55db0bc7 merge: include OA board or-sign timing fix 2026-07-18 13:26:27 +08:00
jingchuan
e1781983aa fix(oa-board): exclude canceled or-sign tasks from timing 2026-07-18 12:06:04 +08:00
WuYongJie
a1228df9ea Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-main-merge-oa-board-20260718 2026-07-18 11:45:19 +08:00
WuYongJie
3b18d3beab Merge remote-tracking branch 'origin/codex/xuxiake-mainline-submit-20260704' into codex/wuyongjie-release-oa-board-20260718 2026-07-18 11:28:45 +08:00
jingchuan
e8d2eb7509 fix(oa-board): parse source timestamps as china time 2026-07-18 10:35:33 +08:00
jingchuan
a35ad4776a fix(oa-board): sum repeated node durations 2026-07-18 10:10:22 +08:00
jingchuan
b0a7ec9591 fix(oa-board): wrap timeout followup exports 2026-07-17 18:28:02 +08:00
jingchuan
bb3445fdd1 fix(oa-board): honor canceled returned tasks 2026-07-17 17:14:54 +08:00
jingchuan
83b12fc11f fix(oa-board): backfill export creators 2026-07-17 15:48:13 +08:00
jingchuan
2cfa2eede0 fix(oa-board): resolve current task owners 2026-07-17 15:41:47 +08:00
jingchuan
1edfaec702 chore(oa-board): add scheduled snapshot sync 2026-07-17 14:32:55 +08:00
jingchuan
03f87e2dcb fix(oa-board): save all edited timeout stages 2026-07-17 13:57:41 +08:00
jingchuan
12b454505e fix(oa-board): preserve detail tab when filtering 2026-07-17 13:34:47 +08:00
WuYongJie
1c9bd11d20 Merge remote-tracking branch 'origin/codex/xuxiake-mainline-submit-20260704' 2026-07-17 13:04:45 +08:00
jingchuan
ebc261a730 feat(oa-board): restrict timeout followup editing 2026-07-17 12:30:19 +08:00
jingchuan
ea82b2c66f fix(oa-board): exclude exact threshold from overtime 2026-07-17 12:14:24 +08:00
jingchuan
a0e9913311 fix(oa-board): allow empty followup due date 2026-07-17 11:36:52 +08:00
WuYongJie
1cfe54ca17 Merge remote-tracking branch 'origin/codex/xuxiake-mainline-submit-20260704' into codex/non-integrated-detail-split 2026-07-17 10:58:59 +08:00
jingchuan
9115c519e1 fix(oa-board): make timeout action plan optional 2026-07-17 10:40:18 +08:00
WuYongJie
fdcfbae436 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/non-integrated-detail-split 2026-07-17 10:37:14 +08:00
WuYongJie
c673fa18e3 refactor: 拆分产品资料详情组件 2026-07-17 10:36:29 +08:00
WuYongJie
f6b045d5ae Merge remote-tracking branch 'origin/codex/xuxiake-mainline-submit-20260704' 2026-07-17 10:10:39 +08:00
jingchuan
081e1f6cc4 feat(oa-board): filter detail by current approver 2026-07-17 09:55:05 +08:00
WuYongJie
cf0fc7c6ec feat: 权限判定增加成功日志(write allowed),方便排查 2026-07-17 09:08:35 +08:00
WuYongJie
8c9c662aeb fix: 更新浏览器页签logo 2026-07-16 15:18:37 +08:00
jingchuan
204ad40e76 perf(oa-board): make sidebar tab switches instant 2026-07-16 09:08:48 +08:00
WuYongJie
34077ca512 Merge branch 'main' of http://git.1080p.hk/WuYongJie/furniture-3d-system-Wu 2026-07-15 18:51:05 +08:00
WuYongJie
329d793705 fix: 厨房储物架/置物架一级类目改为住宅家具 2026-07-15 18:48:29 +08:00
WuYongJie
ad80419fae fix: send jst combine sku childList payload 2026-07-15 18:20:14 +08:00
WuYongJie
393893c295 Merge remote-tracking branch 'origin/codex/xuxiake-mainline-submit-20260704' 2026-07-15 18:11:45 +08:00
jingchuan
0eece49dc0 fix(oa-board): map pricing timeout owners correctly 2026-07-15 18:00:13 +08:00
jingchuan
cb2466c243 feat(oa-board): add timeout analysis and followup workflow 2026-07-15 17:56:48 +08:00
WuYongJie
e0fbc8c988 fix: use dedicated jst combine sku credentials 2026-07-15 17:48:47 +08:00
WuYongJie
f8e9d6855e fix: 权限拦截彻底修复——通用分支去硬拦截+权限判定日志+管理员豁免存量商品 2026-07-15 16:37:45 +08:00
WuYongJie
5e6fc49bb7 fix: 去掉保存时非当前节点的硬拦截,改为静默忽略 2026-07-15 15:46:16 +08:00
WuYongJie
f3e7d93aa3 fix: scope non-integrated product factory saves 2026-07-15 15:05:16 +08:00
WuYongJie
1b4ea170aa fix: scope product factory autosaves 2026-07-15 14:06:29 +08:00
WuYongJie
ff4950b69e fix: scope product factory download saves 2026-07-15 13:37:27 +08:00
WuYongJie
c1215a4617 chore(storage): use current qiniu resumable options 2026-07-14 17:00:02 +08:00
WuYongJie
9ae3dad2d0 fix(storage): use resumable upload for large documents 2026-07-14 16:51:06 +08:00
WuYongJie
299d737da8 fix(intake): harden large document uploads 2026-07-14 16:36:35 +08:00
WuYongJie
694230e30c fix(intake): stream large attachment uploads 2026-07-14 14:44:06 +08:00
WuYongJie
5825c52ad3 fix(intake): raise attachment upload limit to 1.5GB 2026-07-14 14:33:48 +08:00
WuYongJie
bb7f4260cf fix(intake): restore legacy barcode preview scaling 2026-07-14 10:38:22 +08:00
WuYongJie
5d13d2550b feat: add customer service business permissions 2026-07-13 16:02:59 +08:00
WuYongJie
ccfe925b5d feat: improve installation demo mobile share 2026-07-13 15:19:09 +08:00
WuYongJie
e1c4d5dc33 fix(users): classify strategy and general business roles 2026-07-13 15:07:36 +08:00
WuYongJie
5426a3782c fix(intake): add missing node permission targets 2026-07-13 13:53:49 +08:00
WuYongJie
386efdbeb2 feat: align sample review sheet template export 2026-07-13 13:36:12 +08:00
WuYongJie
5bf8f64d15 fix(users): append missing operations roles 2026-07-13 13:28:18 +08:00
WuYongJie
0c72819a12 feat(users): add operations business type 2026-07-13 13:08:18 +08:00
WuYongJie
23cf041ee7 merge: promote external intake confirm fix to main 2026-07-13 11:59:59 +08:00
WuYongJie
36763ca629 fix(intake): scope external confirm saves 2026-07-13 11:59:14 +08:00
WuYongJie
f2af9582af merge: promote intake updates to main 2026-07-13 11:11:20 +08:00
WuYongJie
5ab182a61e Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/tmp-intake-date-filter-20260713-105421 2026-07-13 10:57:37 +08:00
WuYongJie
f3eadddcb3 fix(intake): add product development created date filter 2026-07-13 10:57:01 +08:00
WuYongJie
89da950c07 fix(intake): save pricing workspace explicitly 2026-07-13 10:56:58 +08:00
WuYongJie
bae1563a98 fix(intake): enable suofeiya brand filing package download 2026-07-13 10:51:47 +08:00
WuYongJie
6172ead37c Merge remote-tracking branch 'origin/codex/xuxiake-mainline' 2026-07-13 10:10:57 +08:00
WuYongJie
c1bbdee0b9 refactor: 删除废弃的 buildApprovalNumber/padApprovalNumberPart 2026-07-13 10:09:59 +08:00
WuYongJie
df257bde69 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' 2026-07-12 18:31:55 +08:00
WuYongJie
975ecee94a fix(intake): 确认节点先保存再打勾,SKU未入库提示修正 2026-07-12 09:35:48 +08:00
WuYongJie
35e08a6151 merge: promote brand filing updates to main 2026-07-10 18:24:25 +08:00
WuYongJie
bfcc1bdc39 feat(intake): 完善品牌报备模板流程 2026-07-10 18:10:43 +08:00
WuYongJie
5d8e136f12 merge: promote long-running branch to main 2026-07-10 18:07:56 +08:00
WuYongJie
11ea3e168b fix: restrict external intake owner selections 2026-07-10 18:01:48 +08:00
WuYongJie
a789124f10 feat: refine installation demo numbering preview 2026-07-10 16:49:30 +08:00
WuYongJie
c29e25b880 merge: 合并长期分支到 main 2026-07-10 14:42:28 +08:00
WuYongJie
6c407f208b Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into local/xuxiake-mainline-continue 2026-07-10 14:29:14 +08:00
WuYongJie
9c755282ce merge: 同步长期分支并保留品牌报备更新 2026-07-10 14:27:43 +08:00
WuYongJie
a866e0dffe fix: use public origin for installation demo shares 2026-07-10 14:24:07 +08:00
WuYongJie
d0af5d85dc fix: use public origin for installation demo shares 2026-07-10 14:23:20 +08:00
WuYongJie
be49070962 feat(intake): 完善品牌报备模板与已报备流程 2026-07-10 14:22:30 +08:00
WuYongJie
3346804952 fix(intake): scope factory saves to active sku batch 2026-07-10 13:57:36 +08:00
WuYongJie
58471baaf6 Merge remote-tracking branch 'origin/codex/wuyongjie-intake-factory-save-scope-20260710' into codex/tmp-promote-intake-factory-save-scope-20260710 2026-07-10 13:48:57 +08:00
WuYongJie
72b4581f77 fix(intake): scope product factory saves by target sku 2026-07-10 13:47:39 +08:00
WuYongJie
11be446db2 merge: retain latest main before installation demo build fix 2026-07-10 11:58:52 +08:00
WuYongJie
f035863350 fix(installation-demo): resolve playback focus type checks 2026-07-10 11:56:51 +08:00
WuYongJie
3e67badaf9 fix(installation-demo): resolve playback focus type checks 2026-07-10 11:55:14 +08:00
WuYongJie
c9405db975 merge: promote sku-scoped product factory permissions 2026-07-10 11:51:48 +08:00
WuYongJie
3c97d2b774 fix(intake): scope product factory writes by sku owner 2026-07-10 11:42:24 +08:00
WuYongJie
f178e9cceb fix(intake): scope product factory writes by sku owner 2026-07-10 11:41:24 +08:00
WuYongJie
5734fca012 merge: promote installation demo workbench to main 2026-07-10 11:25:10 +08:00
WuYongJie
d37bfcdde1 feat: add installation demo workbench 2026-07-10 11:16:26 +08:00
WuYongJie
8cb9252115 feat: add installation demo workbench 2026-07-10 11:02:00 +08:00
WuYongJie
fcc94ee37e fix(intake): tighten brand filing entry rules 2026-07-09 18:36:55 +08:00
WuYongJie
a98caca397 fix(intake): tighten brand filing entry rules 2026-07-09 18:35:59 +08:00
WuYongJie
3c78dbf994 feat(intake): add gujia brand filing templates 2026-07-09 17:43:46 +08:00
WuYongJie
05fe0cc783 feat(intake): add gujia brand filing templates 2026-07-09 17:40:54 +08:00
WuYongJie
149870943d feat(intake): add brand filing workspace and planning export 2026-07-09 13:56:23 +08:00
WuYongJie
cf44f1c371 feat(intake): add brand filing workspace and planning export 2026-07-09 13:55:52 +08:00
WuYongJie
29fe5aaeb2 fix: allow purchasing downloads for product data 2026-07-08 16:45:11 +08:00
WuYongJie
5f5dc2ead7 fix: allow purchasing downloads for product data 2026-07-08 16:38:36 +08:00
WuYongJie
071021fe54 fix: default intake sku sales status to 销售 2026-07-08 15:55:45 +08:00
WuYongJie
4982b37864 fix: default intake sku sales status to 销售 2026-07-08 15:45:49 +08:00
WuYongJie
30b1c5740d fix(intake): classify owner dropdown options 2026-07-07 14:08:01 +08:00
WuYongJie
37b2e89b49 fix(intake): classify owner dropdown options 2026-07-07 14:07:04 +08:00
WuYongJie
cec4b449ad fix(auth): 放行主管用户管理入口 2026-07-06 21:51:59 +08:00
WuYongJie
50b8f61a69 fix(auth): 补齐主管权限范围字段 2026-07-06 19:14:13 +08:00
jingchuan
cf54f94ba5 feat(oa-board): add summary tabs and stage graph updates 2026-07-04 16:46:38 +08:00
WuYongJie
5332202f9f fix(external): stream jsreport sync from stable cursor 2026-07-03 16:35:08 +08:00
WuYongJie
883d896406 chore(external): prefix jsreport sync tables 2026-07-03 16:08:00 +08:00
WuYongJie
122df58236 chore(external): sync jsreport every 45 minutes 2026-07-03 15:36:06 +08:00
WuYongJie
6c5ee5e512 chore: keep shell scripts lf 2026-07-03 15:00:15 +08:00
WuYongJie
0300858fdf feat(external): sync readonly views to jsreport 2026-07-03 14:46:59 +08:00
WuYongJie
611b7842c0 feat(external): expose development owners in catalog wide view 2026-07-03 13:26:59 +08:00
WuYongJie
1cbfeba4a6 merge: sync product category filter into main 2026-07-02 14:03:49 +08:00
WuYongJie
d598f30a0c feat(intake): add product category filter to development list 2026-07-02 14:03:07 +08:00
WuYongJie
8a28af209d merge: promote planning analysis upload copy to main 2026-07-02 10:40:20 +08:00
WuYongJie
f455dc47de fix(intake): update planning analysis upload copy 2026-07-02 10:39:56 +08:00
WuYongJie
f4dec4e1a9 merge: promote intake existing spu brand carryover to main
# Conflicts:
#	furniture-3d-system/src/server/services/__tests__/assisted-assembly-ai-render.service.test.ts
2026-07-02 10:15:46 +08:00
WuYongJie
b1f10cd37c feat(intake): align existing spu brand carryover 2026-07-02 10:09:49 +08:00
WuYongJie
553329ac80 test(ai-render): align delete storage mock 2026-07-01 15:50:56 +08:00
WuYongJie
921e0a1503 refactor(ai-render-workbench): split ai render backend flow 2026-07-01 15:44:06 +08:00
WuYongJie
a6fb2d8380 refactor(ai-render-workbench): split gallery and material panel 2026-07-01 15:44:06 +08:00
WuYongJie
0af59ed31b fix(ai-render): protect records when storage cleanup fails 2026-07-01 15:44:06 +08:00
WuYongJie
d02f27b28f refactor(ai-render-workbench): split ai render backend flow 2026-07-01 15:37:39 +08:00
WuYongJie
b59e6a241f fix(jst): push sales status and modular label 2026-07-01 15:05:40 +08:00
WuYongJie
135cab0990 fix(jst): push sales status and modular label 2026-07-01 15:03:36 +08:00
WuYongJie
f6f717eae8 refactor(ai-render-workbench): split gallery and material panel 2026-07-01 14:34:50 +08:00
WuYongJie
708e0679c2 fix(ai-render): protect records when storage cleanup fails 2026-07-01 13:13:40 +08:00
WuYongJie
397767604d fix(ai-render): make render deletion idempotent 2026-07-01 11:03:31 +08:00
WuYongJie
0df9f12c2c fix(intake): refine second-template barcode layout 2026-07-01 10:32:02 +08:00
WuYongJie
26b1d33eca merge: promote ai-white-background regenerate UI fix to main 2026-07-01 10:30:40 +08:00
WuYongJie
ad344c27eb Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/submit-barcode-second-template-followup-20260701 2026-07-01 10:30:06 +08:00
WuYongJie
8510049293 fix(intake): refine second-template barcode layout 2026-07-01 10:28:44 +08:00
WuYongJie
9b0bf2f31a fix(ai-white-background): hide duplicate regenerate entry and title in refinement popover
- 精修气泡展开时隐藏卡片底部「重新生成」按钮,避免功能入口重复占地方
- 去掉气泡内「重新生成 · 正视图」重复标题(上下文已在视图卡片内)
- 保留「生成」态与右侧精修面板,单张首生成能力不受影响
2026-07-01 10:25:27 +08:00
WuYongJie
354a154fee merge: promote second-template barcode print fix to main 2026-07-01 08:44:08 +08:00
WuYongJie
dd4dc279e1 fix(intake): stabilize second-template barcode printing 2026-07-01 08:43:45 +08:00
WuYongJie
91a9585b68 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' 2026-06-30 17:50:37 +08:00
WuYongJie
9b799dd838 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-ai-white-background-color-fix 2026-06-30 17:41:13 +08:00
WuYongJie
c26113a6ec refactor(ai-white-background): 合并生成设置为单一材质方案区块
将「目标与源图」「单张修正」「材质方案」三个并列折叠区块合并为一个
「材质方案」区块,简化用户心智模型:

- 目标花色(本次生图主色) + 源条码 + 禁用原因 上移到材质方案区顶部
- 部位材质定义 保留为材质方案核心
- 各视图精修指令(原单张修正) 收纳为材质方案底部子区
- 「重新生成」按钮改为弹内联编辑气泡: 预填该视图精修提示词,
  可改可不改后确认重做, 不再跳转到独立单张修正区
- 简化 AiWhiteBackgroundSettingsSectionId 类型为单一 materials

纯前端 UI 重组, 不动后端 API/服务/数据模型。
type-check 通过, 19 个相关测试全部通过。
2026-06-30 17:39:51 +08:00
WuYongJie
960f867744 merge: promote opening-code oa prefill to main 2026-06-30 16:26:19 +08:00
WuYongJie
6d89e1c2c3 feat(intake): prefill create form from oa opening code 2026-06-30 16:25:09 +08:00
WuYongJie
70594a287d fix(intake): polish development toolbar controls 2026-06-30 14:31:39 +08:00
WuYongJie
4629eea670 fix(intake): keep development table header sticky 2026-06-30 14:13:51 +08:00
WuYongJie
2b0f3758bb feat(intake): add processed quick scope 2026-06-30 09:49:40 +08:00
WuYongJie
614e568763 fix(intake): align non-integrated quick scopes 2026-06-29 21:33:03 +08:00
WuYongJie
e0bc1db6bf feat(external): 宽表强制一条码一行保护(DISTINCT ON barcode_business_key)
根因:external.product_catalog_wide_v1 的 FROM sku_source 行粒度是 SKU,
barcode 只是字段。当前 43261 行 barcode 全唯一是数据巧合(formal 0 行 +
intake ready 每条码唯一),视图定义没强制一条码一行。一旦出现一条码多 SKU
(正式 ProductSKU 落库 + intake ready 同条码,或 intake 内部同条码多行),
宽表就会重复,污染外部系统 barcode 主同步键。

修复:复用项目已有 DISTINCT ON(barcode_business_key) 模式(latest_quality /
limit_by_barcode CTE),把原主查询整体包成 catalog_by_sku CTE,外层加
DISTINCT ON (barcode_business_key)。优先级:FORMAL > INTAKE_READY,
updated_at 最新 > created_at 最新。

末尾追加 catalog_source 字段输出(数据来源标记,对外部系统有用)。
用 CREATE OR REPLACE VIEW 保留 external_reader 已有 GRANT 权限。

生产验证:行数 43261 不变,0 重复 barcode 组,字段完整。
2026-06-29 19:40:23 +08:00
WuYongJie
9c04e39aaf Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-ai-white-background-color-fix 2026-06-29 16:05:05 +08:00
WuYongJie
a9ec724127 fix(intake): rename modular tag to modular component 2026-06-29 15:59:57 +08:00
WuYongJie
2f59b46444 revert(storage): 恢复CDN直连307跳转,撤回流式转发
之前的流式转发改动(66928576)是误诊。真实根因是七牛AK/SK失效:
- 密钥有效时,307重定向到签名URL → 七牛返回200 + CORS头(Access-Control-Allow-Origin:*)
- 密钥失效时,签名URL无效 → 七牛返回401 → 浏览器误判为ORB拦截

七牛CDN本身已正确配置CORS,不存在跨域问题。流式转发反而违背'图片走CDN减轻
香港带宽压力'的架构目标(图片又挤回香港管道)。现撤回流式转发,恢复307直连CDN。
2026-06-29 15:44:05 +08:00
WuYongJie
906fd5b8e6 Merge remote-tracking branch 'origin/main' into codex/wuyongjie-ai-white-background-color-fix
# Conflicts:
#	furniture-3d-system/src/app/intake/oa-board/OaBoardClient.tsx
2026-06-29 15:01:22 +08:00
WuYongJie
98e89f1573 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-ai-white-background-color-fix 2026-06-29 13:59:38 +08:00
WuYongJie
66928576ac fix(storage): 图片代理改回流式转发修复跨域ORB拦截
根因:buildObjectReadResponse 默认对 https URL 返回 307 重定向到七牛 CDN,
浏览器对 cp.jiansun.vip → qnycdn.jiansun.vip 的跨域图片响应触发
ERR_BLOCKED_BY_ORB,导致所有 <img> 加载失败(图片全炸)。

修复:将 redirectHttps 默认值从 true 改为 false,强制走流式代理。
storage/object、intake assets 两处显式传 false 保持一致。
其余 6 处调用点自动继承新默认值,无需逐个改。

代价:Node 重新承担图片流式转发压力(后续签名URL下发优化解决)。
2026-06-29 13:58:51 +08:00
WuYongJie
5492ba316b fix(intake): prevent barcode print stretching 2026-06-29 13:30:03 +08:00
WuYongJie
772079750d fix(auth): allow redirect back to core workspaces 2026-06-29 13:30:03 +08:00
WuYongJie
cec5e2fcb2 fix(intake): prevent barcode print stretching 2026-06-29 13:11:13 +08:00
WuYongJie
b3d4a6f2ed fix(auth): allow redirect back to core workspaces 2026-06-29 13:11:12 +08:00
WuYongJie
391a085c2c fix(intake): derive product categories from secondary category 2026-06-29 11:54:38 +08:00
WuYongJie
b234134334 fix(intake): derive product categories from secondary category 2026-06-29 09:40:21 +08:00
WuYongJie
a0d597e14e refactor(ai-render-workbench): split workbench client modules 2026-06-26 21:25:20 +08:00
WuYongJie
3926fa4f26 refactor(ai-render-workbench): split workbench client modules 2026-06-26 21:18:28 +08:00
WuYongJie
443f56b099 fix(oa-board): compact filters and enable image preview 2026-06-26 17:56:56 +08:00
jingchuan
9c696e1947 fix(oa-board): stabilize filter and stage sync 2026-06-25 18:00:09 +08:00
WuYongJie
8cc0dc5218 fix(ai-white-background): 无花色借源颜色污染修复 + 工作区重排
根因:无花色 SKU 借源生图时,"未配置花色"占位符被当真实色传入,
后端 normalize 后 prompt 退化为"保持源图原色",AI 跟随源条码原图色调
生成错误颜色(SZ024203V01 案例:借胡桃色源图生成出胡桃色结果)。

A 方案(解开死结):
- Workspace 新增 inlineTargetColor 状态,用户可手动指定本次生图颜色
- isEffectiveAiWhiteBackgroundTargetColor 识别"未配置花色"等占位符为无效色
- targetColor 改为 effectiveTargetColor,占位符不再绕过空值校验
- Panel 目标花色区改为条件可输入:SKU 无色时显示输入框

B 方案(借源必填):
- 借源模式 + 无色时给出明确警示"必须指定目标颜色,否则跟随源条码色调"
- 输入框用警示色高亮

工作区重排(解决"操作小小一块"):
- 设置区加宽 310px→380px,字号放大 10/12px→11/13px
- 视图卡片放大 164/190px→180/210px,间距 gap-2→gap-3
- "目标与源图"小节默认展开(最常用不该藏)

验证:type-check 通过,白底图 61 个测试全过(含新增 3 个 A/B 方案验证)
2026-06-25 15:56:02 +08:00
WuYongJie
c172ce87bc docs(agents): 对齐当前项目真相并固化主线部署流程
- 全局规则引用改为 agent 中性(ZCode/Codex 各取所需)
- 技术栈口径改为以 package.json 为准,补 Tailwind v4 等漏列项
- 模型名 SKU 统一修正为 ProductSKU(业务概念与 Prisma 模型名对齐)
- 主链目录补充 approval-workflows/materials/mobile
- 对象存储路径修正为 src/lib/storage.ts(文件而非目录)
- 第 3 节新增主线部署标准流程(支线→xuxiake→main→部署)
- 合并验证与规范审查两节,修正不存在的 storage:gc 参数
2026-06-25 15:55:33 +08:00
WuYongJie
35ad68bf5e fix(storage): expose client runtime storage config 2026-06-24 18:28:32 +08:00
WuYongJie
caa5ca3c34 feat(draft-groups): add direct modular sku workflow 2026-06-24 17:58:54 +08:00
WuYongJie
e699dbb4b6 refactor(storage): remove legacy s3 aliases 2026-06-24 17:00:22 +08:00
WuYongJie
d42f4311e6 fix(storage): make gc provider aware 2026-06-24 16:54:23 +08:00
WuYongJie
d7d9317977 fix(storage): require explicit object provider 2026-06-24 16:44:01 +08:00
WuYongJie
13f8d94224 fix(intake): allow read-only adaptable brand viewing 2026-06-24 15:39:17 +08:00
WuYongJie
720644869c fix(ai-white-background): inherit related refinement context 2026-06-24 13:36:12 +08:00
WuYongJie
47a83a9f16 fix(ai-white-background): use generated views for refinement consistency 2026-06-24 11:25:08 +08:00
WuYongJie
350936d3c3 feat(asset-center): add asset family name code 2026-06-24 10:57:56 +08:00
WuYongJie
864b07c061 Align draft group deletion with modular detail references 2026-06-24 10:57:50 +08:00
WuYongJie
14ded3cab8 fix(ai-white-background): lock refinement camera prompts 2026-06-24 09:42:38 +08:00
WuYongJie
0eaa0ee023 Merge remote-tracking branch 'origin/main' 2026-06-24 09:38:56 +08:00
WuYongJie
708d28cd94 fix(intake): narrow brand center department permissions 2026-06-23 17:31:23 +08:00
WuYongJie
1188165fa1 fix(intake): narrow brand center department permissions 2026-06-23 17:25:03 +08:00
WuYongJie
2ba5062846 fix(intake): use basic info permission for sku download 2026-06-23 16:46:58 +08:00
WuYongJie
c9dd228c4b fix(ai-render): hide viewport helpers in references 2026-06-23 15:24:20 +08:00
WuYongJie
a62c1ee3b9 fix(ai-render): lock white render reference priority 2026-06-23 15:24:20 +08:00
WuYongJie
6702fd2995 fix(intake): lower document sidebar layer 2026-06-23 15:24:20 +08:00
WuYongJie
adba625280 fix(intake): lower document sidebar layer 2026-06-23 15:18:19 +08:00
WuYongJie
0d24fb3cef fix(draft-group): fit series spu field width 2026-06-23 14:43:11 +08:00
WuYongJie
1093772945 fix(ai-white-background): preserve generation progress 2026-06-23 14:13:13 +08:00
WuYongJie
3a17165562 test(draft-group): remove obsolete packaging fallback scaffolding 2026-06-23 13:01:27 +08:00
WuYongJie
534edddd11 feat(draft-group): refine assembly handoff linkage 2026-06-23 13:01:27 +08:00
WuYongJie
102a68ceb4 fix(assisted-assembly): align grouped barcode quantities
Sync draft-group grouped barcode quantity handling with assisted assembly and cover repeated grouped component sets in tests.
2026-06-23 13:01:27 +08:00
WuYongJie
7eef92219f Merge remote-tracking branch 'origin/main' into codex/wuyongjie-ai-wbg-upload-log 2026-06-23 11:40:56 +08:00
WuYongJie
9578c86201 fix(ai-white-background): trace batch generation views 2026-06-23 11:40:45 +08:00
WuYongJie
ff4f2bc962 fix(ai-render): add manual white-background detail references 2026-06-23 10:10:28 +08:00
WuYongJie
6d721d7b9a fix(ai-render): stabilize white render structure references 2026-06-23 10:10:28 +08:00
WuYongJie
ac03ec27de fix(intake): 串行补白底图生成 2026-06-23 09:07:48 +08:00
WuYongJie
c5733b8377 style(intake): 收纳AI白底图生成设置 2026-06-22 19:36:46 +08:00
WuYongJie
a77c87df35 fix(intake): 精确匹配AI白底图生成文件 2026-06-22 18:32:34 +08:00
WuYongJie
80e343b2d3 fix(intake): 优化产品资料与AI白底图界面密度 2026-06-22 17:40:22 +08:00
WuYongJie
c613a98b61 fix(intake): 增加产品资料缩略图悬浮预览 2026-06-22 17:02:33 +08:00
WuYongJie
2a185e6e79 fix(intake): 优化AI白底图反馈体验 2026-06-22 15:45:49 +08:00
WuYongJie
714036c751 fix(intake): 补齐AI白底图上传成功闭环 2026-06-22 14:20:42 +08:00
WuYongJie
5613f349a7 fix(intake): 为AI白底图素材库调用加超时 2026-06-22 13:32:56 +08:00
WuYongJie
e88bb2c8a5 fix(intake): 修复AI白底图素材库上传字段 2026-06-22 13:14:24 +08:00
WuYongJie
7eb044900c chore(intake): 记录AI白底图素材库上传失败 2026-06-22 12:02:11 +08:00
WuYongJie
531a84f5ab fix(intake): unify existing spu selection scope 2026-06-22 11:23:32 +08:00
WuYongJie
c144d836e9 fix(intake): 优化AI白底图单张修正 2026-06-22 11:12:11 +08:00
WuYongJie
17740ac822 fix(oa-board): 修复审批阶段状态类型收窄 2026-06-20 17:56:14 +08:00
jingchuan
2bd861527f fix(oa-board): align approval stage and owner resolution 2026-06-20 17:42:23 +08:00
WuYongJie
1fad12d718 fix(oa-board): align filter param parsing types 2026-06-20 16:31:39 +08:00
jingchuan
1cff836933 feat(oa-board): refine flow detail filters and layout 2026-06-20 16:10:46 +08:00
WuYongJie
9b5f2d9ee8 fix(ai-render): stabilize workbench white render pipeline 2026-06-20 16:06:11 +08:00
WuYongJie
710a208ba5 feat(intake): enhance product limit workbook flow 2026-06-20 14:12:12 +08:00
WuYongJie
3cb228fc16 feat(intake): scope sku batch workflow by batch 2026-06-20 11:38:52 +08:00
WuYongJie
3e860e0472 fix(asset-center): type mount point tuple 2026-06-20 08:47:21 +08:00
WuYongJie
251930b81d Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-06-18 18:19:33 +08:00
WuYongJie
09c4e75f0e fix(asset-center): allow editing available model info 2026-06-18 18:19:23 +08:00
WuYongJie
30698a9480 feat(intake): 完善AI白底图重生修正交互 2026-06-18 15:15:34 +08:00
WuYongJie
875e8f3fc0 fix(intake): 防止AI白底图结果混入源图 2026-06-18 14:37:44 +08:00
WuYongJie
7bc7008799 fix(intake): 恢复AI白底图借源会话 2026-06-18 13:57:59 +08:00
WuYongJie
1af2c401d3 Merge remote-tracking branch 'origin/main' into codex/release-ai-white-background-structure 2026-06-18 13:25:16 +08:00
WuYongJie
dd4661ff6e Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/release-ai-white-background-structure 2026-06-18 13:20:18 +08:00
WuYongJie
394d2c2ce6 fix(intake): 修复AI白底图借源正视图丢失 2026-06-18 13:20:00 +08:00
WuYongJie
381999d69b fix(intake): stop modular detail fetch regressions 2026-06-18 11:26:59 +08:00
WuYongJie
4139f712cc fix(intake): stop modular detail fetch regressions 2026-06-18 11:25:35 +08:00
WuYongJie
013256dfbd Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/release-ai-white-background-structure 2026-06-18 11:12:02 +08:00
WuYongJie
e3217c6bc1 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-ai-render-texture-resign 2026-06-18 11:08:41 +08:00
WuYongJie
853d22d044 feat(intake): 优化AI白底图多视角生成 2026-06-18 11:08:29 +08:00
WuYongJie
7b62b0e291 fix(intake): require sample measurement before pricing push 2026-06-18 09:34:10 +08:00
WuYongJie
13b94e4263 fix(intake): require sample measurement before pricing push 2026-06-18 09:32:47 +08:00
WuYongJie
62eaf50800 优化AI白底图工作区布局 2026-06-17 19:36:20 +08:00
WuYongJie
d5a1427fff Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-ai-render-texture-resign 2026-06-17 18:38:47 +08:00
WuYongJie
acd7756b2f fix(intake): allow modular parent sku push without cost price 2026-06-17 18:36:19 +08:00
WuYongJie
31391ee27e fix(draft-groups): harden modular detail navigation 2026-06-17 18:36:19 +08:00
WuYongJie
0fdd34ec24 fix(intake): 避免浏览触发自动保存 2026-06-17 18:30:51 +08:00
WuYongJie
d727459273 fix(intake): 按条码隔离实拍九视图 2026-06-17 17:58:54 +08:00
WuYongJie
0d2c648be4 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-ai-render-texture-resign
# Conflicts:
#	furniture-3d-system/src/app/intake/non-integrated/workspaces/ai-white-background/NonIntegratedAiWhiteBackgroundPanel.tsx
#	furniture-3d-system/src/app/intake/non-integrated/workspaces/ai-white-background/NonIntegratedAiWhiteBackgroundWorkspace.tsx
#	furniture-3d-system/src/app/intake/non-integrated/workspaces/documents/ProductPhotoWorkspace.tsx
2026-06-17 17:43:02 +08:00
WuYongJie
af601ddb01 feat(intake): 完善 AI 白底图材质方案工作区 2026-06-17 17:35:43 +08:00
WuYongJie
0a813f2557 Merge remote-tracking branch 'origin/main' into codex/wuyongjie-target-barcode-label-fix 2026-06-17 16:26:14 +08:00
WuYongJie
f3033b0886 fix(intake): show barcode first in photo sku tree 2026-06-17 16:22:01 +08:00
WuYongJie
05524721f3 feat(ai-render): make eight-view renders structure-aware 2026-06-17 16:11:47 +08:00
WuYongJie
286c4c743c fix(intake): require source selection before borrowing images 2026-06-17 15:43:42 +08:00
WuYongJie
26b7e9e8d1 fix(intake): show source barcode values in ai white panel 2026-06-17 14:51:49 +08:00
WuYongJie
97986fd530 fix(intake): show actual target barcode in ai white panel 2026-06-17 14:42:48 +08:00
WuYongJie
2c2baf4b64 fix(intake): show styleless sku barcodes in photo workspace 2026-06-17 14:10:18 +08:00
WuYongJie
b2aaf2acc0 feat(ai-render): add sku-scoped workbench generation 2026-06-17 14:07:03 +08:00
WuYongJie
9bdc6ff0bd fix(intake): support stock spu sku continuation 2026-06-17 12:13:52 +08:00
WuYongJie
ac24f0546c fix(intake): normalize barcode print data source 2026-06-17 11:15:11 +08:00
WuYongJie
882e112a2a fix(ai-render): extend external image generation timeout 2026-06-17 09:44:20 +08:00
WuYongJie
88fd9bc231 merge: promote ai white background barcode backfill 2026-06-17 09:20:48 +08:00
WuYongJie
509c5cf730 merge: promote ai white background barcode backfill 2026-06-17 09:20:00 +08:00
WuYongJie
1b90df2b20 feat(intake): support ai white background barcode backfill 2026-06-17 09:18:58 +08:00
WuYongJie
7de12951b7 fix(ai-render): persist shared workbench data 2026-06-16 20:27:25 +08:00
WuYongJie
2cb01feafd merge: promote ai white background generation hardening 2026-06-16 19:48:22 +08:00
WuYongJie
f4f956bc8c fix(intake): harden ai white background generation 2026-06-16 19:47:51 +08:00
WuYongJie
c6b53f4cb8 Merge remote-tracking branch 'origin/codex/wuyongjie-ai-render-texture-resign' into HEAD
# Conflicts:
#	furniture-3d-system/src/server/services/oa-board.service.ts
2026-06-16 18:44:03 +08:00
WuYongJie
cdd64ade24 fix(oa-board): normalize timeline metric typing 2026-06-16 18:35:42 +08:00
WuYongJie
815ea75745 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-ai-render-texture-resign 2026-06-16 18:31:13 +08:00
WuYongJie
fe139898e4 feat(intake): persist ai white background derived sku links 2026-06-16 18:31:05 +08:00
WuYongJie
c4caf7fc8e feat(intake): persist barcode print data 2026-06-16 18:14:38 +08:00
WuYongJie
15207d11fa fix(oa-board): type timeline metrics for build 2026-06-16 18:11:47 +08:00
jingchuan
03bdc6860f feat(oa-board): refine detail flow monitor 2026-06-16 18:05:26 +08:00
WuYongJie
5ec794e13e Merge remote-tracking branch 'origin/codex/wuyongjie-ai-render-texture-resign' into HEAD 2026-06-16 18:00:12 +08:00
jingchuan
874e67a185 feat(oa-board): refine detail flow monitor 2026-06-16 17:57:50 +08:00
WuYongJie
d6f9a861fc Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-ai-render-texture-resign 2026-06-16 17:56:13 +08:00
WuYongJie
f4d93ddaa2 feat(intake): 支持AI白底图派生同批次SKU 2026-06-16 17:55:53 +08:00
WuYongJie
2e9f163c3a merge: promote ai render sku-bound tasks 2026-06-16 17:10:59 +08:00
WuYongJie
c2f0898784 feat(ai-render): bind render tasks to sku 2026-06-16 17:10:16 +08:00
WuYongJie
35ef013916 merge: promote AI render material signed urls to main 2026-06-16 16:21:30 +08:00
WuYongJie
fce3f46a04 merge: promote AI render material signed urls 2026-06-16 16:19:44 +08:00
WuYongJie
1954226df7 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-ai-render-texture-resign 2026-06-16 16:17:20 +08:00
WuYongJie
7b94f30108 fix(storage): 延长七牛私有读链接有效期 2026-06-16 16:12:55 +08:00
WuYongJie
18dc1aa8ed fix(ai-render): 直传材质贴图签名URL 2026-06-16 16:09:57 +08:00
WuYongJie
e2a97af404 feat(ai-render): finalize model-locked image generation 2026-06-16 16:08:50 +08:00
WuYongJie
1c055d97c5 feat(ai-render): finalize model-locked image generation 2026-06-16 16:08:09 +08:00
WuYongJie
ca2eb8c4a6 merge: 稳定非集成访问链路 2026-06-16 15:09:47 +08:00
WuYongJie
e3fa838b39 merge: 稳定非集成访问链路 2026-06-16 15:09:08 +08:00
WuYongJie
d374470151 fix: stabilize intake navigation requests 2026-06-16 15:08:06 +08:00
WuYongJie
f36c020902 fix(ai-render): 重新签发材质贴图引用 2026-06-16 14:47:10 +08:00
WuYongJie
6eb624f8e4 fix(intake): simplify AI white product photo controls 2026-06-16 13:53:32 +08:00
WuYongJie
351a14d3ad fix(auth): allow restricted users into AI render workbench 2026-06-16 13:26:59 +08:00
WuYongJie
a5fe03b721 fix(ai-render): normalize UV axis config values 2026-06-16 13:05:31 +08:00
WuYongJie
0bfa19ae9c merge: promote AI render workbench 2026-06-16 12:55:27 +08:00
WuYongJie
5ae0b39192 feat(ai-render): add model-locked render workbench 2026-06-16 12:52:22 +08:00
WuYongJie
f12832ee69 merge: fix copied brand SPU reservation 2026-06-16 11:37:32 +08:00
WuYongJie
f49b0c6036 Merge remote-tracking branch 'origin/main' into codex/fix-copy-spu-20260616-111430 2026-06-16 11:36:30 +08:00
WuYongJie
d6b72c7060 fix: prevent copied brand products from reserving new SPU 2026-06-16 11:36:09 +08:00
WuYongJie
88182d671b feat(intake): add barcode print supplier workflow 2026-06-16 11:32:57 +08:00
WuYongJie
1f767cc76f merge: promote AI 白底图花色选择布局修复到主线 2026-06-16 11:28:45 +08:00
WuYongJie
3740bec0d2 fix(intake): constrain AI white target finish picker 2026-06-16 11:25:33 +08:00
WuYongJie
72b49620d8 merge: promote AI 白底图工作区布局修复到主线 2026-06-16 11:11:16 +08:00
WuYongJie
4d137e8734 fix(intake): 修复 AI 白底图工作区布局 2026-06-16 11:09:37 +08:00
WuYongJie
74ccbb3a1e Merge remote-tracking branch 'origin/main' into codex/xuxiake-ai-render-mainline-20260616 2026-06-16 10:47:04 +08:00
WuYongJie
6796944dc8 feat(ai-render): sync render workbench baseline 2026-06-16 10:46:36 +08:00
WuYongJie
6ac6c99811 merge: fix AI 白底图拖动看图类型检查 2026-06-16 10:22:08 +08:00
WuYongJie
d936895438 fix(intake): 修复 AI 白底图拖动状态类型检查 2026-06-16 10:21:10 +08:00
WuYongJie
a5ea1c1889 merge: promote AI 白底图拖动看图到主线 2026-06-16 10:11:29 +08:00
WuYongJie
ce6c403637 fix(intake): 支持拖动查看放大的 AI 白底图原图 2026-06-16 10:09:00 +08:00
WuYongJie
83f02257d1 merge: promote AI 白底图滚轮缩放到主线 2026-06-16 09:55:50 +08:00
WuYongJie
b2fea563ca fix(intake): 支持滚轮缩放 AI 白底图原图 2026-06-16 09:46:38 +08:00
WuYongJie
fec144e6a9 merge: promote AI 白底图黑色遮罩看图到主线 2026-06-16 09:32:34 +08:00
WuYongJie
eda6600a6f fix(intake): 改为黑色遮罩查看 AI 白底图原图 2026-06-16 09:31:37 +08:00
WuYongJie
b8cf682edc merge: promote AI 白底图页内原图查看到主线 2026-06-15 21:20:14 +08:00
WuYongJie
285b1fb2d9 fix(intake): 改为页内查看 AI 白底图原图 2026-06-15 21:19:33 +08:00
WuYongJie
2704d33b6e merge: promote AI 白底图低噪音调整到主线 2026-06-15 20:51:44 +08:00
WuYongJie
57279aa91f fix(intake): 降低 AI 白底图界面噪音 2026-06-15 20:47:31 +08:00
WuYongJie
5e1efa7a64 merge: promote AI 白底图界面改造到主线 2026-06-15 20:20:03 +08:00
WuYongJie
562cf5bba3 feat(intake): 重写 AI 白底图工作区界面 2026-06-15 20:10:32 +08:00
WuYongJie
5fb0028b31 merge: promote clean consolidation to main 2026-06-15 20:00:09 +08:00
WuYongJie
33c2e2899f Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-clean-consolidation-20260615 2026-06-15 19:53:40 +08:00
WuYongJie
5a0593e952 feat(auth): add supplier barcode account access 2026-06-15 19:52:47 +08:00
WuYongJie
e5175cabb0 merge: sync ai white background workspace 2026-06-15 19:19:43 +08:00
WuYongJie
94432e04d3 feat(intake): 将 AI 白底图升级为产品实拍工作区 2026-06-15 19:18:10 +08:00
WuYongJie
2a84f66951 feat(intake): add barcode print workbench 2026-06-15 19:15:22 +08:00
WuYongJie
6b4dcbdd31 feat(asset-center): make modular assets immediately available 2026-06-15 18:51:19 +08:00
WuYongJie
2e42fb220f feat(draft-groups): push pre sku to series product 2026-06-15 18:37:54 +08:00
WuYongJie
ecc294e79f fix(storage): fallback object reads without configured prefix 2026-06-15 18:32:39 +08:00
WuYongJie
9dbb61cdd1 feat(ai-render): add render workbench 2026-06-15 18:28:59 +08:00
WuYongJie
2d789c596c merge: sync storage gc negative status retry 2026-06-15 17:53:14 +08:00
WuYongJie
c36d77fba8 fix(storage): 重试七牛负状态码错误 2026-06-15 17:52:52 +08:00
WuYongJie
d4c20518b3 merge: sync storage gc reserved prefix guard 2026-06-15 17:36:23 +08:00
WuYongJie
8f1751c917 fix(storage): 支持 GC 运行时保留前缀 2026-06-15 17:36:10 +08:00
WuYongJie
f0b0c699ef merge: sync storage gc retry hardening 2026-06-15 17:27:23 +08:00
WuYongJie
cb3d8434e0 fix(storage): 增加七牛 GC 请求重试 2026-06-15 17:27:07 +08:00
WuYongJie
14c632d063 merge: sync storage gc qiniu sdk fix 2026-06-15 17:20:46 +08:00
WuYongJie
169a8df718 fix(storage): 兼容七牛 SDK 默认导出 2026-06-15 17:20:30 +08:00
WuYongJie
3b891b5460 merge: sync oa board type fix from xuxiake mainline 2026-06-15 17:06:14 +08:00
WuYongJie
c180cf2b18 fix(oa-board): 补齐同步看板类型导出 2026-06-15 17:05:41 +08:00
WuYongJie
e7e2a9368d merge: promote storage gc governance to main 2026-06-15 16:58:25 +08:00
WuYongJie
b26a30a8ae merge: promote storage gc governance to xuxiake mainline 2026-06-15 16:57:10 +08:00
WuYongJie
24c65f4da8 feat(storage): 增加七牛垃圾文件治理 2026-06-15 16:51:57 +08:00
WuYongJie
0eda70edd8 merge xuxiake mainline oa board and ai asset fixes 2026-06-15 16:44:34 +08:00
jingchuan
3ee7122380 feat(oa-board): add new product flow monitor 2026-06-15 15:58:09 +08:00
WuYongJie
829f828bbe fix(intake): 延长 AI 白底图请求等待 2026-06-15 15:51:15 +08:00
WuYongJie
455686e1e3 fix(intake): 降低非集成素材读取超时 2026-06-15 15:30:39 +08:00
WuYongJie
68a17b1dff feat(intake): 对接 AI 白底图花色改色与素材库上传 2026-06-15 14:58:37 +08:00
WuYongJie
319d61e41a merge xuxiake mainline asset preview permission fix 2026-06-15 12:11:07 +08:00
WuYongJie
dcfa96206a fix(intake): allow asset previews with view permission 2026-06-15 12:10:50 +08:00
WuYongJie
5f371c7a70 merge xuxiake mainline image proxy fix 2026-06-15 11:54:47 +08:00
WuYongJie
35740a7bc5 fix(intake): proxy non-integrated asset reads 2026-06-15 11:54:29 +08:00
WuYongJie
5fc719ce88 merge: promote platform material flow to main 2026-06-15 11:15:55 +08:00
WuYongJie
3d9aca430d merge: 稳定移动端与素材访问链路 2026-06-15 11:15:29 +08:00
WuYongJie
f802d65490 merge: sync xuxiake mainline before platform material fix
# Conflicts:
#	furniture-3d-system/src/app/assisted-assembly/mobile/AssistedAssemblyMobileView.tsx
#	furniture-3d-system/src/app/mobile/HomeMobileView.tsx
#	furniture-3d-system/src/server/services/asset-center.service.ts
2026-06-15 11:03:11 +08:00
WuYongJie
77a78f4938 fix(platform): 稳定移动端与素材访问链路 2026-06-15 11:02:10 +08:00
WuYongJie
17c915f0a6 merge: promote workbench nav loading fix to main 2026-06-15 10:38:52 +08:00
WuYongJie
4f8d18f454 merge: 修复工作台导航首屏权限加载态 2026-06-15 10:38:12 +08:00
WuYongJie
5fbec8a7c9 fix(layout): 修复工作台导航首屏权限加载态 2026-06-15 10:37:29 +08:00
WuYongJie
12a90477f7 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-promote-asset-center-sku-group-fix-20260615 2026-06-15 08:43:02 +08:00
WuYongJie
a611640927 merge: sync asset center sku group fix with mainline 2026-06-15 08:42:08 +08:00
WuYongJie
c58c7aaa22 fix(asset-center): read retired sku groups from metadata 2026-06-13 23:29:44 +08:00
WuYongJie
55d92d4e75 fix(ui): show immediate workbench nav feedback 2026-06-13 18:34:01 +08:00
WuYongJie
62da3f6993 fix(ui): show immediate workbench nav feedback 2026-06-13 18:33:40 +08:00
WuYongJie
79cc190a0f merge: promote nav smoothness optimization to main 2026-06-13 18:05:18 +08:00
WuYongJie
7ba1e86410 merge: promote nav smoothness optimization 2026-06-13 18:04:36 +08:00
WuYongJie
713b8986c9 fix(ui): smooth intake and quality navigation 2026-06-13 18:04:05 +08:00
WuYongJie
6a4b8775de merge: promote intake filter overflow fix to main 2026-06-13 16:53:23 +08:00
WuYongJie
f3d38be04b merge: promote intake filter overflow fix 2026-06-13 16:53:12 +08:00
WuYongJie
5ea46cdc68 fix(ui): prevent intake mobile filter overflow 2026-06-13 16:53:01 +08:00
WuYongJie
7f69e8271a merge: promote intake overflow fix to main 2026-06-13 16:45:22 +08:00
WuYongJie
6897c38f97 merge: promote intake overflow fix 2026-06-13 16:45:02 +08:00
WuYongJie
e973b1a0a1 fix(ui): constrain intake table overflow on mobile 2026-06-13 16:44:42 +08:00
WuYongJie
a7ad991c59 merge: promote smoothness fixes to main 2026-06-13 16:35:45 +08:00
WuYongJie
984ae53af3 merge: promote smoothness and migration alignment 2026-06-13 16:35:09 +08:00
WuYongJie
df05999c0f fix(ui): stabilize mobile hydration and layout overflow 2026-06-13 16:33:41 +08:00
WuYongJie
b9476733fc fix(db): add asset sku group restore migration 2026-06-13 16:33:36 +08:00
WuYongJie
0b5ae56dd5 merge: promote storage qiniu direct access normalization to main 2026-06-13 10:31:57 +08:00
WuYongJie
3972ca2edd merge: promote storage qiniu direct access normalization 2026-06-13 10:31:09 +08:00
WuYongJie
100e90a3be fix(storage): 统一公开资源直链与持久化地址归一 2026-06-13 10:24:41 +08:00
WuYongJie
e1b78d602e fix: intake 所有工作区表格表头 sticky 固定
- 所有 workspace 表格 thead 添加 sticky top-0 z-10,滚动时表头固定
- 外层容器 overflow-hidden 改为 overflow-auto,配合滚动生效
- 基础信息 SKU 表格合并双层 div 为单层,添加 max-h-[60vh] 高度约束
- BOM 表格同步添加 sticky thead
2026-06-12 18:40:21 +08:00
WuYongJie
e7d2c8626c merge: 解决冲突,保留使用 SharedAssetFamilyDTO 的类型修复 2026-06-12 17:30:49 +08:00
WuYongJie
034817f119 fix(asset-center): 修复移动端 AssetFamilyDTO 类型不兼容导致生产构建失败 2026-06-12 17:25:56 +08:00
WuYongJie
5e5687dcc1 fix: require manual confirmation for factory workflows 2026-06-12 17:16:47 +08:00
WuYongJie
05d90fe57e feat(ai-render): 支持多材质贴图传入外部生图API
- external-image-generate: 新增 additionalReferenceImages 字段支持多文件上传
- assisted-assembly-ai-render: 自动提取 assetSnapshot 中的材质贴图
- 按 materialConfig.id 去重聚合,最多 6 种材质
- 构建材质映射描述追加到 prompt,告诉 AI 每张贴图对应哪个部位
- 支持 FormData 混合模式(文件 + URL)和 curl 降级
2026-06-12 16:37:56 +08:00
WuYongJie
a2e9335fc5 feat: Stitch 移动端视觉重构 — 5 页面全部完成独立移动端视图
新增独立移动端视图文件(遵循用户要求:不堆砌在原臃肿文件中):
- asset-center/mobile/AssetCenterMobileView.tsx (274行) — 系列选择 + Tab + 搜索 + 卡片列表
- assisted-assembly/mobile/AssistedAssemblyMobileView.tsx (148行) — 系列列表 + 工作区提示
- draft-groups/mobile/DraftGroupsMobileView.tsx (173行) — 搜索 + 统计 + 草稿列表

原文件改动最小化(仅 +3 行导入 + 15 行条件分支):
- AssetCenterClient.tsx (7690行) → 添加 useBreakpoint + <AssetCenterMobileView />
- AssistedAssemblyClient.tsx (1580行) → 添加 useBreakpoint + <AssistedAssemblyMobileView />
- DraftGroupsFormalClient.tsx (4943行) → 添加 useBreakpoint + <DraftGroupsMobileView />

全局 Stitch 基础设施(已提交):
- globals.css: stitch-card 圆角修正 2px→12px,新增 20+ 原子类
- AppShell: 移动端背景 #F8F9FA
- MobileTabBar: 精确 Stitch BottomNavBar
- PlatformSplitLayout: 移动端 #F8F9FA 背景
- IntakeWorkspaceShell: 移动端紧凑间距
- StitchMobileLayout: 可复用布局组件

所有改动仅 max-lg 断点生效,桌面端零影响
2026-06-12 16:37:56 +08:00
WuYongJie
f27aa44ec5 refactor: 拆分移动端视图为独立文件,避免臃肿
- HomeClient.workspace.tsx: 925行 → 738行,移动端布局拆为 mobile/HomeMobileView.tsx (445行独立文件)
- IntakeWorkspaceTabBar.tsx: 109行 → 77行,移动端Tab拆为 IntakeWorkspaceTabBarMobile.tsx (56行独立文件)
- 原文件仅保留一行条件引用 <HomeMobileView /> / <IntakeWorkspaceTabBarMobile />
- 桌面端代码零改动
2026-06-12 16:37:56 +08:00
WuYongJie
03d4ee83e1 style: Stitch 移动端像素级复刻
- AppShell 移动端顶栏改为 Stitch TopAppBar(80% glass + 24px blur)
- globals.css 新增 Stitch 原子类库
- MobileTabBar 对齐 Stitch BottomNavBar
- 全部 max-lg 断点,桌面端零影响
2026-06-12 16:37:56 +08:00
WuYongJie
1b992cb89a style: MobileTabBar 对齐 Stitch BottomNavBar + 移动端卡片无边
- MobileTabBar: bg-80% opacity + backdrop-blur-24px + 微阴影
- 移除 TabBar 大圆角激活 pill,改为纯文字+图标
- section 单列模式移动端 rounded-none(全宽无边)
- mainContent 模式左侧栏移动端 rounded-none
2026-06-12 16:37:56 +08:00
WuYongJie
b1deae4c73 refactor: 对齐 Stitch 设计稿视觉规范
- section 改为透明布局容器,背景用 page background
- 侧边栏/主内容/右面板各自独立:rounded-xl + shadow
- 面板间用 gap-3 间距,不再依赖边框分隔
- metrics 芯片移除边框,改用白色卡片 + 微阴影
- 标题使用 Manrope 字体(font-family-display)
- 配色使用 CSS 变量,对齐 Stitch surface 层级
2026-06-12 16:37:56 +08:00
WuYongJie
e333f68cc3 refactor: PlatformSplitLayout 应用 Stitch 设计规范
- 移除 section 的 border/shadow/rounded-[2rem](No-Line Rule)
- 侧边栏和右面板移除 border,改用表面层级色差分离
- 背景改用 surface-container-low 色调
- 移除 metrics 芯片的边框
- IntakeMainLayout 移动端隐藏右面板
2026-06-12 16:37:56 +08:00
WuYongJie
1a8367a9da refactor: split intake detail controllers 2026-06-12 13:21:37 +08:00
WuYongJie
582d2ee8cb fix: allow product factory MOQ sync saves 2026-06-12 12:39:15 +08:00
WuYongJie
307d92b7f5 fix: PlatformSplitLayout 移动端响应式适配
- header/metrics/feedback 区域移动端缩减 padding
- metrics 区域移动端支持横向滚动
- 移除移动端大圆角卡片边框(平铺全宽)
- 侧边栏移动端撑满全宽(有 mainContent 时)
- 右侧面板移动端隐藏(后续可通过抽屉/切换展开)
- 使用 max-lg:! 优先级覆盖父组件的 !important 样式
2026-06-11 22:56:04 +08:00
WuYongJie
311cd74358 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-storage-unify-qiniu-20260611 2026-06-11 20:43:45 +08:00
WuYongJie
61a2834710 fix(storage): 公开素材直出七牛读取地址 2026-06-11 20:43:23 +08:00
WuYongJie
cb3b38a952 fix(intake): unify spu rule truth source 2026-06-11 17:41:11 +08:00
WuYongJie
3768e5d601 feat(intake): add sku order quantity field 2026-06-11 16:56:26 +08:00
WuYongJie
3724764079 merge: sync origin/codex/xuxiake-mainline 2026-06-11 14:08:21 +08:00
WuYongJie
655a882cf0 fix(storage): unify runtime asset reads to qiniu 2026-06-11 14:04:45 +08:00
WuYongJie
2ff31032c6 fix(intake): keep barcode print as reserved entry 2026-06-11 13:38:15 +08:00
WuYongJie
90ff111283 Merge remote-tracking branch 'origin/main' into codex/xuxiake-intake-primary-domain-sync-20260610
# Conflicts:
#	furniture-3d-system/src/app/intake/AccessoryLibraryPanel.tsx
#	furniture-3d-system/src/app/intake/IntakeClient.test.tsx
#	furniture-3d-system/src/app/intake/IntakeClient.tsx
#	furniture-3d-system/src/app/intake/SpuRuleLibraryWorkspace.test.tsx
#	furniture-3d-system/src/app/intake/SpuRuleLibraryWorkspace.tsx
#	furniture-3d-system/src/app/intake/bom-library/AccessoryLibraryLogic.tsx
#	furniture-3d-system/src/app/intake/bom-library/AccessoryLibraryPanel.tsx
#	furniture-3d-system/src/app/intake/bom-library/BomLibraryDomain.tsx
#	furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.tsx
#	furniture-3d-system/src/app/intake/non-integrated/nonIntegratedApi.ts
#	furniture-3d-system/src/app/intake/non-integrated/nonIntegratedAssetServer.ts
#	furniture-3d-system/src/app/intake/product-development/ProductDevelopmentDomain.tsx
#	furniture-3d-system/src/app/intake/product-development/SpuRuleLibraryWorkspace.test.tsx
#	furniture-3d-system/src/app/intake/product-development/SpuRuleLibraryWorkspace.tsx
#	furniture-3d-system/src/app/intake/product-development/spu-rule-library/SpuRuleLibraryWorkspace.test.tsx
#	furniture-3d-system/src/app/intake/product-development/spu-rule-library/SpuRuleLibraryWorkspace.tsx
#	furniture-3d-system/src/app/intake/product-development/spu-rule-library/spuRuleLibraryLogic.test.ts
#	furniture-3d-system/src/app/intake/product-development/spuRuleLibraryLogic.test.ts
#	furniture-3d-system/src/app/intake/product-patent/ProductPatentDomain.tsx
#	furniture-3d-system/src/app/intake/raw-material-library/RawMaterialLibraryDomain.tsx
#	furniture-3d-system/src/app/intake/raw-material-library/RawMaterialLibraryPanel.tsx
#	furniture-3d-system/src/app/intake/reference-estimator/ReferenceEstimatorDomain.tsx
#	furniture-3d-system/src/app/intake/spuRuleLibraryLogic.test.ts
2026-06-11 13:19:58 +08:00
WuYongJie
af27eff911 fix(intake): finalize non-integrated detail split 2026-06-11 12:52:56 +08:00
WuYongJie
5745abf0ae fix(intake): allow merchandisers to download factory files 2026-06-11 12:43:06 +08:00
WuYongJie
d5aba614fe fix(intake): allow deleting sku preview images 2026-06-11 11:56:12 +08:00
WuYongJie
16679896cf refactor(intake): complete primary domain split 2026-06-11 11:46:25 +08:00
WuYongJie
d1ddf2de7a refactor(intake): split non-integrated workspace containers 2026-06-11 11:46:25 +08:00
WuYongJie
1608b8f7ad refactor: split intake workspace structure 2026-06-11 11:46:24 +08:00
WuYongJie
5cc335acde fix(users): allow external intake business role 2026-06-11 09:40:25 +08:00
WuYongJie
bbdb63a9be merge: promote xuxiake mainline storage build closure 2026-06-11 01:43:01 +08:00
WuYongJie
1d4e8cffc2 fix(storage): 打通对象存储边界与生产构建 2026-06-11 01:41:47 +08:00
WuYongJie
71849d4aab merge: promote xuxiake mainline storage unify qiniu 2026-06-11 01:06:14 +08:00
WuYongJie
2f02d72565 feat(storage): 统一对象存储资源到稳定七牛访问口径 2026-06-11 01:03:56 +08:00
WuYongJie
28121b750d Merge remote-tracking branch 'origin/codex/xuxiake-mainline' 2026-06-10 23:11:02 +08:00
WuYongJie
b9698b1fbb fix: use anchor tag + router.push for mobile tab navigation 2026-06-10 23:10:27 +08:00
WuYongJie
f0fef3f906 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' 2026-06-10 22:52:22 +08:00
WuYongJie
9f4e46d06d feat: mobile responsive adaptation for main pages
- Add useMediaQuery hook for breakpoint detection (1024px)
- Add MobileTabBar component for mobile bottom navigation
- Add MobileInspectorDrawer component with 3-position drag
- Update globals.css with responsive variables and touch targets
- Update AppShell/AppTopNav for mobile layout switching
- Update HomeClient, AssetCenter, AssistedAssembly, DraftGroups for responsive layout
- Update Button/Input/Modal/Select with 44px minimum touch targets
2026-06-10 22:33:24 +08:00
WuYongJie
aa2ce7910b refactor(intake): split primary intake domains 2026-06-10 19:35:39 +08:00
WuYongJie
64cd9d67a5 fix(intake): support existing spu sequence creation 2026-06-10 18:42:28 +08:00
WuYongJie
bc945b3328 refactor(intake): extract product patent domain 2026-06-10 18:32:44 +08:00
WuYongJie
b02bb244ec refactor(intake): extract review issue image manager 2026-06-10 18:32:43 +08:00
WuYongJie
df580ae8bd refactor(intake): extract review issue image cell 2026-06-10 18:32:43 +08:00
WuYongJie
bd777856e6 refactor(intake): extract detection workspace 2026-06-10 18:32:43 +08:00
WuYongJie
ac79549aca refactor(intake): extract installation workspace 2026-06-10 18:32:43 +08:00
WuYongJie
4b2e1c435c refactor(intake): extract first quality workspace 2026-06-10 18:32:43 +08:00
WuYongJie
0e923eadff refactor(intake): extract archive preview panel 2026-06-10 18:32:43 +08:00
WuYongJie
d604ee0474 refactor(intake): extract product limit workspace 2026-06-10 18:32:43 +08:00
WuYongJie
1dca970937 refactor(intake): extract product pricing workspace 2026-06-10 18:32:43 +08:00
WuYongJie
3799702d22 refactor(intake): extract quality inspection workspace 2026-06-10 18:32:43 +08:00
WuYongJie
8fc529d2eb refactor(intake): extract planning layout workspace 2026-06-10 18:32:43 +08:00
WuYongJie
9961f8a1b9 refactor(intake): extract finished sample shipment workspace 2026-06-10 18:32:43 +08:00
WuYongJie
921c55c97f refactor(intake): extract sample measurement workspace 2026-06-10 18:32:43 +08:00
WuYongJie
b60a0a45ab refactor(intake): extract patent application workspace 2026-06-10 18:32:42 +08:00
WuYongJie
4994957194 refactor(intake): extract quality standard workspace 2026-06-10 18:32:42 +08:00
WuYongJie
d7278d5fff refactor(intake): extract attachment workspace 2026-06-10 18:32:42 +08:00
WuYongJie
d6b0960272 refactor(intake): extract opening reference workspace 2026-06-10 18:32:42 +08:00
WuYongJie
ffd53ec713 refactor(intake): extract product data workspace 2026-06-10 18:32:42 +08:00
WuYongJie
778d03c903 refactor(intake): extract project review workspace 2026-06-10 18:32:42 +08:00
WuYongJie
a61d1a5dd7 refactor(intake): move basic info field config 2026-06-10 18:32:42 +08:00
WuYongJie
3fa86786e8 refactor(intake): extract progress helpers 2026-06-10 18:32:42 +08:00
WuYongJie
5eba6797ab refactor(intake): move url download helper 2026-06-10 18:32:42 +08:00
WuYongJie
2551018e22 refactor(intake): move blob fetch helper 2026-06-10 18:32:42 +08:00
WuYongJie
46f43eebdf refactor(intake): move download error helper 2026-06-10 18:32:42 +08:00
WuYongJie
ba226085a2 refactor(intake): move workspace zip filename helper 2026-06-10 18:32:41 +08:00
WuYongJie
c4ffbcba0b refactor(intake): move download helpers 2026-06-10 18:32:41 +08:00
WuYongJie
f5a3a6940a refactor(intake): move bom category helpers 2026-06-10 18:32:41 +08:00
WuYongJie
90856bbe3a refactor(intake): move product pricing system entry helper 2026-06-10 18:32:41 +08:00
WuYongJie
d5cafaa699 refactor(intake): move regex escape helper 2026-06-10 18:32:41 +08:00
WuYongJie
4bdb82b7ed refactor(intake): extract workflow confirmation helpers 2026-06-10 18:32:41 +08:00
WuYongJie
9eff0cb944 refactor(intake): extract product patent sync helper 2026-06-10 18:31:59 +08:00
WuYongJie
4589acfed2 refactor(intake): move image upload helpers 2026-06-10 18:31:08 +08:00
WuYongJie
d43feb85d7 refactor(intake): move opening reference form helpers 2026-06-10 18:31:08 +08:00
WuYongJie
9ce35b00a2 refactor(intake): extract product factory helpers 2026-06-10 18:31:08 +08:00
WuYongJie
46a143b4dc refactor(intake): extract bom value helpers 2026-06-10 18:31:07 +08:00
WuYongJie
441c14a97f refactor(intake): extract bom detail key helpers 2026-06-10 18:31:07 +08:00
WuYongJie
52fb2ff638 refactor(intake): extract upload metadata helpers 2026-06-10 18:31:07 +08:00
WuYongJie
4a2731d56e refactor(intake): extract product form value helpers 2026-06-10 18:31:07 +08:00
WuYongJie
2c46f40a38 refactor(intake): extract product workflow row helpers 2026-06-10 18:31:07 +08:00
WuYongJie
993675ef3f refactor(intake): move empty sku item helper 2026-06-10 18:31:07 +08:00
WuYongJie
863e4a470d refactor(intake): move empty style detail state 2026-06-10 18:31:07 +08:00
WuYongJie
cfe38da760 refactor(intake): reuse sku preview node helper 2026-06-10 18:31:07 +08:00
WuYongJie
40e992acf6 refactor(intake): extract sku preview node helpers 2026-06-10 18:31:07 +08:00
WuYongJie
645130a042 refactor(intake): move document workspace initialization 2026-06-10 18:31:07 +08:00
WuYongJie
a30151a24a refactor(intake): extract product factory key helpers 2026-06-10 18:31:07 +08:00
WuYongJie
8f8ea08155 refactor(intake): move sample shipment review helper 2026-06-10 18:31:06 +08:00
WuYongJie
8f9dffa3c6 refactor(intake): move review content helpers 2026-06-10 18:31:06 +08:00
WuYongJie
59689010ed refactor(intake): extract sku import batch helpers 2026-06-10 18:31:06 +08:00
WuYongJie
7b8142e1f6 refactor(intake): extract sku source helpers 2026-06-10 18:31:06 +08:00
WuYongJie
5acf2d3d9e refactor(intake): move sku row clone helper 2026-06-10 18:31:06 +08:00
WuYongJie
4f66a42cec refactor(intake): move detection workspace helpers 2026-06-10 18:31:06 +08:00
WuYongJie
96e7a4db8d refactor(intake): move project review owner helper 2026-06-10 18:31:06 +08:00
WuYongJie
03ac72700b refactor(intake): extract upload storage helper 2026-06-10 18:31:06 +08:00
WuYongJie
14d7ddcd4e refactor(intake): move document revision file helper 2026-06-10 18:31:06 +08:00
WuYongJie
d5834de00e refactor(intake): extract sku brand prefix helper 2026-06-10 18:31:05 +08:00
WuYongJie
ea14712dcc refactor(intake): extract local asset detection helper 2026-06-10 18:31:05 +08:00
WuYongJie
d5b5668154 refactor(intake): extract first quality helpers 2026-06-10 18:31:05 +08:00
WuYongJie
8faa646bb0 refactor(intake): extract sample workflow helpers 2026-06-10 18:31:05 +08:00
WuYongJie
8e8c713675 refactor(intake): extract upload category helper 2026-06-10 18:31:05 +08:00
WuYongJie
c282dd5ff1 refactor(intake): extract preview hole helpers 2026-06-10 18:31:05 +08:00
WuYongJie
46c0cfadca refactor(intake): extract component sku helpers 2026-06-10 18:31:05 +08:00
WuYongJie
b8ea7291a6 refactor(intake): extract series component detail helpers 2026-06-10 18:31:05 +08:00
WuYongJie
cbfad8de93 refactor(intake): extract asset sku content helpers 2026-06-10 18:31:05 +08:00
WuYongJie
9f834babda refactor(intake): move non-integrated sku normalization helpers 2026-06-10 18:31:05 +08:00
WuYongJie
eb24df17c9 refactor(intake): move non-integrated style detail initialization 2026-06-10 18:31:04 +08:00
WuYongJie
8ccf70f6a7 refactor(intake): extract non-integrated style detail state 2026-06-10 18:31:04 +08:00
WuYongJie
7dcee03161 refactor(intake): extract non-integrated basic info config 2026-06-10 18:31:04 +08:00
WuYongJie
a5be04560e refactor(intake): extract non-integrated quality inspection helpers 2026-06-10 18:31:04 +08:00
WuYongJie
76feacb340 refactor(intake): extract non-integrated review sheet helpers 2026-06-10 18:31:04 +08:00
WuYongJie
4124f5bac8 refactor(intake): move packaging workspace helpers 2026-06-10 18:31:04 +08:00
WuYongJie
da1b05fe6c merge: sync origin/codex/xuxiake-mainline before promote 2026-06-10 17:00:43 +08:00
WuYongJie
b5a400b5e9 fix(intake): 完善白底图预览与素材库上传链路 2026-06-10 16:42:46 +08:00
WuYongJie
8db587a4b3 fix(intake): 优先主表同步供应商代码 2026-06-10 16:42:28 +08:00
WuYongJie
6cfd123a70 fix(intake): 嵌入式白底图工作区对齐九视图布局 2026-06-10 13:59:07 +08:00
WuYongJie
3afc247c67 fix(intake): 嵌入式白底图工作区对齐九视图布局 2026-06-10 13:30:19 +08:00
WuYongJie
64b715f416 Improve mobile linked intake detail layout 2026-06-10 13:15:18 +08:00
WuYongJie
49c2fe97df fix(intake): preserve linked detail login redirects 2026-06-10 09:03:02 +08:00
WuYongJie
c413220fd0 Merge codex/xuxiake-mainline into main 2026-06-09 20:09:17 +08:00
WuYongJie
bb28d31c4b Limit production build workers 2026-06-09 20:09:01 +08:00
WuYongJie
be00ce9179 Merge codex/xuxiake-mainline into main 2026-06-09 19:40:25 +08:00
WuYongJie
337f3cfa09 Improve mobile linked intake detail layout 2026-06-09 19:39:37 +08:00
WuYongJie
d45f394e3b feat(intake): 独立归档AI白底图结果 2026-06-09 18:10:01 +08:00
WuYongJie
97dd9b5bfe feat(intake): 独立归档AI白底图结果 2026-06-09 18:09:55 +08:00
WuYongJie
faf11a3c15 feat(intake): 独立归档AI白底图结果 2026-06-09 18:08:17 +08:00
WuYongJie
86c27c772c merge: promote external intake role save fix 2026-06-09 17:50:14 +08:00
WuYongJie
2ad2994f22 fix(users): allow external intake business role 2026-06-09 17:49:39 +08:00
WuYongJie
dac76aabbe fix(intake): 稳定白底图源图预览加载 2026-06-09 17:33:54 +08:00
WuYongJie
a962c8d1c7 fix(intake): 稳定白底图源图预览加载 2026-06-09 17:33:07 +08:00
WuYongJie
27d43c3e11 fix(intake): 稳定白底图源图预览加载 2026-06-09 17:30:49 +08:00
WuYongJie
63926a667f merge: promote product tag and optional register job number 2026-06-09 17:10:53 +08:00
WuYongJie
b829efbaab fix: support product tag push and optional register job number 2026-06-09 17:09:25 +08:00
WuYongJie
6e71151104 merge: promote xuxiake mainline ai white background concurrency 2026-06-09 17:00:48 +08:00
WuYongJie
007dd0a986 fix(intake): 支持白底图按角度并发生成 2026-06-09 16:58:38 +08:00
WuYongJie
169d1a6d8b merge: promote xuxiake mainline external wide ready coverage 2026-06-09 16:09:24 +08:00
WuYongJie
99b56d308f Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-storage-cdn-direct 2026-06-09 16:06:52 +08:00
WuYongJie
59a1106035 fix(external): 补齐宽表对 ready 数据的覆盖 2026-06-09 16:06:10 +08:00
WuYongJie
7276abf589 feat(intake): add external intake role isolation 2026-06-09 13:02:01 +08:00
WuYongJie
3ba9c451a5 feat(intake): support operations product limit workflow 2026-06-09 12:31:23 +08:00
WuYongJie
78a693621e fix: 修正白底图面板空值收窄构建错误 2026-06-09 11:47:23 +08:00
WuYongJie
b430a2cdcb Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-storage-cdn-direct 2026-06-09 11:36:22 +08:00
WuYongJie
6c2c90989b feat: 接通非集成质检 AI 白底图上传链路 2026-06-09 11:35:51 +08:00
WuYongJie
cac624007c fix(intake): hide abandon product action 2026-06-09 10:29:23 +08:00
WuYongJie
53742ad094 fix(intake): avoid factory permission block on sku supplier save 2026-06-09 09:55:29 +08:00
WuYongJie
0419679c5d Merge remote-tracking branch 'origin/main' into codex/xuxiake-mainline 2026-06-08 18:17:18 +08:00
WuYongJie
9cd10c4b1e refactor(intake): move upload utility helpers 2026-06-08 18:12:41 +08:00
WuYongJie
d8b85d071a refactor(intake): move installation version helpers 2026-06-08 18:08:35 +08:00
WuYongJie
300130983d refactor(intake): move non-integrated sample shipment constants 2026-06-08 18:02:45 +08:00
WuYongJie
0c43017c45 refactor(intake): move non-integrated product factory options 2026-06-08 17:58:23 +08:00
WuYongJie
fa6c0d20aa merge: promote external readonly barcode catalog 2026-06-08 17:56:46 +08:00
WuYongJie
3d46e8d65a feat: align external readonly catalog on barcode key 2026-06-08 17:54:44 +08:00
WuYongJie
ad0081f692 refactor(intake): extract non-integrated workspace panel config 2026-06-08 17:49:31 +08:00
WuYongJie
3a8de95bf1 refactor(intake): extract non-integrated planning layout config 2026-06-08 17:42:15 +08:00
WuYongJie
7a84f14f8d refactor(intake): move non-integrated sku ui config 2026-06-08 17:32:40 +08:00
WuYongJie
2d29a50e2b refactor(intake): extract non-integrated sku field config 2026-06-08 17:27:04 +08:00
WuYongJie
0c1b6c1dc2 refactor(intake): extract non-integrated bom display config 2026-06-08 17:20:34 +08:00
WuYongJie
5732a1ab39 refactor(intake): extract non-integrated opening reference helpers 2026-06-08 17:14:20 +08:00
WuYongJie
af03f16690 refactor(intake): extract non-integrated document workspace helpers 2026-06-08 17:02:15 +08:00
WuYongJie
f33659a893 refactor(intake): extract non-integrated value helpers 2026-06-08 16:43:20 +08:00
WuYongJie
6cd4ab5a60 refactor(intake): extract non-integrated style file helpers 2026-06-08 16:38:59 +08:00
WuYongJie
c37e80fb02 refactor(intake): extract non-integrated detail helpers 2026-06-08 16:33:46 +08:00
WuYongJie
af2b5640c8 fix(intake): keep sku supplier ime input responsive 2026-06-08 15:53:24 +08:00
WuYongJie
71598ec0f3 refactor(intake): extract non-integrated sku material field 2026-06-08 15:45:11 +08:00
WuYongJie
0443b33705 refactor(intake): extract non-integrated sku barcode field 2026-06-08 15:39:33 +08:00
WuYongJie
c2f8cee9f0 refactor(intake): extract non-integrated bom image field 2026-06-08 15:33:22 +08:00
WuYongJie
ab9cd0fad5 refactor(intake): extract non-integrated sku directory fields 2026-06-08 15:26:39 +08:00
WuYongJie
922dbfa0bf refactor(intake): extract non-integrated document workspace controls 2026-06-08 15:04:11 +08:00
WuYongJie
a8598a1d80 refactor(intake): extract non-integrated assembly thumbnails 2026-06-08 14:54:09 +08:00
WuYongJie
651342e49d refactor(intake): extract non-integrated sku row fields 2026-06-08 14:44:02 +08:00
WuYongJie
1ca244a116 refactor(intake): extract non-integrated brand field 2026-06-08 14:23:48 +08:00
WuYongJie
fdf5d19480 refactor(intake): extract non-integrated upload field 2026-06-08 14:09:56 +08:00
WuYongJie
e52ad139f9 refactor(intake): extract non-integrated preview panels 2026-06-08 13:49:52 +08:00
WuYongJie
ff2d99ae70 refactor(intake): extract non-integrated shared controls 2026-06-08 13:33:56 +08:00
WuYongJie
d7b47c21f5 feat(intake): support sku group modular order workflow 2026-06-08 12:27:48 +08:00
WuYongJie
5adaec1376 fix(intake): keep sku ime input stable 2026-06-08 12:03:31 +08:00
WuYongJie
f3140692ff merge: promote xuxiake mainline storage object-first hotfix 2026-06-08 11:40:01 +08:00
WuYongJie
2239b7b186 merge: promote storage object-first legacy asset hotfix 2026-06-08 11:38:36 +08:00
WuYongJie
7a5dce6988 fix(storage): 非集成历史素材改为对象存储优先 2026-06-08 11:32:25 +08:00
WuYongJie
7835d69c9f fix(intake): allow payload-backed design owner sku image saves 2026-06-08 10:08:13 +08:00
WuYongJie
f5a6005b3e merge: promote xuxiake mainline qiniu cdn direct read rollout 2026-06-06 22:13:36 +08:00
WuYongJie
27ba97800f merge: promote xuxiake mainline qiniu cdn direct read rollout 2026-06-06 22:12:09 +08:00
WuYongJie
768b988843 feat: 切换对象读取到 CDN 直连签名跳转 2026-06-06 21:49:26 +08:00
WuYongJie
e8933ab8ac Merge codex/xuxiake-mainline jushuitan inventory skip fix 2026-06-06 18:08:23 +08:00
WuYongJie
7a3fd5c42f fix: treat non virtual inventory warehouses as skipped 2026-06-06 18:07:03 +08:00
WuYongJie
05f1b0af3a Merge remote-tracking branch 'origin/main' into codex/xuxiake-mainline 2026-06-06 14:21:44 +08:00
WuYongJie
5d31269736 feat: support modular draft SKU product flow 2026-06-06 14:21:35 +08:00
WuYongJie
aeed70f90f Merge codex/xuxiake-mainline formal jushuitan credentials 2026-06-06 10:20:58 +08:00
WuYongJie
a0eb15b980 fix: parse formal jushuitan credentials 2026-06-06 10:20:32 +08:00
WuYongJie
8ca0691e02 Merge codex/xuxiake-mainline jushuitan copy fix 2026-06-06 09:56:00 +08:00
WuYongJie
b75de86db1 fix: remove remaining jushuitan test copy 2026-06-06 09:55:37 +08:00
WuYongJie
d09a201a6a Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into merge-jst-copyfix 2026-06-06 09:47:18 +08:00
WuYongJie
77ac8f06c0 fix: remove jushuitan test environment copy 2026-06-06 09:45:03 +08:00
WuYongJie
840236610c merge: promote modular sku draft workflow 2026-06-06 09:23:10 +08:00
WuYongJie
575d5aa039 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-06-06 09:18:26 +08:00
WuYongJie
467e4182f2 feat: restore modular sku group draft workflow 2026-06-06 09:18:07 +08:00
WuYongJie
4b8e1d754a Revert "fix(intake): allow assigned purchaser pricing push"
This reverts commit d3093aead7fae7726c3102236fbd40b4df11d8f9.
2026-06-06 09:11:25 +08:00
WuYongJie
d3093aead7 fix(intake): allow assigned purchaser pricing push 2026-06-06 09:09:31 +08:00
WuYongJie
338bfb83e7 fix(intake): remove odmq factory production option 2026-06-05 18:38:04 +08:00
WuYongJie
a5f2fd01dd fix(intake): remove odmq factory production option 2026-06-05 18:35:51 +08:00
WuYongJie
59a52c8b95 merge: promote single sku master data guard 2026-06-05 18:31:56 +08:00
WuYongJie
f3c2b9bbee fix(intake): prevent single sku master data gaps 2026-06-05 18:29:01 +08:00
WuYongJie
369cf8ecf4 fix(intake): add factory shipping method 2026-06-05 18:14:00 +08:00
WuYongJie
1892a5d579 fix(intake): add factory shipping method 2026-06-05 18:09:20 +08:00
WuYongJie
805f8db179 merge: promote xuxiake non-integrated foundation 2026-06-05 18:03:55 +08:00
WuYongJie
b2837c6426 merge: sync non-integrated upload permission foundation 2026-06-05 17:50:57 +08:00
WuYongJie
0765c0596b Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-non-integrated-upload-permission 2026-06-05 17:47:26 +08:00
WuYongJie
f620762a4b fix(master-data): prevent dirty intake sync regressions 2026-06-05 16:25:13 +08:00
WuYongJie
86df630894 fix(master-data): scope shadow env to audit diff steps 2026-06-05 16:03:25 +08:00
WuYongJie
743d480b7c fix(intake): prefer sku images in product limits 2026-06-05 15:49:20 +08:00
WuYongJie
2194824e6c fix(intake): keep imported sku rows editable 2026-06-05 15:33:16 +08:00
WuYongJie
d650e2f562 fix(intake): match sku images by displayed spu 2026-06-05 15:16:29 +08:00
WuYongJie
cfc3e5cc0e fix(intake): 修复非集成图片上传节点权限 2026-06-05 14:59:29 +08:00
WuYongJie
0c2b05ce74 fix(intake): skip unmatched sku image autosave 2026-06-05 14:58:40 +08:00
WuYongJie
704a0b91ac fix(master-data): align merged boundary debt budget 2026-06-05 13:34:44 +08:00
WuYongJie
61da20e9b6 merge: sync data foundation into xuxiake mainline 2026-06-05 13:29:18 +08:00
WuYongJie
4f2414ef5c revert: defer asset sku group workflow for data foundation 2026-06-05 13:29:08 +08:00
WuYongJie
855ab98ce5 fix(master-data): avoid readonly quality false blockers 2026-06-05 12:40:43 +08:00
WuYongJie
54ef4c79f3 chore(master-data): register retired asset sku group migration 2026-06-05 12:35:33 +08:00
WuYongJie
839269ab8e fix(intake): defer autosave during ime input 2026-06-05 12:28:54 +08:00
WuYongJie
d38a373297 chore(master-data): retire legacy asset sku group drift 2026-06-05 12:27:56 +08:00
WuYongJie
fc6241ff46 fix(asset-center): backfill sku groups by goods code 2026-06-05 12:18:39 +08:00
WuYongJie
26e40b0e71 chore(master-data): 收紧资产规格款身份真相 2026-06-05 12:08:29 +08:00
WuYongJie
6cc339cdb5 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-06-05 12:07:29 +08:00
WuYongJie
cc7f3c4b8e feat(asset-center): support sku group asset workflow 2026-06-05 12:05:57 +08:00
WuYongJie
4737184f59 fix(intake): scope derived downstream saves 2026-06-05 11:59:09 +08:00
WuYongJie
8e0addb76c chore(master-data): 加强资产SKU同步副本巡检 2026-06-05 11:56:04 +08:00
WuYongJie
74cf5b0005 chore(master-data): 补齐聚水潭归档真相源矩阵 2026-06-05 11:44:52 +08:00
WuYongJie
3da51e926b chore(master-data): 增加聚水潭残留软归档脚本 2026-06-05 11:38:41 +08:00
WuYongJie
4974869f6a chore(master-data): 增加聚水潭分类匹配计划导出 2026-06-05 10:34:22 +08:00
WuYongJie
16d7f42506 fix(intake): allow sku details after spu assignment 2026-06-05 10:30:59 +08:00
WuYongJie
4ca58ffaa2 chore(master-data): 导出非集成产品分类审阅清单 2026-06-05 09:16:02 +08:00
WuYongJie
e26f958de0 fix(jst): 补齐存量SKU主数据兜底 2026-06-04 22:37:15 +08:00
WuYongJie
b4f088d70a fix(asset-center): 修复生产缩略图渲染域名来源 2026-06-04 21:32:19 +08:00
WuYongJie
3cd1102cd2 fix(data-foundation): 对齐治理应用迁移历史门禁 2026-06-04 20:20:31 +08:00
WuYongJie
d6ac21db0e fix(data-foundation): 收紧资产同步锁定字段门禁 2026-06-04 18:11:34 +08:00
WuYongJie
7459f2aef1 fix(data-foundation): 纳入资产同步事件积压门禁 2026-06-04 18:00:35 +08:00
WuYongJie
67bdc09e1e fix(data-foundation): 补齐生产只读证据验收门禁 2026-06-04 17:52:12 +08:00
WuYongJie
90bfa4b968 fix(data-foundation): 补齐生产只读地基巡检入口 2026-06-04 17:30:11 +08:00
WuYongJie
92af7ea29a fix(data-foundation): 识别已完成迁移链的 no-op 审计状态 2026-06-04 17:08:14 +08:00
WuYongJie
40e5f238a4 fix(data-foundation): 稳定历史对象源检查 2026-06-04 16:24:56 +08:00
WuYongJie
5bed29f6e9 fix(data-foundation): 纳入历史对象存储迁移门禁 2026-06-04 16:00:56 +08:00
WuYongJie
6f09b28196 fix(data-foundation): 加强历史素材迁移门禁 2026-06-04 15:47:55 +08:00
WuYongJie
7f41d0bca5 fix: preserve component detail spu on stock conflicts 2026-06-04 15:33:10 +08:00
WuYongJie
5de6de67e6 fix(data-foundation): 收口主数据边界 readiness 2026-06-04 15:25:17 +08:00
WuYongJie
e4809ef843 fix: sync configured component detail spu before opening 2026-06-04 15:15:30 +08:00
WuYongJie
d829ea6c05 fix: allow product owners to upload sku assets 2026-06-04 14:55:43 +08:00
WuYongJie
8f6f61858e fix: avoid duplicate default asset series seed 2026-06-04 14:28:53 +08:00
WuYongJie
cbe545f3e3 fix(data-foundation): 区分已恢复的回滚迁移记录 2026-06-04 14:18:50 +08:00
WuYongJie
d9db45ea3a fix: allow editing sku spec names 2026-06-04 14:04:08 +08:00
WuYongJie
6416408031 fix(data-foundation): 区分已恢复的回滚迁移记录 2026-06-04 13:58:46 +08:00
WuYongJie
f5d8f450b3 fix: sync configured series spu coding 2026-06-04 13:40:05 +08:00
WuYongJie
849e74118d fix: compress intake image uploads before submit 2026-06-04 12:17:05 +08:00
WuYongJie
01eb1647bc fix: allow packaging variants without real model 2026-06-04 10:09:33 +08:00
WuYongJie
5cb25981ef fix: prevent opening codes from becoming formal spu 2026-06-04 10:03:48 +08:00
WuYongJie
2ccaab0e72 fix: preserve material storage texture urls 2026-06-04 09:06:49 +08:00
WuYongJie
8d0930a5b2 fix(storage): 跳过未迁历史快照对象检查 2026-06-03 22:10:06 +08:00
WuYongJie
2764ac1e31 fix(storage): 阻止源缺失时执行历史素材迁移 2026-06-03 21:55:05 +08:00
WuYongJie
aeb6919da6 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-06-03 21:41:00 +08:00
WuYongJie
79106b69da feat(storage): 支持七牛历史素材迁移 2026-06-03 21:40:13 +08:00
WuYongJie
d16a2a201a fix: improve material upload and category deletion 2026-06-03 18:08:20 +08:00
WuYongJie
14db8a7816 feat: prevent duplicate assisted assembly specs 2026-06-03 18:08:09 +08:00
WuYongJie
252c744ddd fix(asset-center): scope hardware families by owning series 2026-06-03 17:13:40 +08:00
WuYongJie
b6c2a8ec62 fix(asset-center): uppercase deleted variant suffix 2026-06-03 16:53:37 +08:00
WuYongJie
b22ff2084c fix(asset-center): normalize variant soft delete marker 2026-06-03 16:36:34 +08:00
WuYongJie
91340f5d5f Merge remote-tracking branch 'origin/main' into codex/xuxiake-mainline 2026-06-03 15:52:33 +08:00
WuYongJie
6a362bcdde feat(data-foundation): sync draft packaging and series cleanup 2026-06-03 15:52:05 +08:00
WuYongJie
67ff6fba96 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-06-03 14:10:55 +08:00
WuYongJie
12a0f26bb7 merge: sync xuxiake data foundation for prod deploy 2026-06-03 13:16:16 +08:00
WuYongJie
2ca459e027 fix(data-foundation): 结构化核价行并归一化素材引用 2026-06-03 12:10:14 +08:00
WuYongJie
de045f4a56 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-06-03 11:12:47 +08:00
WuYongJie
e2a6dd1487 test(data-foundation): 收紧客户端缓存边界证据门禁 2026-06-03 10:46:06 +08:00
WuYongJie
b086e02dcd fix(data-foundation): 补齐对象存储外部契约与迁移历史 2026-06-03 10:31:21 +08:00
WuYongJie
4e5939071a merge: sync material foundation into xuxiake mainline 2026-06-03 10:26:08 +08:00
WuYongJie
5f2033ea90 wip: protect local material updates before mainline sync 2026-06-03 10:04:59 +08:00
WuYongJie
02ce03aeaa merge: sync storage model proxy fix for prod deploy 2026-06-03 09:28:53 +08:00
WuYongJie
8fd40e0846 fix(storage): 通过同源代理加载对象存储模型 2026-06-02 22:28:21 +08:00
WuYongJie
e41e93ede8 merge: sync xuxiake normalized migration fix for prod deploy 2026-06-02 22:04:18 +08:00
WuYongJie
73655af744 fix(data-foundation): 对齐非集成 SKU 规范化迁移预检 2026-06-02 22:03:56 +08:00
WuYongJie
b77fcb3351 merge: sync xuxiake data constraint fix for prod deploy 2026-06-02 21:55:22 +08:00
WuYongJie
6fee15d4aa fix(data-foundation): 放行非集成 SKU 冲突质量状态约束 2026-06-02 21:54:11 +08:00
WuYongJie
56252d92f3 merge: sync xuxiake data foundation fix for prod deploy 2026-06-02 21:45:26 +08:00
WuYongJie
420b159f7e fix(data-foundation): 兼容非集成 SKU 历史编码冲突 2026-06-02 21:44:37 +08:00
WuYongJie
1b26801f12 merge: sync xuxiake mainline for prod deploy 2026-06-02 21:13:39 +08:00
WuYongJie
5025f4c5c1 merge: 接入七牛对象存储 2026-06-02 20:57:16 +08:00
WuYongJie
6d17f7684d feat(storage): 接入七牛私有对象存储与稳定读取代理 2026-06-02 18:33:25 +08:00
WuYongJie
1c74c2b630 merge: bring data foundation updates into xuxiake mainline 2026-06-02 16:16:13 +08:00
WuYongJie
545b1165c4 chore(data-foundation): close master data readiness gaps 2026-06-02 16:05:45 +08:00
WuYongJie
ebeb7624d6 chore(data-foundation): clarify structure repair evidence 2026-06-02 14:06:47 +08:00
WuYongJie
64341cab77 chore(data-foundation): require fresh audit checklist evidence 2026-06-02 13:48:03 +08:00
WuYongJie
0031533f97 chore(data-foundation): prevent auth cache permission truth 2026-06-02 13:40:55 +08:00
WuYongJie
f0664e77ca chore(data-foundation): clarify json truth boundaries 2026-06-02 13:27:12 +08:00
WuYongJie
0b22729225 chore(data-foundation): tighten client storage evidence gate 2026-06-02 13:17:30 +08:00
WuYongJie
6bc8e3348e feat(data-foundation): add master data governance scaffolding 2026-06-02 11:50:50 +08:00
WuYongJie
f0d3f5daae chore(data-foundation): sync xuxiake mainline and stabilize master data 2026-06-02 10:57:38 +08:00
WuYongJie
35fed448cf Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-assisted-assembly-ai-render
# Conflicts:
#	furniture-3d-system/src/app/draft-groups/DraftGroupsClient.tsx
2026-06-02 10:34:08 +08:00
WuYongJie
f563bc9989 Add warehouse quality and product data updates 2026-06-01 13:53:47 +08:00
WuYongJie
a09fbc3057 style(asset-center): lint 格式化与类型收口 2026-05-29 20:27:18 +08:00
WuYongJie
fe80bc77ba @
refactor(asset-center): 提取预览右键菜单子菜单为独立组件

- PreviewSubmenus.tsx(252 行):基材选择子菜单 + UV 渲染参数子菜单
- AssetCenterClient.tsx 3268→3085 行
@
2026-05-29 19:02:41 +08:00
WuYongJie
9333ed5f17 Merge remote-tracking branch 'origin/main' into codex/xuxiake-mainline 2026-05-29 18:35:17 +08:00
WuYongJie
3908aa0d80 Refine product data naming and draft group preview 2026-05-29 18:33:54 +08:00
WuYongJie
2e618d334b Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into merge-jst-main 2026-05-29 18:17:35 +08:00
WuYongJie
6e7cb87312 fix: remove intake tab block 2026-05-29 18:16:50 +08:00
WuYongJie
f7164140e1 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into merge-jst-main 2026-05-29 18:10:09 +08:00
WuYongJie
34ea57c707 fix: repair jushuitan pricing row fields 2026-05-29 18:09:25 +08:00
WuYongJie
53d97a08d4 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into merge-jst-main 2026-05-29 17:55:33 +08:00
WuYongJie
3e07813900 feat: add jushuitan push flow 2026-05-29 17:53:49 +08:00
WuYongJie
85d9eb7acc @
refactor(asset-center): 提取资产族列表为独立组件

- FamilyListPanel.tsx(332 行):资产族+规格款列表渲染
- AssetCenterClient.tsx 3506→3268 行
@
2026-05-29 17:23:59 +08:00
WuYongJie
6e743e991b @
refactor(asset-center): 提取 CAD 导入弹窗为独立组件

- CadImportModal.tsx(306 行):CAD 导入弹窗独立组件
- AssetCenterClient.tsx 3733→3506 行
@
2026-05-29 16:49:05 +08:00
WuYongJie
de33fc385c @
refactor(asset-center): 拆分 AssetCenterClient 大文件(4775→3733 行)

提取 7 个独立模块:
- asset-center-types.ts(210 行):类型定义、常量、CSS 类名
- asset-center-utils.ts(615 行):纯工具函数(UV 编辑、键盘快捷键、预览节点变换、状态标签、相似性、CAD 导入、系列筛选)
- VariantEditModal.tsx(235 行):规格款编辑弹窗
- FamilyEditModal.tsx(261 行):资产族编辑弹窗
- ReviewRejectModal.tsx(55 行):驳回弹窗
- RoleEditorModal.tsx(47 行):连接角色编辑弹窗

主组件减少 1042 行,功能完全不变。
@
2026-05-29 16:09:23 +08:00
WuYongJie
a5d728c7b9 @
feat(draft-groups): 草稿组页面优化与预估成本修复

- 保存品类按钮移到品类同一行,加宽按钮
- 删除草稿组加 try/catch 错误处理
- 预估成本:costPrice 为 null 时回退到 estimatedCostPrice
  - OpenProductAssetVariant 类型加 estimatedCostPrice
  - toOpenProductVariants 映射加 estimatedCostPrice
  - buildSlotSnapshot/buildHardwareSnapshot 用 costPrice ?? estimatedCostPrice
  - BOM 聚合用 costPrice ?? estimatedCostPrice
- 颜色统一为白曜石设计令牌(--obs-shell-color/heading-color/muted/surface-bg/border)
- 状态标签颜色改用固定色值,降低饱和度
- 板材/组件/五金件列表每行可点击,弹出只读 Modal 展示完整信息
- 组件详情弹窗增加规格款 ID、资产族 ID、基材 ID、饰面 ID、连接位、3D 模型 URL
@
2026-05-29 15:29:23 +08:00
WuYongJie
e07c12a34f Update accessory library and intake permissions 2026-05-29 13:50:58 +08:00
WuYongJie
9217b8db16 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-assisted-assembly-ai-render 2026-05-29 12:20:58 +08:00
WuYongJie
a6946d3750 feat(assisted-assembly): 标签预览图改为静态资源并优化弹出交互 2026-05-29 12:18:40 +08:00
WuYongJie
f57d6030a3 feat(draft-groups): 重构草稿组工作台与资料补齐 2026-05-29 12:17:27 +08:00
WuYongJie
285bb3c180 feat(admin): 接入AI服务商配置与提示词优化 2026-05-29 12:14:26 +08:00
WuYongJie
83f3cb498c Merge remote-tracking branch 'origin/main' into codex/xuxiake-mainline 2026-05-28 17:57:51 +08:00
WuYongJie
11afc62a0c Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-05-28 17:57:39 +08:00
WuYongJie
1c65ba4dcc Update intake permissions and workflow states 2026-05-28 17:56:30 +08:00
WuYongJie
1cbabf46fa merge: promote xuxiake mainline to main 2026-05-28 13:53:02 +08:00
WuYongJie
cab450f263 merge: assisted assembly ai render and employee provisioning 2026-05-28 13:48:58 +08:00
WuYongJie
bd02176bdf feat(assisted-assembly): 接入AI生图配置与历史记录 2026-05-28 13:42:31 +08:00
WuYongJie
035cf170b7 fix(auth): 按花名册规则自动划归账号 2026-05-28 13:41:56 +08:00
WuYongJie
5c837ee8e4 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-assisted-assembly-ai-render
# Conflicts:
#	furniture-3d-system/src/lib/business-role-inference.test.ts
2026-05-28 12:03:58 +08:00
WuYongJie
f6eba0418f fix(users): 排除非内部部门自动业务划归 2026-05-28 12:01:49 +08:00
WuYongJie
46c0098ae3 fix(intake): allow planning layout save noops 2026-05-28 11:12:09 +08:00
WuYongJie
6d6e4b299d fix(intake): allow stock planning layout save noops 2026-05-28 11:02:33 +08:00
WuYongJie
d8c08bd625 fix(intake): guard stock product workspace saves 2026-05-28 10:44:10 +08:00
WuYongJie
180680f348 feat(intake): add sku hole snapping preview 2026-05-28 08:54:42 +08:00
WuYongJie
90432bb243 feat(intake): refine accessory library and bom exports 2026-05-27 15:44:52 +08:00
WuYongJie
2a1058ec77 fix(intake): allow space visual department layout saves 2026-05-27 10:54:49 +08:00
WuYongJie
1b0141908e fix(intake): align space visual planning role 2026-05-27 09:27:55 +08:00
WuYongJie
1dbd7f1d63 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-05-27 08:41:17 +08:00
WuYongJie
51b1341c5c fix: improve non-integrated detail saves 2026-05-27 08:41:10 +08:00
WuYongJie
6558ba763b fix: 完善辅助装配吸附与私有目录接口保护 2026-05-26 18:41:04 +08:00
WuYongJie
396e3dcabb fix(intake): allow space team planning layout edits 2026-05-26 17:19:13 +08:00
WuYongJie
9700d84c66 feat(intake): refine bom libraries and accessory rods 2026-05-26 16:30:37 +08:00
WuYongJie
8ae5540eef fix(intake): tighten non-integrated detail permissions 2026-05-26 13:54:14 +08:00
WuYongJie
d151d9a056 fix(intake): stabilize node permissions and accessory categories 2026-05-26 10:36:39 +08:00
WuYongJie
d544c14c5d fix(intake): improve planning layout autosave and scrolling 2026-05-25 16:58:17 +08:00
WuYongJie
1e8c5970b0 fix: 修复数据落库一致性 2026-05-25 16:29:26 +08:00
WuYongJie
2cc34127e9 fix(intake): allow space visual planning layout edits 2026-05-25 09:58:13 +08:00
WuYongJie
491c2a24ac 加固产品分享链接公开访问 2026-05-24 21:21:16 +08:00
WuYongJie
ca81d44421 保护图片估算接口鉴权边界 2026-05-24 20:49:03 +08:00
WuYongJie
588ed37097 加强非集成上传与分享安全边界 2026-05-24 20:18:14 +08:00
WuYongJie
01caee8b81 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-05-24 19:39:20 +08:00
WuYongJie
e489d3bba0 fix(security): harden auth and response headers 2026-05-24 19:39:14 +08:00
WuYongJie
4e088de15f revert(intake): remove public install video links 2026-05-23 17:03:15 +08:00
WuYongJie
8311b7a155 fix(intake): allow public install video pages 2026-05-23 16:39:15 +08:00
WuYongJie
23ae5296f1 feat(intake): add public install video links 2026-05-23 16:33:32 +08:00
WuYongJie
5916d80657 fix(intake): open shared product links as full detail 2026-05-23 14:42:48 +08:00
WuYongJie
6c906e57cc style(intake): compact admin sidebar actions 2026-05-23 14:26:46 +08:00
WuYongJie
40c6ab14ea fix(intake): show readonly nodes for designers 2026-05-23 14:21:58 +08:00
WuYongJie
78a036ca2d feat(intake): add node permissions and accessory library controls 2026-05-23 13:19:43 +08:00
WuYongJie
320670fa92 fix(intake): 收敛设计权限与开品链接视图 2026-05-23 11:17:14 +08:00
WuYongJie
df1483cc82 Merge remote-tracking branch 'origin/main' into codex/xuxiake-mainline 2026-05-22 19:51:13 +08:00
WuYongJie
08093bb748 feat(intake): restrict formal product workspace access 2026-05-22 19:50:38 +08:00
WuYongJie
9b5dbbe856 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-05-22 18:49:52 +08:00
WuYongJie
320f9abbc9 fix(users): 修正多部门员工主部门与密码编辑 2026-05-22 18:49:43 +08:00
WuYongJie
e8e83bc531 fix(intake): align stock-created product detail sku flow 2026-05-22 18:01:37 +08:00
WuYongJie
88fb5e7ba3 fix(intake): lock factory model shared fields 2026-05-22 14:09:16 +08:00
WuYongJie
c05855601d feat(intake): add factory contracts and 3d packaging refinements 2026-05-22 12:53:49 +08:00
WuYongJie
aac475f982 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-05-21 18:54:51 +08:00
WuYongJie
872f7cbc14 fix: refine intake 3d material and sku confirmation flow 2026-05-21 18:54:40 +08:00
WuYongJie
31562766a7 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-05-21 16:18:33 +08:00
WuYongJie
ade68c56de fix(asset-center): 由后端生成 CAD 导入规格编码 2026-05-21 16:18:18 +08:00
WuYongJie
4f7c7cdf45 fix(intake): 按节点校验开品保存权限 2026-05-21 13:24:10 +08:00
WuYongJie
603b114345 fix(intake): 调整开品字段口径与样评填写逻辑 2026-05-20 19:09:53 +08:00
WuYongJie
ac785bb495 feat: update intake brand rules and access scopes 2026-05-20 17:14:00 +08:00
WuYongJie
a1209cc7f8 fix(intake): 调整空间布局字段选项 2026-05-20 16:13:04 +08:00
WuYongJie
606dc9612c Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-05-20 15:20:25 +08:00
WuYongJie
fbbf8d85ef fix(intake): 允许企划编辑空间布局节点 2026-05-20 15:19:42 +08:00
WuYongJie
c5a6f044c7 feat: refine non-integrated intake workflow 2026-05-20 14:17:39 +08:00
WuYongJie
b7c794ac2f Merge remote-tracking branch 'origin/main' into codex/xuxiake-mainline 2026-05-20 12:54:57 +08:00
WuYongJie
314c4d1ba0 feat(intake): 完善正式入库定厂与寄样流程 2026-05-20 12:54:49 +08:00
WuYongJie
55cba13828 fix(users): 全量花名册同步后清理查询缓存 2026-05-20 12:03:17 +08:00
WuYongJie
65fb8fec42 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-05-20 11:56:29 +08:00
WuYongJie
a6c26a0581 fix(users): 接入家具系统花名册定时同步 2026-05-20 11:56:23 +08:00
WuYongJie
05b34bcec1 fix: refine non-integrated department permissions 2026-05-20 09:27:54 +08:00
WuYongJie
5daa93d3eb merge: 同步 xuxiake 主线用户匹配修复 2026-05-19 20:22:55 +08:00
WuYongJie
12d92a1b4b merge: 收口用户匹配与花名册同步修复 2026-05-19 20:20:54 +08:00
WuYongJie
66491cf49e Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-assisted-assembly-next 2026-05-19 20:19:05 +08:00
WuYongJie
60fdf33bad fix(users): 修复员工库匹配与生产入口口径 2026-05-19 20:18:13 +08:00
WuYongJie
e2be21d21b fix: load jst directory options for intake 2026-05-19 19:34:35 +08:00
WuYongJie
954948dae1 fix: align intake brand copy workflow 2026-05-19 19:23:03 +08:00
WuYongJie
0a5c6b7a43 fix: treat stock sku batches as confirmed 2026-05-19 19:09:16 +08:00
WuYongJie
d8aadfa0e1 fix: refine non-integrated sku confirmation 2026-05-19 18:30:11 +08:00
WuYongJie
81500dcc07 fix: refine stock detail filters and planning layout 2026-05-19 14:08:04 +08:00
WuYongJie
aa31e4e230 fix: exclude accessory stock sku imports 2026-05-19 13:09:55 +08:00
WuYongJie
e9a6deb3a1 fix: align jst sku sync with stock spus 2026-05-19 12:15:02 +08:00
WuYongJie
273097f8b5 fix: sync stock spu filters and imported sku codes 2026-05-19 12:08:11 +08:00
WuYongJie
668391f983 feat: sync and filter jst sku stock products 2026-05-19 11:33:01 +08:00
WuYongJie
30e1d2f5b5 fix(intake): reset persisted workspace tab cache 2026-05-19 09:11:06 +08:00
WuYongJie
fd79dcae56 fix(intake): defer workspace tab restore until client mount 2026-05-19 08:58:07 +08:00
WuYongJie
92275301af merge: codex xuxiake mainline build storage placeholders 2026-05-18 20:22:26 +08:00
WuYongJie
a18fdd8fbb fix(deploy): 使用构建期存储占位配置 2026-05-18 20:21:05 +08:00
WuYongJie
1a11bb51e3 merge: codex xuxiake mainline storage build fix 2026-05-18 20:15:14 +08:00
WuYongJie
b9be83d84a fix(deploy): 传入构建期公开存储地址 2026-05-18 20:14:33 +08:00
WuYongJie
c0bca713ce merge: codex xuxiake mainline deploy source fix 2026-05-18 20:09:49 +08:00
WuYongJie
dbb3ea8fd7 merge: 同步远端 xuxiake 部署收口 2026-05-18 20:08:35 +08:00
WuYongJie
6bf85d79c6 fix(deploy): 使用官方 Alpine 包源构建镜像 2026-05-18 20:07:33 +08:00
WuYongJie
2b50f74b12 Merge remote-tracking branch 'origin/main' into codex/xuxiake-mainline 2026-05-18 20:01:24 +08:00
WuYongJie
09606adcb9 fix(intake): keep stock products in product development list 2026-05-18 19:59:57 +08:00
WuYongJie
666211714a merge: codex xuxiake mainline puppeteer deploy fix 2026-05-18 19:59:33 +08:00
WuYongJie
aaae569af1 fix(deploy): 跳过 Puppeteer 构建期浏览器下载 2026-05-18 19:58:48 +08:00
WuYongJie
81209f73ce merge: codex xuxiake mainline deploy fix 2026-05-18 19:56:15 +08:00
WuYongJie
48557987e0 fix(deploy): 修复生产镜像构建基线 2026-05-18 19:54:58 +08:00
WuYongJie
1f66aab483 merge: codex xuxiake mainline format fix 2026-05-18 19:42:36 +08:00
WuYongJie
0640dd8dec fix(asset-center): 清理系列接口文件尾空行 2026-05-18 19:41:48 +08:00
WuYongJie
fa633e3ca0 merge: codex xuxiake mainline 2026-05-18 19:38:29 +08:00
WuYongJie
486bf9333e merge: 接入系列编码与资料同步
# Conflicts:
#	furniture-3d-system/src/app/intake/IntakeClient.tsx
#	furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.test.tsx
2026-05-18 19:36:54 +08:00
WuYongJie
8eaa82db52 merge: codex xuxiake mainline 2026-05-18 18:58:00 +08:00
WuYongJie
271a7b86ad feat(intake): split stock product entry 2026-05-18 18:57:31 +08:00
WuYongJie
dcab35873c feat(asset-center): 统一系列编码与资料同步 2026-05-18 18:30:13 +08:00
WuYongJie
45bc7eaebf merge: codex xuxiake mainline 2026-05-18 17:40:52 +08:00
WuYongJie
1d668c58a3 fix(intake): improve product list pagination 2026-05-18 17:40:20 +08:00
WuYongJie
db7023e45e Merge remote-tracking branch 'origin/codex/wuyongjie-assisted-assembly-next' into codex/xuxiake-mainline
# Conflicts:
#	furniture-3d-system/src/app/intake/IntakeClient.test.tsx
2026-05-18 17:06:19 +08:00
WuYongJie
c643c09512 feat(intake): refine product list and sku workflow 2026-05-18 15:32:37 +08:00
WuYongJie
2a4dbadbdc merge: 同步 xuxiake 正式入库系列明细 2026-05-18 15:17:44 +08:00
WuYongJie
d661273b23 fix(assisted-assembly): 修复多孔吸附与资产预览 2026-05-18 14:04:29 +08:00
WuYongJie
e5f10d6eda feat(intake): support series component sku details 2026-05-18 12:40:18 +08:00
WuYongJie
331aac6961 feat(assisted-assembly): 接入系列物料区与统一孔位高亮 2026-05-16 11:05:15 +08:00
WuYongJie
b603555df7 feat: 重构资产中心系列优先入口 2026-05-15 14:39:45 +08:00
WuYongJie
1f2d2af257 feat(users): 收口账号权限与登录注册视觉 2026-05-15 10:41:38 +08:00
WuYongJie
38a182bc8d merge: 同步 xuxiake 主线 2026-05-14 18:49:23 +08:00
WuYongJie
d6ddac46c2 feat(auth): 设计师角色迁移为设计业务类型 2026-05-14 18:26:06 +08:00
WuYongJie
016acd0c30 feat(intake): add spu rule library and stock product flow 2026-05-14 17:20:16 +08:00
WuYongJie
a59c1bf82f feat: refine intake DingTalk node permissions 2026-05-14 12:04:24 +08:00
WuYongJie
3cd41b4921 fix: 优化资产中心与管理页白曜石视觉 2026-05-14 11:39:06 +08:00
WuYongJie
f1f5c2ff80 feat: 完善资产中心规格款保存与回收站流程 2026-05-14 10:25:07 +08:00
WuYongJie
2e954f3219 feat: 接入资产中心回收站与规格款渲染配置 2026-05-13 21:54:01 +08:00
WuYongJie
286347582c Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-assisted-assembly-next 2026-05-13 21:49:51 +08:00
WuYongJie
0f7e647127 feat: 优化正式入库人员字段与首批质检 2026-05-13 19:10:48 +08:00
WuYongJie
7f3fe36da9 fix: 优化材质金属度渲染表现 2026-05-13 16:35:43 +08:00
WuYongJie
8d76d4ef9d fix: 优化材质贴图上传与预览兜底 2026-05-13 15:08:18 +08:00
WuYongJie
ed291bc8c9 merge: 同步 xuxiake 主线 2026-05-13 10:06:30 +08:00
WuYongJie
c3f3b8e218 fix: polish planning layout selectors 2026-05-13 09:50:17 +08:00
WuYongJie
62bceabc66 feat: refine planning layout fields 2026-05-13 09:17:32 +08:00
WuYongJie
4e0af7c27a fix: 修复资产贴图预览并收敛白曜石视觉 2026-05-12 20:13:49 +08:00
WuYongJie
0ed04b81bf Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-assisted-assembly-next 2026-05-12 17:36:57 +08:00
WuYongJie
371b5985d7 修复资产中心CAD导入与孔位预览 2026-05-12 17:36:28 +08:00
WuYongJie
c1604fde1e fix(intake): open material config in workspace tab 2026-05-12 17:05:17 +08:00
WuYongJie
d4fbe1ca0d fix(intake): choose brand before creating product 2026-05-12 15:08:41 +08:00
WuYongJie
ca908eb970 fix(intake): lock product detail brand field 2026-05-12 14:04:25 +08:00
WuYongJie
1410d5e2f1 fix: remove stale product workflow relation 2026-05-12 11:25:27 +08:00
WuYongJie
05abd68518 fix: polish intake detail interactions 2026-05-12 11:18:25 +08:00
WuYongJie
b1f2bf7b06 调整工作台工具条与品牌区视觉 2026-05-12 10:05:52 +08:00
WuYongJie
81d53ee4db Merge remote-tracking branch 'origin/codex/wuyongjie-assisted-assembly-next' into codex/xuxiake-mainline 2026-05-12 09:34:21 +08:00
WuYongJie
116c2b3705 收敛CAD导入弹窗白曜石视觉 2026-05-12 09:23:05 +08:00
WuYongJie
21f1cd5a1b 优化资产中心CAD导入与白曜石弹窗视觉 2026-05-11 21:53:31 +08:00
WuYongJie
2c06163575 fix(cad): make FEXPORT default to single asset export 2026-05-11 20:23:00 +08:00
WuYongJie
db6d345249 fix(intake): keep bom sku overrides isolated 2026-05-11 20:09:03 +08:00
WuYongJie
cf4b370f4a feat: add planning layout and product limit fields 2026-05-11 19:42:43 +08:00
WuYongJie
4415ef8b33 feat(intake): add upload progress and zip batch download 2026-05-11 18:22:02 +08:00
WuYongJie
e2766b2cfb chore(ui): simplify product development brand name 2026-05-11 17:42:12 +08:00
WuYongJie
b46f041e3e chore(cad): bump exporter data version to 2.6 2026-05-11 17:40:21 +08:00
WuYongJie
256f15369a fix(assembly): stabilize snap bounds and hole hover highlight 2026-05-11 17:39:17 +08:00
WuYongJie
e86c1417d2 fix(intake): allow creators to edit product details 2026-05-11 17:36:24 +08:00
WuYongJie
36873ba3cb feat(intake): update non-integrated detail workflow 2026-05-11 16:25:37 +08:00
WuYongJie
ea2fdea847 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-assisted-assembly-next
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 11:58:15 +08:00
WuYongJie
55233acb3d fix(intake): align linked entry permissions 2026-05-11 11:21:50 +08:00
WuYongJie
5ac5c6f7b1 fix(intake): keep copied product workspace tabs 2026-05-11 09:05:26 +08:00
WuYongJie
2ae4817412 feat(intake): link bom material libraries 2026-05-09 18:07:15 +08:00
WuYongJie
2b204c6a98 feat(intake): add edge banding library 2026-05-09 15:46:10 +08:00
WuYongJie
94f7c0a302 fix(intake): streamline workflow node headers 2026-05-09 13:57:13 +08:00
WuYongJie
6a8e2da6d7 feat(intake): refine non-integrated workflows 2026-05-09 12:53:26 +08:00
WuYongJie
19d5d426e6 feat: 完善SKU批次资料导入 2026-05-09 09:51:52 +08:00
WuYongJie
0e9db44bc4 feat(intake): add sku batch workflow 2026-05-08 18:28:04 +08:00
WuYongJie
2f02681a69 fix(intake): preserve bom details across barcode updates 2026-05-08 13:13:39 +08:00
WuYongJie
368b4427f0 fix(intake): isolate node confirmation updates 2026-05-08 11:17:01 +08:00
WuYongJie
8761242931 fix: repair non-integrated create workspace flow 2026-05-08 11:15:44 +08:00
WuYongJie
b2ec7d356b fix(intake): chunk product uploads before proxy limit 2026-05-08 09:04:23 +08:00
WuYongJie
41164f022c fix(intake): close new product workspace on cancel 2026-05-07 19:05:37 +08:00
WuYongJie
41fb1b0aac fix(intake): support large non-integrated uploads 2026-05-07 18:45:41 +08:00
WuYongJie
6dd75c502a fix(intake): avoid empty chunk upload 502 2026-05-07 17:19:15 +08:00
WuYongJie
11e67ff4ed fix(intake): stabilize chunked upload response 2026-05-07 16:58:30 +08:00
WuYongJie
ea67e56480 fix(intake): bypass production upload body limit 2026-05-07 16:22:14 +08:00
WuYongJie
cddbae3ab6 Merge branch 'codex/xuxiake-mainline' into main 2026-05-07 14:49:55 +08:00
WuYongJie
2cdb29c650 fix(intake): support range requests for uploaded assets 2026-05-07 14:47:18 +08:00
WuYongJie
6a2a926fbf Merge branch 'codex/xuxiake-mainline' into codex/merge-main-20260507 2026-05-07 14:31:27 +08:00
WuYongJie
8baa41dd0b Refine non-integrated edit permissions 2026-05-07 14:12:31 +08:00
WuYongJie
fa27245c66 Raise non-integrated upload limit 2026-05-07 14:07:21 +08:00
WuYongJie
028faaf0af feat: update intake non-integrated workflow 2026-05-07 12:10:03 +08:00
WuYongJie
171b61aec1 Adjust intake list and sidebar display 2026-05-07 08:43:56 +08:00
WuYongJie
4ab12803f5 Update non-integrated intake workflow 2026-05-06 17:47:54 +08:00
WuYongJie
ac7c5a81d8 fix: wrap app shell search params in suspense 2026-05-06 12:35:33 +08:00
WuYongJie
e4ef58a06a feat: refine non-integrated workflow nodes 2026-05-06 12:09:19 +08:00
WuYongJie
3fcd0d2c4d feat(intake): streamline opening detail workflow 2026-05-05 19:00:39 +08:00
WuYongJie
2b0dc5fd23 feat(intake): sync patent application ledger 2026-05-05 15:59:53 +08:00
WuYongJie
f8a0952042 feat(intake): move approval workflow entry to demand draft 2026-04-30 16:45:30 +08:00
WuYongJie
1e9fec0bda fix(users): refresh inspector after account edit 2026-04-30 13:59:09 +08:00
WuYongJie
72d1408027 feat: add approval workflow workspace and account templates 2026-04-30 13:45:25 +08:00
WuYongJie
f90bc1b1fa feat: add approval workflow workspace and account templates 2026-04-30 12:57:12 +08:00
WuYongJie
1182549bba feat: add product patent ledger workflow 2026-04-30 11:51:37 +08:00
WuYongJie
d2ae88ce23 Merge remote-tracking branch 'origin/codex/wuyongjie-assisted-assembly-next' into codex/xuxiake-mainline 2026-04-30 10:45:35 +08:00
WuYongJie
b3ad3c1195 feat(intake): refine workflow and reference estimation 2026-04-30 10:45:21 +08:00
WuYongJie
edfe43d15f Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-assisted-assembly-next 2026-04-30 10:31:26 +08:00
WuYongJie
f5f9ca6ba7 feat(users): support department assignment and employee matching 2026-04-30 10:31:18 +08:00
WuYongJie
20516b2a87 Merge remote-tracking branch 'origin/codex/wuyongjie-assisted-assembly-next' into codex/xuxiake-mainline 2026-04-29 14:06:29 +08:00
WuYongJie
d63c937fac feat(intake): 优化产品开发进度看板和开品资料展示 2026-04-29 10:59:15 +08:00
WuYongJie
a4daa71bfa fix: align users workspace with white obsidian 2026-04-29 09:12:39 +08:00
WuYongJie
e580b9625e Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-assisted-assembly-next 2026-04-29 09:09:06 +08:00
WuYongJie
ab3d2d77d2 feat: align admin workspaces with white obsidian 2026-04-28 18:47:54 +08:00
3ff8ea12d9 Merge codex/xuxiake-mainline into main
Merge latest intake workflow updates.
2026-04-28 18:39:53 +08:00
WuYongJie
9a73dcb17e feat: refine intake product workflow 2026-04-28 18:34:16 +08:00
ee8f218712 Merge codex/xuxiake-mainline into main
Merge codex/xuxiake-mainline into main
2026-04-28 15:01:24 +08:00
WuYongJie
1740036d24 feat: add non-integrated project review flow 2026-04-28 14:48:55 +08:00
WuYongJie
43775c8e44 Merge remote-tracking branch 'origin/codex/wuyongjie-assisted-assembly-next' into codex/xuxiake-mainline 2026-04-28 14:14:10 +08:00
WuYongJie
baca2fde9d feat(assembly): add snap basis workflow 2026-04-28 13:48:32 +08:00
ddc863fbc8 Merge pull request 'feat: polish intake detail navigation' (#13) from codex/xuxiake-mainline into main 2026-04-28 10:51:04 +08:00
WuYongJie
04838e3f74 feat: polish intake detail navigation 2026-04-28 10:09:44 +08:00
WuYongJie
f85d4b9c5b merge: integrate material library layout updates 2026-04-27 21:04:36 +08:00
WuYongJie
9681a54754 merge: integrate assisted assembly material layout updates 2026-04-27 21:01:23 +08:00
WuYongJie
90ef2f9c72 style: refine material library layout 2026-04-27 20:55:15 +08:00
WuYongJie
278fad104e merge: integrate xuxiake intake workspace updates 2026-04-27 18:44:01 +08:00
WuYongJie
1cbf87dc84 feat(intake): refine non-integrated document workspaces 2026-04-27 18:36:57 +08:00
WuYongJie
45bf25a6b3 feat(ui): refine material library obsidian workbench 2026-04-27 18:24:24 +08:00
WuYongJie
8cf7cf19d8 merge: integrate asset center next updates 2026-04-27 13:24:04 +08:00
WuYongJie
0b2b458c17 feat(ui): refine obsidian workbench flows 2026-04-27 12:22:34 +08:00
WuYongJie
90546679f2 feat(ui): restore obsidian modal and empty workbench 2026-04-25 20:02:45 +08:00
WuYongJie
696faded1d feat(asset-center): make empty workbench full-bleed 2026-04-25 19:25:29 +08:00
25b4a20e92 merge: sync confirmed intake workflow updates
Merge codex/xuxiake-mainline into main.
2026-04-25 19:17:20 +08:00
WuYongJie
c23128147b Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-asset-center-next 2026-04-25 19:08:05 +08:00
WuYongJie
22c983ab16 feat(asset-center): add light animated empty workbench model 2026-04-25 19:07:21 +08:00
WuYongJie
460c4f9eae feat(intake): sync confirmed workflow updates 2026-04-25 19:06:27 +08:00
WuYongJie
0e857c6814 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-04-25 19:04:11 +08:00
WuYongJie
093d8a215c fix(asset-center): hide gizmo until node selection 2026-04-25 18:51:38 +08:00
WuYongJie
925dfc8ad5 Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/xuxiake-mainline 2026-04-25 18:45:53 +08:00
WuYongJie
4d80f3a7d6 chore(docs): rename obsidian visual standard 2026-04-25 18:39:57 +08:00
WuYongJie
96732d46d1 feat(asset-center): refine obsidian 3d workbench 2026-04-25 18:37:05 +08:00
WuYongJie
a9aa01495a feat(asset-center): transplant obsidian workbench UI onto xuxiake baseline 2026-04-25 16:03:45 +08:00
590891deaa merge: sync confirmed intake material workflows
Merge codex/xuxiake-mainline into main.\n\nIncludes confirmed 3100 updates through d8acbf6.
2026-04-25 11:52:03 +08:00
WuYongJie
d8acbf60a0 fix(intake): hide edit sticky actions 2026-04-25 11:40:20 +08:00
WuYongJie
5ba2ea231a feat(platform): sync confirmed intake material workflows 2026-04-25 10:51:11 +08:00
322e6eedd4 Merge pull request 'merge: sync 3100 substrate library defaults' (#10) from codex/xuxiake-mainline into main 2026-04-24 10:42:10 +08:00
WuYongJie
5a22d2a2ae feat(asset-center): sync 3100 substrate library defaults 2026-04-24 10:25:40 +08:00
75bed8ad77 Merge pull request 'merge: restore substrate library workflow' (#9) from codex/xuxiake-mainline into main 2026-04-24 09:04:00 +08:00
WuYongJie
e45d46398e feat(asset-center): restore substrate library workflow 2026-04-23 19:03:27 +08:00
f287c16bf3 merge: refine intake previews and product photos
Merge codex/xuxiake-mainline into main.\n\nValidated: npm run type-check; NonIntegratedDetailClient jest suite.
2026-04-23 15:04:13 +08:00
WuYongJie
8ded8bc5e1 Merge remote-tracking branch 'origin/main' into codex/xuxiake-mainline 2026-04-23 14:59:47 +08:00
WuYongJie
e263b24f5a fix(intake): refine product photo uploads and document previews 2026-04-23 13:30:40 +08:00
a519345079 Merge pull request 'merge: intake runtime asset serving and sku directories' (#7) from codex/xuxiake-mainline into main 2026-04-22 19:27:05 +08:00
WuYongJie
cf82b30096 fix(intake): add sku directories and runtime asset serving 2026-04-22 19:24:12 +08:00
WuYongJie
8fc6d56183 fix(deploy): link shared intake assets after build 2026-04-22 17:10:53 +08:00
WuYongJie
dd1c96dddc fix(deploy): preserve shared intake assets in fallback release 2026-04-22 17:06:23 +08:00
WuYongJie
b83c81d679 Merge remote-tracking branch 'origin/main' into codex/xuxiake-mainline 2026-04-22 16:43:01 +08:00
WuYongJie
70b4d3b2fb fix(intake): route shared uploads through product endpoints 2026-04-22 16:42:47 +08:00
f2573f154b merge: restore intake shared uploads and 3d actions
3100 ?????,???????????? 3D ???????
2026-04-22 15:01:53 +08:00
WuYongJie
9b70901324 fix(intake): restore shared uploads and 3d actions 2026-04-22 14:55:44 +08:00
1ef71a21aa Merge pull request 'merge: fix prod deploy script line endings' (#5) from codex/wuyongjie-main-release into main 2026-04-22 12:30:43 +08:00
WuYongJie
86c5007c03 fix(deploy): write unix newlines for remote scripts 2026-04-22 12:30:19 +08:00
af961287d1 Merge pull request 'merge: restore intake shared persistence and package corner saves' (#4) from codex/xuxiake-mainline into main 2026-04-22 12:25:16 +08:00
WuYongJie
2d1d32bc29 Merge remote-tracking branch 'origin/main' into codex/xuxiake-mainline 2026-04-22 12:23:40 +08:00
WuYongJie
c620050809 fix(intake): restore shared persistence and package corner saves 2026-04-22 12:23:21 +08:00
140da11f82 merge: deploy script fix into main before prod deploy
Merge deploy script parser fix into main before production release.
2026-04-22 09:58:01 +08:00
WuYongJie
6ad7786af5 fix(deploy): restore mainline prod deploy script 2026-04-22 09:57:30 +08:00
1ad68f23bf merge: xuxiake mainline into main for prod deploy
?? codex/xuxiake-mainline ? main,??????
2026-04-22 09:52:20 +08:00
WuYongJie
f44fb28934 fix(intake): refine bom packaging subgroup inference 2026-04-22 09:52:02 +08:00
Codex
bf9c0127a0 fix(intake): normalize bom package subcategory locally 2026-04-22 09:39:56 +08:00
Codex
cba797dc10 fix: stabilize intake mainline merge baseline 2026-04-21 21:09:09 +08:00
Codex
8b3c6570e3 fix: align intake compatibility after main sync 2026-04-21 20:29:40 +08:00
Codex
d7263fecd5 chore(intake): checkpoint mainline merge-prep state 2026-04-21 20:22:35 +08:00
Codex
ac8bcdfd63 Merge remote-tracking branch 'origin/main' into codex/xuxiake-non-integrated-clean-20260416
# Conflicts:
#	furniture-3d-system/next.config.js
#	furniture-3d-system/src/app/HomeClient.workspace.tsx
#	furniture-3d-system/src/app/__tests__/assisted-assembly-page.test.tsx
#	furniture-3d-system/src/app/admin/page.tsx
#	furniture-3d-system/src/app/api/categories/route.test.ts
#	furniture-3d-system/src/app/api/intake/non-integrated/presentation-preview/route.ts
#	furniture-3d-system/src/app/api/intake/non-integrated/upload/route.ts
#	furniture-3d-system/src/app/api/users/[id]/handover/route.test.ts
#	furniture-3d-system/src/app/api/users/[id]/route.test.ts
#	furniture-3d-system/src/app/api/users/organization-tree/route.ts
#	furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.tsx
#	furniture-3d-system/src/app/assisted-assembly/page.tsx
#	furniture-3d-system/src/app/intake/IntakeClient.test.tsx
#	furniture-3d-system/src/app/intake/IntakeClient.tsx
#	furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.test.tsx
#	furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.tsx
#	furniture-3d-system/src/app/intake/non-integrated/[id]/page.tsx
#	furniture-3d-system/src/app/intake/non-integrated/nonIntegratedStorage.test.ts
#	furniture-3d-system/src/app/intake/non-integrated/nonIntegratedStorage.ts
#	furniture-3d-system/src/app/intake/page.tsx
#	furniture-3d-system/src/app/products/[id]/skus/[skuId]/assembly/page.tsx
#	furniture-3d-system/src/app/users/page.tsx
#	furniture-3d-system/src/app/warehouse/WarehouseClient.tsx
#	furniture-3d-system/src/components/layout/SidebarUserCenter.tsx
#	furniture-3d-system/src/components/layout/__tests__/AppSideNav.test.tsx
#	furniture-3d-system/src/components/platform/AssemblyWorkbenchV2.tsx
#	furniture-3d-system/src/components/platform/ViewportStageShell.tsx
#	furniture-3d-system/src/components/platform/__tests__/AssemblyWorkbenchV2.test.tsx
#	furniture-3d-system/src/components/users/CreateUserModal.tsx
#	furniture-3d-system/src/components/users/DepartmentAccessTree.tsx
#	furniture-3d-system/src/components/users/EditUserModal.tsx
#	furniture-3d-system/src/components/users/UserListItem.tsx
#	furniture-3d-system/src/components/users/__tests__/DepartmentAccessTree.test.tsx
#	furniture-3d-system/src/lib/3d/engine.ts
#	furniture-3d-system/src/lib/model-preview-zip.test.ts
#	furniture-3d-system/src/lib/model-preview-zip.ts
#	furniture-3d-system/src/lib/non-integrated-sku-export.ts
#	furniture-3d-system/src/lib/open-product/__tests__/assembly-workspace.test.ts
#	furniture-3d-system/src/lib/open-product/assembly-workspace.ts
#	furniture-3d-system/src/server/repositories/auth.repository.ts
#	furniture-3d-system/src/server/services/__tests__/material.service.test.ts
#	furniture-3d-system/src/server/services/__tests__/user-handover.service.test.ts
#	furniture-3d-system/src/server/services/__tests__/user-management.organization-tree.test.ts
#	furniture-3d-system/src/server/services/__tests__/user-management.service.test.ts
#	furniture-3d-system/src/server/services/dingtalk-auth.service.ts
#	furniture-3d-system/src/server/services/user-handover.service.ts
#	furniture-3d-system/src/server/services/user-management.service.ts
#	furniture-3d-system/src/server/services/user.service.ts
2026-04-21 20:08:40 +08:00
WuYongJie
d4bc44579e feat: align intake workflow and git-based prod deploy 2026-04-21 19:33:46 +08:00
WuYongJie
3c1675c78e feat: merge intake workflow and current workspace source updates 2026-04-21 17:06:48 +08:00
Codex
06587e828d feat: refine formal intake product workflow 2026-04-21 14:45:57 +08:00
WuYongJie
a4ef213950 feat: tighten user activation and role assignment limits 2026-04-17 16:05:19 +08:00
WuYongJie
013b5dd3f4 fix: tighten identity center layout and asset auth 2026-04-17 15:08:50 +08:00
WuYongJie
7ab4fc6ff4 feat: add reusable mobile camera scan and upload support 2026-04-17 13:35:58 +08:00
WuYongJie
91584f21c6 feat: consolidate workspace release updates 2026-04-17 12:50:06 +08:00
WuYongJie
87bb0e126d feat: refresh branding and material library workspace 2026-04-17 11:07:18 +08:00
WuYongJie
d7737909bb feat: sync remaining workspace branding updates 2026-04-17 10:25:31 +08:00
WuYongJie
c8d3d3d9e7 feat: restore material library and assembly workflows 2026-04-16 23:13:16 +08:00
WuYongJie
602e2fd33f fix: recover assembly context and clear typecheck baseline 2026-04-16 21:44:04 +08:00
WuYongJie
18d2a49d2e feat: converge assembly entrypoints and handover roles 2026-04-16 21:08:41 +08:00
WuYongJie
56a29b8be2 feat: polish home assembly and material workflows 2026-04-16 18:42:37 +08:00
Codex
bb0ebc2981 feat: add integrated intake detail starter 2026-04-16 17:30:44 +08:00
Codex
c7baacb2db feat: show integrated series rows in intake 2026-04-16 17:26:21 +08:00
Codex
0362931e95 test: add integrated draft group adapter 2026-04-16 17:01:47 +08:00
WuYongJie
44677da317 feat: improve non-integrated intake workflow 2026-04-16 16:45:34 +08:00
Codex
7dc99e3725 docs: add integrated intake design spec 2026-04-16 16:32:32 +08:00
WuYongJie
2a4382dcb2 feat: refresh admin and user management portals 2026-04-16 14:07:13 +08:00
Codex
5c752ae2cf chore: sync main outside intake and warehouse 2026-04-16 13:55:17 +08:00
e1645a164a Merge pull request 'feat: integrate selected xuxiake intake enhancements into mainline' (#1) from codex/wuyongjie-mainline into main
Reviewed-on: http://git.1080p.hk/WuYongJie/furniture-3d-system-Wu/pulls/1
2026-04-16 13:05:41 +08:00
WuYongJie
5731018bed feat: integrate selected xuxiake intake enhancements 2026-04-16 12:38:52 +08:00
Codex
3c8c775c30 feat: update non-integrated intake workflow 2026-04-16 11:18:24 +08:00
WuYongJie
2adb1d8e98 feat: persist home workspace tab state 2026-04-15 21:45:18 +08:00
WuYongJie
69b1daf157 fix: close assisted assembly cad snap and prod deploy 2026-04-15 21:27:09 +08:00
Codex
8ad93a2917 feat: finish non-integrated intake flow 2026-04-15 20:22:09 +08:00
WuYongJie
3158e87908 fix: tighten handover scope checks 2026-04-15 17:52:38 +08:00
WuYongJie
9f2ce7ee87 chore: add warehouse location binding migration 2026-04-15 16:55:01 +08:00
WuYongJie
d6ef2f3bf2 fix: stabilize xuxiake intake merge integration 2026-04-15 16:49:47 +08:00
Codex
ab0ed57312 feat: update intake and warehouse workspace 2026-04-15 16:40:10 +08:00
Codex
f1a9e853e0 feat: sync intake and warehouse workflow updates 2026-04-15 16:40:10 +08:00
Codex
e1407c8464 chore: checkpoint latest intake and warehouse changes 2026-04-15 16:40:10 +08:00
Codex
bcbe884be1 feat(warehouse): 配件SKU和成品SKU模块接入真实数据
- page.tsx: 并行获取资产族变体(配件)和草稿组(成品)数据
- 配件SKU库位: 按资产类型(板材/组件/五金/包装)分组展示,支持筛选和搜索
- 成品SKU库位: 展示草稿组及其PreSku,支持搜索,显示状态标签
- 右侧AccordionSection展示真实统计数据
- 测试文件适配新props接口
2026-04-15 16:40:10 +08:00
Codex
ce85aa8def chore: checkpoint intake and warehouse workspace state 2026-04-15 16:40:10 +08:00
Codex
995030a11d chore(intake): checkpoint right workbench toggle layout 2026-04-15 16:40:10 +08:00
WuYongJie
15d5e6fb89 feat: sync validated admin auth material and exporter updates 2026-04-15 16:36:00 +08:00
Codex
ee0fdfb5f9 feat: update intake and warehouse workspace 2026-04-15 15:55:27 +08:00
WuYongJie
7ae45d4f0b fix: add pending activation auth error code 2026-04-15 15:25:17 +08:00
WuYongJie
5a54bcedae fix: allow nullable thumbnails in context menus 2026-04-15 15:24:25 +08:00
WuYongJie
305a99d291 fix: normalize nullable material thumbnails in service layer 2026-04-15 15:23:01 +08:00
WuYongJie
7d73276995 fix: allow pending users in auth core types 2026-04-15 15:22:05 +08:00
WuYongJie
18e1403c2a fix: align admin material editor thumbnail typing 2026-04-15 15:21:01 +08:00
WuYongJie
1c72957333 fix: align material thumbnail types with nullable schema 2026-04-15 15:20:04 +08:00
WuYongJie
1e5237945b chore: align prisma schema with production user data 2026-04-15 15:18:19 +08:00
WuYongJie
66c4113bc9 fix: remove access management dependency on employee directory relation 2026-04-15 15:15:20 +08:00
WuYongJie
45c20500ae fix: import handover modal class utility 2026-04-15 15:14:10 +08:00
WuYongJie
238bd74c49 fix: support modal class overrides in access flows 2026-04-15 15:13:20 +08:00
WuYongJie
6d07abf9d4 fix: align access management modal user types 2026-04-15 15:12:13 +08:00
WuYongJie
96662689f3 feat: complete access management organization tree 2026-04-15 15:09:32 +08:00
Codex
6d9fd415a6 feat: sync intake and warehouse workflow updates 2026-04-15 09:44:11 +08:00
Codex
4de7ea9bfb chore: checkpoint latest intake and warehouse changes 2026-04-14 14:57:19 +08:00
Codex
4c298983ff feat(warehouse): 配件SKU和成品SKU模块接入真实数据
- page.tsx: 并行获取资产族变体(配件)和草稿组(成品)数据
- 配件SKU库位: 按资产类型(板材/组件/五金/包装)分组展示,支持筛选和搜索
- 成品SKU库位: 展示草稿组及其PreSku,支持搜索,显示状态标签
- 右侧AccordionSection展示真实统计数据
- 测试文件适配新props接口
2026-04-14 11:26:08 +08:00
Codex
ad70b534bf chore: checkpoint intake and warehouse workspace state 2026-04-14 10:00:51 +08:00
Codex
7f5218247d chore(intake): checkpoint right workbench toggle layout 2026-04-14 08:58:57 +08:00
Codex
795efe5970 chore: record 3100 recovered main-aligned state 2026-04-13 19:05:22 +08:00
WuYongJie
d12470419e merge: adopt xuxiake material library workflow 2026-04-13 17:26:23 +08:00
WuYongJie
73818e480b feat: finalize assisted assembly mainline workflow 2026-04-13 17:18:57 +08:00
Codex
7fdaaacb2d feat(资料库): 收口基材与花色新建逻辑 2026-04-13 16:09:38 +08:00
WuYongJie
b901e19ebd fix(material-library): align substrate migration and labels 2026-04-13 14:55:03 +08:00
Codex
a531b16b38 feat(基础资料库): 移除系统名称、优化花色筛选和表单布局
- 全栈移除 SubstrateSystemNameOption(字典管理、表单、API、数据库)
- 花色改为单选 + 自动搜索下拉,排除五金油漆/漆面颜色/五金漆面色
- 基材名称自动生成格式:尺寸+环保等级+材质+花色+贴面
- 材质改为纯下拉选择,贴面始终显示
- 花色框体与 Select 组件宽度/高度统一
- 厚度规格字典只保留厚度数值+排序值
2026-04-13 14:41:46 +08:00
Codex
cb6cb0cc57 feat(基础资料库): 实现 5 字段唯一约束收口
createSubstrate 新增合并逻辑:

1. **完全重复** (相同 5 字段 + 相同花色) → 拒绝,报错「基材库已有相同规格的基材,不可重复创建」
2. **花色不同** (相同 5 字段 + 不同花色) → 合并新花色到已有基材,重建名称
3. **规格不同** (5 字段不同) → 新建基材,自动编码 BC-XXXX

5 字段 = categoryId + environmentGradeId + thicknessSpecId + surfaceOptionId + systemNameOptionId
实木 (无 surfaceOptionId) 按 null 处理,纳入 5 字段匹配

- TypeScript 编译: 0 errors
- 基础资料库测试: 48/48 passed
2026-04-13 14:41:08 +08:00
WuYongJie
2846c7397e feat: finalize assisted assembly viewport integration 2026-04-13 14:32:24 +08:00
WuYongJie
ae9acb4091 feat: add asset thumbnail generation workflow 2026-04-11 15:13:26 +08:00
5019 changed files with 960851 additions and 24607 deletions

10
.gitignore vendored
View File

@ -21,6 +21,16 @@ _logs/
# Temp
temp/
*.tmp
/.tmp-*/
/tmp-*/
/tmp-*.html
/tmp-*.png
/tmp-*.env
/tmp-*.service
/tmp-*.timer
/tmp-*.err
/tmp-*.out
/.gemini/
# Build artifacts (C# / .NET)
**/bin/

197
AGENTS.md
View File

@ -1,80 +1,167 @@
# AGENTS.md
适用范围:`D:\AI Code\3danzhuang` 全仓库。
适用范围:`D:\AI Code\3danzhuang-wuyongjie-mainline` 全仓库。
未单独说明时,先遵循当前 agent 自己的全局规则(ZCode:`C:\Users\54972\.zcode\v2\AGENTS.md`;Codex:`C:\Users\54972\.codex\AGENTS.md`)。
这份文件只记录本仓库当前有效的补充规则和核查入口,不保存密码、密钥、数据库连接串或对象存储密钥。
## 1. 默认方式
重要边界:`D:\AI Code\3danzhuang` 是历史旧现场;当前正式工作目录以 `D:\AI Code\3danzhuang-wuyongjie-mainline` 为准。除非用户明确要求,不要混用旧目录、旧服务器别名、旧部署脚本或旧截图结论。
- 默认中文沟通,面向非程序员表达。
- 默认回复尽量简短、直白、好理解,非必要不展开。
- 默认先说“现在你要做什么 / 结论是什么”;技术细节、文件路径、命令和长解释只在用户要求时展开。
- 默认按当前会话直接开发。
- 没有“规范审查结论”,任务不算完成。
## 1. 当前项目真相
## 2. 三条硬规则
- 外层目录 `D:\AI Code\3danzhuang-wuyongjie-mainline` 是 Git 仓库根目录。
- 主应用目录是 `furniture-3d-system/`;用户未指定目录时,默认在这里执行代码、测试、构建和脚本。
- 技术栈以当前 `furniture-3d-system/package.json` 为准(核心:Next.js App Router + React + TypeScript + Prisma/PostgreSQL + Jest + Playwright + Tailwind CSS + Three.js/R3F;另有 ioredis、puppeteer、exceljs/xlsx、zod、zustand 等)。package.json 变动后文档不自动同步,以实时核查为准。
- `CLAUDE.md`、旧 README、`docs/历史归档/` 和早期部署文档里可能仍有旧苹果风格、MinIO、旧 Docker 部署、旧模型数量、旧 Product/SKU 口径等信息;只能当历史参考,不能覆盖当前代码、workflow 文档和实时命令结果。
- 工作树可能有用户或其他协作者留下的未跟踪文件、日志、贴图和本地产物。只处理本次任务明确相关的文件,不要顺手清理或回退无关现场。
- 必须符合企业级架构标准。
- 必须符合统一视觉标准。
- 必须优先做根因修复,不能拿补丁冒充完成。
## 2. 开工前固定入口
## 3. 固定流程
1. 开始前先用 1-2 句说明:目标、验收标准、根因判断。
2. 开发时优先复用现有实现,不顺手改无关问题。
3. 改完先做最小必要验证。
4. 交付前必须单独做一次规范审查。
5. 最终默认只交代:现在你要做什么、是否完成、有没有风险;技术细节只在用户要求时展开。
### 团队协作固定入口
当任务发生在 `furniture-3d-system/` 时,人和 AI 都必须先读取下面这些协作文档,再开始改代码:
当任务发生在 `furniture-3d-system/` 时,改代码前必须先读这 3 份当前协作入口:
- `furniture-3d-system/docs/workflow/团队协作模式.md`
- `furniture-3d-system/docs/workflow/模块边界说明.md`
- `furniture-3d-system/docs/workflow/主线同步操作手册.md`
这 3 份文档就是进入 `furniture-3d-system/` 的固定协作入口,不再额外约定第二套平级入口,也不再靠聊天记录补口头规则。
按任务类型额外读取:
如果任务和包装协作有关,再额外读取:
- 生产、部署、线上排障:`furniture-3d-system/docs/workflow/生产部署入口说明.md`,并核对 `scripts/deploy-yydd-prod-from-git.ps1` 与其调用的 `scripts/create-yydd-prod-deploy.ps1` 当前默认参数。
- 前端、视觉、交互:`furniture-3d-system/docs/设计规范/白曜石工坊统一视觉标准.md` 和 `furniture-3d-system/docs/workflow/规范审查清单.md`。
- 包装、仓库、轻仓库协作:再读包装/仓库相关交接文档和 `src/app/warehouse`、`src/app/api/warehouse`、`src/app/api/pre-skus/[id]/packaging/route.ts` 的当前实现。
- 数据库、主数据、迁移:优先读 `docs/架构设计/内部主数据巡检基线-20260529.md`、`docs/架构设计/数据库混乱点治理台账.md` 和相关 `db:foundation:*` 脚本说明。
- `furniture-3d-system/docs/workflow/同事交接模板.md`
- `furniture-3d-system/docs/workflow/同事本地AGENTS模板.md`
- `furniture-3d-system/docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md`
如果文档和代码冲突,按这个顺序判断:当前代码和脚本、当前线上/命令返回、当前 workflow 文档、历史文档或旧记忆。
固定协作口径:
## 3. Git 与主线协作
- `main` 只作为 `WuYongJie` 的主线收口分支,其他协作者默认不能直接推主线
- `xuxiake` 的长期包装分支固定为 `codex/xuxiake-packaging`
- `xuxiake` 的临时任务分支固定为 `codex/xuxiake-packaging-<任务简称>`
- 新分支统一使用 `codex/<负责人>-<工作域>` 或 `codex/<负责人>-<工作域>-<任务简称>`
- 不再使用 `feature/packaging`、`packaging/...`、`codex/xuxiake-dev` 这类泛分支名
- 长期包装分支同步 `main` 时固定使用 `merge main`
- 临时个人任务分支同步 `main` 时才使用 `rebase main`
- `main` 是最终发布主线,由 `WuYongJie` 收口;不要直接把半成品推到 `main`。
- `origin/codex/xuxiake-mainline` 是日常共享开发主线和默认同步基线。
- 新功能、优化和修复默认从最新 `origin/codex/xuxiake-mainline` 开任务分支,再按协作流程回流。
- 新分支统一使用 `codex/<负责人>-<工作域>` 或 `codex/<负责人>-<工作域>-<任务简称>`,不要再新建 `feature/...`、`packaging/...`、`dev`、`codex/xuxiake-dev` 这类泛分支名。
- 长期共享分支同步共享开发主线时固定使用 `merge origin/codex/xuxiake-mainline`;只有确认无人共用的临时个人任务分支才使用 `rebase origin/codex/xuxiake-mainline`。
- 每次开工或接续旧任务时,先看 `git status --short --branch`;涉及主线同步时再看 `git rev-list --left-right --count HEAD...origin/codex/xuxiake-mainline`,不要把“当前所在分支”误当成“已同步共享主线”。
- 用户问“xuxiake 主线有没有新提交”时,必须先 `git fetch origin`,再基于最新远端状态回答。
- 不要在脏工作树上直接 merge/rebase。先看 `git status --short`,分清本次改动、用户改动、本地产物和无关未跟踪文件。
- 常规任务完成必要验证后,可以按协作流程推进 `commit / push`;`main`、生产发布、破坏性操作、删除数据、改远端历史等高风险动作必须有用户明确授权。
## 4. 规范审查
### 主线部署标准流程(每次"主线部署"都走这一套)
规范审查固定只查三件事:
- 架构有没有被破坏
- 视觉有没有跑偏
- 这次是不是根因修复
本仓库有两根主线,都不直接提交:`main` 是生产部署唯一来源(由 `WuYongJie` 收口),`origin/codex/xuxiake-mainline` 是共享开发次主线(日常回流基线)。
结论只能是:
- `passed`
- `passed with warnings`
- `rejected`
每次用户要求"主线部署",严格按以下顺序,不跳步、不直接支线 → main:
只要架构违规、视觉违规,或明显是补丁式修复,就必须 `rejected`。
如果这次不是独立复审,必须明确写出“这是自审,不是独立复审”。
1. **在支线开发** → 提交到任务分支(如 `codex/wuyongjie-xxx`),不碰两根主线。
2. **fetch xuxiake** → 必须先 `git fetch origin`,看 `origin/codex/xuxiake-mainline` 有没有新提交。
3. **有新提交** → `merge origin/codex/xuxiake-mainline` 拉下来处理冲突(长期共享分支用 merge,不 rebase);只处理冲突文件,不扩范围。
4. **合并到 xuxiake** → 把支线合进 `origin/codex/xuxiake-mainline`,并 `push` 更新次主线。
5. **合并到 main** → 把 xuxiake 合进 `main`,并 `push origin main`(本步需用户授权)。
6. **用 main 部署** → 走 `scripts/deploy-yydd-prod-from-git.ps1`(默认源 `origin/main`),部署后核对 commit / 进程 / 端口 / 日志 / 关键页面(本步需用户授权)。
统一清单:`docs/workflow/规范审查清单.md`
发布前必须确认 `git ls-remote origin refs/heads/main`、本地 `origin/main`、目标部署 commit 三者一致。
## 5. 标准来源
## 4. 生产与部署
- `furniture-3d-system/.cursor/rules/*.mdc`
- `furniture-3d-system/docs/设计规范/苹果风格视觉标准.md`
- `docs/workflow/规范审查清单.md`
- 当前正式网址:`https://cp.jiansun.vip/`。
- 当前权威生产 SSH 入口:`root@ltai5tdb2ogyj59fquyedh.yydd.ai`,端口 `60022`。
- 当前生产部署目录:`/opt/furniture-3d-system-prod`。
- 当前 PM2 进程名:`furniture-3d-system-prod`。
- 当前应用本机端口:`3001`。
- 默认生产部署来源:`origin/main`。
- 当前主部署入口是外层 `scripts/deploy-yydd-prod-from-git.ps1`;它默认部署 `origin/main`,并调用 `scripts/create-yydd-prod-deploy.ps1` 生成发布包/远端发布脚本。不要把下层打包脚本单独当成唯一部署入口。
- 发布前必须确认 `git ls-remote origin refs/heads/main`、本地 `origin/main`、目标部署 commit 三者一致;本地 `main` 是否领先或落后远端不能替代 `origin/main` 的真实发布来源。
- 旧 `192.168.58.20`、本机泛别名 `prod`、旧 `gongxiangp2 / youyidian-3d-material` 生产信息都不能套用到本项目。
- 生产动作前至少实时确认:目标主机、远端目录、PM2 进程、`current` 软链接、监听端口和 `https://cp.jiansun.vip/api/health`。
- 生产数据库、迁移、主数据地基相关动作先只读核查;需要写入时必须有备份、目标库身份、确认口令和可回滚路径。
- 对象存储当前要按 `src/lib/storage.ts`、`storage:*` 脚本、七牛迁移/GC 文档和生产环境变量实时核对;不要把历史 MinIO/S3 备份脚本当成当前生产存储真相。
## 6. 边界
## 5. 当前主链与模块边界
- 用户未指定目录时,默认在 `furniture-3d-system/` 执行。
- 未经用户明确要求,不主动 `commit / push`。
- 禁止破坏性操作,除非用户明确授权。
当前新平台数据主链按这条线理解:
`资产中心 -> 辅助装配 -> 草稿组 -> 产品资料/正式入库 -> 仓库管理/轻仓库`
当前全局工作台导航还包含首页、OA 新品流程看板、仓库质检、AI 出图工作台等运营入口。导航入口不等于数据主链,不要把页面入口和主数据归属混成一件事。
主链默认入口和目录:
- 首页与全局壳:`src/app/page.tsx`、`src/components/layout`
- 资产中心:`src/app/asset-center`、`src/app/api/asset-center`
- 辅助装配:`src/app/assisted-assembly`、`src/app/api/assisted-assembly`
- 草稿组:`src/app/draft-groups`、`src/app/api/draft-groups`
- 产品资料/正式入库:`src/app/intake`、`src/app/api/intake`
- 仓库管理/轻仓库:`src/app/warehouse`、`src/app/api/warehouse`
- 仓库质检:`src/app/warehouse-quality`、`src/app/api/warehouse-quality`
- AI 出图工作台:`src/app/ai-render-workbench`
- 审批工作流:`src/app/approval-workflows`
- 素材库:`src/app/materials`
- 移动端:`src/app/mobile`
- 平台组件库:`src/components/platform`
- 主链规则与类型:`src/lib/open-product`、`src/lib/planning`
业务真相:
- 资产族:`AssetFamily`
- 规格款:`AssetVariant`
- 草稿组:`DraftGroup`
- 准 SKU:`PreSku`
- 入库结果:正式 `Product / ProductSKU`(`SKU` 是业务概念,Prisma 模型名是 `ProductSKU`)
- 产品资料侧非集成开品:`IntakeNonIntegratedProduct / IntakeNonIntegratedSkuItem`
旧 CAD 兼容链继续存在,但不是新平台主数据源:
- 页面:`src/app/products`、`src/app/products/[id]`、`src/app/products/[id]/assembly`、`src/app/products/[id]/skus/[skuId]`
- 旧链模型和服务包括 `Product / ProductSKU / Panel / Hole / ProductHardware`、`assembly.service`、`panel.service`、`hole.service` 等。
- 旧链可以继续服务历史产品、CAD 导入、装配步骤和兼容发布后能力,但不要反向主导资产中心、草稿组或产品资料主链。
共享接缝默认谨慎处理:
- `prisma/schema.prisma`
- `prisma/migrations`
- `src/app/layout.tsx`
- `src/app/intake`
- `src/app/draft-groups/[id]`
- `src/app/api/platform/schemas.ts`
- `src/server/services/draft-group.service.ts`
- `src/server/services/intake.service.ts`
- `src/server/repositories/draft-group.repository.ts`
改这些地方前先说明原因、影响范围和验证计划;不要为了局部 UI 或临时兼容绕过模块边界。
## 6. 架构硬规则
- API Route 负责收参、鉴权和返回,不直接堆业务规则或 Prisma 查询。
- Service 负责业务规则、权限校验、事务编排、缓存和跨模块协调。
- Repository 负责数据访问。
- 任何新接口必须有明确输入输出 DTO/校验口径。
- 不是必须的话,不要使用兜底逻辑;原生主逻辑能做好,就先把主逻辑、状态契约和数据来源修正确。兜底只能用于明确的兼容、降级或应急场景,不能用来掩盖本应修好的业务规则、接口契约或前端状态问题。
- 任何 Prisma 模型调整必须带迁移,并补齐主数据/迁移核查;生产/准生产禁止用 `prisma db push` 替代迁移。
- 影响命名、BOM、包装、安装步骤、SKU、barcode、批次、入库承接的数据问题,优先从数据源和业务规则修,不在页面上压报错或绕校验。
- SPU、SKU、barcode、批次不能混用:SPU 是父级产品款,SKU 是 SPU 下的具体子项(Prisma 模型名 `ProductSKU`),barcode 是绑定到最终 SKU 的业务定位值;基于某 SKU 派生的新 SKU 必须仍属于同一 SPU,并按业务要求继承或明确指定批次。
## 7. 前端与视觉硬规则
- 白曜石工坊统一视觉标准是本项目新平台第一视觉标准;默认是浅色工作台,不再提供深浅色主题切换入口。
- 本项目有自己的组件库与 `Meta Component(元组件)` 体系;前端实现必须优先复用 `src/components/platform`、资产中心已沉淀的工作台表达和现有元组件,不允许绕开组件库另起一套孤立 UI。
- 当用户指定 Stitch 设计稿或已有 Stitch 设计稿可参考时,Stitch 是当前功能的视觉规范与验收标准;实现方式是在本项目组件库/元组件体系内对齐 Stitch 的密度、层级、控件形态、留白、字号、圆角、阴影和低噪音表达,而不是无视组件库硬抄静态稿。
- 资产中心页面是当前项目内的优先参考样板;涉及资产、图片、SKU、AI 白底图、3D 预览、工作区和检视器时,默认参考资产中心的工作台感、元组件表达、低噪音操作面板和重点图片区。
- `Inspector(检视器)`、数据列表、表格、长文本和表单密集区必须优先保证可读性;毛玻璃只允许用于顶栏、HUD、浮动工具条和轻量状态浮层,不承载正文。
- 板材、五金、组件、包装等资产必须按元组件表达,不能退回普通图片卡片或营销式大卡片布局。
- 现有 `apple-*` token、深色 `ObsidianControlShell`、历史主题 CSS 只能作为兼容遗留;新功能不得以它们作为视觉方向。
- 前端改造不得把现有后台习惯、普通 SaaS 审美或共享组件默认皮肤当成最终依据;如果组件库默认视觉和 Stitch/资产中心标准冲突,应在组件库体系内做专用变体、重写皮肤或局部重构。
- 没有出现在设计稿、组件规范或业务必要路径里的说明文案、辅助卡片、重复统计块、解释区,不要为了“更易懂”自行添加;工作区必须抓重点,图片类功能要让图片和关键动作成为第一层级。
- 涉及前端动画、入退场、滚动联动、时间线、拖拽、SVG 或动效性能时,先判断是否有助于理解和操作;需要时优先按技术栈调用对应 GSAP Skill,并继续遵守 Impeccable。React 必须使用 `useGSAP` 或带 `gsap.context().revert()` 的完整清理,滚动相关实例必须销毁并在布局变化时正确刷新;优先动画 `transform/opacity`,尊重 `prefers-reduced-motion`,不得为装饰滥用动效。
- 前端修改后优先用真实页面、截图、1920×1080 视口和必要的移动端视口验证,不只看代码。
## 8. 验证、收口与规范审查
- 修改后必须做最小但有效的验证;不能验证就明确说明未验证原因和风险。
- 常用验证命令以当前 `package.json` 为准,优先选和改动范围匹配的最小集合:`npm run type-check`、`npm run build`、`npm test -- <相关测试>`、`npm run lint`;对象存储用 `npm run storage:gc:audit` / `npm run storage:gc:apply`,主数据用 `npm run db:foundation:*` 相关只读或授权脚本。
- 前端体验类改动要结合浏览器实际操作和截图验证;涉及 3D/canvas 时要确认画面非空、尺寸正确、交互可用。
- 生产验证必须包含真实健康检查和日志/PM2/端口等现场证据,不能只说"本地构建通过"。
- 规范审查固定只查三件事:架构有没有被破坏、视觉有没有跑偏、这次是不是根因修复。结论只能是 `passed` / `passed with warnings` / `rejected`;只要架构违规、视觉违规或明显补丁式修复,就必须 `rejected`。非独立复审时必须写明"这是自审,不是独立复审"。统一清单:`furniture-3d-system/docs/workflow/规范审查清单.md`。没有规范审查结论,任务不算完成。
## 9. 敏感信息和交接
- 账号密码、访问密钥、数据库连接串、对象存储密钥等敏感信息只能放在本地安全材料里,不写入 Git、共享文档、普通交接文本或长期记忆;生产域名、PM2 名称、部署目录这类非密钥运行入口可以写进仓库文档,但必须保持最新。
- 仓库内协作文档只写非敏感入口、流程和职责;凭据类信息必须用 `[REDACTED_SECRET]` 或本地路径说明替代。
- 发现摘要、日志或交接文件混有明文凭据时,先提示风险,再决定是否拆成本地敏感版和仓库协作版。
- 给下一个 agent 或同事交接时,写清:本次只改了什么、没改什么、验证跑了什么、风险是什么、下一步要看哪些文件。

View File

@ -1,4 +1,4 @@
# 3D 家具开品平台工作区
# 由壹点「产品开发平台」工作区
> 最后更新:2026-04-07
> 当前主线:`资产中心 -> 自动组合 -> 草稿组 -> 包装方案 -> 正式入库 -> 轻仓库`

View File

@ -20,7 +20,7 @@ namespace FurnitureExporter;
/// </summary>
public class FurnitureExporterApp : IExtensionApplication
{
private const string PluginVersion = "2.5.0";
private const string PluginVersion = "2.6.0";
public void Initialize()
{
@ -30,12 +30,13 @@ public class FurnitureExporterApp : IExtensionApplication
doc?.Editor.WriteMessage($"║ 家具导出插件 v{PluginVersion} 已加载 ║\n");
doc?.Editor.WriteMessage("║ ║\n");
doc?.Editor.WriteMessage("║ 命令: ║\n");
doc?.Editor.WriteMessage("║ FEXPORT - 整套家具兼容导出 ║\n");
doc?.Editor.WriteMessage("║ FEXPORTASSET - 单资产导出到资产中心 ║\n");
doc?.Editor.WriteMessage("║ FEXPORT - 单资产导出到资产中心 ║\n");
doc?.Editor.WriteMessage("║ FEXPORTASSET - 单资产导出别名 ║\n");
doc?.Editor.WriteMessage("║ FEXPORTPRODUCT - 整套家具兼容导出 ║\n");
doc?.Editor.WriteMessage("║ FINFO - 查看选中实体信息 ║\n");
doc?.Editor.WriteMessage("║ ║\n");
doc?.Editor.WriteMessage($"║ v{PluginVersion} 新功能: ║\n");
doc?.Editor.WriteMessage("║ · 新增资产中心单资产导出模式 ║\n");
doc?.Editor.WriteMessage("║ · 默认进入资产中心单资产导出 ║\n");
doc?.Editor.WriteMessage("║ · 新增 assembly 装配语义导出层 ║\n");
doc?.Editor.WriteMessage("║ · 语义优先用孔位/五金/配合面推导 ║\n");
doc?.Editor.WriteMessage("║ · 安装顺序仍保留给 Web 端人工设定 ║\n");
@ -54,7 +55,7 @@ public class FurnitureExporterApp : IExtensionApplication
/// </summary>
public class Commands
{
private const string PluginVersion = "2.5.0";
private const string PluginVersion = "2.6.0";
private sealed class SingleAssetExportMode
{
@ -71,6 +72,9 @@ public class Commands
public List<PanelInfo> Panels { get; } = new();
public List<HoleInfo> Holes { get; } = new();
public List<HoleGroupInfo> HoleGroups { get; } = new();
public List<MountPatternInfo> MountPatterns { get; } = new();
public List<MountPatternInfo> SnapPatterns { get; } = new();
public List<SnapSurfaceInfo> SnapSurfaces { get; } = new();
public int LogicalHoleCount { get; set; }
public List<EdgeFeatureInfo> EdgeFeatures { get; } = new();
public List<ExcludedHoleInfo> ExcludedHoles { get; } = new();
@ -336,6 +340,9 @@ public class Commands
var holeGroups = HoleGrouping.BuildEccentricScrewGroups(allHoles);
HoleGrouping.AttachGroupsToPanels(panels, holeGroups);
var mountPatterns = MountPatternAnalyzer.BuildMountPatterns(panels, allHoles, holeGroups);
MountPatternAnalyzer.AttachPatternsToPanels(panels, mountPatterns);
var snapSurfaces = SnapSurfaceBuilder.BuildSnapSurfaces(panels);
var logicalHoleCount = HoleGrouping.ComputeLogicalHoleCount(allHoles);
foreach (var hole in allHoles)
@ -353,6 +360,9 @@ public class Commands
result.Holes.AddRange(allHoles);
result.HoleGroups.AddRange(holeGroups);
result.MountPatterns.AddRange(mountPatterns);
result.SnapPatterns.AddRange(mountPatterns);
result.SnapSurfaces.AddRange(snapSurfaces);
result.LogicalHoleCount = logicalHoleCount;
result.EdgeFeatures.AddRange(allEdgeFeatures);
result.ExcludedHoles.AddRange(panelAnalyzer.ExcludedHoles);
@ -363,7 +373,7 @@ public class Commands
/// <summary>
/// 主导出命令 - 导出板材 GLB + 五金件位置 JSON
/// </summary>
[CommandMethod("FEXPORT")]
[CommandMethod("FEXPORTPRODUCT")]
public void ExportFurniture()
{
var doc = AcApp.DocumentManager.MdiActiveDocument;
@ -405,7 +415,7 @@ public class Commands
ed.WriteMessage(" 1. 输入命令 UNITS 打开单位设置\n");
ed.WriteMessage(" 2. 将 '插入时的缩放单位' 改为 '毫米'\n");
ed.WriteMessage(" 3. 点击确定\n");
ed.WriteMessage(" 4. 重新运行 FEXPORT 命令\n");
ed.WriteMessage(" 4. 重新运行 FEXPORTPRODUCT 命令\n");
ed.WriteMessage("\n");
// 询问是否继续(仅供高级用户)
@ -445,7 +455,7 @@ public class Commands
panelIds = FilterSolids(panelIds, db);
if (panelIds.Length == 0)
{
ed.WriteMessage(" ⚠️ 无法恢复板材(可能图纸已变更/实体被删除)。已清除会话,请重新运行 FEXPORT。\n");
ed.WriteMessage(" ⚠️ 无法恢复板材(可能图纸已变更/实体被删除)。已清除会话,请重新运行 FEXPORTPRODUCT。\n");
ExportSessionManager.Clear(docKey);
return;
}
@ -546,7 +556,7 @@ public class Commands
ed.WriteMessage($" ✓ 已记录 {panelIds.Length} 个板材\n");
ed.WriteMessage("\n 你现在可以自由调整图层显示/冻结/隔离等操作。\n");
ed.WriteMessage(" 调整完成后,再次运行 FEXPORT 继续第 2 步(选五金并导出)。\n");
ed.WriteMessage(" 调整完成后,再次运行 FEXPORTPRODUCT 继续第 2 步(选五金并导出)。\n");
var proceedOpt = new PromptKeywordOptions(
"\n是否现在直接继续第 2 步? [继续(C)/稍后(R)]",
@ -558,7 +568,7 @@ public class Commands
if (proceedResult.Status != PromptStatus.OK || proceedResult.StringResult != "Continue")
{
ed.WriteMessage("\n已暂停导出。提示:再次运行 FEXPORT 继续;运行 FEXPORTRESET 可清除暂存。\n");
ed.WriteMessage("\n已暂停导出。提示:再次运行 FEXPORTPRODUCT 继续;运行 FEXPORTRESET 可清除暂存。\n");
return;
}
}
@ -683,6 +693,9 @@ public class Commands
// 2) 偏心孔位分组:把“主孔+侧孔”合并为 1 个逻辑孔位(不改变 holes 的几何数据,仅提供分组与逻辑计数)
var holeGroups = HoleGrouping.BuildEccentricScrewGroups(allHoles);
HoleGrouping.AttachGroupsToPanels(panels, holeGroups);
var mountPatterns = MountPatternAnalyzer.BuildMountPatterns(panels, allHoles, holeGroups);
MountPatternAnalyzer.AttachPatternsToPanels(panels, mountPatterns);
var snapSurfaces = SnapSurfaceBuilder.BuildSnapSurfaces(panels);
var logicalHoleCount = HoleGrouping.ComputeLogicalHoleCount(allHoles);
@ -729,6 +742,9 @@ public class Commands
exportData.Panels = panels;
exportData.Holes = allHoles;
exportData.HoleGroups = holeGroups;
exportData.MountPatterns = mountPatterns;
exportData.SnapPatterns = mountPatterns;
exportData.SnapSurfaces = snapSurfaces;
exportData.LogicalHoleCount = logicalHoleCount;
if (panelAnalyzer.ExcludedHoles.Count > 0)
@ -842,8 +858,25 @@ public class Commands
}
}
[CommandMethod("FEXPORTBUNDLE")]
public void ExportFurnitureBundle()
{
ExportFurniture();
}
[CommandMethod("FEXPORT")]
public void ExportAssetDefault()
{
ExportSingleAssetCore();
}
[CommandMethod("FEXPORTASSET")]
public void ExportSingleAsset()
{
ExportSingleAssetCore();
}
private void ExportSingleAssetCore()
{
var doc = AcApp.DocumentManager.MdiActiveDocument;
if (doc == null)
@ -985,6 +1018,9 @@ public class Commands
exportData.Panels = panelAnalysis.Panels;
exportData.Holes = panelAnalysis.Holes;
exportData.HoleGroups = panelAnalysis.HoleGroups;
exportData.MountPatterns = panelAnalysis.MountPatterns;
exportData.SnapPatterns = panelAnalysis.SnapPatterns;
exportData.SnapSurfaces = panelAnalysis.SnapSurfaces;
exportData.LogicalHoleCount = panelAnalysis.LogicalHoleCount;
exportData.EdgeFeatures = panelAnalysis.EdgeFeatures;
exportData.PanelCount = panelAnalysis.Panels.Count;
@ -1024,21 +1060,11 @@ public class Commands
if (mode.AnalyzePanels && panelAnalysis.Panels.Count > 0)
{
ed.WriteMessage(" [4/5] 生成装配语义...");
var assemblyAnalyzer = new AssemblySemanticAnalyzer();
exportData.Assembly = assemblyAnalyzer.Analyze(
panelAnalysis.Panels,
panelAnalysis.Holes,
panelAnalysis.HoleGroups,
exportData.HardwareTypes);
var highConfidenceCount = exportData.Assembly.PanelAssemblies.Count(item =>
item.Confidence >= 0.75 &&
!string.Equals(item.MountMode, "unknown", StringComparison.OrdinalIgnoreCase));
ed.WriteMessage($" ✓ ({exportData.Assembly.PanelAssemblies.Count} 个板件语义, {highConfidenceCount} 个高置信)\n");
ed.WriteMessage(" [4/5] 跳过旧装配语义... ✓ (single_asset 使用 snapPatterns/snapSurfaces)\n");
}
else
{
ed.WriteMessage(" [4/5] 生成装配语义... ✓ (当前模式无需生成)\n");
ed.WriteMessage(" [4/5] 跳过旧装配语义... ✓ (当前模式无需生成)\n");
}
ed.WriteMessage(" [5/5] 打包文件...");
@ -1108,7 +1134,7 @@ public class Commands
var ed = doc.Editor;
var docKey = string.IsNullOrWhiteSpace(doc.Name) ? "Untitled" : doc.Name;
ExportSessionManager.Clear(docKey);
ed.WriteMessage("\n已清除 FEXPORT 暂存会话。请重新运行 FEXPORT。\n");
ed.WriteMessage("\n已清除整套兼容导出暂存会话。请重新运行 FEXPORTPRODUCT。\n");
}
private static List<string> GetEntityHandles(Database db, ObjectId[] ids)

View File

@ -1,229 +1,236 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Xunit;
namespace FurnitureExporter.Tests;
public class AssemblySemanticAnalyzerTests
namespace FurnitureExporter.Tests
{
[Fact]
public void Analyze_BuildsAssemblySection_AndDoesNotEmitInstallOrderFields()
public class AssemblySemanticAnalyzerTests
{
var exportData = new ExportData
[Fact]
public void Analyze_BuildsAssemblySection_AndDoesNotEmitInstallOrderFields()
{
Panels = new List<PanelInfo>
var exportData = new ExportData
{
Panels = new List<PanelInfo>
{
CreatePanel(
"panel_001",
"side_panel",
new[] { 9.0, 280.0, 360.0 },
new[] { 1.0, 0.0, 0.0 },
new[] { 0.0, 0.0, 0.0 },
new[] { 18.0, 560.0, 720.0 }),
CreatePanel(
"panel_002",
"shelf",
new[] { 200.0, 280.0, 359.0 },
new[] { 0.0, 0.0, 1.0 },
new[] { 18.0, 0.0, 350.0 },
new[] { 382.0, 560.0, 368.0 }),
},
};
exportData.Assembly = new AssemblySemanticAnalyzer().Analyze(
exportData.Panels,
exportData.Holes,
exportData.HoleGroups,
exportData.HardwareTypes);
var json = JsonConvert.SerializeObject(
exportData,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
});
var root = JObject.Parse(json);
Assert.Equal("1.0", root["assembly"]?["version"]?.Value<string>());
Assert.NotNull(root["assembly"]?["panelAssemblies"]);
Assert.NotNull(root["assembly"]?["connections"]);
Assert.DoesNotContain("installOrder", json, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void Analyze_PrefersHardwareRelation_ForConnectedPanel()
{
var panels = new List<PanelInfo>
{
CreatePanel(
"panel_001",
"side_panel",
position: new[] { 9.0, 280.0, 360.0 },
normal: new[] { 1.0, 0.0, 0.0 },
boundingBoxMin: new[] { 0.0, 0.0, 0.0 },
boundingBoxMax: new[] { 18.0, 560.0, 720.0 }),
new[] { 9.0, 280.0, 360.0 },
new[] { 1.0, 0.0, 0.0 },
new[] { 0.0, 0.0, 0.0 },
new[] { 18.0, 560.0, 720.0 }),
CreatePanel(
"panel_002",
"shelf",
position: new[] { 200.0, 280.0, 359.0 },
normal: new[] { 0.0, 0.0, 1.0 },
boundingBoxMin: new[] { 18.0, 0.0, 350.0 },
boundingBoxMax: new[] { 382.0, 560.0, 368.0 }),
},
};
new[] { 200.0, 280.0, 359.0 },
new[] { 0.0, 0.0, 1.0 },
new[] { 18.0, 0.0, 350.0 },
new[] { 382.0, 560.0, 368.0 }),
};
exportData.Assembly = AssemblySemanticAnalyzer.Analyze(
exportData.Panels,
exportData.Holes,
exportData.HoleGroups,
exportData.HardwareTypes);
var json = JsonConvert.SerializeObject(
exportData,
new JsonSerializerSettings
var holes = new List<HoleInfo>
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
});
var root = JObject.Parse(json);
Assert.Equal("1.0", root["assembly"]?["version"]?.Value<string>());
Assert.NotNull(root["assembly"]?["panelAssemblies"]);
Assert.NotNull(root["assembly"]?["connections"]);
Assert.DoesNotContain("installOrder", json, System.StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void Analyze_PrefersHoleGroupAndHardwareEvidence_OverPanelTypeFallback()
{
var panels = new List<PanelInfo>
{
CreatePanel(
"panel_001",
"side_panel",
position: new[] { 9.0, 280.0, 360.0 },
normal: new[] { 1.0, 0.0, 0.0 },
boundingBoxMin: new[] { 0.0, 0.0, 0.0 },
boundingBoxMax: new[] { 18.0, 560.0, 720.0 }),
CreatePanel(
"panel_002",
"shelf",
position: new[] { 200.0, 280.0, 359.0 },
normal: new[] { 0.0, 0.0, 1.0 },
boundingBoxMin: new[] { 18.0, 0.0, 350.0 },
boundingBoxMax: new[] { 382.0, 560.0, 368.0 }),
};
var holes = new List<HoleInfo>
{
new()
{
Id = "panel_001_hole_001",
PanelId = "panel_001",
Position = new[] { 9.0, 260.0, 360.0 },
Normal = new[] { 1.0, 0.0, 0.0 },
Diameter = 15.0,
Radius = 7.5,
Depth = 12.0,
Type = "blind",
},
new()
{
Id = "panel_001_hole_002",
PanelId = "panel_001",
Position = new[] { 16.0, 260.0, 360.0 },
Normal = new[] { 0.0, 1.0, 0.0 },
Diameter = 8.0,
Radius = 4.0,
Depth = 10.0,
Type = "blind",
},
};
var holeGroups = new List<HoleGroupInfo>
{
new()
{
Id = "panel_001_group_000",
PanelId = "panel_001",
GroupType = "eccentric_screw",
HoleIds = new[] { "panel_001_hole_001", "panel_001_hole_002" },
Position = new[] { 9.0, 260.0, 360.0 },
Normal = new[] { 1.0, 0.0, 0.0 },
Confidence = 0.96,
Reason = "偏心件孔组",
},
};
panels[0].Holes = holes.ToList();
panels[0].HoleGroups = holeGroups.ToList();
var hardwareTypes = new List<HardwareType>
{
new()
{
TypeId = "type_000",
InferredHardwareType = "connector",
Instances = new List<HardwareInstance>
new HoleInfo
{
new()
Id = "panel_001_hole_001",
PanelId = "panel_001",
Position = new[] { 9.0, 260.0, 360.0 },
Normal = new[] { 1.0, 0.0, 0.0 },
Diameter = 15.0,
Radius = 7.5,
Depth = 12.0,
Type = "blind",
},
new HoleInfo
{
Id = "panel_001_hole_002",
PanelId = "panel_001",
Position = new[] { 16.0, 260.0, 360.0 },
Normal = new[] { 0.0, 1.0, 0.0 },
Diameter = 8.0,
Radius = 4.0,
Depth = 10.0,
Type = "blind",
},
};
var holeGroups = new List<HoleGroupInfo>
{
new HoleGroupInfo
{
Id = "panel_001_group_000",
PanelId = "panel_001",
GroupType = "eccentric_screw",
HoleIds = new[] { "panel_001_hole_001", "panel_001_hole_002" },
Position = new[] { 9.0, 260.0, 360.0 },
Normal = new[] { 1.0, 0.0, 0.0 },
Confidence = 0.96,
Reason = "偏心件孔组",
},
};
panels[0].Holes = holes.ToList();
panels[0].HoleGroups = holeGroups.ToList();
var hardwareTypes = new List<HardwareType>
{
new HardwareType
{
TypeId = "type_000",
InferredHardwareType = "connector",
Instances = new List<HardwareInstance>
{
Position = new Position3D { X = 12.0, Y = 260.0, Z = 360.0 },
PrimaryPanelId = "panel_001",
SecondaryPanelId = "panel_002",
BoundHoleGroupId = "panel_001_group_000",
BoundHoleIds = new[] { "panel_001_hole_001", "panel_001_hole_002" },
BindingMode = "hole_group",
BindingConfidence = 0.97,
BindingReason = "优先按孔组绑定",
NearbyPanelIds = new[] { "panel_001", "panel_002" },
new HardwareInstance
{
Position = new Position3D { X = 12.0, Y = 260.0, Z = 360.0 },
PrimaryPanelId = "panel_001",
SecondaryPanelId = "panel_002",
BoundHoleGroupId = "panel_001_group_000",
BoundHoleIds = new[] { "panel_001_hole_001", "panel_001_hole_002" },
BindingMode = "hole_group",
BindingConfidence = 0.97,
BindingReason = "优先按孔组绑定",
NearbyPanelIds = new[] { "panel_001", "panel_002" },
},
},
},
},
};
};
var assembly = AssemblySemanticAnalyzer.Analyze(panels, holes, holeGroups, hardwareTypes);
var panelAssembly = Assert.Single(assembly.PanelAssemblies.Where(item => item.PanelId == "panel_001"));
var assembly = new AssemblySemanticAnalyzer().Analyze(panels, holes, holeGroups, hardwareTypes);
var connection = Assert.Single(
assembly.Connections.Where(item =>
item.SourcePanelId == "panel_001" &&
item.TargetPanelId == "panel_002"));
var panelAssembly = Assert.Single(assembly.PanelAssemblies.Where(item => item.PanelId == "panel_001"));
Assert.Equal("connector_lock", panelAssembly.MountMode);
Assert.Equal("panel_002", panelAssembly.PrimarySupportPanelId);
Assert.Contains("panel_002", panelAssembly.TargetPanelIds);
Assert.Contains("panel_002", panelAssembly.DependsOn);
Assert.Contains(panelAssembly.Mates, mate => mate.MateType == "hole_group_hardware" && mate.HoleGroupId == "panel_001_group_000");
Assert.True(panelAssembly.Confidence >= 0.9);
Assert.DoesNotContain(panelAssembly.Warnings, warning => warning.Contains("仅按 panelType", System.StringComparison.OrdinalIgnoreCase));
}
Assert.Equal("hole_group_hardware", connection.ConnectionType);
Assert.Equal("slide_in", panelAssembly.MountMode);
Assert.Equal("left", panelAssembly.AssemblyDirection);
Assert.Equal("panel_002", panelAssembly.PrimarySupportPanelId);
Assert.Contains("panel_002", panelAssembly.TargetPanelIds);
Assert.Contains("panel_002", panelAssembly.DependsOn);
Assert.Contains("panel_002", panelAssembly.Mates.Right ?? new List<string>());
Assert.True(panelAssembly.Confidence >= 0.75);
}
[Fact]
public void Analyze_FallsBackToFaceMate_WhenNoHardwareBindingExists()
{
var panels = new List<PanelInfo>
[Fact]
public void Analyze_FallsBackToShelfDirection_WhenNoHardwareBindingExists()
{
CreatePanel(
"panel_left",
"side_panel",
position: new[] { 9.0, 280.0, 360.0 },
normal: new[] { 1.0, 0.0, 0.0 },
boundingBoxMin: new[] { 0.0, 0.0, 0.0 },
boundingBoxMax: new[] { 18.0, 560.0, 720.0 }),
CreatePanel(
"panel_right",
"side_panel",
position: new[] { 391.0, 280.0, 360.0 },
normal: new[] { 1.0, 0.0, 0.0 },
boundingBoxMin: new[] { 382.0, 0.0, 0.0 },
boundingBoxMax: new[] { 400.0, 560.0, 720.0 }),
CreatePanel(
"panel_shelf",
"shelf",
position: new[] { 200.0, 280.0, 359.0 },
normal: new[] { 0.0, 0.0, 1.0 },
boundingBoxMin: new[] { 18.0, 0.0, 350.0 },
boundingBoxMax: new[] { 382.0, 560.0, 368.0 }),
};
var panels = new List<PanelInfo>
{
CreatePanel(
"panel_left",
"side_panel",
new[] { 9.0, 280.0, 360.0 },
new[] { 1.0, 0.0, 0.0 },
new[] { 0.0, 0.0, 0.0 },
new[] { 18.0, 560.0, 720.0 }),
CreatePanel(
"panel_right",
"side_panel",
new[] { 391.0, 280.0, 360.0 },
new[] { 1.0, 0.0, 0.0 },
new[] { 382.0, 0.0, 0.0 },
new[] { 400.0, 560.0, 720.0 }),
CreatePanel(
"panel_shelf",
"shelf",
new[] { 200.0, 280.0, 359.0 },
new[] { 0.0, 0.0, 1.0 },
new[] { 18.0, 0.0, 350.0 },
new[] { 382.0, 560.0, 368.0 }),
};
var assembly = AssemblySemanticAnalyzer.Analyze(
panels,
new List<HoleInfo>(),
new List<HoleGroupInfo>(),
new List<HardwareType>());
var assembly = new AssemblySemanticAnalyzer().Analyze(
panels,
new List<HoleInfo>(),
new List<HoleGroupInfo>(),
new List<HardwareType>());
var shelf = Assert.Single(assembly.PanelAssemblies.Where(item => item.PanelId == "panel_shelf"));
var shelf = Assert.Single(assembly.PanelAssemblies.Where(item => item.PanelId == "panel_shelf"));
Assert.Equal("slot_insert", shelf.MountMode);
Assert.Contains("panel_left", shelf.TargetPanelIds);
Assert.Contains("panel_right", shelf.TargetPanelIds);
Assert.Contains(shelf.Mates, mate => mate.MateType == "face_contact");
Assert.True(shelf.TravelDistance > 0);
Assert.True(System.Math.Abs(shelf.AssemblyAxis[0]) > 0.9);
}
Assert.Equal("drop_in", shelf.MountMode);
Assert.Equal("top", shelf.AssemblyDirection);
Assert.Contains("panel_left", shelf.TargetPanelIds);
Assert.Contains("panel_right", shelf.TargetPanelIds);
Assert.Contains("panel_left", shelf.Mates.Left ?? new List<string>());
Assert.Contains("panel_right", shelf.Mates.Right ?? new List<string>());
Assert.True(shelf.TravelDistance > 0);
}
private static PanelInfo CreatePanel(
string id,
string panelType,
double[] position,
double[] normal,
double[] boundingBoxMin,
double[] boundingBoxMax)
{
return new PanelInfo
private static PanelInfo CreatePanel(
string id,
string panelType,
double[] position,
double[] normal,
double[] boundingBoxMin,
double[] boundingBoxMax)
{
Id = id,
PanelType = panelType,
Position = position,
Normal = normal,
BoundingBox = new BoundingBox3D
return new PanelInfo
{
Min = boundingBoxMin,
Max = boundingBoxMax,
},
Size = new[]
{
System.Math.Round(boundingBoxMax[0] - boundingBoxMin[0], 2),
System.Math.Round(boundingBoxMax[1] - boundingBoxMin[1], 2),
System.Math.Round(boundingBoxMax[2] - boundingBoxMin[2], 2),
},
};
Id = id,
PanelType = panelType,
Position = position,
Normal = normal,
BoundingBox = new BoundingBox3D
{
Min = boundingBoxMin,
Max = boundingBoxMax,
},
Size = new[]
{
Math.Round(boundingBoxMax[0] - boundingBoxMin[0], 2),
Math.Round(boundingBoxMax[1] - boundingBoxMin[1], 2),
Math.Round(boundingBoxMax[2] - boundingBoxMin[2], 2),
},
};
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
namespace FurnitureExporter.Tests
{
public class CommandRegistrationTests
{
[Fact]
public void Fexport_DefaultsToSingleAssetExport()
{
var source = ReadCommandsSource();
Assert.Matches(CommandMethodPattern("FEXPORT", "ExportAssetDefault"), source);
Assert.Matches(CommandMethodPattern("FEXPORTASSET", "ExportSingleAsset"), source);
Assert.DoesNotMatch(CommandMethodPattern("FEXPORT", "ExportFurniture"), source);
}
[Fact]
public void LegacyProductBundleExport_UsesExplicitCompatibilityCommands()
{
var source = ReadCommandsSource();
Assert.Matches(CommandMethodPattern("FEXPORTPRODUCT", "ExportFurniture"), source);
Assert.Matches(CommandMethodPattern("FEXPORTBUNDLE", "ExportFurnitureBundle"), source);
}
private static string ReadCommandsSource()
{
var path = Path.GetFullPath(
Path.Combine(AppContext.BaseDirectory, @"..\..\..\..\Commands.cs"));
return File.ReadAllText(path);
}
private static Regex CommandMethodPattern(string commandName, string methodName)
{
return new Regex(
$@"\[CommandMethod\(""{Regex.Escape(commandName)}""\)\]\s+public\s+void\s+{Regex.Escape(methodName)}\s*\(",
RegexOptions.Multiline);
}
}
}

View File

@ -0,0 +1,89 @@
using System.Collections.Generic;
using System.Linq;
namespace FurnitureExporter.Tests
{
public class MountPatternAnalyzerTests
{
[Fact]
public void BuildMountPatterns_ForTwoHoleLine_RequiresTwoHoles()
{
var panel = CreatePanel("panel_line");
var holes = new List<HoleInfo>
{
CreateHole("hole_a", "panel_line", new[] { -40.0, 0.0, 0.0 }, new[] { 0.0, -1.0, 0.0 }, new[] { "top" }),
CreateHole("hole_b", "panel_line", new[] { 40.0, 0.0, 0.0 }, new[] { 0.0, -1.0, 0.0 }, new[] { "top" }),
};
var patterns = MountPatternAnalyzer.BuildMountPatterns(
new[] { panel },
holes,
new List<HoleGroupInfo>());
var pattern = Assert.Single(patterns);
Assert.Equal("line_2", pattern.PatternType);
Assert.Equal(2, pattern.MinRequiredHoles);
Assert.Equal(2, pattern.PreferredHoleCount);
Assert.Single(panel.SnapPatterns);
Assert.Equal(panel.MountPatterns, panel.SnapPatterns);
}
[Fact]
public void BuildMountPatterns_ForSymmetricRectangle_DefaultsToTwoRequiredHoles()
{
var panel = CreatePanel("panel_rect");
var holes = new List<HoleInfo>
{
CreateHole("hole_a", "panel_rect", new[] { -40.0, 0.0, -20.0 }, new[] { 0.0, -1.0, 0.0 }, new[] { "top" }),
CreateHole("hole_b", "panel_rect", new[] { 40.0, 0.0, -20.0 }, new[] { 0.0, -1.0, 0.0 }, new[] { "top" }),
CreateHole("hole_c", "panel_rect", new[] { -40.0, 0.0, 20.0 }, new[] { 0.0, -1.0, 0.0 }, new[] { "top" }),
CreateHole("hole_d", "panel_rect", new[] { 40.0, 0.0, 20.0 }, new[] { 0.0, -1.0, 0.0 }, new[] { "top" }),
};
var patterns = MountPatternAnalyzer.BuildMountPatterns(
new[] { panel },
holes,
new List<HoleGroupInfo>());
var pattern = Assert.Single(patterns);
Assert.Equal("rectangle_4", pattern.PatternType);
Assert.Equal(2, pattern.MinRequiredHoles);
Assert.Equal(4, pattern.PreferredHoleCount);
Assert.True(pattern.Symmetry);
Assert.Empty(pattern.RequiredHoleIds);
Assert.Equal(4, pattern.OptionalHoleIds.Length);
}
private static PanelInfo CreatePanel(string id)
{
return new PanelInfo
{
Id = id,
Position = new[] { 0.0, 0.0, 0.0 },
Normal = new[] { 0.0, 1.0, 0.0 },
BoundingBox = new BoundingBox3D
{
Min = new[] { -300.0, -9.0, -300.0 },
Max = new[] { 300.0, 9.0, 300.0 },
},
Size = new[] { 600.0, 18.0, 600.0 },
};
}
private static HoleInfo CreateHole(string id, string panelId, double[] position, double[] normal, string[] surfaceFaces)
{
return new HoleInfo
{
Id = id,
PanelId = panelId,
Position = position,
Normal = normal,
Diameter = 8.0,
Radius = 4.0,
Depth = 12.0,
Type = "blind",
SurfaceFaces = surfaceFaces,
};
}
}
}

View File

@ -0,0 +1,73 @@
namespace FurnitureExporter.Tests
{
public class PanelFaceSemanticResolverTests
{
[Fact]
public void ResolveSurfaceFaces_ForThroughHole_ReturnsOppositeFaces()
{
var faces = PanelFaceSemanticResolver.ResolveSurfaceFaces(
new BoundingBox3D
{
Min = new[] { 0.0, 0.0, 0.0 },
Max = new[] { 18.0, 560.0, 720.0 },
},
new[] { 9.0, 280.0, 360.0 },
new[] { 1.0, 0.0, 0.0 },
18.0);
Assert.Contains("left", faces);
Assert.Contains("right", faces);
}
[Fact]
public void ResolveSurfaceFaces_ForBlindHole_ReturnsSingleExteriorFace()
{
var faces = PanelFaceSemanticResolver.ResolveSurfaceFaces(
new BoundingBox3D
{
Min = new[] { 0.0, 0.0, 0.0 },
Max = new[] { 18.0, 560.0, 720.0 },
},
new[] { 4.0, 280.0, 360.0 },
new[] { 1.0, 0.0, 0.0 },
8.0);
Assert.Single(faces);
Assert.Equal("left", faces[0]);
}
[Fact]
public void ResolveSurfaceFaces_ForTopBottomThroughHole_ReturnsTopAndBottom()
{
var faces = PanelFaceSemanticResolver.ResolveSurfaceFaces(
new BoundingBox3D
{
Min = new[] { 0.0, 0.0, 0.0 },
Max = new[] { 600.0, 720.0, 18.0 },
},
new[] { 300.0, 360.0, 9.0 },
new[] { 0.0, 0.0, 1.0 },
18.0);
Assert.Contains("top", faces);
Assert.Contains("bottom", faces);
}
[Fact]
public void ResolveSurfaceFaces_ForSideBlindHole_PrefersSingleSideFace()
{
var faces = PanelFaceSemanticResolver.ResolveSurfaceFaces(
new BoundingBox3D
{
Min = new[] { 0.0, 0.0, 0.0 },
Max = new[] { 600.0, 18.0, 720.0 },
},
new[] { 300.0, 4.0, 360.0 },
new[] { 0.0, 1.0, 0.0 },
8.0);
Assert.Single(faces);
Assert.Equal("front", faces[0]);
}
}
}

View File

@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace FurnitureExporter.Tests
{
public class SnapSurfaceBuilderTests
{
[Fact]
public void BuildSnapSurfaces_FromBoundingBox_CreatesSixAxisFaces()
{
var panel = CreatePanel("panel_001");
var surfaces = SnapSurfaceBuilder.BuildSnapSurfaces(new[] { panel });
Assert.Equal(6, surfaces.Count);
Assert.Equal(6, panel.SnapSurfaces.Count);
var top = Assert.Single(surfaces.Where(surface => surface.Face == "top"));
Assert.Equal("panel_001_snap_top", top.Id);
Assert.Equal("panel_001", top.PanelId);
Assert.Equal(new[] { 0.0, 0.0, 1.0 }, top.Normal);
Assert.Equal(new[] { 0.0, 0.0, 18.0 }, top.Center);
Assert.Equal(18.0, top.Bounds.Min[2]);
Assert.Equal(18.0, top.Bounds.Max[2]);
Assert.Equal(1.0, top.Confidence);
var front = Assert.Single(surfaces.Where(surface => surface.Face == "front"));
Assert.Equal(new[] { 0.0, -1.0, 0.0 }, front.Normal);
Assert.Equal(-280.0, front.Center[1]);
Assert.Equal(-280.0, front.Bounds.Min[1]);
Assert.Equal(-280.0, front.Bounds.Max[1]);
}
[Fact]
public void ExportData_ForSingleAsset_EmitsSnapFieldsAndOmitsAssembly()
{
var panel = CreatePanel("panel_001");
var pattern = new MountPatternInfo
{
Id = "panel_001_mount_top_000",
PanelId = "panel_001",
MountFace = "top",
PatternType = "line_2",
HoleIds = new[] { "hole_a", "hole_b" },
RequiredHoleIds = new[] { "hole_a", "hole_b" },
MinRequiredHoles = 2,
PreferredHoleCount = 2,
Normal = new[] { 0.0, 0.0, 1.0 },
Position = new[] { 0.0, 0.0, 18.0 },
Confidence = 0.95,
};
panel.MountPatterns = new List<MountPatternInfo> { pattern };
panel.SnapPatterns = panel.MountPatterns;
var exportData = new ExportData
{
ExportMode = "single_asset",
Panels = new List<PanelInfo> { panel },
MountPatterns = new List<MountPatternInfo> { pattern },
SnapPatterns = new List<MountPatternInfo> { pattern },
SnapSurfaces = SnapSurfaceBuilder.BuildSnapSurfaces(new[] { panel }),
};
var json = JsonConvert.SerializeObject(
exportData,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
});
var root = JObject.Parse(json);
Assert.Null(root["assembly"]);
Assert.DoesNotContain("\"mates\"", json, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("\"connections\"", json, StringComparison.OrdinalIgnoreCase);
Assert.Single(Assert.IsType<JArray>(root["mountPatterns"]));
Assert.Single(Assert.IsType<JArray>(root["snapPatterns"]));
Assert.Equal(6, Assert.IsType<JArray>(root["snapSurfaces"]).Count);
var panels = Assert.IsType<JArray>(root["panels"]);
Assert.Equal(6, Assert.IsType<JArray>(panels[0]?["snapSurfaces"]).Count);
}
private static PanelInfo CreatePanel(string id)
{
return new PanelInfo
{
Id = id,
Position = new[] { 0.0, 0.0, 9.0 },
Normal = new[] { 0.0, 0.0, 1.0 },
BoundingBox = new BoundingBox3D
{
Min = new[] { -300.0, -280.0, 0.0 },
Max = new[] { 300.0, 280.0, 18.0 },
},
Size = new[] { 600.0, 560.0, 18.0 },
};
}
}
}

View File

@ -11,7 +11,7 @@
<!-- AutoCAD 2024 插件配置 -->
<AssemblyName>FurnitureExporter</AssemblyName>
<RootNamespace>FurnitureExporter</RootNamespace>
<Version>2.5.0</Version>
<Version>2.6.0</Version>
<Authors>Furniture 3D System</Authors>
<Description>家具 3D 模型导出插件 - 导出 GLB 模型和五金件位置数据</Description>

View File

@ -162,6 +162,31 @@ public class HoleDetector
{
try
{
BoundingBox3D? panelBoundingBox = null;
try
{
var ext = solid.GeometricExtents;
panelBoundingBox = new BoundingBox3D
{
Min = new[]
{
Math.Round(ext.MinPoint.X + _originOffset.X, 2),
Math.Round(ext.MinPoint.Y + _originOffset.Y, 2),
Math.Round(ext.MinPoint.Z + _originOffset.Z, 2),
},
Max = new[]
{
Math.Round(ext.MaxPoint.X + _originOffset.X, 2),
Math.Round(ext.MaxPoint.Y + _originOffset.Y, 2),
Math.Round(ext.MaxPoint.Z + _originOffset.Z, 2),
},
};
}
catch
{
panelBoundingBox = null;
}
var radius = cylinder.Radius;
var diameter = radius * 2;
@ -203,7 +228,12 @@ public class HoleDetector
Depth = Math.Round(depth, 2),
PanelId = panelId,
Type = holeType,
Radius = Math.Round(radius, 2)
Radius = Math.Round(radius, 2),
SurfaceFaces = PanelFaceSemanticResolver.ResolveSurfaceFaces(
panelBoundingBox,
new[] { offsetPosition.X, offsetPosition.Y, offsetPosition.Z },
new[] { axis.X, axis.Y, axis.Z },
depth)
};
}
catch (System.Exception ex)

View File

@ -92,7 +92,13 @@ public static class HoleGrouping
Position = mainHole.Position,
Normal = mainHole.Normal,
Confidence = confidence,
Reason = $"主孔直径 {mainHole.Diameter:0.#}mm + 侧孔直径 {best.Diameter:0.#}mm,距离 {dist:0.#}mm,法向点积 {dot:0.00}"
Reason = $"主孔直径 {mainHole.Diameter:0.#}mm + 侧孔直径 {best.Diameter:0.#}mm,距离 {dist:0.#}mm,法向点积 {dot:0.00}",
MountFace = mainHole.SurfaceFaces.FirstOrDefault(face => !string.IsNullOrWhiteSpace(face)) ?? "unknown",
SurfaceFaces = mainHole.SurfaceFaces
.Concat(best.SurfaceFaces ?? Array.Empty<string>())
.Where(face => !string.IsNullOrWhiteSpace(face))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray(),
};
groups.Add(group);

View File

@ -374,6 +374,11 @@ public sealed class HoleMarkerDetector
BindingSource = binding.BindingSource,
BindingConfidence = binding.Confidence,
BindingReason = binding.BindingReason,
SurfaceFaces = PanelFaceSemanticResolver.ResolveSurfaceFaces(
binding.PanelCandidate.Panel.BoundingBox,
new[] { p.X, p.Y, p.Z },
new[] { axis.X, axis.Y, axis.Z },
depth),
});
}
@ -418,6 +423,11 @@ public sealed class HoleMarkerDetector
BindingSource = binding.BindingSource,
BindingConfidence = binding.Confidence,
BindingReason = binding.BindingReason,
SurfaceFaces = PanelFaceSemanticResolver.ResolveSurfaceFaces(
binding.PanelCandidate.Panel.BoundingBox,
new[] { p.X, p.Y, p.Z },
new[] { axis.X, axis.Y, axis.Z },
depth),
});
}

View File

@ -10,7 +10,7 @@ public class ExportData
/// <summary>
/// 数据格式版本
/// </summary>
public string Version { get; set; } = "2.4";
public string Version { get; set; } = "2.6";
/// <summary>
/// 导出时间 (ISO 8601)
@ -46,6 +46,13 @@ public class ExportData
/// 图纸单位(毫米/厘米/米等)
/// </summary>
public string Units { get; set; } = "毫米";
/// <summary>
/// CAD 世界坐标系标识。
/// 当前导出统一按 AutoCAD 世界坐标:X 向右,Y 向前,Z 向上。
/// Web 端后续做孔位吸附时,不再需要靠启发式猜测坐标系。
/// </summary>
public string CoordinateSystem { get; set; } = "AUTOCAD_WORLD_Z_UP";
/// <summary>
/// 原点偏移 [X, Y, Z] (mm)
@ -103,6 +110,24 @@ public class ExportData
/// </summary>
public List<HoleGroupInfo> HoleGroups { get; set; } = new();
/// <summary>
/// 安装图样(v2.5 兼容字段)。
/// 新方案主字段是 SnapPatterns;这里继续保留给旧 Web 端兼容。
/// </summary>
public List<MountPatternInfo> MountPatterns { get; set; } = new();
/// <summary>
/// 吸附图样(v2.6 新主字段)。
/// 内容与 MountPatterns 保持一致,Web 端后续优先读取 snapPatterns。
/// </summary>
public List<MountPatternInfo> SnapPatterns { get; set; } = new();
/// <summary>
/// 吸附面(v2.6 新增)。
/// 每块板件至少提供 6 个轴向外表面,用于辅助装配阶段做面级吸附候选。
/// </summary>
public List<SnapSurfaceInfo> SnapSurfaces { get; set; } = new();
/// <summary>
/// 逻辑孔位数量(v2.2.4 新增)
/// 计算方式:以 HoleGroupId 合并计数(HoleGroupId 为空则按 Hole.Id 计数)。
@ -313,6 +338,21 @@ public class PanelInfo
/// 该板件上的逻辑孔位分组(v2.2.4 新增)
/// </summary>
public List<HoleGroupInfo> HoleGroups { get; set; } = new();
/// <summary>
/// 该板件上的安装图样(v2.5 兼容字段)
/// </summary>
public List<MountPatternInfo> MountPatterns { get; set; } = new();
/// <summary>
/// 该板件上的吸附图样(v2.6 新主字段)
/// </summary>
public List<MountPatternInfo> SnapPatterns { get; set; } = new();
/// <summary>
/// 该板件的吸附面(v2.6 新增)
/// </summary>
public List<SnapSurfaceInfo> SnapSurfaces { get; set; } = new();
}
/// <summary>
@ -391,6 +431,14 @@ public class HoleInfo
/// </summary>
public double BindingConfidence { get; set; } = 1.0;
/// <summary>
/// 孔位整体可用置信度(0-1)
/// 说明:
/// - BindingConfidence 只表示“孔位和板件绑定得稳不稳”;
/// - Confidence 表示“这个孔位几何与语义整体是否可信”。
/// </summary>
public double Confidence { get; set; } = 1.0;
/// <summary>
/// 绑定原因说明(调试/回放)
/// </summary>
@ -418,12 +466,24 @@ public class HoleInfo
/// 例如:main(主孔)/ side(侧孔)
/// </summary>
public string? HoleGroupRole { get; set; }
/// <summary>
/// 当前孔实际打在板件外表面的面标签。
/// 盲孔通常只有 1 个面;通孔通常会有 2 个相对面。
/// 例如:top / bottom / left / right / front / back
/// </summary>
public string[] SurfaceFaces { get; set; } = System.Array.Empty<string>();
/// <summary>
/// 推断的用途(根据直径)
/// </summary>
public string? InferredUsage { get; set; }
/// <summary>
/// 低置信度或歧义提示
/// </summary>
public List<string> Warnings { get; set; } = new();
/// <summary>
/// 针对该孔位的紧固件建议(v2.3 新增)
/// 说明:
@ -511,6 +571,140 @@ public class HoleGroupInfo
/// 可读原因(调试/回放)
/// </summary>
public string? Reason { get; set; }
/// <summary>
/// 当前孔组默认作用的安装面。
/// 例如:top / bottom / left / right / front / back / unknown
/// </summary>
public string MountFace { get; set; } = "unknown";
/// <summary>
/// 逻辑孔位实际落在哪些板件外表面上。
/// 默认按主孔 + 组内成员孔合并去重。
/// </summary>
public string[] SurfaceFaces { get; set; } = System.Array.Empty<string>();
/// <summary>
/// 低置信度或歧义提示
/// </summary>
public List<string> Warnings { get; set; } = new();
}
/// <summary>
/// 安装图样(v2.5 新增)
/// </summary>
public class MountPatternInfo
{
/// <summary>
/// 图样 ID(如 panel_001_mount_top_001)
/// </summary>
public string Id { get; set; } = "";
/// <summary>
/// 所属板件 ID
/// </summary>
public string PanelId { get; set; } = "";
/// <summary>
/// 默认安装面:top / bottom / left / right / front / back / unknown
/// </summary>
public string MountFace { get; set; } = "unknown";
/// <summary>
/// 图样类型:single / line_2 / triangle_3 / rectangle_4 / generic
/// </summary>
public string PatternType { get; set; } = "generic";
/// <summary>
/// 属于该图样的几何孔位 ID
/// </summary>
public string[] HoleIds { get; set; } = System.Array.Empty<string>();
/// <summary>
/// 必需命中的孔位 ID
/// </summary>
public string[] RequiredHoleIds { get; set; } = System.Array.Empty<string>();
/// <summary>
/// 可选命中的孔位 ID
/// </summary>
public string[] OptionalHoleIds { get; set; } = System.Array.Empty<string>();
/// <summary>
/// 吸附成立所需的最少命中孔数
/// </summary>
public int MinRequiredHoles { get; set; }
/// <summary>
/// 推荐命中孔数(越接近越稳)
/// </summary>
public int PreferredHoleCount { get; set; }
/// <summary>
/// 是否允许对称复用
/// </summary>
public bool Symmetry { get; set; }
/// <summary>
/// 图样参考点(默认取图样中心)
/// </summary>
public double[] Position { get; set; } = new double[3];
/// <summary>
/// 图样参考法向(默认取安装面法向)
/// </summary>
public double[] Normal { get; set; } = new double[3];
/// <summary>
/// 图样置信度(0-1)
/// </summary>
public double Confidence { get; set; }
/// <summary>
/// 低置信度或歧义提示
/// </summary>
public List<string> Warnings { get; set; } = new();
}
/// <summary>
/// 吸附面信息(v2.6 新增)
/// </summary>
public class SnapSurfaceInfo
{
/// <summary>
/// 吸附面 ID(如 panel_001_snap_top)
/// </summary>
public string Id { get; set; } = "";
/// <summary>
/// 面标签:left / right / front / back / bottom / top
/// </summary>
public string Face { get; set; } = "";
/// <summary>
/// 所属板件 ID
/// </summary>
public string PanelId { get; set; } = "";
/// <summary>
/// 外法向量 [X, Y, Z]
/// </summary>
public double[] Normal { get; set; } = new double[3];
/// <summary>
/// 面中心 [X, Y, Z] (mm)
/// </summary>
public double[] Center { get; set; } = new double[3];
/// <summary>
/// 面边界框。薄轴最小值和最大值相同,表示该外表面所在平面。
/// </summary>
public BoundingBox3D Bounds { get; set; } = new();
/// <summary>
/// 推导置信度(0-1)
/// </summary>
public double Confidence { get; set; }
}
/// <summary>

View File

@ -0,0 +1,445 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace FurnitureExporter;
/// <summary>
/// 安装图样识别器。
///
/// 设计口径:
/// - 吸附面(mount face)先缩小候选范围;
/// - HoleGroup 负责把几何孔组织成业务孔位;
/// - MountPattern 负责定义“至少命中几个孔才允许吸附成立”。
/// </summary>
public static class MountPatternAnalyzer
{
private const double DefaultClusterDistanceMm = 220.0;
public static List<MountPatternInfo> BuildMountPatterns(
IReadOnlyList<PanelInfo> panels,
IReadOnlyList<HoleInfo> holes,
IReadOnlyList<HoleGroupInfo> holeGroups)
{
var patterns = new List<MountPatternInfo>();
if (panels == null || panels.Count == 0)
{
return patterns;
}
var holesByPanel = holes
.Where(h => !string.IsNullOrWhiteSpace(h.PanelId))
.GroupBy(h => h.PanelId, StringComparer.OrdinalIgnoreCase)
.ToDictionary(group => group.Key, group => group.ToList(), StringComparer.OrdinalIgnoreCase);
var groupsByPanel = holeGroups
.Where(group => !string.IsNullOrWhiteSpace(group.PanelId))
.GroupBy(group => group.PanelId, StringComparer.OrdinalIgnoreCase)
.ToDictionary(group => group.Key, group => group.ToList(), StringComparer.OrdinalIgnoreCase);
foreach (var panel in panels)
{
var panelHoles = holesByPanel.TryGetValue(panel.Id, out var panelHoleList)
? panelHoleList
: new List<HoleInfo>();
var panelGroups = groupsByPanel.TryGetValue(panel.Id, out var panelGroupList)
? panelGroupList
: new List<HoleGroupInfo>();
var units = BuildUnits(panel.Id, panelHoles, panelGroups);
if (units.Count == 0)
{
panel.MountPatterns = new List<MountPatternInfo>();
panel.SnapPatterns = new List<MountPatternInfo>();
continue;
}
var panelPatterns = new List<MountPatternInfo>();
var unitsByFace = units
.SelectMany(unit =>
unit.MountFaces.Select(face => new { Face = face, Unit = unit }))
.GroupBy(item => item.Face, StringComparer.OrdinalIgnoreCase);
foreach (var faceGroup in unitsByFace)
{
var clusters = BuildClusters(faceGroup.Select(item => item.Unit).Distinct().ToList());
var patternIndex = 0;
foreach (var cluster in clusters)
{
var pattern = BuildPattern(panel.Id, faceGroup.Key, patternIndex++, cluster);
panelPatterns.Add(pattern);
}
}
panel.MountPatterns = panelPatterns;
panel.SnapPatterns = panelPatterns;
patterns.AddRange(panelPatterns);
}
return patterns;
}
public static void AttachPatternsToPanels(IReadOnlyList<PanelInfo> panels, IReadOnlyList<MountPatternInfo> patterns)
{
var map = patterns
.GroupBy(pattern => pattern.PanelId, StringComparer.OrdinalIgnoreCase)
.ToDictionary(group => group.Key, group => group.ToList(), StringComparer.OrdinalIgnoreCase);
foreach (var panel in panels)
{
var panelPatterns = map.TryGetValue(panel.Id, out var items)
? items
: new List<MountPatternInfo>();
panel.MountPatterns = panelPatterns;
panel.SnapPatterns = panelPatterns;
}
}
private static List<MountPatternUnit> BuildUnits(
string panelId,
IReadOnlyList<HoleInfo> holes,
IReadOnlyList<HoleGroupInfo> holeGroups)
{
var units = new List<MountPatternUnit>();
var groupedHoleIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var group in holeGroups)
{
foreach (var holeId in group.HoleIds ?? Array.Empty<string>())
{
groupedHoleIds.Add(holeId);
}
var mountFaces = NormalizeMountFaces(group.SurfaceFaces, group.MountFace);
group.MountFace = mountFaces.FirstOrDefault() ?? "unknown";
units.Add(new MountPatternUnit
{
Id = group.Id,
PanelId = panelId,
HoleIds = group.HoleIds ?? Array.Empty<string>(),
Position = group.Position,
Normal = group.Normal,
GroupType = group.GroupType,
MountFaces = mountFaces,
Confidence = Clamp01(group.Confidence <= 0 ? 0.72 : group.Confidence),
Warnings = group.Warnings?.ToList() ?? new List<string>(),
Source = "hole-group",
});
}
foreach (var hole in holes)
{
if (groupedHoleIds.Contains(hole.Id))
{
continue;
}
units.Add(new MountPatternUnit
{
Id = hole.Id,
PanelId = panelId,
HoleIds = new[] { hole.Id },
Position = hole.Position,
Normal = hole.Normal,
GroupType = hole.HoleGroupType,
MountFaces = NormalizeMountFaces(hole.SurfaceFaces),
Confidence = Clamp01(hole.Confidence <= 0 ? 0.76 : hole.Confidence),
Warnings = hole.Warnings?.ToList() ?? new List<string>(),
Source = "single-hole",
});
}
return units;
}
private static List<List<MountPatternUnit>> BuildClusters(IReadOnlyList<MountPatternUnit> units)
{
var clusters = new List<List<MountPatternUnit>>();
var visited = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var unit in units)
{
if (!visited.Add(unit.Id))
{
continue;
}
var cluster = new List<MountPatternUnit>();
var queue = new Queue<MountPatternUnit>();
queue.Enqueue(unit);
while (queue.Count > 0)
{
var current = queue.Dequeue();
cluster.Add(current);
foreach (var candidate in units)
{
if (visited.Contains(candidate.Id))
{
continue;
}
if (Distance(current.Position, candidate.Position) > DefaultClusterDistanceMm)
{
continue;
}
visited.Add(candidate.Id);
queue.Enqueue(candidate);
}
}
clusters.Add(cluster.OrderBy(item => item.Position[0]).ThenBy(item => item.Position[1]).ThenBy(item => item.Position[2]).ToList());
}
return clusters;
}
private static MountPatternInfo BuildPattern(
string panelId,
string mountFace,
int patternIndex,
IReadOnlyList<MountPatternUnit> cluster)
{
var holeIds = cluster
.SelectMany(unit => unit.HoleIds)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
var warnings = cluster
.SelectMany(unit => unit.Warnings)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
var pointCount = cluster.Count;
var patternType = ResolvePatternType(cluster);
var isSymmetricRectangle = string.Equals(patternType, "rectangle_4", StringComparison.OrdinalIgnoreCase);
var minRequiredHoles = ResolveMinRequiredHoleCount(patternType, pointCount, isSymmetricRectangle);
var preferredHoleCount = pointCount;
var requiredHoleIds = Array.Empty<string>();
var optionalHoleIds = Array.Empty<string>();
if (pointCount <= 3 || !isSymmetricRectangle)
{
requiredHoleIds = holeIds;
}
else
{
optionalHoleIds = holeIds;
warnings.Add("四孔对称图样默认按 2 孔完成初始定位,剩余孔位作为增强固定。");
}
if (string.Equals(patternType, "generic", StringComparison.OrdinalIgnoreCase))
{
warnings.Add("当前图样由几何聚类自动生成,建议后续在资产录入阶段人工复核。");
}
return new MountPatternInfo
{
Id = $"{panelId}_mount_{mountFace}_{patternIndex:D3}",
PanelId = panelId,
MountFace = mountFace,
PatternType = patternType,
HoleIds = holeIds,
RequiredHoleIds = requiredHoleIds,
OptionalHoleIds = optionalHoleIds,
MinRequiredHoles = minRequiredHoles,
PreferredHoleCount = preferredHoleCount,
Symmetry = isSymmetricRectangle,
Position = AveragePosition(cluster),
Normal = ResolveMountFaceNormal(mountFace),
Confidence = ComputePatternConfidence(cluster, patternType, warnings.Count),
Warnings = warnings,
};
}
private static string ResolvePatternType(IReadOnlyList<MountPatternUnit> cluster)
{
return cluster.Count switch
{
1 => "single",
2 => "line_2",
3 => "triangle_3",
4 when IsRectangle(cluster) => "rectangle_4",
_ => "generic",
};
}
private static int ResolveMinRequiredHoleCount(string patternType, int pointCount, bool isSymmetricRectangle)
{
if (string.Equals(patternType, "single", StringComparison.OrdinalIgnoreCase))
{
return 1;
}
if (string.Equals(patternType, "line_2", StringComparison.OrdinalIgnoreCase))
{
return 2;
}
if (string.Equals(patternType, "triangle_3", StringComparison.OrdinalIgnoreCase))
{
return 3;
}
if (isSymmetricRectangle)
{
return 2;
}
if (pointCount <= 0)
{
return 1;
}
return pointCount <= 4 ? pointCount : Math.Min(pointCount, 4);
}
private static bool IsRectangle(IReadOnlyList<MountPatternUnit> cluster)
{
if (cluster.Count != 4)
{
return false;
}
var distances = new List<double>();
for (var i = 0; i < cluster.Count; i++)
{
for (var j = i + 1; j < cluster.Count; j++)
{
distances.Add(Distance(cluster[i].Position, cluster[j].Position));
}
}
distances.Sort();
if (distances.Count != 6)
{
return false;
}
var sideA = distances[0];
var sideB = distances[2];
var diagonalA = distances[4];
var diagonalB = distances[5];
return AreClose(sideA, distances[1], 0.12) &&
AreClose(sideB, distances[3], 0.12) &&
AreClose(diagonalA, diagonalB, 0.08);
}
private static double[] AveragePosition(IReadOnlyList<MountPatternUnit> cluster)
{
if (cluster.Count == 0)
{
return new[] { 0.0, 0.0, 0.0 };
}
return new[]
{
Math.Round(cluster.Average(item => item.Position[0]), 2),
Math.Round(cluster.Average(item => item.Position[1]), 2),
Math.Round(cluster.Average(item => item.Position[2]), 2),
};
}
private static double[] ResolveMountFaceNormal(string mountFace)
{
return mountFace.ToLowerInvariant() switch
{
"left" => new[] { -1.0, 0.0, 0.0 },
"right" => new[] { 1.0, 0.0, 0.0 },
"front" => new[] { 0.0, -1.0, 0.0 },
"back" => new[] { 0.0, 1.0, 0.0 },
"bottom" => new[] { 0.0, 0.0, -1.0 },
"top" => new[] { 0.0, 0.0, 1.0 },
_ => new[] { 0.0, 0.0, 1.0 },
};
}
private static double ComputePatternConfidence(
IReadOnlyList<MountPatternUnit> cluster,
string patternType,
int warningCount)
{
if (cluster.Count == 0)
{
return 0;
}
var baseConfidence = cluster.Average(item => item.Confidence);
if (string.Equals(patternType, "generic", StringComparison.OrdinalIgnoreCase))
{
baseConfidence = Math.Min(baseConfidence, 0.68);
}
if (warningCount > 0)
{
baseConfidence -= Math.Min(0.14, warningCount * 0.04);
}
return Math.Round(Clamp01(baseConfidence), 4);
}
private static string[] NormalizeMountFaces(IEnumerable<string>? faces, string? fallbackFace = null)
{
var normalized = (faces ?? Array.Empty<string>())
.Where(face => !string.IsNullOrWhiteSpace(face))
.Select(face => face.Trim().ToLowerInvariant())
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (normalized.Count > 0)
{
return normalized.ToArray();
}
if (!string.IsNullOrWhiteSpace(fallbackFace))
{
return new[] { fallbackFace!.Trim().ToLowerInvariant() };
}
return new[] { "unknown" };
}
private static double Distance(IReadOnlyList<double> left, IReadOnlyList<double> right)
{
var dx = left[0] - right[0];
var dy = left[1] - right[1];
var dz = left[2] - right[2];
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
private static bool AreClose(double left, double right, double toleranceRatio)
{
var baseValue = Math.Max(Math.Abs(left), Math.Abs(right));
if (baseValue <= 1e-6)
{
return true;
}
return Math.Abs(left - right) / baseValue <= toleranceRatio;
}
private static double Clamp01(double value)
{
if (value < 0) return 0;
if (value > 1) return 1;
return value;
}
private sealed class MountPatternUnit
{
public string Id { get; set; } = "";
public string PanelId { get; set; } = "";
public string[] HoleIds { get; set; } = Array.Empty<string>();
public double[] Position { get; set; } = new double[3];
public double[] Normal { get; set; } = new double[3];
public string? GroupType { get; set; }
public string[] MountFaces { get; set; } = Array.Empty<string>();
public double Confidence { get; set; }
public List<string> Warnings { get; set; } = new();
public string Source { get; set; } = "single-hole";
}
}

View File

@ -210,8 +210,24 @@ public class PanelAnalyzer
// 判断板材方向(法向量)
var normal = DeterminePanelNormal(dims, size);
var panelBoundingBox = new BoundingBox3D
{
Min = new double[]
{
Math.Round(ext.MinPoint.X + _originOffset.X, 2),
Math.Round(ext.MinPoint.Y + _originOffset.Y, 2),
Math.Round(ext.MinPoint.Z + _originOffset.Z, 2)
},
Max = new double[]
{
Math.Round(ext.MaxPoint.X + _originOffset.X, 2),
Math.Round(ext.MaxPoint.Y + _originOffset.Y, 2),
Math.Round(ext.MaxPoint.Z + _originOffset.Z, 2)
}
};
// 提取孔位
var holes = ExtractHoles(solid, $"panel_{index:D3}", thickness);
var holes = ExtractHoles(solid, $"panel_{index:D3}", thickness, panelBoundingBox);
// v2.2.5:孔位过滤(与设计师“孔位口径”对齐)
// - 极小薄件(如脚垫/垫片)不作为板件孔位统计对象,避免误把“几何细节”当孔
@ -253,21 +269,7 @@ public class PanelAnalyzer
Math.Round(dims[2], 2) // 厚
},
Normal = normal,
BoundingBox = new BoundingBox3D
{
Min = new double[]
{
Math.Round(ext.MinPoint.X + _originOffset.X, 2),
Math.Round(ext.MinPoint.Y + _originOffset.Y, 2),
Math.Round(ext.MinPoint.Z + _originOffset.Z, 2)
},
Max = new double[]
{
Math.Round(ext.MaxPoint.X + _originOffset.X, 2),
Math.Round(ext.MaxPoint.Y + _originOffset.Y, 2),
Math.Round(ext.MaxPoint.Z + _originOffset.Z, 2)
}
},
BoundingBox = panelBoundingBox,
Holes = holes
};
}
@ -299,7 +301,11 @@ public class PanelAnalyzer
/// <summary>
/// 从板材实体中提取孔位信息
/// </summary>
private List<HoleInfo> ExtractHoles(Solid3d solid, string panelId, double panelThickness)
private List<HoleInfo> ExtractHoles(
Solid3d solid,
string panelId,
double panelThickness,
BoundingBox3D panelBoundingBox)
{
var holes = new List<HoleInfo>();
@ -315,7 +321,7 @@ public class PanelAnalyzer
var surface = BrepSurfaceUtils.Unwrap(face.Surface);
if (surface is Cylinder cylinder)
{
var hole = ExtractHoleFromCylinder(cylinder, face, panelId, holeIndex, panelThickness);
var hole = ExtractHoleFromCylinder(cylinder, face, panelId, holeIndex, panelThickness, panelBoundingBox);
if (hole != null)
{
holes.Add(hole);
@ -326,7 +332,7 @@ public class PanelAnalyzer
{
// 柜脚/三角铁等五金件上常见“沉头孔/倒角孔”,其孔壁在 Brep 中可能表现为锥面(Cone),
// 仅识别 Cylinder 会漏掉这类孔位。
var hole = ExtractHoleFromCone(cone, face, panelId, holeIndex, panelThickness);
var hole = ExtractHoleFromCone(cone, face, panelId, holeIndex, panelThickness, panelBoundingBox);
if (hole != null)
{
holes.Add(hole);
@ -344,7 +350,7 @@ public class PanelAnalyzer
// 某些孔位(尤其是五金件的沉头孔/复杂倒角)在 AutoCAD Brep 中,孔壁面可能被表达为 NurbSurface/其它曲面,
// 导致 surface is Cylinder/Cone 判断失败。
// 这里用“闭合圆边界 + 相邻端面平面法向推断孔轴”的方式做一次兜底,尽量补齐漏孔。
var hole = ExtractHoleFromFallbackFace(face, panelId, holeIndex, panelThickness);
var hole = ExtractHoleFromFallbackFace(face, panelId, holeIndex, panelThickness, panelBoundingBox);
if (hole != null)
{
holes.Add(hole);
@ -374,7 +380,8 @@ public class PanelAnalyzer
Autodesk.AutoCAD.BoundaryRepresentation.Face face,
string panelId,
int index,
double panelThickness)
double panelThickness,
BoundingBox3D panelBoundingBox)
{
try
{
@ -458,7 +465,12 @@ public class PanelAnalyzer
DetectionSource = "brep",
BindingSource = "panel_brep",
BindingConfidence = 1.0,
BindingReason = "从当前板件实体 Brep 直接提取"
BindingReason = "从当前板件实体 Brep 直接提取",
SurfaceFaces = PanelFaceSemanticResolver.ResolveSurfaceFaces(
panelBoundingBox,
new[] { offsetPosition.X, offsetPosition.Y, offsetPosition.Z },
new[] { axis.X, axis.Y, axis.Z },
depth)
};
}
catch
@ -475,7 +487,8 @@ public class PanelAnalyzer
Autodesk.AutoCAD.BoundaryRepresentation.Face face,
string panelId,
int index,
double panelThickness)
double panelThickness,
BoundingBox3D panelBoundingBox)
{
try
{
@ -560,7 +573,12 @@ public class PanelAnalyzer
DetectionSource = "brep",
BindingSource = "panel_brep",
BindingConfidence = 1.0,
BindingReason = "从当前板件实体 Brep 直接提取"
BindingReason = "从当前板件实体 Brep 直接提取",
SurfaceFaces = PanelFaceSemanticResolver.ResolveSurfaceFaces(
panelBoundingBox,
new[] { offsetPosition.X, offsetPosition.Y, offsetPosition.Z },
new[] { axis.X, axis.Y, axis.Z },
depth)
};
}
catch
@ -577,7 +595,8 @@ public class PanelAnalyzer
BrepFace face,
string panelId,
int index,
double panelThickness)
double panelThickness,
BoundingBox3D panelBoundingBox)
{
try
{
@ -651,7 +670,12 @@ public class PanelAnalyzer
DetectionSource = "brep",
BindingSource = "panel_brep",
BindingConfidence = 1.0,
BindingReason = "从当前板件实体 Brep 兜底面直接提取"
BindingReason = "从当前板件实体 Brep 兜底面直接提取",
SurfaceFaces = PanelFaceSemanticResolver.ResolveSurfaceFaces(
panelBoundingBox,
new[] { offsetPosition.X, offsetPosition.Y, offsetPosition.Z },
new[] { axis.X, axis.Y, axis.Z },
depth)
};
}
catch

View File

@ -0,0 +1,226 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace FurnitureExporter;
public static class PanelFaceSemanticResolver
{
private const double SurfaceFaceToleranceMm = 3.0;
private const double InteriorFaceToleranceMm = 0.8;
public static string[] ResolveSurfaceFaces(
BoundingBox3D? boundingBox,
IReadOnlyList<double>? position,
IReadOnlyList<double>? normal,
double depth)
{
if (boundingBox?.Min?.Length != 3 || boundingBox.Max?.Length != 3)
{
return Array.Empty<string>();
}
var center = ToVector3(position);
var axis = Normalize(ToVector3(normal));
if (center == null || axis == null)
{
return Array.Empty<string>();
}
var faces = new List<string>();
var halfDepth = Math.Max(0, depth) / 2.0;
var dominantFaces = ResolveDominantAxisFaces(axis);
if (halfDepth > 0.01)
{
AddFace(
faces,
ResolveNearestSurfaceFace(
boundingBox,
Offset(center, axis, halfDepth),
dominantFaces.Positive,
dominantFaces.Negative,
SurfaceFaceToleranceMm));
AddFace(
faces,
ResolveNearestSurfaceFace(
boundingBox,
Offset(center, axis, -halfDepth),
dominantFaces.Negative,
dominantFaces.Positive,
SurfaceFaceToleranceMm));
}
if (faces.Count == 0)
{
AddFace(
faces,
ResolveNearestSurfaceFace(
boundingBox,
center,
dominantFaces.Positive,
dominantFaces.Negative,
SurfaceFaceToleranceMm + InteriorFaceToleranceMm));
}
return faces
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
}
public static string OppositeFace(string? face)
{
return face?.Trim().ToLowerInvariant() switch
{
"left" => "right",
"right" => "left",
"front" => "back",
"back" => "front",
"top" => "bottom",
"bottom" => "top",
_ => "unknown",
};
}
private static void AddFace(ICollection<string> faces, string? face)
{
if (string.IsNullOrWhiteSpace(face))
{
return;
}
faces.Add(face!.Trim().ToLowerInvariant());
}
private static double[]? ToVector3(IReadOnlyList<double>? value)
{
if (value == null || value.Count < 3)
{
return null;
}
return new[] { value[0], value[1], value[2] };
}
private static double[]? Normalize(double[]? value)
{
if (value == null)
{
return null;
}
var length = Math.Sqrt(value[0] * value[0] + value[1] * value[1] + value[2] * value[2]);
if (length < 1e-9)
{
return null;
}
return new[]
{
value[0] / length,
value[1] / length,
value[2] / length,
};
}
private static double[] Offset(double[] origin, double[] direction, double distance)
{
return new[]
{
origin[0] + direction[0] * distance,
origin[1] + direction[1] * distance,
origin[2] + direction[2] * distance,
};
}
private static string? ResolveNearestSurfaceFace(BoundingBox3D box, IReadOnlyList<double> point)
{
return ResolveNearestSurfaceFace(box, point, null, null, SurfaceFaceToleranceMm);
}
private static string? ResolveNearestSurfaceFace(
BoundingBox3D box,
IReadOnlyList<double> point,
string? preferredFace,
string? secondaryFace,
double tolerance)
{
var candidates = new List<(string Face, double Distance)>();
if (IsWithinRange(point[1], box.Min[1], box.Max[1], tolerance) &&
IsWithinRange(point[2], box.Min[2], box.Max[2], tolerance))
{
candidates.Add(("left", Math.Abs(point[0] - box.Min[0])));
candidates.Add(("right", Math.Abs(point[0] - box.Max[0])));
}
if (IsWithinRange(point[0], box.Min[0], box.Max[0], tolerance) &&
IsWithinRange(point[2], box.Min[2], box.Max[2], tolerance))
{
candidates.Add(("front", Math.Abs(point[1] - box.Min[1])));
candidates.Add(("back", Math.Abs(point[1] - box.Max[1])));
}
if (IsWithinRange(point[0], box.Min[0], box.Max[0], tolerance) &&
IsWithinRange(point[1], box.Min[1], box.Max[1], tolerance))
{
candidates.Add(("bottom", Math.Abs(point[2] - box.Min[2])));
candidates.Add(("top", Math.Abs(point[2] - box.Max[2])));
}
var best = candidates
.Where(item => item.Distance <= tolerance)
.OrderBy(item => GetFacePriority(item.Face, preferredFace, secondaryFace))
.ThenBy(item => item.Distance)
.FirstOrDefault();
return string.IsNullOrWhiteSpace(best.Face) ? null : best.Face;
}
private static (string Positive, string Negative) ResolveDominantAxisFaces(IReadOnlyList<double> axis)
{
var absX = Math.Abs(axis[0]);
var absY = Math.Abs(axis[1]);
var absZ = Math.Abs(axis[2]);
if (absX >= absY && absX >= absZ)
{
return axis[0] >= 0
? ("right", "left")
: ("left", "right");
}
if (absY >= absX && absY >= absZ)
{
return axis[1] >= 0
? ("back", "front")
: ("front", "back");
}
return axis[2] >= 0
? ("top", "bottom")
: ("bottom", "top");
}
private static int GetFacePriority(string face, string? preferredFace, string? secondaryFace)
{
if (!string.IsNullOrWhiteSpace(preferredFace) &&
string.Equals(face, preferredFace, StringComparison.OrdinalIgnoreCase))
{
return 0;
}
if (!string.IsNullOrWhiteSpace(secondaryFace) &&
string.Equals(face, secondaryFace, StringComparison.OrdinalIgnoreCase))
{
return 1;
}
return 2;
}
private static bool IsWithinRange(double value, double min, double max, double tolerance)
{
return value >= min - tolerance && value <= max + tolerance;
}
}

View File

@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
namespace FurnitureExporter;
/// <summary>
/// 从板件包围盒推导辅助装配吸附面。
/// </summary>
public static class SnapSurfaceBuilder
{
private static readonly FaceDefinition[] Faces =
{
new("left", 0, false, new[] { -1.0, 0.0, 0.0 }),
new("right", 0, true, new[] { 1.0, 0.0, 0.0 }),
new("front", 1, false, new[] { 0.0, -1.0, 0.0 }),
new("back", 1, true, new[] { 0.0, 1.0, 0.0 }),
new("bottom", 2, false, new[] { 0.0, 0.0, -1.0 }),
new("top", 2, true, new[] { 0.0, 0.0, 1.0 }),
};
public static List<SnapSurfaceInfo> BuildSnapSurfaces(IReadOnlyList<PanelInfo> panels)
{
var allSurfaces = new List<SnapSurfaceInfo>();
foreach (var panel in panels)
{
var panelSurfaces = BuildPanelSurfaces(panel);
panel.SnapSurfaces = panelSurfaces;
allSurfaces.AddRange(panelSurfaces);
}
return allSurfaces;
}
private static List<SnapSurfaceInfo> BuildPanelSurfaces(PanelInfo panel)
{
var (min, max, confidence) = ResolveBounds(panel);
var surfaces = new List<SnapSurfaceInfo>(Faces.Length);
foreach (var face in Faces)
{
var center = new[]
{
Round((min[0] + max[0]) / 2.0),
Round((min[1] + max[1]) / 2.0),
Round((min[2] + max[2]) / 2.0),
};
var boundsMin = new[] { min[0], min[1], min[2] };
var boundsMax = new[] { max[0], max[1], max[2] };
var planeValue = face.UseMax ? max[face.Axis] : min[face.Axis];
center[face.Axis] = planeValue;
boundsMin[face.Axis] = planeValue;
boundsMax[face.Axis] = planeValue;
surfaces.Add(new SnapSurfaceInfo
{
Id = $"{panel.Id}_snap_{face.Name}",
Face = face.Name,
PanelId = panel.Id,
Normal = face.Normal,
Center = center,
Bounds = new BoundingBox3D
{
Min = RoundAll(boundsMin),
Max = RoundAll(boundsMax),
},
Confidence = confidence,
});
}
return surfaces;
}
private static (double[] Min, double[] Max, double Confidence) ResolveBounds(PanelInfo panel)
{
if (panel.BoundingBox?.Min?.Length == 3 && panel.BoundingBox.Max?.Length == 3)
{
return (RoundAll(panel.BoundingBox.Min), RoundAll(panel.BoundingBox.Max), 1.0);
}
var center = panel.Position?.Length == 3
? panel.Position
: new[] { 0.0, 0.0, 0.0 };
var size = panel.Size?.Length == 3
? panel.Size
: new[] { 0.0, 0.0, 0.0 };
var min = new[]
{
center[0] - Math.Abs(size[0]) / 2.0,
center[1] - Math.Abs(size[1]) / 2.0,
center[2] - Math.Abs(size[2]) / 2.0,
};
var max = new[]
{
center[0] + Math.Abs(size[0]) / 2.0,
center[1] + Math.Abs(size[1]) / 2.0,
center[2] + Math.Abs(size[2]) / 2.0,
};
return (RoundAll(min), RoundAll(max), 0.6);
}
private static double[] RoundAll(IReadOnlyList<double> values)
{
return new[]
{
Round(values[0]),
Round(values[1]),
Round(values[2]),
};
}
private static double Round(double value)
{
return Math.Round(value, 2);
}
private sealed class FaceDefinition
{
public FaceDefinition(string name, int axis, bool useMax, double[] normal)
{
Name = name;
Axis = axis;
UseMax = useMax;
Normal = normal;
}
public string Name { get; }
public int Axis { get; }
public bool UseMax { get; }
public double[] Normal { get; }
}
}

View File

@ -1,13 +1,13 @@
# AutoCAD 家具导出插件
用于 AutoCAD 2024 的家具 3D 模型导出插件。导出板材模型和五金件安装位置,用于 Web 端 3D 展示和安装视频生成。
用于 AutoCAD 2024 的家具 3D 模型导出插件。当前主流程是按资产中心口径导出单板材、单组件或单五金件,用于后续辅助装配和规格款入库。
## 功能特点
- **只导出板材模型** - 五金件使用位置引用,文件体积小(1-3MB vs 30-50MB)
- **自动分组五金件** - 按几何特征自动识别相同类型的五金件
- **精确位置记录** - 记录每个五金件的安装位置和旋转角度
- **服务端自动匹配** - 上传后自动匹配五金件库
- **单资产导出** - `FEXPORT` 默认导出单板材、单组件或单五金件
- **板材语义保留** - 导出孔位、孔组、吸附图样和吸附面,供辅助装配使用
- **五金规格提取** - 单五金模式保留几何签名、图层类型和实例信息
- **整套兼容保留** - 旧整套家具导出迁移到 `FEXPORTPRODUCT`
## 系统要求
@ -20,7 +20,7 @@
### 方法 1:手动安装发版包(推荐给产品/实施)
1. 完全关闭 AutoCAD 2024
2. 解压 `dist/家具导出插件-v2.3.1-一键安装版.zip`
2. 解压 `dist/家具导出插件-v2.6.0-一键安装版.zip`
3. 将整个 `FurnitureExporter.bundle` 文件夹复制到:
```text
@ -39,7 +39,7 @@
### 方法 2:一键安装
双击 `dist/家具导出插件-v2.3.1-一键安装版/安装.bat`
双击 `dist/家具导出插件-v2.6.0-一键安装版/安装.bat`
脚本会自动:
1. 复制插件到 `%AppData%\Autodesk\ApplicationPlugins\FurnitureExporter.bundle`
@ -90,7 +90,7 @@ dotnet build -c Release
## 使用方法
### FEXPORT - 导出家具模型
### FEXPORT - 导出单资产到资产中心
```
命令: FEXPORT
@ -98,13 +98,21 @@ dotnet build -c Release
**操作步骤:**
1. **选择板材** - 框选所有板材实体,按 Enter 确认
2. **选择五金件** - 框选所有五金件,按 Enter 确认(可跳过)
3. **保存文件** - 选择保存位置
1. **选择资产类型** - 板材 / 组件 / 五金
2. **选择实体** - 框选该单资产包含的 3D Solid 或图块,按 Enter 确认
3. **保存文件** - 选择 ZIP 保存位置
**输出文件:** `xxx_export.zip`,包含:
- `model.glb` - 板材 3D 模型
- `hardware.json` - 五金件位置数据
**输出文件:** `xxx_board_asset.zip` / `xxx_component_asset.zip` / `xxx_hardware_asset.zip`,包含:
- `model.obj` - 单资产模型
- `hardware.json` - 单资产结构化数据,包含 `exportMode: "single_asset"` 与 `assetTypeHint`
### FEXPORTPRODUCT - 整套家具兼容导出
```
命令: FEXPORTPRODUCT
```
仅用于旧产品链路兼容。新资产中心和辅助装配测试请优先使用 `FEXPORT`。
### FINFO - 查看实体信息
@ -120,36 +128,19 @@ dotnet build -c Release
```json
{
"version": "1.0",
"exportedAt": "2024-01-15T10:30:00Z",
"sourceFile": "衣柜.dwg",
"version": "2.6",
"exportedAt": "2026-05-11T10:30:00Z",
"sourceFile": "桌腿.dwg",
"software": "AutoCAD 2024",
"pluginVersion": "1.0.0",
"modelFile": "model.glb",
"panelCount": 12,
"hardwareTypes": [
{
"typeId": "type_000",
"signature": {
"width": 35.0,
"height": 12.0,
"depth": 12.0,
"volume": 4032.5
},
"instances": [
{
"position": { "x": 100.0, "y": 50.0, "z": 18.0 },
"rotation": { "x": 0, "y": 0, "z": 1.5708 },
"sourceLayer": "五金件"
},
{
"position": { "x": 100.0, "y": 150.0, "z": 18.0 },
"rotation": { "x": 0, "y": 0, "z": 1.5708 },
"sourceLayer": "五金件"
}
]
}
]
"pluginVersion": "2.6.0",
"modelFile": "model.obj",
"exportMode": "single_asset",
"assetTypeHint": "COMPONENT",
"sourceEntityCount": 1,
"panelCount": 1,
"snapPatterns": [],
"snapSurfaces": [],
"hardwareTypes": []
}
```
@ -160,17 +151,17 @@ dotnet build -c Release
─────── ──────
1. 打开 DWG 文件
2. FEXPORT
3. 框选板材 ────────────────→
4. 框选五金件 ──────────────→
5. 保存 ZIP ────────────────→ 上传 ZIP
3. 选择资产类型(板材/组件/五金)
4. 框选单个资产实体 ───────→
5. 保存 ZIP ────────────────→ 上传到资产中心
↓
自动解析
↓
匹配五金件库
创建/更新资产族规格款
↓
保存到数据库
↓
3D 预览 ✓
辅助装配可用 ✓
```
## 开发

View File

@ -49,7 +49,7 @@ Write-Host ""
# 路径设置
$PluginName = "FurnitureExporter"
$Version = "2.5.0"
$Version = "2.6.0"
$SourceDir = Join-Path $PSScriptRoot "FurnitureExporter"
$InstallerDir = Join-Path $PSScriptRoot "installer"
$OutputDir = Join-Path $PSScriptRoot "dist"
@ -90,7 +90,7 @@ $PackageXml = @"
SchemaVersion="1.0"
AutodeskProduct="AutoCAD"
Name="$PluginName"
Description="家具导出插件 v$Version - 支持整套兼容导出与资产中心单资产导出"
Description="家具导出插件 v$Version - 默认资产中心单资产导出,保留整套兼容导出"
Author="Furniture 3D System"
AppVersion="$Version"
FriendlyVersion="$Version"
@ -119,7 +119,7 @@ $PackageXml = @"
AppName="$PluginName"
Version="$Version"
ModuleName="./Contents/$PluginName.dll"
AppDescription="家具导出插件 - 支持整套兼容导出与资产中心单资产导出"
AppDescription="家具导出插件 - 默认资产中心单资产导出,保留整套兼容导出"
LoadOnAutoCADStartup="True"
AppType="Net" />
</Components>
@ -306,7 +306,7 @@ New-ItemProperty -Path $appKey -Name "DESCRIPTION" -PropertyType String -Value "
Write-Step "[3/3] 完成"
Write-Host ""
Write-Host "安装完成。请完全关闭 AutoCAD 后重新启动。" -ForegroundColor Green
Write-Host "启动后命令行输入:FEXPORTASSET(单资产)或 FEXPORT(整套兼容)" -ForegroundColor Green
Write-Host "启动后命令行输入:FEXPORT(单资产)或 FEXPORTPRODUCT(整套兼容)" -ForegroundColor Green
Write-Host ""
'@
Write-TextFileUtf8Bom -Path (Join-Path $InstallerDir "install.ps1") -Content $InstallPs1
@ -480,8 +480,10 @@ $Readme = @"
使用方法
───────────────────────────────────────────────────────────────────────
打开 AutoCAD 后,命令行输入:
FEXPORTASSET -> 单资产导出到资产中心(板材 / 组件 / 五金)
FEXPORT -> 整套家具兼容导出
FEXPORT -> 单资产导出到资产中心(板材 / 组件 / 五金)
FEXPORTASSET -> 单资产导出别名
FEXPORTPRODUCT -> 整套家具兼容导出
FEXPORTBUNDLE -> 整套家具兼容导出别名
FINFO -> 查看实体信息
安装脚本做了什么(便于 IT/排查)
@ -495,7 +497,7 @@ $Readme = @"
常见问题
───────────────────────────────────────────────────────────────────────
Q: 提示“未知命令 FEXPORTASSET / FEXPORT”
Q: 提示“未知命令 FEXPORT / FEXPORTPRODUCT”
A: 1) 确认已重启 AutoCAD(关闭再打开)
2) 确认插件目录存在:
%AppData%\Autodesk\ApplicationPlugins\FurnitureExporter.bundle\Contents\FurnitureExporter.dll

View File

@ -75,7 +75,8 @@ if %ERRORLEVEL% EQU 0 (
echo 1. 打开 AutoCAD 2024
echo 2. 输入命令: NETLOAD
echo 3. 选择上述 DLL 文件
echo 4. 使用命令: FEXPORT 导出模型
echo 4. 使用命令: FEXPORT 导出单资产模型
echo 旧整套兼容导出: FEXPORTPRODUCT
echo.
) else (
echo.

View File

@ -84,7 +84,8 @@ try {
Write-Host " 1. 打开 AutoCAD 2024"
Write-Host " 2. 输入命令: NETLOAD"
Write-Host " 3. 选择上述 DLL 文件"
Write-Host " 4. 使用命令: FEXPORT 导出模型"
Write-Host " 4. 使用命令: FEXPORT 导出单资产模型"
Write-Host " 旧整套兼容导出: FEXPORTPRODUCT"
Write-Host ""
}
else {

View File

@ -3,12 +3,12 @@
SchemaVersion="1.0"
AutodeskProduct="AutoCAD"
Name="FurnitureExporter"
Description="家具导出插件 v2.5.0 - 支持整套兼容导出与资产中心单资产导出"
Description="家具导出插件 v2.6.0 - 默认资产中心单资产导出,保留整套兼容导出"
Author="Furniture 3D System"
AppVersion="2.5.0"
FriendlyVersion="2.5.0"
AppVersion="2.6.0"
FriendlyVersion="2.6.0"
ProductType="Application"
Version="2.5.0"
Version="2.6.0"
ProductCode="{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}">
<CompanyDetails
@ -30,9 +30,9 @@
<ComponentEntry
AppName="FurnitureExporter"
Version="2.5.0"
Version="2.6.0"
ModuleName="./Contents/FurnitureExporter.dll"
AppDescription="家具导出插件 - 支持整套兼容导出与资产中心单资产导出"
AppDescription="家具导出插件 - 默认资产中心单资产导出,保留整套兼容导出"
LoadOnAutoCADStartup="True"
AppType="Net" />
</Components>

View File

@ -174,5 +174,5 @@ New-ItemProperty -Path $appKey -Name "DESCRIPTION" -PropertyType String -Value "
Write-Step "[3/3] 完成"
Write-Host ""
Write-Host "安装完成。请完全关闭 AutoCAD 后重新启动。" -ForegroundColor Green
Write-Host "启动后命令行输入:FEXPORTASSET(单资产)或 FEXPORT(整套兼容)" -ForegroundColor Green
Write-Host "启动后命令行输入:FEXPORT(单资产)或 FEXPORTPRODUCT(整套兼容)" -ForegroundColor Green
Write-Host ""

View File

@ -1,5 +1,5 @@
═══════════════════════════════════════════════════════════════════════
家具导出插件 v2.5.0 - 安装指南
家具导出插件 v2.6.0 - 安装指南
═══════════════════════════════════════════════════════════════════════
安装步骤(只需30秒)
@ -16,8 +16,10 @@
使用方法
───────────────────────────────────────────────────────────────────────
打开 AutoCAD 后,命令行输入:
FEXPORTASSET -> 单资产导出到资产中心(板材 / 组件 / 五金)
FEXPORT -> 整套家具兼容导出
FEXPORT -> 单资产导出到资产中心(板材 / 组件 / 五金)
FEXPORTASSET -> 单资产导出别名
FEXPORTPRODUCT -> 整套家具兼容导出
FEXPORTBUNDLE -> 整套家具兼容导出别名
FINFO -> 查看实体信息
安装脚本做了什么(便于 IT/排查)
@ -31,10 +33,10 @@
常见问题
───────────────────────────────────────────────────────────────────────
Q: 提示“未知命令 FEXPORTASSET / FEXPORT”
Q: 提示“未知命令 FEXPORT / FEXPORTPRODUCT”
A: 1) 确认已重启 AutoCAD(关闭再打开)
2) 确认插件目录存在:
%AppData%\Autodesk\ApplicationPlugins\FurnitureExporter.bundle\Contents\FurnitureExporter.dll
3) 如果 SECURELOAD=2,脚本会将其降到 1;如仍无效请截图安装日志给开发排查
版本: v2.5.0
版本: v2.6.0

View File

@ -212,3 +212,69 @@
| `src/app/warehouse/WarehouseClient.tsx` | 移除 headerTools/eyebrow |
| `src/app/HomeClient.workspace.tsx` | 移除 topTools 传参 |
| ~20 个业务文件 | 硬编码颜色替换 |
---
## 第三阶段:管理基础设施视觉升级 (2026-04-16)
### 10. 管理端专属视觉模式 (Management UI Patterns)
在重构 `/admin` 和 `/users` 页面时,引入了针对“高密度控制类页面”的增强视觉模式,以平衡“工业精密感”与“视觉通透度”。
**1. 沉浸式页头 (Immersive Page Header)**
- **视觉特征**:
- 采用 `bg-white/60` + `backdrop-blur-2xl`。
- 强化左侧 Icon 视觉,使用 `rounded-[2rem] bg-black` 黑色底座,并叠加状态徽章(如 `ShieldCheck`)。
- **排版建议**:Manrope 3XL 标题 + 紧跟其后的 `text-apple-blue` 高亮关键词。
- **副标题**:使用 `text-[10px] tracking-[0.4em] uppercase` 的精密说明文字。
**2. 精密控制条 (Precision Control Bar)**
- **定义**:将原本分散的“搜索框”与“筛选器”整合进一个逻辑整体。
- **视觉实现**:
- 容器:`rounded-[2rem]` 极圆圆角,`border-white/80` 微光边框,`bg-white/40` 背景。
- 搜索框:沉浸式设计,背景为 `bg-white/80`,聚焦时通过 `ring-apple-blue/5` 产生柔和晕染。
- 筛选器:图标 (Lucide Icon) + Select 组件的紧凑组合,图标作为装饰前缀。
**3. 玻璃态卡片增强 (Enhanced Glass Cards)**
- **改动**:在 `rounded-[2rem]` 的基础上,引入了“装饰线”动效。
- **动效逻辑**:`group-hover:w-full` 的底部 1.5px 强调线,颜色与模块 Icon 色调一致。
- **背景文字装饰**:绝对定位的 `text-[80px]` 背景大字(如模块 path),设置 `opacity-[0.03]` 极低透明度,增加“空间感”。
**4. 状态指标看板 (System Health Dashboard)**
- 应用场景:`/admin` 首页。
- 组成:Icon 容器 + 上下两行文本(上行为微缩标签,下行为状态值)。
- 配色:动态使用 `emerald-500` (正常), `blue-500` (活动), `amber-500` (注意)。
**5. 三栏工作区 (Three-Column Workspace)**
- 应用场景:`/asset-center/material-library`。
- 定义:左侧极简分类 (Navigation) + 中间响应式网格 (Grid) + 右侧实时定义面板 (Inspector)。
- 优势:极大降低页面跳转成本,实现“即时反馈”。
**6. 精密滑块交互 (Precision Sliders)**
- 视觉表现:细条轨道 + 圆形 Thumb + 右侧数值百分比徽章。
- 应用场景:物理参数调节 (Metallic/Roughness/Opacity)。
- 规范:取代原有的输入框,滑动步长固定为 0.01 (1%)。
**7. 悬浮坞系统 (Floating Dock System)**
- 应用场景:`/assisted-assembly`。
- 定义:左侧布局的交互式入口,采用 `bg-black/60` + `backdrop-blur-3xl`。
- 动效:点击后向右水平展开列表。
- 逻辑:通过视觉发光 (Glow) 和计数徽标反馈当前分类的选择状态。
**8. 环境 HUD (Ambient HUD)**
- 定义:底部的非侵入式信息条。
- 样式:极圆胶囊型,`backdrop-blur-xl`。
- 作用:提供当前操作环境的实时状态(如“捕捉已就绪”)。
### 11. 改动文件清单
| 文件 | 改动类型 | 备注 |
|------|----------|------|
| `src/app/admin/page.tsx` | 重构 | 系统总控制台首页视觉重载 |
| `src/app/users/page.tsx` | 重构 | 身份控制中心 (用户管理) 三栏重写 |
| `src/app/asset-center/material-library/MaterialLibraryClient.tsx` | 重构 | 材质库首页视觉重写 (草稿图 12.png) |
| `src/app/assisted-assembly/AssistedAssemblyClient.tsx` | 重构 | 拼装工作台沉浸式重写 & 引入 Floating Dock |
| `src/components/platform/PlatformSplitLayout.tsx` | 基础重构 | 统一加装 backHref 导航标准 |

View File

@ -1,28 +1,47 @@
# 前端 UI 改动清单
# 协作任务板
> 这份文档只保留"正在进行 / 未完成 / 待联调"的前端交接项。
> 已完成且已确认正确的内容,直接删除,不保留历史记录。
> 前端 UI agent 只按本文档执行;如果联调后确认是接口、数据、保存链路、状态口径问题,再回传后端 agent。
## 已完成任务 (Completed)
- **管理基础设施视觉升级 (Management Infrastructure UI Refactor)**
- **重构页面**:
- `系统总控制台 (/admin)`:引入“中枢枢纽”布局,增加系统状态看板 (Mock),优化卡片视觉张力。
- `身份控制中心 (/users)`:引入“沉浸式页头”与“精密控制条”,整合搜索与筛选器。
- **负责人**:Gemini UI Engineer
- **完成日期**:2026-04-16
- **基础资料库重构 (Material Library Pro Refactor)**
- **参考稿**:基于用户草稿 (12.png) 与 Precision Atelier 规范。
- **核心改动**:
- **三栏架构**:左侧分类 - 中间网格 - 右侧实时工作区。
- **实时预览 (Real-time Preview)**:面板顶部集成大面积材质物理表现预览。
- **精密滑块 (Precision Sliders)**:金属度、粗糙度、透明度全部由 Input 切换为 Slider 交互。
- **网格升级**:优化了花色卡片的视觉尺寸与 Metadata(元数据)展示。
- **负责人**:Gemini UI Engineer
- **完成日期**:2026-04-16
- **辅助装配工作台沉浸式重构 (Assisted Assembly Workbench Immersive Refactor)**
- **重构页面**:`辅助装配工作台 (/assisted-assembly)`。
- **核心改动**:
- **沉浸式 3D 画布**:移除所有边框,采用 `#0c0e10` 深邃背景,占据 100% 中心区域。
- **悬浮 Dock 系统 (Floating Dock)**:左侧零件选择改为三个磨砂玻璃质感的悬浮坞(板件/支撑/连接件),支持平滑展开与计数反馈。
- **实时装配报告 (Inspector)**:右侧面板重塑为技术报告风格,展示拓扑节点、已连接对数及结构稳固性指标。
- **环境提示条 (Ambient HUD)**:底部增加半透明环境信息条,提示“孔位捕捉已就绪”。
- **导航标准化**:加装“资产中心 / 拼装”精密返回按钮。
- **负责人**:Gemini UI Engineer
- **完成日期**:2026-04-17
## 处理归属说明
- `前端负责`:视觉、布局、交互、动效、前端展示层接线。
- `联调确认`:先按当前实现点流程;若发现根因在接口、数据、保存失败、状态不一致,再转后端。
- **重要边界**:
- 前端 `agent` 只负责前端层,不负责拍板业务真相,不负责改接口,不负责改数据库,不负责处理保存链路真相。
- 只要问题已经超出“展示层接线 / 复用现有前端能力”的边界,就停止继续扩写,并回传后端 `agent`。
---
## 当前未完成项
## 进行中任务 (In Progress)
- 当前无未完成前端交接项。
- 2026-03-26 已完成交互重构:
- 背景改为淡灰底色+40px网格线(匹配图2资产中心)
- 物料区交互:预览图列表 + hover显示Tooltip + 拖拽到3D区后弹出规格款选择
- 类型检查通过,测试通过
- 2026-04-10 已确认:
- `type-check(类型检查)` 通过
- 辅助装配相关单元测试通过
- 测试环境已完成部署
- 真实页面已完成最小联调:登录后进入 `辅助装配(assisted assembly)`,勾选资产族后左侧物料区可正常出现 `加入工作台`
- assisted-assembly(辅助装配):真实样本联调与验收
- 负责人:Codex
- 当前进展:
- 已补回孔位吸附的真实距离闸门,避免组件离目标过远时仍被误判为可吸附。
- 已移除缺少孔位 / 安装图样语义时回退到旧中心点假吸附的旧逻辑。
- 已将右侧提示收口为状态反馈卡,可区分“已进入吸附范围 / 缺少安装图样语义 / 缺少孔位语义”等状态。
- 已补齐辅助装配工作台与装配规则层回归测试,并通过 `jest(测试)` 与 `type-check(类型检查)`。
- 待完成:
- 用真实样本在浏览器里完成最终点验,确认拖拽、预吸附、松手吸附与右侧提示在真实模型下都一致。

View File

@ -75,7 +75,6 @@ dist
# Gatsby文件
.cache/
public
# Vuepress构建输出
.vuepress/dist
@ -225,4 +224,4 @@ PROJECT_SETUP_COMPLETE.md
产品安装展示系统-*.md
开发部署计划.md
项目*.md
架构*.md
架构*.md

View File

@ -0,0 +1,220 @@
# 本地运行
NODE_ENV=development
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
JST_REPORT_DB_NAME=
JST_REPORT_DB_SCHEMA=rpt
JST_REPORT_DB_USER=
JST_REPORT_DB_PASSWORD=
JST_REPORT_DB_ENCRYPT=true
JST_REPORT_DB_TRUST_SERVER_CERTIFICATE=true
# 换货规则表 app.jst_sku_replace_rule 与 rpt 视图使用独立授权;留空时复用上面的主机、库和账号。
JST_SKU_REPLACE_RULE_SCHEMA=app
JST_SKU_REPLACE_RULE_DB_USER=
JST_SKU_REPLACE_RULE_DB_PASSWORD=
# 停产下架链接日快照最大导入延迟(小时);超时保留已有链接。
JST_DISCONTINUATION_LINK_MAX_AGE_HOURS=26
# 数据库链接快照仅用于诊断;低于已有链接该比例时拒绝覆盖。
JST_DM_MIN_EXISTING_COVERAGE_RATIO=0.5
# 停产下架服务器 Chrome / RPA。
JST_RPA_BROWSER_URL=http://127.0.0.1:9222
JST_RPA_PROFILE_DIR=
JST_RPA_CHROME_EXECUTABLE=
JST_RPA_HEADLESS=true
JST_RPA_DEBUG_PORT=9222
JST_RPA_PYTHON=/usr/bin/python3
# 聚水潭账号密码登录,仅配置在服务器私有环境文件,禁止提交真实值。
JST_RPA_LOGIN_ID=
JST_RPA_LOGIN_PASSWORD=
JST_RPA_ATTEMPTS=3
JST_RPA_FALLBACK_BATCH_SIZE=10000
# 限价流程历史快照首次构建的追溯起点;后续只按更新时间增量同步。
LIMIT_PRICE_HISTORY_SOURCE_START=2026-01-01T00:00:00
# 产品限价页面已确认记录来源库(限价历史抽屉展示来源记录时使用)。
PROD_ARCHIVE_DB_HOST=
PROD_ARCHIVE_DB_PORT=5432
PROD_ARCHIVE_DB_NAME=
PROD_ARCHIVE_DB_USER=
PROD_ARCHIVE_DB_PASSWORD=
PROD_ARCHIVE_DB_SSL=true
# OA 新品流程看板数据源。默认 mysql;切换到 JSReportReplica 时设为 mssql。
OA_BOARD_SOURCE_DRIVER=mysql
# MySQL 来源兼容配置。
OA_BOARD_SOURCE_DB_HOST=
OA_BOARD_SOURCE_DB_PORT=3306
OA_BOARD_SOURCE_DB_NAME=
OA_BOARD_SOURCE_DB_USER=
OA_BOARD_SOURCE_DB_PASSWORD=
OA_BOARD_SOURCE_DB_CHARSET=utf8mb4
OA_BOARD_SOURCE_DB_SSL=false
# SQL Server 来源:JSReportReplica.rpt 的 ding_Workflow、ding_WorkflowScheme、ding_WorkflowTask 只读视图。
# 使用 mssql 来源时,快照仍写入下方配置的企划 MySQL 库。
OA_BOARD_MSSQL_HOST=
OA_BOARD_MSSQL_PORT=1433
OA_BOARD_MSSQL_DATABASE=JSReportReplica
OA_BOARD_MSSQL_SCHEMA=rpt
OA_BOARD_MSSQL_USERNAME=
OA_BOARD_MSSQL_PASSWORD=
OA_BOARD_MSSQL_ENCRYPT=false
OA_BOARD_MSSQL_TRUST_SERVER_CERTIFICATE=true
OA_BOARD_MSSQL_CONNECTION_TIMEOUT_MS=15000
OA_BOARD_MSSQL_REQUEST_TIMEOUT_MS=120000
# OA 看板快照目标(MySQL)。留空时兼容复用 PLANNING_DATABASE_URL / PLANNING_DB_*。
# 配置此组变量后,可保证切换 SQL Server 来源时所有快照、超时跟进和日报数据仍保存在企划库。
OA_BOARD_SNAPSHOT_DATABASE_URL=
OA_BOARD_SNAPSHOT_DB_HOST=
OA_BOARD_SNAPSHOT_DB_PORT=3306
OA_BOARD_SNAPSHOT_DB_NAME=
OA_BOARD_SNAPSHOT_DB_USER=
OA_BOARD_SNAPSHOT_DB_PASSWORD=
OA_BOARD_SNAPSHOT_DB_CHARSET=utf8mb4
OA_BOARD_SNAPSHOT_DB_SSL=false
# HR 员工目录(SQL Server)。OA 快照仅在本地员工目录无法匹配时使用,
# 用于将 ding_WorkflowTask.userId 解析成当前负责人的姓名。
HR_SQLSERVER_HOST=
HR_SQLSERVER_PORT=1433
HR_SQLSERVER_DATABASE=
HR_SQLSERVER_USERNAME=
HR_SQLSERVER_PASSWORD=
HR_SQLSERVER_ENCRYPT=false
HR_SQLSERVER_TRUST_SERVER_CERTIFICATE=true
# 产品资料供应商与仓库目录(SQL Server 报表库,只读)
JST_PLANNING_SQLSERVER_HOST=
JST_PLANNING_SQLSERVER_PORT=1433
JST_PLANNING_SQLSERVER_DATABASE=
JST_PLANNING_SQLSERVER_USERNAME=
JST_PLANNING_SQLSERVER_PASSWORD=
JST_PLANNING_SQLSERVER_SCHEMA=rpt
JST_PLANNING_SQLSERVER_ENCRYPT=false
JST_PLANNING_SQLSERVER_TRUST_SERVER_CERTIFICATE=true
# 登录和令牌
JWT_SECRET=replace-with-random-secret
JWT_EXPIRES_IN=7d
# 钉钉登录(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=
DINGTALK_TODO_SOURCE_APP=oa-board
DINGTALK_TODO_ESCALATION_ENABLED=false
DINGTALK_TODO_ESCALATION_DAYS=3
# DINGTALK_AGENT_ID=
# 清货池多包标记钉钉通知:填写在职员工目录中的姓名,默认推送卷帘大将。
SUPPLY_CHAIN_CLEARANCE_COMBO_NOTIFICATION_RECIPIENT=卷帘大将
# 由壹点通知中心(仅服务端使用,项目密钥禁止提交或暴露给浏览器)
YYD_NOTIFICATION_CENTER_URL=https://sc.jiansun.vip/notification-center
YYD_NOTIFICATION_PROJECT_CODE=furniture_platform
YYD_NOTIFICATION_PROJECT_SECRET=
YYD_NOTIFICATION_TIMEOUT_MS=10000
# 店铺账号新增时,梦汐/扬灵群使用的钉钉“由壹点”群机器人。
# webhook 地址和签名密钥只配置在本地/正式站运行环境,不要提交真实值。
DINGTALK_STORE_ACCOUNT_GROUP_WEBHOOK_URL=
DINGTALK_STORE_ACCOUNT_GROUP_WEBHOOK_SECRET=
DINGTALK_STORE_ACCOUNT_GROUP_WEBHOOK_TIMEOUT_MS=10000
STORE_ACCOUNT_NOTIFICATION_RETRY_SECRET=
# 产品开发待办定时任务(仅服务端/部署 cron 使用,须与其他任务密钥分开)
PRODUCT_DEVELOPMENT_TODO_CRON_SECRET=
# 对象存储
# 必填,不能留空。当前生产桶使用 qiniu;只有明确切换适配器时才改为 s3/minio。
STORAGE_PROVIDER=qiniu
OBJECT_STORAGE_FILE_PATH=dev
STORAGE_FILE_PATH=dev
# 七牛云 Kodo
QINIU_ACCESS_KEY=replace-with-qiniu-access-key
QINIU_SECRET_KEY=replace-with-qiniu-secret-key
QINIU_BUCKET=sucaiku-js
QINIU_PUBLIC_DOMAIN=https://qny.jiansun.vip
QINIU_FILE_PATH=dev
QINIU_USE_HTTPS=true
QINIU_PRIVATE_BUCKET=true
QINIU_PRIVATE_URL_EXPIRES_SECONDS=7200
# S3/MinIO 兼容适配器
# STORAGE_PROVIDER=s3
S3_ENDPOINT=http://127.0.0.1:9000
S3_PUBLIC_ENDPOINT=http://127.0.0.1:9000
S3_ACCESS_KEY=replace-with-s3-access-key
S3_SECRET_KEY=replace-with-s3-secret-key
S3_BUCKET=replace-with-s3-bucket
S3_FILE_PATH=dev
S3_REGION=us-east-1
S3_ENABLE_PATH_STYLE=true
# 外部 AI 生图
EXTERNAL_IMAGE_GENERATE_TIMEOUT_MS=180000
# BOM 核算竞品分析
# 留空表示分析服务尚未接入;本地开发可显式填 fixture 验证完整工作台。
# 生产环境禁止使用 fixture。
BOM_COSTING_ANALYSIS_PROVIDER=
# 真实竞品分析使用 market-detail-multimodal;Key 只放本地/生产环境变量。
BOM_COSTING_MARKET_API_BASE_URL=https://qh.yydd.ai
BOM_COSTING_MARKET_API_KEY=replace-with-market-open-api-key
# 复用 AI 服务配置面板中已激活的多模态模型连接。
BOM_COSTING_AI_SERVICE_TYPE=prompt-optimizer
BOM_COSTING_MARKET_TIMEOUT_MS=60000
# 最终结构与 BOM 汇总默认最多等待 110 秒;GPT-5 兼容网关会强制收紧到该上限,避免上游两分钟截断造成任务假死。
BOM_COSTING_AI_TIMEOUT_MS=110000
BOM_COSTING_AI_MAX_IMAGES=20
# GPT-5 兼容网关的最终 BOM 汇总最多使用 12000 个输出额度,避免冗长回复拖垮整项任务。
BOM_COSTING_AI_MAX_TOKENS=12000
BOM_COSTING_AI_REASONING_EFFORT=low
BOM_COSTING_MODELING_PROVIDER=
# BOM 核算实时同款采购预估(1688 官方图搜)。
# 网关固定为 https://skills-gateway.1688.com/api/find_product/1.0.0。
# AK 只放服务器私密环境变量,服务端会按 x-csk-* 协议签名;实时同款价固定 × 90%
# 是整件采购预估,历史 BOM 仅保留为结构参考。不要把 AK 写入 Git 或 NEXT_PUBLIC_* 变量。
ALIBABA_1688_IMAGE_SEARCH_AK=replace-with-1688-image-search-ak
# 仅在官方网关地址调整时设置;系统会拒绝非官方 HTTPS 地址。
ALIBABA_1688_IMAGE_SEARCH_ENDPOINT_URL=https://skills-gateway.1688.com/api/find_product/1.0.0
ALIBABA_1688_IMAGE_SEARCH_TIMEOUT_MS=45000
ALIBABA_1688_IMAGE_SEARCH_MIN_SIMILARITY=0.78
ALIBABA_1688_IMAGE_SEARCH_PAGE_SIZE=10
ALIBABA_1688_IMAGE_SEARCH_PURCHASE_AMOUNT=1
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=
IMAGE_SEARCH_MODEL_SOURCE_BASE_URL=https://www.modelscope.cn/models/Xenova/dinov2-small/resolve/master
IMAGE_SEARCH_MIN_SHAPE_COSINE=0.78
IMAGE_SEARCH_MIN_SHAPE_MARGIN=0.08

1
furniture-3d-system/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.sh text eol=lf

View File

@ -2,6 +2,7 @@
# dependencies
/node_modules
/node_modules.pnpm-mixed
/.pnp
.pnp.*
.yarn/*
@ -15,7 +16,11 @@
# next.js
/.next/
/.next-*/
/.next-oa-preview*/
/.next-oa-webpack/
/out/
/.data/
# production
/build
@ -32,6 +37,7 @@ yarn-error.log*
# env files (can opt-in for committing if needed)
.env*
!.env.example
# vercel
.vercel
@ -41,8 +47,14 @@ yarn-error.log*
next-env.d.ts
/src/generated/prisma
.database-backups/
.master-data-audits/
.storage-migration-audits/
.storage-gc-audits/
.master-data-cleanup/
# local workspace artifacts
.codex-runtime/
.codex-logs/
_logs/
output/
@ -58,4 +70,12 @@ nul
dev-server.log
trace-deprecation-build.log
typecheck-latest.log
.local-test-*.log
tsconfig.typecheck.generated.json
local-packages/npm-cache/cache/_logs/
# 脚本监控 Agent 本机凭据
agents/script-monitor/config.json
# 下载中心本地运行时产物
.download-center/

View File

@ -1,92 +0,0 @@
# 家具 3D 新平台
## What This Is
这是一个面向家具开品与入库流程的 3D 平台,目标是把“资产中心、辅助装配(手动拼装)、草稿组、包装、正式入库、轻仓库”串成一条可持续维护的新平台主链。
项目同时保留旧的 `CAD -> 板件/孔位/五金/装配步骤` 兼容链,用来承接历史产品和旧编辑能力,但它不再作为新平台的业务主入口。
## Core Value
让新平台主链能够稳定地从资产与组合规则出发,生成可入库、可追踪、可协作的正式产品数据,而不是继续依赖旧 CAD 链条拼装业务真相。
## Scope Baseline
### Established Baseline
- 资产中心、辅助装配(手动拼装)、草稿组、入库、轻仓库已经具备基础页面、接口和服务骨架。
- 基础资料库(基材 / 花色)已并入主线,并支持基材多花色关联、自动编码和过渡期缩略图上传。
- 旧 CAD 兼容链仍可维护板件、孔位、五金、装配步骤等历史能力。
- 项目已经建立服务层、仓储层、Prisma 模型和 App Router 的基本架构。
### Out of Scope
- 把旧 `Product / SKU` 页面重新做成新平台首页。
- 让包装流程绕过草稿组直接生成正式产品。
- 在当前阶段里大改前端视觉实现。
## Operating Context
- 当前仓库的推荐主链是:`资产中心 -> 辅助装配(手动拼装) -> 草稿组 -> 包装方案 -> 正式入库 -> 轻仓库`
- 包装和包装方案由同事 `xuxiake` 负责,长期分支固定为 `codex/xuxiake-packaging`
- 主线由 `WuYongJie` 收口,`main` 只作为主线收口分支
- 当前已存在两套测试环境:
- 同事包装测试环境:`192.168.42.134:3100`
- `WuYongJie` 主线独立测试环境:`192.168.42.134:3200`
- 现有架构是 Next.js App Router 单体仓库,后端采用 `Route -> Service -> Repository -> Prisma`
- 当前已经完成对 `draft-group.service.ts`、`assembly.service.ts`、`planning.service.ts`、`panel.service.ts` 的主线式拆分收口,并已把包装支线中的资料库与基材规则并回主线
- 前端 UI 不由当前工作流直接实现,如需前端配合,只记录到 `D:\AI Code\3danzhuang\docs\前端UI改动清单.md`
## Constraints
- **协作**: `main` 仅由 `WuYongJie` 收口,其他协作者走各自职责分支。
- **模块边界**: 包装负责人默认不直接改草稿组/入库共享接缝。
- **技术栈**: 现有项目基于 Next.js 16、React 19、Prisma 7、PostgreSQL、S3/Redis 可选缓存。
- **交付方式**: 默认不主动 commit / push。
- **前端职责**: UI 改动交给独立前端 agent,当前主线工作流聚焦后端与项目结构。
## Requirements
- 当前业务真相以 `资产中心 -> 辅助装配(手动拼装) -> 草稿组 -> 包装 -> 正式入库 -> 轻仓库` 为准。
- `自动组合` 属于历史阶段主线产物,不再作为当前用户主操作入口;旧文档里的相关描述按历史背景理解。
- 基础资料库当前已纳入主线:`substrate(基材)` 支持多 `finish(花色)` 关联、`surfaceOptionId(贴面类型)` 可空、自动 `BC-XXXX(基材编码)` 与 `HS-XXXX(花色编码)`。
- `weightKg(整板重量)` 当前指整板重量,不等于切割后单块板件重量;实际板件重量后续继续按切割规则或材质系统换算。
- `5 字段唯一约束` 的最终业务规则仍是外部专题,当前主线已先完成包装支线并线与资料库落地,但该规则后续仍需同事单独拍板。
## Project Memory Model
- `STATE.md`:唯一快速恢复入口,只放当前阶段、下一步、风险和恢复路径。
- `ROADMAP.md`:放阶段目标、依赖、成功标准和计划清单。
- `REQUIREMENTS.md`:放需求本体、阶段映射和计划覆盖关系。
- `PROJECT.md`:只保留长期项目定义、长期约束和关键决策,不复制运行态状态。
## Key Decisions
| Decision | Rationale | Outcome |
|----------|-----------|---------|
| 新平台主链和旧 CAD 兼容链明确分开维护 | 避免旧数据模型反向主导新平台业务 | Good |
| 包装协作固定使用 `codex/xuxiake-packaging` 及其任务分支 | 降低团队协作中的分支歧义和并线混乱 | Good |
| 主线优先做服务层职责拆分,而不是继续叠加补丁 | 当前主要风险是结构失控,不是单点功能缺失 | Good |
| GSD 作为项目外置记忆和阶段工作流补充层接入 | 便于跨会话、跨 AI 延续项目上下文 | Good |
| 真实业务测试必须使用与同事分离的独立测试环境 | 避免包装测试数据和主线真实业务测试互相污染 | Good |
| 当前主链入口已从早期 `自动组合` 迁移为 `辅助装配(手动拼装)` | 避免 GSD 项目记忆继续把旧入口当成现行真相 | Good |
| 包装支线资料库规则允许先以过渡能力落主线 | 先收业务真相与资料库主链,再把完整材质系统作为后续专题推进 | Good |
## Evolution
This document evolves at phase transitions and milestone boundaries.
**After each phase transition**:
1. Requirements invalidated? -> Move to Out of Scope with reason
2. Requirements validated? -> Move to Validated with phase reference
3. New requirements emerged? -> Add to roadmap or requirements, not to running state
4. Decisions to log? -> Add to Key Decisions
5. "What This Is" still accurate? -> Update if drifted
**After each milestone**:
1. Full review of all sections
2. Core Value check -> still the right priority?
3. Audit Out of Scope -> reasons still valid?
4. Confirm Project Memory Model still has a single quick recovery入口
---
*Last updated: 2026-04-11 after packaging branch material-library merge and GSD memory sync*

View File

@ -1,87 +0,0 @@
# Requirements: 家具 3D 新平台
**Defined:** 2026-04-02
**Core Value:** 让新平台主链能够稳定地从资产与组合规则出发,生成可入库、可追踪、可协作的正式产品数据
## v1 Requirements
> 当前业务入口已演进为 `辅助装配(手动拼装)` 主链。下面保留阶段编号不变,但凡旧文档里出现 `自动组合`,默认按“历史阶段方案”理解,不再视为当前唯一入口。
### Mainline Platform
- [ ] **MAIN-01**: 平台负责人可以在资产中心维护资产族与资产规格,并供新平台后续流程直接使用
- [ ] **MAIN-02**: 平台负责人可以基于资产中心与系列模板完成辅助装配(手动拼装),并进入草稿组流程,而不是直接落到正式产品
- [ ] **MAIN-03**: 系统可以在草稿组中保存每个预 SKU 的装配结果、显示摘要和后续接缝状态
- [ ] **MAIN-04**: 预 SKU 可以保存包装方案结果,并把包装结果稳定传递给正式入库流程
- [ ] **MAIN-05**: 正式入库流程可以把草稿组/预 SKU 结果转为正式 `Product / SKU`
- [ ] **MAIN-06**: 轻仓库可以查看正式产品与 SKU 的基础结果,形成入库后的轻量管理闭环
### Collaboration Workflow
- [ ] **COLL-01**: 主线负责人和包装负责人拥有固定、明确、不歧义的分支命名规则
- [ ] **COLL-02**: 协作入口文档足够清晰,新的 AI 会话进入项目后能先读到正确规则
- [ ] **COLL-03**: 包装线默认不直接修改主线共享接缝文件,必须通过约定流程对齐后再改
- [ ] **COLL-04**: 主线收口人可以在同事提交分支后完成检查、收口并决定 merge / cherry-pick / 手工整合
### Mainline Maintainability
- [ ] **ARCH-01**: 新平台主链的核心服务需要逐步拆分为更清晰的编排层、校验层、映射层和规则层
- [ ] **ARCH-02**: 旧 CAD 兼容链需要继续保留,但不能继续侵入新平台主链的数据真相
- [ ] **ARCH-03**: 新增主线后端能力时,需要延续统一的 `Route -> Service -> Repository -> Prisma` 结构
- [ ] **ARCH-04**: GSD 需要能基于 `.planning` 文件在跨对话场景下恢复项目上下文
## Phase 1 Coverage
| Requirement | Phase | Plan Coverage | Notes |
|-------------|-------|---------------|-------|
| COLL-01 | Phase 1 | 01-01-PLAN.md | 固化分支命名、主线收口和同步口径 |
| COLL-02 | Phase 1 | 01-01-PLAN.md, 01-02-PLAN.md | 一半来自协作文档入口,一半来自 `.planning` 恢复链 |
| ARCH-04 | Phase 1 | 01-02-PLAN.md | `STATE -> ROADMAP / REQUIREMENTS / PROJECT` 恢复链已在 `.planning` 内闭环 |
## v2 Requirements
### Packaging Expansion
- **PACK-01**: 包装方案结果支持更完整的成本、体积、材料、工艺扩展
- **PACK-02**: 新平台包装结果和正式产品包装链进一步对齐,减少重复维护
### Governance
- **GOV-01**: 为主要阶段建立更细的 UAT 和交付检查闭环
- **GOV-02**: 引入更完整的阶段化 roadmap 与 milestone 管理
## Out of Scope
| Feature | Reason |
|---------|--------|
| 大规模重做前端视觉设计 | 当前前端 UI 由独立 agent 负责,不是当前主工作流范围 |
| 把旧 CAD 装配链改造成新平台主链 | 会继续混淆新旧业务边界 |
| 绕过草稿组直接做包装或入库 | 会破坏新平台主线的数据收口 |
## Traceability
| Requirement | Phase | Status |
|-------------|-------|--------|
| MAIN-01 | Phase 2 | Completed by 02-01 |
| MAIN-02 | Phase 2 | Completed by 02-01, 02-02; current mainline has since evolved from historical auto-combination wording to assisted assembly/manual assembly |
| MAIN-03 | Phase 2 | Completed by 02-02 |
| MAIN-04 | Phase 3 | Completed by 03-01 |
| MAIN-05 | Phase 4 | Completed by 04-01 |
| MAIN-06 | Phase 4 | Completed by 04-02 |
| COLL-01 | Phase 1 | Completed by 01-01 |
| COLL-02 | Phase 1 | Completed by 01-01, 01-02 |
| COLL-03 | Phase 3 | Completed by 03-02 |
| COLL-04 | Phase 4 | Completed by 04-03 |
| ARCH-01 | Phase 5 | Pending |
| ARCH-02 | Phase 5 | Pending |
| ARCH-03 | Phase 5 | Pending |
| ARCH-04 | Phase 1 | Completed by 01-02 |
**Coverage:**
- v1 requirements: 14 total
- Mapped to phases: 14
- Unmapped: 0
---
*Requirements defined: 2026-04-02*
*Last updated: 2026-04-11 after mainline shift to assisted assembly/manual assembly*

View File

@ -1,115 +0,0 @@
# Roadmap: 家具 3D 新平台
## Overview
这是一个 brownfield 路线图,不把已存在的资产中心、辅助装配(手动拼装)、草稿组、入库、轻仓库和旧 CAD 兼容链重新当成“从零建设”。本轮路线图只围绕三件事推进:把新平台主链继续收口成稳定闭环,把主线负责人和包装负责人之间的协作规则固定下来,并用 GSD 的 `.planning` 记忆把阶段、风险和后续执行路径沉淀成可恢复的项目上下文。
## Phases
**Phase Numbering:**
- Integer phases (1, 2, 3): Planned milestone work
- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED)
Decimal phases appear between their surrounding integers in numeric order.
- [x] **Phase 1: 协作入口与项目记忆稳定化** - 先把分支规则、协作文档入口和 GSD 项目记忆固定下来。
- [x] **Phase 2: 资产到草稿组主链收口** - 让资产中心、装配入口、草稿组继续按新平台主链稳定衔接。
- [x] **Phase 3: 包装接缝稳定化** - 让预 SKU 的包装结果可保存、可交接,同时守住共享接缝边界。
- [x] **Phase 4: 正式入库与轻仓库闭环** - 把草稿组结果稳定转成正式产品,并形成入库后的轻量管理闭环。
- [x] **Phase 5: 主链可维护性收口** - 把核心服务拆分和新旧链边界继续收紧,避免主线重新膨胀。
## Phase Details
### Phase 1: 协作入口与项目记忆稳定化
**Goal**: 新会话和协作者进入项目时,能按同一套规则接入主链,不再靠口头传递恢复上下文
**Depends on**: Nothing (first phase)
**Requirements**: COLL-01, COLL-02, ARCH-04
**Success Criteria** (what must be TRUE):
1. 协作者可以按固定、明确的分支命名规则开始工作,不再出现职责不清的泛分支名。
2. 新的 AI 会话进入仓库后,只读 `.planning` 和记忆入口文档,就能知道主链、包装线和禁止直改区。
3. 主线当前阶段、下一步和已知风险可以从 `.planning/STATE.md` 直接恢复,不依赖上一轮聊天记录。
**Status**: Complete
**Plans**: 2 plans
Plans:
- [x] 01-01-PLAN.md — 固化仓库级协作入口、分支命名和同步口径
- [x] 01-02-PLAN.md — 固化 `.planning` 项目记忆恢复层与需求追踪闭环
**Execution note**: `01-01` 与 `01-02` 已全部完成;Phase 1 的协作入口、分支规则和 `.planning` 恢复链已经收口完成,下一步转入 Phase 2。
### Phase 2: 资产到草稿组主链收口
**Goal**: 平台负责人可以从资产与组合规则稳定走到草稿组结果,且这段主链继续沿用统一后端分层
**Depends on**: Phase 1
**Requirements**: MAIN-01, MAIN-02, MAIN-03
**Success Criteria** (what must be TRUE):
1. 平台负责人可以在资产中心维护资产族与资产规格,并直接供新平台后续流程使用。
2. 平台负责人可以基于资产、模板和装配规则稳定进入草稿组,而不是直接落到正式产品。
3. 草稿组可以保存每个预 SKU 的候选结果、显示摘要,并看见后续接缝状态。
**Status**: Complete
**Plans**: 2 plans
Plans:
- [x] 02-01-PLAN.md — 把历史自动组合候选收口成可确认、可预览的阶段基线
- [x] 02-02-PLAN.md — 把候选到草稿组的保存口径与前端交接要求统一收口
**UI hint**: yes
**Execution note**: `02-01`、`02-02` 已完成历史阶段基线收口;当前主线后来已从早期 `自动组合` 演进为 `辅助装配(手动拼装)` 入口,因此 Phase 2 相关文件按历史阶段理解,不再直接代表今天的页面入口。
### Phase 3: 包装接缝稳定化
**Goal**: 预 SKU 的包装结果可以稳定保存并交给后续流程,同时包装线和主线的共享接缝保持可控
**Depends on**: Phase 2
**Requirements**: MAIN-04, COLL-03
**Success Criteria** (what must be TRUE):
1. 预 SKU 可以保存包装方案结果,并在后续流程中被稳定读取。
2. 草稿组到包装的共享接缝有清晰边界,包装线默认不会直接修改主线共享文件。
3. 当包装任务确实必须碰共享接缝时,主线负责人可以根据约定流程先对齐原因和影响范围再放行。
**Status**: Complete
**Plans**: 2 plans
Plans:
- [x] 03-01-PLAN.md — 锁定包装结果保存、试算与正式入库承接只认一套新结构
- [x] 03-02-PLAN.md — 固定共享接缝协作规则、包装占位入口口径与前端联调边界
**UI hint**: yes
**Execution note**: `03-01` 已用测试守住包装结果保存、试算与正式入库承接;`03-02` 已固定共享接缝、占位入口和联调边界。下一步进入 Phase 4 的正式入库与轻仓库闭环。
### Phase 4: 正式入库与轻仓库闭环
**Goal**: 主线负责人可以把草稿组结果转成正式产品,并在入库后用轻仓库完成基础验收和收口
**Depends on**: Phase 3
**Requirements**: MAIN-05, MAIN-06, COLL-04
**Success Criteria** (what must be TRUE):
1. 正式入库流程可以把草稿组或预 SKU 结果转成正式 `Product / SKU`。
2. 轻仓库可以查看正式产品与 SKU 的基础结果,形成入库后的轻量管理闭环。
3. 主线收口人收到协作者分支后,可以按约定完成检查,并决定 `merge`、`cherry-pick` 或手工整合。
**Plans**: 3 plans
Plans:
- [x] 04-01-PLAN.md — 把草稿组/准 SKU 到正式 Product/SKU 的入库主链收口成稳定能力
- [x] 04-02-PLAN.md — 把轻仓库收口成正式入库后的基础验收与查看闭环
- [x] 04-03-PLAN.md — 固定主线收口检查单、merge/cherry-pick/手工整合决策与交接模板
**UI hint**: yes
**Execution note**: `04-01` 已补正式入库关键失败场景与 schema 契约;`04-02` 已补轻仓库 overview 与包装成品关联 SKU 编码;`04-03` 已固定主线收口检查单、决策规则与交接模板。Phase 4 已完成,下一步可进入 Phase 5。
### Phase 5: 主链可维护性收口
**Goal**: 新平台主链在继续演进时不再靠堆补丁前进,旧 CAD 兼容链也不会重新侵入新平台业务真相
**Depends on**: Phase 4
**Requirements**: ARCH-01, ARCH-02, ARCH-03
**Success Criteria** (what must be TRUE):
1. 新平台主链的核心服务按编排、校验、映射、规则等职责继续拆分,新需求不会再堆进单个膨胀服务。
2. 旧 CAD 兼容链继续可用,但不会反向成为新平台资产、草稿组和入库链路的数据真相。
3. 新增主线后端能力时,代码位置和职责保持可预测,继续遵守 `Route -> Service -> Repository -> Prisma` 结构。
**Plans**: 3 plans
Plans:
- [x] 05-01-PLAN.md — 继续压缩已拆但未拆透的热点 service,建立更稳定的职责边界
- [ ] 05-02-PLAN.md — 继续收紧新旧链边界与主链分层,压住结构残留风险
- [x] 05-03-PLAN.md — 收包装支线资料库并线,并把 GSD 项目记忆校正到当前真实主线
**Execution note**: `05-01`、`05-02`、`05-03` 已全部完成;当前主线的服务分层、资料库迁移、GSD 项目记忆已经回到同一套真实口径。`5 字段唯一约束` 仍是外部专题,后续待包装同事单独定稿后再按新阶段或插入计划处理。
## Progress
**Execution Order:**
Phases execute in numeric order: 1 -> 2 -> 2.1 -> 3 -> 3.1 -> 4 -> 5
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 1. 协作入口与项目记忆稳定化 | 2/2 | Complete | 01-01, 01-02 |
| 2. 资产到草稿组主链收口 | 2/2 | Complete | 02-01, 02-02 |
| 3. 包装接缝稳定化 | 2/2 | Complete | 03-01, 03-02 |
| 4. 正式入库与轻仓库闭环 | 3/3 | Complete | 04-01, 04-02, 04-03 |
| 5. 主链可维护性收口 | 3/3 | Complete | 05-01, 05-02, 05-03 |
---
*Last updated: 2026-04-11 after 05-02 structure closure and full Phase 5 completion*

View File

@ -1,49 +0,0 @@
# Project State
## Quick Recovery
**Current focus:** Roadmap complete - 等待新任务或外部专题(5 字段唯一约束)回流
**Current phase:** Phase 5 of 5
**Latest completed plan:** 05-02-PLAN.md - intake 分层残留、新旧链边界文档与主仓库 Prisma 模型已重新对齐,Phase 5 已完成
**Phase status:** Phase 5 complete - `05-01`、`05-02`、`05-03` 全部完成
**Last activity:** 2026-04-11 - 已补齐主仓库 `schema.prisma(数据库模型)` 与缺失迁移,重新跑通类型检查和关键测试,并把 GSD 记忆收口到当前真实主线
## Next Step
1. 如果包装同事回传 `5 字段唯一约束` 最终方案,先新开插入计划或新阶段,不要直接口头改主线。
2. 若继续联调资料库,优先核对:多花色关联、自动编码、临时新建分类/花色、整板重量口径。
3. 新会话先读本文件,再按需下钻 `ROADMAP.md`、`REQUIREMENTS.md`、`PROJECT.md`、`phases/05-主链可维护性收口/05-CONTEXT.md`、`phases/05-主链可维护性收口/05-02-SUMMARY.md`、`phases/05-主链可维护性收口/05-03-SUMMARY.md`。
## Risks
- 正式入库与轻仓库现在会直接承接 `PreSku` 与包装结果,Phase 4 要避免把这条链重新拉回旧兼容模型。
- `COLL-04` 已落成第一轮规则,但包装支线后续如果继续回传资料库专题,仍要继续按协作边界执行。
- `assembly / panel / planning / intake / warehouse` 等主链服务如果后续再次自然膨胀,会直接破坏 Phase 5 已收好的结构。
- 当前 `.planning` 已开始回到真实主线,但早期 Phase 2 历史文档仍保留 `自动组合` 话术;阅读时必须按“历史阶段记录”理解,不能再把它当当前入口。
- 基础资料库里的 `5 字段唯一约束` 最终规则仍未拍板;当前主线只是先完成并线与可运行落地,不等于最终业务真相已定稿。
- `192.168.42.134:3200` 虽然已独立隔离,但数据库初始数据来自同事测试库快照,不是完全空白环境。
## Recovery Path
- `STATE.md`:先看当前阶段、下一步、风险。
- `ROADMAP.md`:确认 Phase 5 的目标和成功标准。
- `REQUIREMENTS.md`:确认主线当前聚焦 `ARCH-01`、`ARCH-02`、`ARCH-03`。
- `PROJECT.md`:查看长期项目定义、约束和关键决策。
- `phases/05-主链可维护性收口/05-CONTEXT.md`:恢复 Phase 5 已锁定的结构目标、边界与关键热点。
- `phases/05-主链可维护性收口/05-02-SUMMARY.md`:恢复主仓库 schema / intake 分层 / 文档边界收口的最新结果。
- `phases/05-主链可维护性收口/05-03-SUMMARY.md`:恢复包装支线资料库并线、测试环境部署和 GSD 项目记忆校正的最新结果。
- `docs/会话交接-20260411-包装支线并线与资料库收口.md`:查看这轮包装支线并线到底收了什么、留了什么。
- `phases/04-正式入库与轻仓库闭环/04-01-SUMMARY.md`:快速恢复正式入库已收口到什么程度。
- `phases/04-正式入库与轻仓库闭环/04-02-SUMMARY.md`:快速恢复轻仓库已收口到什么程度。
- `phases/04-正式入库与轻仓库闭环/04-03-SUMMARY.md`:快速恢复协作收口规则已固定到什么程度。
## Collaboration Guardrails
- `main` 只由 `WuYongJie` 收口。
- `codex/xuxiake-packaging` 是包装长期分支;临时任务分支固定为 `codex/xuxiake-packaging-<任务简称>`。
- 长期协作分支同步 `main` 用 `merge main`;临时个人任务分支同步 `main` 才用 `rebase main`。
- 同事测试环境:`192.168.42.134:3100`
- `WuYongJie` 独立测试环境:`192.168.42.134:3200`
---
*Last updated: 2026-04-11 after 05-02 closure and Phase 5 completion*

View File

@ -1,137 +0,0 @@
# Architecture
**Analysis Date:** 2026-04-02
## Pattern Overview
**Overall:** Next.js App Router monolith with domain-oriented server modules.
**Key Characteristics:**
- Use `src/app` for both SSR pages and API routes; page loaders often call services directly, while browser clients call `/api/*`.
- Keep backend layers split as `Route -> Service -> Repository -> Prisma`, with shared error/response wrappers in `src/server/core`.
- Separate new platform mainline rules from legacy CAD compatibility rules through dedicated domain libraries in `src/lib/open-product` and `src/lib/planning`.
## Layers
**Application Entry Layer:**
- Purpose: render pages, guard navigation, and host API routes.
- Location: `src/app`, `src/app/api`, `src/app/layout.tsx`, `src/proxy.ts`
- Contains: App Router pages such as `src/app/page.tsx`, platform pages like `src/app/asset-center/page.tsx`, legacy pages like `src/app/products/[id]/assembly/page.tsx`, and route handlers like `src/app/api/draft-groups/route.ts`.
- Depends on: `src/lib/server-session.ts`, `src/server/services`, `src/server/core/response.ts`, Zod schemas in `src/app/api/platform/schemas.ts`.
- Used by: browser requests and Next.js runtime.
**UI Composition Layer:**
- Purpose: hold reusable page shells and feature clients.
- Location: `src/components`, especially `src/components/platform`, `src/components/assembly`, `src/components/3d`, plus feature clients under `src/app/*/*Client.tsx`.
- Contains: shared frame components like `src/components/platform/PlatformPageFrame.tsx`, navigation in `src/components/platform/PlatformFlowNav.tsx`, 3D/editor clients in `src/app/products/[id]/assembly/AssemblyEditorWorkspace.tsx`.
- Depends on: typed helpers from `src/lib/*`, API helper wrappers such as `src/lib/open-product/platform-api.ts`, and server-returned DTOs.
- Used by: page files in `src/app`.
**Domain Service Layer:**
- Purpose: enforce permissions, workflow rules, state transitions, DTO mapping, and orchestration across repositories.
- Location: `src/server/services`
- Contains: domain services for mainline (`src/server/services/asset-center.service.ts`, `src/server/services/combination.service.ts`, `src/server/services/draft-group.service.ts`, `src/server/services/intake.service.ts`, `src/server/services/warehouse.service.ts`), planning (`src/server/services/planning.service.ts`), and legacy compatibility (`src/server/services/product.service.ts`, `src/server/services/assembly.service.ts`, `src/server/services/panel.service.ts`, `src/server/services/hole.service.ts`).
- Depends on: repositories from `src/server/repositories`, core errors in `src/server/core/errors.ts`, cache in `src/server/cache`, and pure rules from `src/lib/open-product` / `src/lib/planning`.
- Used by: App Router pages and route handlers.
**Persistence Layer:**
- Purpose: isolate Prisma queries, relation loading, batching, and transactions.
- Location: `src/server/repositories`
- Contains: generic base in `src/server/repositories/base.repository.ts`, domain repositories like `src/server/repositories/asset-center.repository.ts`, `src/server/repositories/draft-group.repository.ts`, `src/server/repositories/product.repository.ts`, `src/server/repositories/assembly.repository.ts`.
- Depends on: Prisma singleton in `src/lib/prisma.ts` and Prisma schema in `prisma/schema.prisma`.
- Used by: service classes only; route handlers do not query Prisma directly in the mainline path.
**Rule / Contract Layer:**
- Purpose: keep deterministic business rules and parsing separate from transport and persistence.
- Location: `src/lib/open-product`, `src/lib/planning`, plus selected legacy helpers like `src/lib/assembly-generator.ts`
- Contains: open-platform asset validation and review rules in `src/lib/open-product/asset-validation.ts` and `src/lib/open-product/asset-review.ts`, candidate generation in `src/lib/open-product/single-board-desk.ts`, planning parsers/generator in `src/lib/planning/parsers.ts` and `src/lib/planning/candidate-generator.ts`.
- Depends on: TypeScript types and lightweight utility code only.
- Used by: services, selected clients, and tests.
## Data Flow
**New Platform Mainline Flow:**
1. Server pages such as `src/app/asset-center/page.tsx`, `src/app/auto-combinations/page.tsx`, `src/app/draft-groups/page.tsx`, and `src/app/intake/page.tsx` load authenticated data through `src/lib/server-session.ts` and call services from `src/server/services`.
2. Browser actions submit to API routes like `src/app/api/asset-center/families/route.ts`, `src/app/api/auto-combinations/generate/route.ts`, `src/app/api/draft-groups/route.ts`, `src/app/api/pre-skus/[id]/packaging/route.ts`, and `src/app/api/intake/formalize/route.ts`.
3. Route handlers validate input with schemas in `src/app/api/platform/schemas.ts`, then services orchestrate repositories and pure rule modules, finally persisting workflow state in Prisma models such as `AssetFamily`, `AssetVariant`, `DraftGroup`, `PreSku`, `PreSkuPackagingPlan`, `Product`, and `ProductSKU` in `prisma/schema.prisma`.
**Legacy CAD Compatibility Flow:**
1. Legacy pages under `src/app/products/*` and `src/app/products/[id]/assembly/*` fetch product-centric data from Prisma or services, for example `src/app/products/[id]/assembly/page.tsx`.
2. Editors and detail clients call APIs such as `src/app/api/products/[id]/assembly/route.ts`, `src/app/api/products/[id]/assembly/generate/route.ts`, `src/app/api/products/[id]/panels/route.ts`, `src/app/api/products/[id]/holes/route.ts`, and `src/app/api/products/[id]/import-hardware/route.ts`.
3. Legacy services use repositories plus rule helpers like `src/lib/assembly-generator.ts`, `src/lib/panel-assembly-semantic.ts`, `src/lib/sku-cad-data.ts`, and `src/lib/3d/engine.ts` to maintain `Product`, `ProductSKU`, `Panel`, `Hole`, `ProductHardware`, and `AssemblyStep` data.
**Planning Workspace Flow:**
1. Planning APIs in `src/app/api/planning/templates/*` and `src/app/api/planning/workspaces/*` accept template and workspace definitions.
2. `src/server/services/planning.service.ts` validates structure with `src/lib/planning/parsers.ts`, scopes visibility through `src/server/services/planning-scope.service.ts`, and generates candidates through `src/server/services/planning-generation.service.ts`.
3. Generated candidates and saved drafts are stored in `PlanningTemplate`, `PlanningWorkspace`, and `PlanningDraft` models in `prisma/schema.prisma`.
**State Management:**
- Use server-first data loading for page entry (`src/app/page.tsx`, `src/app/asset-center/page.tsx`, `src/app/intake/page.tsx`).
- Use local React state inside feature clients and shared Zustand stores in `src/stores` for UI/session state such as `src/stores/ui.store.ts`, `src/stores/auth.store.ts`, and `src/stores/product.store.ts`.
- Use cache abstractions in `src/server/cache/cache.service.ts` for selective backend caching, especially product list and assembly results.
## Key Abstractions
**Mainline Domain Entities:**
- Purpose: represent the new platform business truth before formal product creation.
- Examples: `prisma/schema.prisma`, `src/lib/open-product/types.ts`, `src/server/services/draft-group.service.ts`, `src/server/services/intake.service.ts`
- Pattern: asset family and variant data stay independent from formal `Product / ProductSKU` until intake.
**Service Facade Index:**
- Purpose: provide stable import entrypoints for pages and routes.
- Examples: `src/server/services/index.ts`, `src/server/repositories/index.ts`
- Pattern: explicit named exports instead of wildcard barrels to reduce conflicts while still centralizing access.
**Scoped Assembly Model:**
- Purpose: let legacy assembly logic work at product scope and optional SKU scope.
- Examples: `src/server/services/assembly.service.ts`, `src/server/services/assembly-scope.service.ts`, `src/server/repositories/assembly.repository.ts`, `prisma/schema.prisma`
- Pattern: normalize optional `skuId`, then branch cache keys, queries, and versioning rules by scope.
**Platform Rule Libraries:**
- Purpose: keep combinatorics and validation deterministic and testable.
- Examples: `src/lib/open-product/single-board-desk.ts`, `src/lib/open-product/asset-validation.ts`, `src/lib/planning/candidate-generator.ts`
- Pattern: pure function modules return candidates, warnings, and contracts; services own repository lookups and permission checks.
## Entry Points
**Root Application Shell:**
- Location: `src/app/layout.tsx`
- Triggers: every App Router request
- Responsibilities: register global CSS, initialize theme script, wrap the app with `PreferencesProvider`, and set shared metadata.
**Authentication Gate:**
- Location: `src/proxy.ts` and `src/lib/server-session.ts`
- Triggers: page navigation and server-side page loaders
- Responsibilities: redirect unauthenticated users, validate auth cookie, and provide normalized `AuthUser` context.
**Platform Home:**
- Location: `src/app/page.tsx`
- Triggers: visit `/`
- Responsibilities: combine legacy product list from `productService` with current draft groups from `draftGroupService`, then hand off to `src/app/HomeClient.tsx`.
**API Surface:**
- Location: `src/app/api/**/route.ts`
- Triggers: client-side fetch and external HTTP requests
- Responsibilities: parse request data, call `handleRequest` from `src/server/core/response.ts`, enforce auth, and translate `AppError` results to JSON envelopes.
## Error Handling
**Strategy:** centralized application errors with JSON response helpers and service-thrown `AppError`.
**Patterns:**
- Service and route code throw or catch `AppError` from `src/server/core/errors.ts`; routes convert them through `jsonError` / `jsonErrorFromAppError` in `src/server/core/response.ts`.
- `handleRequest` in `src/server/core/response.ts` assigns `requestId`, resolves auth, checks roles, and standardizes unknown errors into `SYSTEM_INTERNAL`.
- Server pages still use Next.js control flow (`redirect`, `notFound`) directly in files such as `src/app/products/[id]/assembly/page.tsx` and `src/lib/server-session.ts`.
## Cross-Cutting Concerns
**Logging:** request-level and operational logging is centralized under `src/server/services/log.service.ts`, `src/server/repositories/log.repository.ts`, `src/server/middleware/request-logger.ts`, and `src/lib/logger.ts`.
**Validation:** API inputs use Zod schemas in `src/app/api/platform/schemas.ts` and route-local schemas; deeper domain checks live in service or rule modules such as `src/lib/open-product/asset-validation.ts`.
**Authentication:** page access uses `src/proxy.ts` and `src/lib/server-session.ts`; API auth is resolved inside `src/server/core/response.ts` through `src/lib/auth-middleware.ts`.
---
*Architecture analysis: 2026-04-02*

View File

@ -1,123 +0,0 @@
# Codebase Concerns
**Analysis Date:** 2026-04-02
## Tech Debt
**Route / Page 越层直接访问 Prisma,业务规则分散在入口层:**
- Issue: 多个页面和 API Route 直接 `import { prisma } from '@/lib/prisma'` 并自行查询产品、SKU、板件、孔位或活动日志,绕过 `Service` / `Repository` 统一边界。
- Files: `src/app/api/products/[id]/packaging/route.ts`, `src/app/api/trash/products/[id]/route.ts`, `src/app/api/trash/skus/[id]/route.ts`, `src/app/products/[id]/page.tsx`, `src/app/products/[id]/assembly/page.tsx`, `src/app/products/[id]/skus/[skuId]/page.tsx`, `src/app/products/[id]/skus/[skuId]/assembly/page.tsx`
- Impact: 权限校验、软删除判断、日志写入和字段裁剪会在多个入口重复实现;后续改产品权限或主链字段时,容易出现“有的入口修了,有的入口没修”的回归。
- Fix approach: 把产品读取、包装权限前置校验、回收站操作日志写入统一收回到 `src/server/services/*.ts` 与 `src/server/repositories/*.ts`;Route 只保留收参、鉴权、响应格式。
**包装旧链仍是“双真相”实现,服务层自身也夹带数据库访问:**
- Issue: `src/server/services/packaging.service.ts` 既直接查 `prisma.product.findUnique`,又调用 `src/server/repositories/packaging.repository.ts`;同时新主链包装能力已经落在 `src/server/services/pre-sku-packaging.service.ts` 与 `src/server/services/draft-group.service.ts`,形成两套包装规则入口。
- Files: `src/server/services/packaging.service.ts`, `src/server/repositories/packaging.repository.ts`, `src/server/services/pre-sku-packaging.service.ts`, `src/server/services/draft-group.service.ts`, `src/app/api/products/[id]/packaging/route.ts`, `src/app/api/pre-skus/[id]/packaging/route.ts`
- Impact: 后续一旦包装结构、默认箱规、成本汇总口径变化,旧产品链和新主链很容易继续分叉,正式产品包装与 `PreSku` 包装结果可能长期不一致。
- Fix approach: 明确“正式产品包装”与“PreSku 包装”的唯一真相和映射时机;把旧链权限/存在性校验下沉到仓储或服务内部,避免服务层继续混用 Prisma 与 Repository。
**演示种子与初始化逻辑和正式业务混在主服务目录:**
- Issue: `src/server/services/open-platform-demo-seed.service.ts` 在一个超大服务里同时做账号创建、资产族审批、组合生成、包装写入、正式入库和清理旧演示数据;`src/server/services/system-init.service.ts` 同时负责连接重试、默认管理员创建、降级模式和数据完整性检查。
- Files: `src/server/services/open-platform-demo-seed.service.ts`, `src/server/services/system-init.service.ts`, `src/lib/init.ts`
- Impact: 这些脚本既能改真实数据,又承载启动行为,维护者很难判断“这是开发辅助逻辑”还是“生产运行逻辑”;一处改动可能同时影响启动、种子数据和正式链路。
- Fix approach: 把启动初始化、开发种子、演示数据清理拆成独立模块或脚本入口;保留服务层只负责可复用业务动作,避免“一个文件串完整主链流程”。
## Known Bugs
**包装接口测试只覆盖 GET,写操作回归容易漏掉:**
- Symptoms: `src/app/api/products/[id]/packaging/route.test.ts` 只有一个 GET 用例,POST / PUT / DELETE 的权限、默认方案切换、错误分支和参数校验没有自动兜底。
- Files: `src/app/api/products/[id]/packaging/route.ts`, `src/app/api/products/[id]/packaging/route.test.ts`, `src/server/services/packaging.service.ts`
- Trigger: 修改包装方案默认值逻辑、删除逻辑、协作者权限或请求体结构后,很容易在无感情况下破坏写接口。
- Workaround: 变更该链路时至少手测四个接口;更稳妥的做法是先补 POST / PUT / DELETE 路由测试与 `PackagingService` 单测。
**回收站产品 / SKU 路由缺少独立测试,恢复与硬删除日志链路脆弱:**
- Symptoms: `src/app/api/trash/products/[id]/route.ts` 和 `src/app/api/trash/skus/[id]/route.ts` 直接写 `activityLog`,但仓库内未检测到对应路由测试。
- Files: `src/app/api/trash/products/[id]/route.ts`, `src/app/api/trash/skus/[id]/route.ts`, `src/server/middleware/request-logger.ts`
- Trigger: 调整 `productService.deleteProduct`、`skuService`、活动日志结构或认证上下文后,路由很可能出现“业务成功但日志失败”或“日志成功但响应异常”的割裂。
- Workaround: 变更回收站逻辑时手动验证恢复、彻底删除、活动日志三条链路;后续应补针对 `POST` / `DELETE` 的 API 测试。
## Security Considerations
**仓库内存在硬编码默认管理员账号与密码:**
- Risk: `src/server/services/system-init.service.ts` 直接声明 `admin@system.local` 与 `Admin@123456`,并在启动时自动创建默认超级管理员。
- Files: `src/server/services/system-init.service.ts`, `src/lib/init.ts`
- Current mitigation: 创建后标记 `isFirstLogin: true`,并提示首次登录修改信息。
- Recommendations: 改为仅在显式初始化命令下创建;邮箱和密码由环境变量或一次性 bootstrap token 注入;生产环境默认禁用自动超级管理员创建。
**演示种子服务写死演示账号和统一密码:**
- Risk: `src/server/services/open-platform-demo-seed.service.ts` 固定使用 `open-platform-planner@example.com`、`open-platform-reviewer@example.com` 和 `Seed123456!`,并通过 `seed()` 返回登录信息。
- Files: `src/server/services/open-platform-demo-seed.service.ts`
- Current mitigation: 密码入库前会做哈希;服务内用 marker 区分演示数据。
- Recommendations: 将演示种子限定在开发环境脚本;禁止在生产构建或公共接口中暴露演示登录信息;密码改为一次性生成并只在本地终端输出。
## Performance Bottlenecks
**3D 选中与高亮链路大量遍历场景树,模型变大时会卡顿:**
- Problem: `src/lib/3d/engine.ts` 在命中解析、对象查找、批量高亮、取所有 mesh 等路径上反复调用 `scene.traverse`、`modelRoot.traverse`、`getAllMeshes()` 和 `getObjectByName()`。
- Files: `src/lib/3d/engine.ts`
- Cause: 运行时按名字做多轮模糊匹配,没有稳定的节点索引缓存;调试日志也散落在模型加载和选择过程中。
- Improvement path: 建立模型加载后的节点索引与 alias cache,把查找从“整树遍历”收敛到字典读取;把调试日志严格收束到显式 debug 开关。
**孔位 2D 覆盖层会在映射和投影阶段多次遍历模型:**
- Problem: `src/components/3d/HoleOverlay2D.tsx` 在板件对象解析、严格孔位映射、leader 生成等阶段多次 `modelRoot.traverse`,同时大量做世界坐标到屏幕坐标转换。
- Files: `src/components/3d/HoleOverlay2D.tsx`
- Cause: 叠加层同时承担“查对象、容错匹配、投影、去重、绘制准备”多种职责,缺少预计算缓存。
- Improvement path: 拆出一次性计算的板件映射索引与包围盒缓存;把实时 render 依赖限制在必要的相机变化和 transform 变化。
## Fragile Areas
**旧装配编辑器工作区文件过大,状态和 UI 耦合严重:**
- Files: `src/app/products/[id]/assembly/AssemblyEditorWorkspace.tsx`
- Why fragile: 单文件约 3600 行,混合了安装顺序、上下文菜单、材质、孔位高亮、组件装配、播放控制和移动端 UI 状态;轻微改动就可能引发跨功能回归。
- Safe modification: 先在现有 hook 与纯函数层补测试,再按子域拆分为安装顺序、材质、预览、菜单几个独立模块;避免直接在工作区文件里继续叠加新分支。
- Test coverage: 未检测到 `src/app/products/[id]/assembly/AssemblyEditorWorkspace.test.tsx`;当前只有 `src/app/products/[id]/assembly/hooks/__tests__/useAssemblyEditorApi.test.tsx` 和部分 3D / lib 层测试,无法直接覆盖整页状态机。
**开放平台演示种子服务一次串完整主链,任何一个子步骤变更都可能牵一发动全身:**
- Files: `src/server/services/open-platform-demo-seed.service.ts`
- Why fragile: 同时依赖资产中心、自动组合、草稿组、包装、正式入库、轻仓库六条链路,并在执行前删除旧演示数据。
- Safe modification: 只在需要联调演示时触碰;拆为“账号准备”“资产准备”“草稿组生成”“包装注入”“正式入库”几个明确步骤,并用幂等标记分别验证。
- Test coverage: 有 `src/server/services/__tests__/open-platform-demo-seed.service.test.ts`,但主要覆盖候选选择、包装输入拼装和 happy path;对异常清理、并发执行、部分失败回滚覆盖不足。
## Scaling Limits
**请求日志只保存在进程内存,无法支撑多实例和长周期排障:**
- Current capacity: `src/server/middleware/request-logger.ts` 只保留最多 `1000` 条 `requestLogs`,进程重启即丢失。
- Limit: 多实例部署、Serverless 冷启动或高并发下,请求统计和慢请求分析没有全局一致性,也不能追溯历史。
- Scaling path: 将请求日志接入集中式日志系统或数据库表;保留本地内存日志仅作为开发态调试缓冲。
## Dependencies at Risk
**React 19 + Next 16 + Three 生态组合依赖版本锁定较紧:**
- Risk: `package.json` 通过 `overrides` 强制 `@react-three/drei`、`@react-three/fiber`、`@react-spring/*` 适配 React `19.2.4`,说明当前 3D 栈对上游版本兼容性比较脆弱。
- Impact: 升级 React、Next 或 3D 相关依赖时,`src/lib/3d/*`、`src/components/3d/*`、`src/app/products/[id]/assembly/*` 很可能先出问题。
- Migration plan: 后续升级前先单独跑 3D/装配测试集,优先验证 `src/lib/3d/__tests__/*`、`src/components/3d/__tests__/*`、`src/app/products/[id]/assembly/hooks/__tests__/*`。
## Missing Critical Features
**缺少对旧链包装与新主链包装的一致性治理:**
- Problem: 正式产品包装仍走 `PackagingPlan`,新主链包装走 `PreSkuPackagingPlan`,当前只在正式入库时做一次复制,没有统一的差异检测或同步规则。
- Blocks: 后续如果要做包装版本追踪、重新入库、包装成本对账或“草稿组变更回刷正式 SKU”,现有结构难以保证一致性。
## Test Coverage Gaps
**系统初始化与降级模式未检测到自动化测试:**
- What's not tested: 默认管理员创建、数据库重试、降级模式进入/恢复、`throwOnFailure` 分支。
- Files: `src/server/services/system-init.service.ts`, `src/lib/init.ts`
- Risk: 启动期问题会直接影响整站可用性,但当前改动没有自动回归护栏。
- Priority: High
**请求日志中间件未检测到自动化测试:**
- What's not tested: 请求 ID、慢请求统计、错误日志筛选、内存上限裁剪、开发态打印分支。
- Files: `src/server/middleware/request-logger.ts`
- Risk: 监控与排障数据出错时,开发阶段不容易第一时间发现。
- Priority: Medium
**旧产品详情 / SKU 详情页直接查库,但未检测到对应页面测试:**
- What's not tested: 权限校验后的详情装载、敏感字段裁剪、SKU 级板件 / 孔位 / 五金范围隔离。
- Files: `src/app/products/[id]/page.tsx`, `src/app/products/[id]/skus/[skuId]/page.tsx`, `src/app/products/[id]/skus/[skuId]/assembly/page.tsx`
- Risk: 详情页容易在权限、软删除、兼容链字段变化时静默回归。
- Priority: High
---
*Concerns audit: 2026-04-02*

View File

@ -1,145 +0,0 @@
# Coding Conventions
**Analysis Date:** 2026-04-02
## Naming Patterns
**Files:**
- Use kebab-case for most source files under `src/server/services`, `src/server/repositories`, `src/lib`, and route segments such as `src/server/services/planning.service.ts`, `src/server/repositories/planning.repository.ts`, `src/lib/panel-selection-resolution.ts`, `src/app/api/warehouse/route.ts`.
- Use PascalCase for React component files under `src/components` and page clients such as `src/components/platform/PlatformPageFrame.tsx`, `src/components/ui/Button.tsx`, `src/app/warehouse/WarehouseClient.tsx`.
- Use suffix-based names to encode responsibility. Common suffixes already used in code:
- `*.service.ts` for business logic in `src/server/services`
- `*.repository.ts` for data access in `src/server/repositories`
- `*.mapper.ts` for DTO/entity mapping in `src/server/services/planning.mapper.ts`, `src/server/services/draft-group.mapper.ts`
- `*.types.ts` for module-local types in `src/server/services/planning.service.types.ts`, `src/app/home/home-client.types.ts`
- `*.container.tsx` and `*.workspace.tsx` for wrapper/composition variants in `src/app/products/[id]/ProductDetailClient.container.tsx`, `src/app/products/[id]/ProductDetailClient.workspace.tsx`
- `route.ts` and `route.test.ts` for API handlers and colocated API tests in `src/app/api/auth/login/route.ts`, `src/app/api/auth/login/route.test.ts`
**Functions:**
- Use camelCase for functions and methods. Examples: `parsePlanningSlotSelections` in `src/lib/planning/parsers.ts`, `getWorkspaceDetail` in `src/server/services/planning.service.ts`, `calculatePagination` in `src/server/core/response.ts`.
- Use verb-led names for actions and booleans that read like capabilities. Examples: `assertCanWritePlanning` in `src/server/services/planning-scope.service.ts`, `canReadWorkspace` in `src/server/services/planning-scope.service.ts`, `useCreateUser` in `src/hooks/api/useUsers.ts`.
- Use `toXxxDTO` naming for mappers. Examples: `toTemplateDTO`, `toWorkspaceDTO`, `toDraftDTO` in `src/server/services/planning.mapper.ts`.
**Variables:**
- Use camelCase for local variables and object keys. Examples: `requestId`, `generationContext`, `slotAssetOptions`, `pendingRequestRef`.
- Use ALL_CAPS only for true constants and config maps. Examples: `USER_STORAGE_KEY` in `src/hooks/useAuth.ts`, `MODAL_IDS` in `src/stores/ui.store.ts`, `CACHE_TTL` in `src/server/core/constants.ts`.
- Prefix intentionally unused parameters with `_` to satisfy lint configuration. Examples appear in `src/server/services/base.service.ts` (`_ctx`) and `src/server/services/draft-group.service.ts`.
**Types:**
- Use PascalCase for interfaces, classes, DTOs, and aliases. Examples: `PlanningWorkspaceDTO`, `PlanningTemplateWithRelations`, `RequestContext`, `ButtonProps`.
- Use explicit `Props` suffix for component props and `Result`/`DTO` suffix for return models. Examples: `PlatformPageFrameProps` in `src/components/platform/PlatformPageFrame.tsx`, `ListResult` in `src/server/services/base.service.ts`, `WarehouseOverviewDTO` in `src/server/services/warehouse.service.types.ts`.
## Code Style
**Formatting:**
- No Prettier or Biome config is detected at repo root. Formatting is effectively governed by existing file style plus ESLint in `eslint.config.mjs`.
- The codebase is not fully normalized. Both semicolon-heavy and semicolon-free styles coexist:
- Semicolon-light style: `src/server/services/planning.service.ts`, `src/components/platform/PlatformPageFrame.tsx`
- Semicolon-heavy legacy style: `src/hooks/useAuth.ts`, `src/components/ui/Button.tsx`
- Keep edits consistent with the file you touch instead of trying to reformat unrelated lines.
- TypeScript strict mode is enabled in `tsconfig.json`, but `noImplicitAny` is explicitly disabled. Prefer real types for new code even though existing code allows gradual typing.
**Linting:**
- ESLint is configured through `eslint.config.mjs` with `eslint-config-next/core-web-vitals` and `eslint-config-next/typescript`.
- Treat lint warnings as standards debt, not as permission to add more debt. The current config downgrades several rules to `warn`, including:
- `@typescript-eslint/no-explicit-any`
- `@typescript-eslint/no-unused-vars` with `_` ignore patterns
- `@typescript-eslint/ban-ts-comment`
- `react-hooks/exhaustive-deps`
- `react-hooks/set-state-in-effect`
- `react-hooks/immutability`
- Tests under `tests/**` and `**/*.test.ts(x)` are excluded from lint in `eslint.config.mjs`, so do not rely on lint alone to keep tests clean.
## Import Organization
**Order:**
1. Framework and third-party packages first. Examples: `next/server`, `react`, `@prisma/client`, `lucide-react`.
2. Alias imports from `@/` second. Examples in `src/app/api/warehouse/route.ts` and `src/components/platform/PlatformPageFrame.tsx`.
3. Relative imports last for same-folder modules. Examples: `./planning.mapper`, `./PlatformFlowNav`, `../repositories`.
**Path Aliases:**
- Use the `@/*` alias from `tsconfig.json` for cross-folder imports.
- Prefer alias imports for anything outside the current feature folder. Examples:
- `@/server/core/response`
- `@/components/ui/Button`
- `@/lib/planning/parsers`
- Use relative imports only for near-neighbor modules inside the same slice. Examples:
- `./planning.service.types`
- `./route`
- `../repositories`
## Error Handling
**Patterns:**
- API routes should wrap logic with `handleRequest` from `src/server/core/response.ts`. Example: `src/app/api/warehouse/route.ts`.
- Domain failures should use `AppError` and `ErrorCode` from `src/server/core/errors.ts`. Example usage is visible throughout `src/server/services/planning.service.ts`.
- Route handlers may catch `AppError` only to convert to `jsonError`; unknown errors should be rethrown so `handleRequest` can produce a standard 500 response. Example: `src/app/api/warehouse/route.ts`.
- Service-layer validation is performed before repository writes and should fail fast with readable Chinese messages. Example messages in `src/server/services/planning.service.ts` include `'规划模板不存在'` and `'模板编码和名称不能为空'`.
- Client-side code currently uses `console.error` for recoverable UI/network failures in places like `src/hooks/useAuth.ts`. Preserve user-facing recovery behavior if touching those paths.
## Logging
**Framework:** `console` plus thin service wrappers.
**Patterns:**
- Core request logging and error output currently rely on `console.error` inside `src/server/core/response.ts`.
- Additional logging helpers exist in `src/lib/logger.ts`, `src/server/services/logger.service.ts`, and `src/server/middleware/request-logger.ts`, but direct `console` usage is still common.
- Use request-scoped IDs when logging server request failures; `handleRequest` already generates `requestId` in `src/server/core/response.ts`.
## Comments
**When to Comment:**
- Prefer brief comments before non-obvious behavior, polyfills, compatibility workarounds, or architecture boundaries.
- Existing examples:
- Jest environment/polyfill rationale in `jest.setup.js`
- accessibility and focus behavior notes in `src/components/ui/Button.tsx`
- cache behavior notes in `src/server/services/base.service.ts`
- Avoid restating obvious code. Most newer files such as `src/components/platform/PlatformPageFrame.tsx` and `src/server/services/planning.service.ts` are readable without heavy comments.
**JSDoc/TSDoc:**
- JSDoc is used inconsistently.
- Heavy JSDoc in older foundational files: `src/components/ui/Button.tsx`, `src/server/services/base.service.ts`, `src/server/repositories/base.repository.ts`
- Minimal or no JSDoc in newer platform files: `src/server/services/planning.service.ts`, `src/components/platform/PlatformPageFrame.tsx`
- For new work, add JSDoc only for shared utilities, base classes, or APIs whose contracts are not obvious from names and types.
## Function Design
**Size:** Keep route handlers small and orchestration-focused. Business logic belongs in services, not in `src/app/api/**/route.ts`.
**Parameters:**
- Prefer typed input objects for service methods when there are multiple related inputs. Examples: `createTemplate(input, user)` and `createWorkspace(input, user)` in `src/server/services/planning.service.ts`.
- Prefer explicit `Props` interfaces for React components. Example: `PlatformPageFrameProps` in `src/components/platform/PlatformPageFrame.tsx`.
- In hooks, return structured objects rather than tuples when more than one value is exposed. Example: `useAuth` and `usePermissions` in `src/hooks/useAuth.ts`.
**Return Values:**
- API routes should return `jsonSuccess(...)` / `jsonError(...)` from `src/server/core/response.ts`.
- Services usually return DTOs directly in newer modules such as `src/server/services/planning.service.ts`; generic base services may still use `ServiceResult<T>` in `src/server/services/base.service.ts`.
- Repository methods should return raw persistence models or relation-enriched records, not DTOs. Example: `PlanningWorkspaceWithRelations` from `src/server/repositories/planning.repository.ts`.
## Module Design
**Exports:**
- Prefer named exports for reusable functions, hooks, DTO helpers, services, and most components. Examples: `PlatformPageFrame`, `planningService`, `calculatePagination`, `useUsers`.
- Default exports are common for page-level components and some legacy UI primitives. Examples: `src/app/warehouse/WarehouseClient.tsx`, `src/app/asset-center/page.tsx`, `src/components/ui/Button.tsx`.
- Keep both the class and singleton export when a service/repository is meant to be mocked or instantiated in tests. Examples:
- `export const planningService = new PlanningService()`
- `export { PlanningService }`
- similar pattern in `src/server/repositories/planning.repository.ts`
**Barrel Files:**
- Barrel files are used selectively, not universally.
- Present for grouped APIs: `src/server/services/index.ts`, `src/server/repositories/index.ts`, `src/hooks/api/index.ts`, `src/components/upload/index.ts`, `src/components/packaging/index.ts`
- Many feature folders still import concrete files directly.
- When adding a new module to an existing barrel-managed folder, update that folder's `index.ts`. Otherwise import the file directly and do not create a new barrel just for one file.
## Prescriptive Patterns To Follow
- For new backend work under `src/app/api`, keep the route as input/auth/response glue only, and push rules into `src/server/services`.
- For new server business logic, colocate DTO types in `*.service.types.ts` and mapping helpers in `*.mapper.ts` when transformation logic grows.
- For new frontend pages on the new platform path, reuse `src/components/platform` and `src/components/ui` primitives before introducing page-specific primitives.
- For imports, keep third-party first, `@/` aliases second, and same-folder relative imports last.
- For touched files with legacy semicolon style, match local style. Do not mass-reformat to enforce a repo-wide style that the repo does not currently enforce.
---
*Convention analysis: 2026-04-02*

View File

@ -1,107 +0,0 @@
# External Integrations
**Analysis Date:** 2026-04-02
## APIs & External Services
**Database & ORM:**
- PostgreSQL - primary transactional database for all business domains
- SDK/Client: `@prisma/client`, `@prisma/adapter-pg`, `pg`
- Auth: `DATABASE_URL`
- Evidence: `prisma/schema.prisma`, `prisma.config.ts`, `src/lib/prisma.ts`
**Object Storage:**
- MinIO or another S3-compatible object store - stores uploaded models, thumbnails, and generated asset files
- SDK/Client: `@aws-sdk/client-s3`
- Auth: `S3_ENDPOINT`, `S3_PUBLIC_ENDPOINT`, `S3_PUBLIC_URL`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`, `S3_REGION`, `S3_BUCKET`
- Evidence: `src/lib/storage.ts`, `src/app/api/upload/model/route.ts`, `src/server/services/product-upload.service.ts`, `src/server/services/product-cad-upload.service.ts`
**Caching:**
- Redis (optional) - L2 cache when available, with in-memory fallback when `REDIS_URL` is absent or unavailable
- SDK/Client: `ioredis`
- Auth: `REDIS_URL`
- Evidence: `src/server/cache/cache.service.ts`
**CAD / Conversion Services:**
- DWG conversion HTTP service - converts DWG files through `/health`, `/convert`, `/status/:taskId`, and `/download/:taskId`
- SDK/Client: native `fetch`
- Auth: `DWG_CONVERTER_URL`
- Evidence: `src/lib/storage.ts`, `src/server/services/sku-upload.service.ts`, `src/server/services/product-upload.service.ts`
- ODA File Converter - local/native DWG conversion executable fallback for CAD processing
- SDK/Client: child process wrapper around local binary
- Auth: `ODA_FILE_CONVERTER`
- Evidence: `src/lib/dwg/oda-wrapper.ts`, `src/lib/dwg/converter.ts`
## Data Storage
**Databases:**
- PostgreSQL
- Connection: `DATABASE_URL`
- Client: Prisma with `PrismaPg` adapter and `pg` connection pool in `src/lib/prisma.ts`
**File Storage:**
- S3-compatible object storage
- Upload API entry: `src/app/api/upload/model/route.ts`
- Service-layer usage: `src/server/services/product-upload.service.ts`, `src/server/services/product-cad-upload.service.ts`
**Caching:**
- Redis when configured; otherwise in-process memory cache
- Implementation: `src/server/cache/cache.service.ts`
## Authentication & Identity
**Auth Provider:**
- Custom JWT auth backed by Prisma `User` records
- Implementation: `src/server/services/auth.service.ts`, `src/lib/jwt.ts`, `src/lib/token-validator.ts`, `src/proxy.ts`
- Session transport: `auth-token` HTTP-only cookie set by `src/app/api/auth/login/route.ts`
- Password hashing: `bcryptjs` via `src/lib/password.ts`
## Monitoring & Observability
**Error Tracking:**
- None detected for external SaaS error tracking; no Sentry/Datadog/New Relic SDK usage was found in `src/`
**Logs:**
- Console-based application/runtime logging in files such as `src/lib/storage.ts`, `src/lib/jwt.ts`, and `src/server/cache/cache.service.ts`
- In-memory request log buffer for API diagnostics in `src/server/middleware/request-logger.ts`
- Persistent business audit logs in PostgreSQL `ActivityLog` via `src/server/services/logger.service.ts`
- Health endpoint for container and ops checks: `src/app/api/health/route.ts` and `src/server/services/health.service.ts`
## CI/CD & Deployment
**Hosting:**
- Dockerized Next.js standalone app is the main deployment shape
- Build/runtime: `Dockerfile`
- Compose manifests present: `docker-compose.yml`, `docker-compose.dev.yml`, `docker-compose.prod.yml`, `docker-compose.standard.yml`, `docker-compose.lightweight.yml`, `docker-compose.test.yml`
**CI Pipeline:**
- Not detected
- Deployment is script-driven through `scripts/deploy.ps1`, `scripts/deploy.sh`, and `scripts/remote-deploy.sh`
## Environment Configuration
**Required env vars:**
- Core runtime: `DATABASE_URL`, `JWT_SECRET`
- Auth/session support: `JWT_EXPIRES_IN`, `NEXT_PUBLIC_APP_URL`
- Storage: `S3_ENDPOINT`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`
- Common optional/runtime extensions: `S3_PUBLIC_ENDPOINT`, `S3_PUBLIC_URL`, `S3_REGION`, `S3_BUCKET`, `REDIS_URL`, `DWG_CONVERTER_URL`, `ODA_FILE_CONVERTER`, `DWG_TEMP_DIR`, `DWG_OUTPUT_DIR`, `PRISMA_LOG_QUERIES`, `NEXTAUTH_SECRET`, `NEXTAUTH_URL`
**Secrets location:**
- Repo root `.env` and `.env.test` are present; `.env.example` exists as the template source
- Prisma bootstrapping reads `.env` in `prisma.config.ts`
- Remote deployment generates a target `.env` from `.env.example` in `scripts/remote-deploy.sh`
## Webhooks & Callbacks
**Incoming:**
- None detected under `src/app/api/` for webhook-style integrations
**Outgoing:**
- DWG conversion HTTP calls to the service behind `DWG_CONVERTER_URL` from `src/lib/storage.ts`
- S3 API calls to the configured object store from `src/lib/storage.ts`
- PostgreSQL and Redis network connections from `src/lib/prisma.ts` and `src/server/cache/cache.service.ts`
- Remote SSH deployment calls from `scripts/remote-deploy.sh`
---
*Integration audit: 2026-04-02*

View File

@ -1,89 +0,0 @@
# Technology Stack
**Analysis Date:** 2026-04-02
## Languages
**Primary:**
- TypeScript 5 - application code in `src/`, `tests/`, `scripts/`, and Prisma seed/config files such as `prisma/seed.ts` and `prisma.config.ts`
**Secondary:**
- JavaScript - Node/Next config and helper scripts in `package.json`, `next.config.js`, `jest.config.js`, and `scripts/*.js`
- SQL via Prisma schema - relational data model in `prisma/schema.prisma`
- Shell/PowerShell - deployment, backup, restore, and environment validation in `scripts/*.sh`, `scripts/*.ps1`, and `scripts/remote-deploy.sh`
## Runtime
**Environment:**
- Node.js 18+ for local development, stated in `README.md`
- Node.js 18 Alpine container runtime in `Dockerfile`
**Package Manager:**
- npm - driven by `package.json`
- Lockfile: present in `package-lock.json`
## Frameworks
**Core:**
- Next.js 16.1.6 - App Router web application and API routes in `package.json` and `src/app/`
- React 19.2.4 - UI runtime for pages, components, and hooks in `package.json` and `src/components/`
- Prisma 7.3.0 - ORM/data access layer configured by `prisma.config.ts`, `prisma/schema.prisma`, and `src/lib/prisma.ts`
**Testing:**
- Jest 30.2.0 - test runner configured in `jest.config.js`
- React Testing Library 16.3.2 - component and UI assertions from `package.json`
- Supertest 7.2.2 - API test client from `package.json`
- Fast-check 4.5.3 - property-based testing dependency from `package.json`
**Build/Dev:**
- TypeScript 5 - static typing via `tsconfig.json`
- ESLint 9 + `eslint-config-next` 16.1.6 - linting from `package.json` and `eslint.config.mjs`
- Tailwind CSS 4.1.18 + PostCSS - styling pipeline from `package.json` and `postcss.config.mjs`
- `tsx` 4.21.0 - TypeScript runtime for seeds and scripts in `package.json`
- Docker multi-stage build - production packaging in `Dockerfile`
## Key Dependencies
**Critical:**
- `@prisma/client` 7.3.0 - generated DB client used throughout repositories and services such as `src/server/repositories/*.ts`
- `@prisma/adapter-pg` 7.3.0 + `pg` 8.17.2 - Prisma 7 PostgreSQL adapter/pool setup in `src/lib/prisma.ts` and `prisma/seed.ts`
- `jsonwebtoken` 9.0.3 + `bcryptjs` 3.0.3 - custom auth stack in `src/lib/jwt.ts`, `src/server/services/auth.service.ts`, and `src/server/services/system-init.service.ts`
- `@aws-sdk/client-s3` 3.948.0 - S3-compatible object storage client in `src/lib/storage.ts`
- `ioredis` 5.9.2 - optional cache backend with memory fallback in `src/server/cache/cache.service.ts`
**Infrastructure:**
- `three` 0.182.0, `@react-three/fiber` 9.5.0, `@react-three/drei` 10.7.7 - 3D rendering and scene tooling in `src/lib/3d/` and `src/components/3d/`
- `gsap` 3.12.5 and `framer-motion` 12.29.2 - animation systems used in interactive UI and playback code
- `react-hook-form` 7.54.2 + `@hookform/resolvers` 5.2.2 + `zod` 4.3.6 - form state and validation across API/UI boundaries
- `zustand` 5.0.2 - client state store in `src/stores/`
- `obj2gltf` 3.2.0 - DWG/OBJ to glTF conversion pipeline support in `src/lib/dwg/converter.ts`
## Configuration
**Environment:**
- Runtime configuration is env-driven; code references `DATABASE_URL`, `JWT_SECRET`, `JWT_EXPIRES_IN`, `REDIS_URL`, `S3_ENDPOINT`, `S3_PUBLIC_ENDPOINT`, `S3_PUBLIC_URL`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`, `S3_REGION`, `S3_BUCKET`, `NEXT_PUBLIC_APP_URL`, `NEXTAUTH_SECRET`, `NEXTAUTH_URL`, `DWG_CONVERTER_URL`, `ODA_FILE_CONVERTER`, `DWG_TEMP_DIR`, `DWG_OUTPUT_DIR`, and `PRISMA_LOG_QUERIES`
- `.env`, `.env.example`, and `.env.test` exist at repo root; `prisma.config.ts` explicitly loads `DATABASE_URL` from `.env` when needed
- Auth and storage fail fast on missing secrets in `src/lib/jwt.ts` and `src/lib/storage.ts`
**Build:**
- TypeScript path alias `@/*` is defined in `tsconfig.json`
- Next standalone output, headers, redirects, image remote patterns, and bundling rules are defined in `next.config.js`
- Prisma schema and migrations are configured in `prisma.config.ts`
- Docker build and runtime packaging are defined in `Dockerfile`
- Compose variants exist for local/dev/prod/lightweight/test deployment: `docker-compose.yml`, `docker-compose.dev.yml`, `docker-compose.prod.yml`, `docker-compose.lightweight.yml`, `docker-compose.standard.yml`, and `docker-compose.test.yml`
## Platform Requirements
**Development:**
- Install Node.js 18+, PostgreSQL 14+, MinIO or another S3-compatible object store, and optionally Redis, per `README.md`
- Docker is used for local dependency orchestration and optional DWG conversion service, per `README.md`
- ODA File Converter can be installed locally for native DWG conversion, with path discovery in `src/lib/dwg/oda-wrapper.ts`
**Production:**
- Primary target is a Dockerized Next.js standalone deployment produced by `Dockerfile`
- Health-check and ops scripts assume containerized services plus shell/PowerShell tooling in `scripts/validate-config.*`, `scripts/deploy.*`, `scripts/backup-*.sh`, and `scripts/backup-*.ps1`
- Remote deployment over SSH is supported by `scripts/remote-deploy.sh`
---
*Stack analysis: 2026-04-02*

View File

@ -1,160 +0,0 @@
# Codebase Structure
**Analysis Date:** 2026-04-02
## Directory Layout
```text
furniture-3d-system/
├── prisma/ # Prisma schema, migrations, seed data
├── public/ # Static assets and uploaded/runtime-served files
├── scripts/ # Local maintenance, seed, sync, and type-check scripts
├── src/
│ ├── app/ # Next.js App Router pages and API routes
│ ├── components/ # Shared UI, 3D viewers, assembly editors, platform shells
│ ├── hooks/ # Client hooks and API hooks
│ ├── lib/ # Shared utilities, rule engines, auth/session helpers
│ ├── providers/ # React providers
│ ├── server/ # Core server abstractions, services, repositories, cache
│ ├── stores/ # Zustand state stores
│ └── types/ # Cross-feature TS types
├── tests/ # Cross-cutting integration/security/performance tests
├── docs/ # Workflow, standards, architecture, and feature docs
└── .planning/codebase/ # GSD-generated codebase mapping docs
```
## Directory Purposes
**`src/app`:**
- Purpose: hold all routeable UI pages and HTTP API handlers.
- Contains: root pages (`src/app/page.tsx`), new platform pages (`src/app/asset-center`, `src/app/auto-combinations`, `src/app/draft-groups`, `src/app/intake`, `src/app/warehouse`), legacy pages (`src/app/products`, `src/app/share`), and App Router APIs (`src/app/api`).
- Key files: `src/app/layout.tsx`, `src/app/page.tsx`, `src/app/api/platform/schemas.ts`, `src/app/api/asset-center/families/route.ts`, `src/app/api/products/[id]/assembly/generate/route.ts`
**`src/components`:**
- Purpose: keep reusable UI and rich client widgets out of route files.
- Contains: platform shell components in `src/components/platform`, 3D/assembly widgets in `src/components/3d` and `src/components/assembly`, generic UI atoms in `src/components/ui`, and domain widgets such as `src/components/packaging`.
- Key files: `src/components/platform/PlatformPageFrame.tsx`, `src/components/platform/platform-navigation.ts`, `src/components/3d/ModelViewer.tsx`, `src/components/assembly/AssemblyEditorClient.tsx`
**`src/lib`:**
- Purpose: shared non-DB logic used by both server and client.
- Contains: auth/session helpers (`src/lib/auth-middleware.ts`, `src/lib/server-session.ts`), Prisma singleton (`src/lib/prisma.ts`), open-platform rules (`src/lib/open-product/*`), planning rules (`src/lib/planning/*`), legacy CAD/3D helpers (`src/lib/assembly-generator.ts`, `src/lib/3d/*`).
- Key files: `src/lib/prisma.ts`, `src/lib/open-product/single-board-desk.ts`, `src/lib/open-product/platform-api.ts`, `src/lib/planning/candidate-generator.ts`
**`src/server`:**
- Purpose: central backend module tree.
- Contains: `core` request/response/error types, `services` business orchestration, `repositories` data access, `cache`, `validators`, `middleware`, and `monitoring`.
- Key files: `src/server/core/response.ts`, `src/server/services/index.ts`, `src/server/services/draft-group.service.ts`, `src/server/repositories/base.repository.ts`, `src/server/repositories/draft-group.repository.ts`
**`src/stores`:**
- Purpose: client-side Zustand stores for shared UI state.
- Contains: auth, product, and UI stores.
- Key files: `src/stores/auth.store.ts`, `src/stores/product.store.ts`, `src/stores/ui.store.ts`
**`prisma`:**
- Purpose: define persistence schema and DB evolution history.
- Contains: `schema.prisma`, seed file, config, and migration SQL folders.
- Key files: `prisma/schema.prisma`, `prisma/seed.ts`, `prisma/migrations/20260330193000_add_open_product_platform/migration.sql`, `prisma/migrations/20260331193000_upgrade_pre_sku_packaging_plan/migration.sql`
**`tests`:**
- Purpose: hold repo-level tests that are not colocated with a single source folder.
- Contains: API tests, security tests, performance checks, shared mocks, and library tests.
- Key files: `tests/api/assembly-generate-from-order.route.test.ts`, `tests/security/authorization.test.ts`, `tests/services/product-list-cache-version.test.ts`
## Key File Locations
**Entry Points:**
- `src/app/layout.tsx`: global App Router shell and metadata.
- `src/app/page.tsx`: authenticated home/dashboard entry.
- `src/proxy.ts`: page-level auth gate for Next.js requests.
**Configuration:**
- `package.json`: scripts and runtime dependencies.
- `prisma/schema.prisma`: application data model.
- `prisma/prisma.config.ts`: Prisma configuration.
**Core Logic:**
- `src/server/services`: business logic layer.
- `src/server/repositories`: persistence abstraction layer.
- `src/lib/open-product`: new platform combination, validation, and view contracts.
- `src/lib/planning`: planning template parsing and candidate generation.
**Testing:**
- `src/app/__tests__`: page-level tests.
- `src/server/services/__tests__`: service-layer tests.
- `src/components/*/__tests__`: component tests.
- `tests/`: broader API, security, performance, and library tests.
## Naming Conventions
**Files:**
- Route files use Next.js conventions: `src/app/**/page.tsx`, `src/app/api/**/route.ts`.
- Service and repository files use dot-suffixed domain names: `src/server/services/draft-group.service.ts`, `src/server/repositories/draft-group.repository.ts`.
- Client-heavy page companions use `*Client.tsx`, `*.container.tsx`, and `*.workspace.tsx`, for example `src/app/HomeClient.tsx` and `src/app/products/[id]/ProductDetailClient.workspace.tsx`.
**Directories:**
- Feature directories use kebab-case route names in `src/app`, such as `src/app/asset-center`, `src/app/auto-combinations`, `src/app/draft-groups`.
- Shared backend directories are pluralized by role, such as `src/server/services`, `src/server/repositories`, `src/server/validators`.
## Where to Add New Code
**New Feature:**
- Primary code: put route entrypoints under the closest feature path in `src/app`; for new mainline steps, prefer `src/app/<feature>` plus matching APIs in `src/app/api/<feature>`.
- Tests: colocate page/client tests beside the feature in `src/app/<feature>/__tests__` or `src/components/<feature>/__tests__`; add cross-feature API/security coverage under `tests/` when it spans modules.
**New Component/Module:**
- Implementation: reusable platform-shell UI belongs in `src/components/platform`; legacy CAD or 3D viewer widgets belong in `src/components/assembly` or `src/components/3d`; generic atoms belong in `src/components/ui`.
**Utilities:**
- Shared helpers: place deterministic domain rules in `src/lib/open-product` for mainline asset/PreSku logic, in `src/lib/planning` for planning workspaces, and in broader `src/lib` only when the helper is not specific to one domain.
## Special Directories
**`src/app/api/platform`:**
- Purpose: shared Zod transport schemas for new platform APIs.
- Generated: No
- Committed: Yes
**`src/components/platform`:**
- Purpose: the approved visual shell for new platform mainline pages.
- Generated: No
- Committed: Yes
**`src/server/cache`:**
- Purpose: backend cache helpers and cache key policy.
- Generated: No
- Committed: Yes
**`.planning/codebase`:**
- Purpose: persistent codebase maps consumed by later GSD planning and execution commands.
- Generated: Yes
- Committed: Yes
**`.next`:**
- Purpose: Next.js build output and dev runtime artifacts.
- Generated: Yes
- Committed: No
## Placement Guidance By Domain
**New Platform Mainline:**
- Put page shells under `src/app/asset-center`, `src/app/auto-combinations`, `src/app/draft-groups`, `src/app/intake`, or `src/app/warehouse` when extending the existing chain.
- Put APIs under matching folders like `src/app/api/asset-center`, `src/app/api/draft-groups`, `src/app/api/intake`, and `src/app/api/warehouse`.
- Put business rules in `src/server/services/asset-center.service.ts`, `src/server/services/combination.service.ts`, `src/server/services/draft-group.service.ts`, `src/server/services/intake.service.ts`, or sibling services rather than inside route files.
**Legacy Product / CAD Compatibility:**
- Put product-detail and assembly UI under `src/app/products/[id]`, `src/app/products/[id]/assembly`, or `src/app/products/[id]/skus/[skuId]`.
- Put backend handlers under `src/app/api/products/[id]/**`, `src/app/api/holes/**`, and `src/app/api/hardware/**`.
- Put data transformations in legacy helper modules such as `src/lib/assembly-generator.ts`, `src/lib/panel-assembly-semantic.ts`, and `src/server/services/sku-cad-import.helper.ts`.
**Planning Workspace Domain:**
- Put APIs under `src/app/api/planning/templates` and `src/app/api/planning/workspaces`.
- Put parsing/generation logic in `src/lib/planning` and orchestration in `src/server/services/planning.service.ts` and `src/server/services/planning-generation.service.ts`.
**Packaging Bridge:**
- Put PreSku-side packaging endpoints under `src/app/api/pre-skus/[id]/packaging`.
- Put legacy product packaging endpoints under `src/app/api/products/[id]/packaging`.
- Put persistence and calculations in `src/server/services/pre-sku-packaging.service.ts`, `src/server/services/packaging.service.ts`, and `src/server/repositories/packaging.repository.ts` instead of route handlers.
---
*Structure analysis: 2026-04-02*

View File

@ -1,247 +0,0 @@
# Testing Patterns
**Analysis Date:** 2026-04-02
## Test Framework
**Runner:**
- Jest 30 via `jest` in `package.json`
- Config: `jest.config.js`
**Assertion Library:**
- Jest built-in assertions plus `@testing-library/jest-dom` from `jest.setup.js`
**Run Commands:**
```bash
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage
```
## Test File Organization
**Location:**
- Both colocated and centralized tests are used.
- Colocated tests sit beside the source under `src/**/__tests__` and `src/**/route.test.ts`. Examples:
- `src/server/services/__tests__/planning.service.test.ts`
- `src/components/platform/__tests__/PlatformPageFrame.test.tsx`
- `src/app/api/auth/login/route.test.ts`
- Centralized suites live under the root `tests/` tree for broader concerns. Examples:
- `tests/api/auth.test.ts`
- `tests/security/authorization.test.ts`
- `tests/performance/rendering.test.tsx`
- `tests/services/trash.service.test.ts`
**Naming:**
- Use `*.test.ts` or `*.test.tsx`.
- API route tests usually mirror the handler path with `route.test.ts`.
- Unit tests under colocated folders normally mirror the source file name, such as `src/lib/__tests__/panel-selection-resolution.test.ts` for `src/lib/panel-selection-resolution.ts`.
**Structure:**
```text
src/
app/**/route.test.ts
*/__tests__/*.test.ts(x)
tests/
api/*.test.ts
security/*.test.ts(x)
performance/*.test.tsx
services/*.test.ts
mocks/*.ts
```
## Test Structure
**Suite Organization:**
```typescript
describe('planningService', () => {
beforeEach(() => {
jest.clearAllMocks()
})
it('读取工作台详情时返回候选、草稿和已解析的资产挂载点', async () => {
const result = await planningService.getWorkspaceDetail('workspace-1', user)
expect(result.slotAssetOptions).toHaveLength(1)
expect(result.candidates[0].key).toBe('candidate-1')
})
})
```
- This pattern is taken directly from `src/server/services/__tests__/planning.service.test.ts`.
**Patterns:**
- Group tests by module, then by scenario using nested `describe(...)`. Examples: `tests/security/authorization.test.ts`, `tests/pages/login.test.tsx`.
- Reset mocks in `beforeEach` or `afterEach`. Examples:
- `jest.clearAllMocks()` in `src/server/services/__tests__/planning.service.test.ts`
- `jest.resetAllMocks()` in `src/hooks/__tests__/useAuth.test.tsx`
- Use Chinese test names extensively. Keep new test descriptions behavior-focused and readable for reviewers.
- For async expectations, use `await expect(...).rejects...` or `waitFor(...)` instead of arbitrary timers. Examples:
- `src/server/services/__tests__/planning.service.test.ts`
- `src/hooks/__tests__/useAuth.test.tsx`
## Mocking
**Framework:** Jest mocks via `jest.mock`, `jest.fn`, `jest.mocked`, and occasional `require('./route')` for route modules.
**Patterns:**
```typescript
jest.mock('../../repositories', () => ({
planningTemplateRepository: {
findByIdWithRelations: jest.fn(),
create: jest.fn(),
},
}))
const mockedPlanningTemplateRepository = jest.mocked(planningTemplateRepository)
mockedPlanningTemplateRepository.findByIdWithRelations.mockResolvedValue(...)
```
- This repository-mocking pattern is used in `src/server/services/__tests__/planning.service.test.ts`.
```typescript
jest.mock('@/server/core/response', () => ({
handleRequest: (...args: unknown[]) => mockHandleRequest(...args),
jsonError: (...args: unknown[]) => mockJsonError(...args),
jsonSuccess: (...args: unknown[]) => mockJsonSuccess(...args),
}))
const { GET, PUT } = require('./route')
```
- This route-level mock pattern is used in `src/app/api/pre-skus/[id]/packaging/route.test.ts`.
```typescript
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: async () => ({ success: true, data: { authenticated: true, user: ... } }),
} as Response)
```
- This browser/network mock pattern is used in `src/hooks/__tests__/useAuth.test.tsx`.
**What to Mock:**
- Mock repositories when testing services in `src/server/services/__tests__`.
- Mock `@/server/core/response` and `@/server/services` when testing route handlers in `src/app/api/**/route.test.ts`.
- Mock browser APIs and `global.fetch` for hooks/components using network or router behavior. Examples: `src/hooks/__tests__/useAuth.test.tsx`, `tests/pages/login.test.tsx`.
- Use `tests/mocks/nanoid.ts` through `moduleNameMapper` when deterministic IDs matter.
**What NOT to Mock:**
- Do not mock pure algorithmic helpers unless the test is explicitly isolating a higher-level orchestrator. Pure logic in `src/lib`, `src/lib/3d`, `src/lib/open-product`, and `src/lib/planning` is commonly tested directly.
- Do not mock DOM rendering helpers when the goal is UI output verification. Component tests like `src/components/platform/__tests__/PlatformPageFrame.test.tsx` render the actual component and assert on visible output.
## Fixtures and Factories
**Test Data:**
```typescript
const user = {
id: 'user-1',
email: 'planner@example.com',
name: '规划员',
role: 'DESIGNER' as const,
status: 'ACTIVE' as const,
departmentId: 'dept-1',
teamId: 'team-1',
}
```
- Inline object fixtures are the dominant style. This pattern appears in `src/server/services/__tests__/planning.service.test.ts`, `tests/security/authorization.test.ts`, and many root-level API tests.
**Location:**
- Most fixtures are declared inline inside each spec file.
- Shared mock modules are minimal and currently limited to helpers like `tests/mocks/nanoid.ts`.
- Global environment setup lives in `jest.setup.js`, not in per-suite helpers.
## Coverage
**Requirements:** Global threshold enforced in `jest.config.js`
- branches: 70
- functions: 70
- lines: 70
- statements: 70
**View Coverage:**
```bash
npm run test:coverage
```
## Test Types
**Unit Tests:**
- Heavy coverage exists for pure logic and feature helpers under `src/lib/**/__tests__`, `src/lib/3d/__tests__`, `src/lib/open-product/__tests__`, and `src/lib/planning/__tests__`.
- Service unit tests mock repositories and assert business rules. Examples:
- `src/server/services/__tests__/planning.service.test.ts`
- `src/server/services/__tests__/draft-group.service.test.ts`
- `src/server/services/__tests__/intake.service.test.ts`
**Integration Tests:**
- Root `tests/api/*.test.ts` and some route tests exercise route/service contracts more broadly.
- Security-oriented integration-style checks live under `tests/security/`.
- Some API tests likely depend on test environment configuration because `.env.test` exists at repo root. Do not read secret values; only rely on the file's presence when setting up environment expectations.
**E2E Tests:**
- Playwright and Cypress config files are not detected at repo root.
- Browser E2E framework: Not used in the current repo snapshot.
## Common Patterns
**Async Testing:**
```typescript
await waitFor(() => {
expect(screen.getByTestId('user-name')).toHaveTextContent('管理员')
})
await expect(
planningService.createDraft('workspace-1', input, user),
).rejects.toMatchObject({
code: ErrorCode.VALIDATION_FAILED,
})
```
- These patterns come from `src/hooks/__tests__/useAuth.test.tsx` and `src/server/services/__tests__/planning.service.test.ts`.
**Error Testing:**
```typescript
const response = await PUT(
{
json: async () => ({
resultSummary: { estimatedCost: 12 },
items: [],
}),
} as any,
{ params: Promise.resolve({ id: 'pre-sku-1' }) },
)
expect(mockSavePackagingPlan).not.toHaveBeenCalled()
expect(response).toEqual(
expect.objectContaining({
success: false,
status: 400,
}),
)
```
- This route-validation pattern comes from `src/app/api/pre-skus/[id]/packaging/route.test.ts`.
## Environment and Setup
**Jest Environment:**
- `testEnvironment` is `jsdom` in `jest.config.js`, even for many server-focused tests.
- `setupFilesAfterEnv` points to `jest.setup.js`.
**Global Setup in `jest.setup.js`:**
- Loads `@testing-library/jest-dom`
- Polyfills `TextEncoder` and `TextDecoder`
- Polyfills `setImmediate` for Prisma-related code
- Defines fallback `Request` and `Response` implementations for Next.js server code
- Mocks `IntersectionObserver`, `ResizeObserver`, `window.matchMedia`, `CSS.supports`, and `performance.now`
**Ignored Paths:**
- Jest ignores `.next`, `output`, and `temp` via `modulePathIgnorePatterns` and `watchPathIgnorePatterns` in `jest.config.js`.
- ESLint ignores test files, so rely on test review and actual execution rather than lint for test quality.
## Prescriptive Patterns To Follow
- Put narrow unit tests next to the code in `__tests__` when the module is owned by a specific slice under `src`.
- Put cross-cutting, regression, security, or workflow tests under the root `tests/` tree.
- For a new service test, mock repositories and other downstream collaborators; assert the business rule and returned DTO shape.
- For a new route test, mock `handleRequest`, response helpers, and the service entrypoint instead of booting the entire app.
- For a new component or hook test, use React Testing Library with `render`, `screen`, and `waitFor`; prefer behavior assertions over snapshot tests. Snapshot usage is not detected in the current repo.
- Keep test names in Chinese and aligned with actual user-observable behavior or explicit business rule outcomes.
---
*Testing analysis: 2026-04-02*

View File

@ -1,28 +0,0 @@
{
"mode": "yolo",
"granularity": "standard",
"parallelization": {
"enabled": true
},
"planning": {
"commit_docs": true,
"search_gitignored": false
},
"model_profile": "balanced",
"workflow": {
"research": true,
"plan_check": true,
"verifier": true,
"nyquist_validation": true,
"auto_advance": false,
"research_before_questions": false,
"discuss_mode": "assumptions",
"skip_discuss": false,
"text_mode": false
},
"git": {
"branching_strategy": "none",
"phase_branch_template": "gsd/phase-{phase}-{slug}",
"milestone_branch_template": "gsd/{milestone}-{slug}"
}
}

View File

@ -1,139 +0,0 @@
---
phase: 01-协作入口与项目记忆稳定化
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- AGENTS.md
- docs/workflow/团队协作模式.md
- docs/workflow/模块边界说明.md
- docs/workflow/主线同步操作手册.md
autonomous: true
requirements:
- COLL-01
- COLL-02
must_haves:
truths:
- "新协作者进入 furniture-3d-system 后,能从固定入口读到统一的分支命名与同步规则。"
- "AI 会话不会再把协作规则散读成多个互相矛盾的口径。"
- "包装长期分支和临时任务分支的 merge/rebase 规则可以直接从文档验证。"
artifacts:
- path: "AGENTS.md"
provides: "仓库级固定入口与强制阅读说明"
contains: "团队协作固定入口"
- path: "docs/workflow/团队协作模式.md"
provides: "角色分工、分支命名、收口规则"
contains: "codex/xuxiake-packaging"
- path: "docs/workflow/模块边界说明.md"
provides: "主链、包装线、兼容链和共享接缝边界"
contains: "共享层边界"
- path: "docs/workflow/主线同步操作手册.md"
provides: "长期分支 merge 与临时分支 rebase 的同步口径"
contains: "git merge main"
key_links:
- from: "AGENTS.md"
to: "docs/workflow/团队协作模式.md"
via: "进入 furniture-3d-system 前置阅读"
pattern: "团队协作模式"
- from: "docs/workflow/团队协作模式.md"
to: "docs/workflow/主线同步操作手册.md"
via: "同步规则引用"
pattern: "主线同步操作手册"
- from: "docs/workflow/模块边界说明.md"
to: "docs/workflow/团队协作模式.md"
via: "角色边界与工作域映射"
pattern: "团队协作模式"
---
<objective>
固化 `furniture-3d-system` 的协作入口与分支口径,让新协作者和新 AI 会话都从同一组文档进入项目。
Purpose: 落地 D-01、D-02、D-03、D-04,消除“入口分散、口径不一致、分支职责含糊”的协作恢复成本。
Output: 对齐后的仓库级入口说明与三份协作文档。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/01-协作入口与项目记忆稳定化/01-CONTEXT.md
@AGENTS.md
@docs/workflow/团队协作模式.md
@docs/workflow/模块边界说明.md
@docs/workflow/主线同步操作手册.md
</context>
<tasks>
<task type="auto">
<name>Task 1: 对齐仓库级入口与协作主文档</name>
<files>AGENTS.md, docs/workflow/团队协作模式.md</files>
<read_first>
AGENTS.md
.planning/phases/01-协作入口与项目记忆稳定化/01-CONTEXT.md
.planning/ROADMAP.md
docs/workflow/团队协作模式.md
docs/workflow/模块边界说明.md
docs/workflow/主线同步操作手册.md
</read_first>
<acceptance_criteria>
- `AGENTS.md` 明确把 `docs/workflow/团队协作模式.md`、`docs/workflow/模块边界说明.md`、`docs/workflow/主线同步操作手册.md` 作为进入 `furniture-3d-system/` 的固定入口,符合 D-01、D-02。
- `docs/workflow/团队协作模式.md` 保留并强化 `codex/&lt;负责人&gt;-&lt;工作域&gt;` 和 `codex/&lt;负责人&gt;-&lt;工作域&gt;-&lt;任务简称&gt;` 规则,符合 D-03。
- 文档中明确 `main` 只由 `WuYongJie` 收口、`codex/xuxiake-packaging` 为包装长期分支,符合 D-04。
</acceptance_criteria>
<action>按 D-01 至 D-04 统一仓库级入口和主协作文档文案:去掉容易让人误解成“还有第二套入口”的表述;把固定入口、固定分支命名、主线收口责任、包装长期分支命名写成可以直接复制执行的规则;不要新增平级入口文档,也不要把业务主链规划塞进本任务。</action>
<verify>
<automated>rg -n "团队协作模式|模块边界说明|主线同步操作手册|codex/<负责人>-<工作域>|codex/xuxiake-packaging|WuYongJie" AGENTS.md docs/workflow/团队协作模式.md</automated>
</verify>
<done>仓库级入口与主协作文档没有互相打架的口径,新协作者只读这两层就能知道从哪里开始、谁收口、分支怎么命名。</done>
</task>
<task type="auto">
<name>Task 2: 补齐边界文档与同步手册的可执行约束</name>
<files>docs/workflow/模块边界说明.md, docs/workflow/主线同步操作手册.md</files>
<read_first>
docs/workflow/模块边界说明.md
docs/workflow/主线同步操作手册.md
docs/workflow/团队协作模式.md
.planning/phases/01-协作入口与项目记忆稳定化/01-CONTEXT.md
AGENTS.md
</read_first>
<acceptance_criteria>
- `docs/workflow/模块边界说明.md` 能直接看出新平台主链、包装线、旧兼容链、共享接缝和禁止直改区。
- `docs/workflow/主线同步操作手册.md` 明确长期共享分支使用 `merge main`、临时个人任务分支使用 `rebase main`,并与 `团队协作模式.md` 保持一致。
- 两份文档都不引入新的职责划分或新的协作入口,符合 D-07、D-08。
</acceptance_criteria>
<action>把边界文档和同步手册修整成执行型约束:让协作者可以直接判断哪些目录可改、哪些共享接缝必须先对齐;让长期分支与临时分支的同步方式不再靠口头解释;禁止顺手扩写到 `draft-group`、`intake`、`schema.prisma` 等敏感接缝的实现改动,符合 D-07、D-08。</action>
<verify>
<automated>rg -n "共享接缝|不要乱碰|merge main|rebase main|codex/xuxiake-packaging" docs/workflow/模块边界说明.md docs/workflow/主线同步操作手册.md docs/workflow/团队协作模式.md</automated>
</verify>
<done>模块边界和主线同步口径可直接执行,包装线和主线负责人不会再因为“能不能改、该怎么同步”去补口头说明。</done>
</task>
</tasks>
<verification>
执行后运行文档关键词校验,确认固定入口、分支命名、长期分支 merge、临时分支 rebase、共享接缝边界都能被命令直接检出。
</verification>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`COLL-01` 与 `COLL-02` 的协作入口部分可从 `AGENTS.md` 和三份 workflow 文档直接恢复;新会话不看聊天记录,也能找到入口、边界和同步规则。
</success_criteria>
<output>
After completion, create `.planning/phases/01-协作入口与项目记忆稳定化/01-01-SUMMARY.md`
</output>

View File

@ -1,49 +0,0 @@
# Phase 01 Plan 01 Summary
## 结果
本次执行把 `furniture-3d-system` 的第一层协作入口收口到同一组文档:`AGENTS.md` 负责声明固定入口,`团队协作模式.md` 负责固定分工和分支命名,`模块边界说明.md` 负责固定主链/包装线/兼容链与共享接缝边界,`主线同步操作手册.md` 负责固定长期分支 `merge main`、临时任务分支 `rebase main` 的同步口径。
这次没有新增第二套入口,也没有把阶段范围扩到业务实现。重点是把新会话和新协作者进入项目时最容易散掉的规则,改成可以直接读、直接照做、彼此不打架的文案。
## 改动文件
- `D:\AI Code\3danzhuang\AGENTS.md`
- `D:\AI Code\3danzhuang\furniture-3d-system\docs\workflow\团队协作模式.md`
- `D:\AI Code\3danzhuang\furniture-3d-system\docs\workflow\模块边界说明.md`
- `D:\AI Code\3danzhuang\furniture-3d-system\docs\workflow\主线同步操作手册.md`
## 验证
已执行:
```powershell
rg -n "团队协作模式|模块边界说明|主线同步操作手册|codex/<负责人>-<工作域>|codex/xuxiake-packaging|WuYongJie" "D:\AI Code\3danzhuang\AGENTS.md" "D:\AI Code\3danzhuang\furniture-3d-system\docs\workflow\团队协作模式.md"
```
结果:命中固定入口、统一分支命名、`WuYongJie` 主线收口、`codex/xuxiake-packaging` 长期包装分支等关键词。
已执行:
```powershell
rg -n "共享接缝|不要乱碰|merge main|rebase main|codex/xuxiake-packaging" "D:\AI Code\3danzhuang\furniture-3d-system\docs\workflow\模块边界说明.md" "D:\AI Code\3danzhuang\furniture-3d-system\docs\workflow\主线同步操作手册.md" "D:\AI Code\3danzhuang\furniture-3d-system\docs\workflow\团队协作模式.md"
```
结果:命中共享接缝边界、长期分支 `merge main`、临时任务分支 `rebase main`、固定包装分支等关键词。
## 规范审查
- 结论:`passed`
- 审查范围:架构、视觉、根因修复
- 标注:这是自审,不是独立复审
说明:
- 架构:本次仅调整协作文档入口和执行口径,没有引入新的架构分层冲突,也没有扩写到业务实现层。
- 视觉:本次未修改 UI 或视觉实现,不存在偏离统一视觉标准的问题。
- 根因修复:本次针对“入口分散、口径不一致、同步方式靠口头解释”这个根因做收口,不是靠临时说明或聊天补丁维持。
## 风险
- 本次只完成 Phase 01 Plan 01 的文档收口,没有覆盖 `.planning` 恢复层的后续计划内容。
- 按用户要求未执行 `commit`,因此本次结果只体现在工作区文件变更中。

View File

@ -1,143 +0,0 @@
---
phase: 01-协作入口与项目记忆稳定化
plan: 02
type: execute
wave: 1
depends_on: []
files_modified:
- .planning/PROJECT.md
- .planning/REQUIREMENTS.md
- .planning/ROADMAP.md
- .planning/STATE.md
autonomous: true
requirements:
- COLL-02
- ARCH-04
must_haves:
truths:
- "新 AI 会话只读 `.planning` 就能恢复当前阶段、下一步和主要风险。"
- "Phase 1 的目标、需求、成功标准和当前状态在 `.planning` 内部没有相互矛盾。"
- "项目记忆不会再分散到零散说明文档里,`STATE.md` 保持唯一快速恢复入口。"
artifacts:
- path: ".planning/STATE.md"
provides: "当前阶段、下一步、风险的快速恢复入口"
contains: "Current focus"
- path: ".planning/ROADMAP.md"
provides: "Phase 目标、依赖、成功标准和计划清单"
contains: "Phase 1: 协作入口与项目记忆稳定化"
- path: ".planning/REQUIREMENTS.md"
provides: "需求追踪与阶段映射"
contains: "ARCH-04"
- path: ".planning/PROJECT.md"
provides: "项目核心价值、上下文和关键决策"
contains: "GSD 作为项目外置记忆"
key_links:
- from: ".planning/STATE.md"
to: ".planning/ROADMAP.md"
via: "当前阶段与下一步映射"
pattern: "Phase 1 - 协作入口与项目记忆稳定化"
- from: ".planning/ROADMAP.md"
to: ".planning/REQUIREMENTS.md"
via: "Requirement ID 覆盖"
pattern: "COLL-01, COLL-02, ARCH-04"
- from: ".planning/PROJECT.md"
to: ".planning/STATE.md"
via: "项目核心价值与当前焦点对齐"
pattern: "Current focus"
---
<objective>
把 `.planning` 固化成 Phase 1 的唯一项目记忆恢复层,让新会话可以不依赖聊天记录恢复上下文。
Purpose: 落地 D-05、D-06,并把 `COLL-02` 与 `ARCH-04` 需要的恢复链条固定到 `.planning` 内部。
Output: 对齐后的 `PROJECT / REQUIREMENTS / ROADMAP / STATE` 四层项目记忆。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/REQUIREMENTS.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01-协作入口与项目记忆稳定化/01-CONTEXT.md
@AGENTS.md
@docs/workflow/团队协作模式.md
@docs/workflow/模块边界说明.md
@docs/workflow/主线同步操作手册.md
</context>
<tasks>
<task type="auto">
<name>Task 1: 收紧 STATE 快速恢复入口并清理重复状态</name>
<files>.planning/STATE.md, .planning/PROJECT.md</files>
<read_first>
.planning/STATE.md
.planning/PROJECT.md
.planning/ROADMAP.md
.planning/phases/01-协作入口与项目记忆稳定化/01-CONTEXT.md
AGENTS.md
docs/workflow/团队协作模式.md
docs/workflow/模块边界说明.md
docs/workflow/主线同步操作手册.md
</read_first>
<acceptance_criteria>
- `.planning/STATE.md` 明确保留当前阶段、当前 focus、下一步和风险,符合 D-05。
- `.planning/PROJECT.md` 只保留项目级长期背景与关键决策,不复制 `STATE.md` 的运行态状态,符合 D-06。
- 两个文件都不新增 `.planning` 之外的状态入口。
</acceptance_criteria>
<action>按 D-05、D-06 重新梳理 `STATE.md` 和 `PROJECT.md` 的职责:`STATE.md` 只做快速恢复入口,写清当前阶段、下一步、风险和恢复文件;`PROJECT.md` 只保留长期项目定义、约束和关键决策。删除会导致状态分散的重复段落,但不要引入新的记忆文档或把 Phase 2 之后的内容提前塞进来。</action>
<verify>
<automated>rg -n "Current focus|Phase: 1 of 5|Blockers/Concerns|GSD 作为项目外置记忆|Key Decisions" .planning/STATE.md .planning/PROJECT.md</automated>
</verify>
<done>新会话先打开 `STATE.md` 就能知道现在在哪一阶段、接下来做什么、有哪些风险;`PROJECT.md` 不再和 `STATE.md` 抢角色。</done>
</task>
<task type="auto">
<name>Task 2: 对齐路线图与需求追踪的 Phase 1 记忆闭环</name>
<files>.planning/ROADMAP.md, .planning/REQUIREMENTS.md</files>
<read_first>
.planning/ROADMAP.md
.planning/REQUIREMENTS.md
.planning/STATE.md
.planning/phases/01-协作入口与项目记忆稳定化/01-CONTEXT.md
docs/workflow/团队协作模式.md
docs/workflow/模块边界说明.md
docs/workflow/主线同步操作手册.md
</read_first>
<acceptance_criteria>
- `.planning/ROADMAP.md` 中 Phase 1 的目标、成功标准、计划数量和计划清单与实际 `PLAN.md` 文件一致。
- `.planning/REQUIREMENTS.md` 里 `COLL-01`、`COLL-02`、`ARCH-04` 仍明确映射到 Phase 1,且没有漏映射。
- 两个文件能让后续执行器直接判断“Phase 1 做什么、为什么、做到什么算完成”。
</acceptance_criteria>
<action>把 `ROADMAP.md` 和 `REQUIREMENTS.md` 调整成可执行追踪面板:保持 Phase 1 goal、requirements、success criteria、plans list 同步;确保 `COLL-01 / COLL-02 / ARCH-04` 的映射与计划文件一致;不要新增额外阶段,也不要把未开始的业务主链任务混入 Phase 1。</action>
<verify>
<automated>rg -n "COLL-01|COLL-02|ARCH-04|Phase 1: 协作入口与项目记忆稳定化|01-01-PLAN.md|01-02-PLAN.md" .planning/ROADMAP.md .planning/REQUIREMENTS.md</automated>
</verify>
<done>`.planning` 内部对 Phase 1 的目标、需求、成功标准、计划清单和当前状态形成单一闭环,执行阶段不需要再靠聊天记录补全上下文。</done>
</task>
</tasks>
<verification>
执行后分别用关键词校验 `STATE / PROJECT / ROADMAP / REQUIREMENTS`,确认快速恢复入口、需求映射、Phase 1 成功标准和计划清单一致。
</verification>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`ARCH-04` 达成:新 AI 会话只读 `.planning/STATE.md` 再按需下钻 `PROJECT / ROADMAP / REQUIREMENTS`,即可恢复项目上下文;`COLL-02` 的 `.planning` 部分同步达成。
</success_criteria>
<output>
After completion, create `.planning/phases/01-协作入口与项目记忆稳定化/01-02-SUMMARY.md`
</output>

View File

@ -1,38 +0,0 @@
# Phase 01 Plan 02: 项目记忆恢复层与需求追踪闭环 Summary
## Outcome
把 `.planning` 收紧成 Phase 1 的单一恢复链:`STATE.md` 只保留当前阶段、下一步、风险与恢复路径;`PROJECT.md` 只保留长期项目定义、约束和关键决策;`ROADMAP.md` 与 `REQUIREMENTS.md` 对齐 Phase 1 的计划清单、需求映射和执行状态。
## Files Updated
- `.planning/STATE.md`
- `.planning/PROJECT.md`
- `.planning/ROADMAP.md`
- `.planning/REQUIREMENTS.md`
## Verification
- `rg -n "Current focus|Phase 1 of 5|Risks|Recovery Path|GSD 作为项目外置记忆|Key Decisions" .planning/STATE.md .planning/PROJECT.md`
- `rg -n "COLL-01|COLL-02|ARCH-04|Phase 1: 协作入口与项目记忆稳定化|01-01-PLAN.md|01-02-PLAN.md" .planning/ROADMAP.md .planning/REQUIREMENTS.md`
- 人工核对:`STATE -> ROADMAP / REQUIREMENTS / PROJECT` 的引用职责没有重复、没有把运行态状态散落回其他文档
## Deviations from Plan
None - 本次只在计划允许的 `.planning` 文件范围内收口,没有扩写到代码或协作文档正文。
## Risks
- Phase 1 还没整体结束,`01-01-PLAN.md` 仍需完成,协作入口规则才能与 `.planning` 记忆层一起闭环。
- 当前结论基于你限定的写入范围,没有去改动其他 executor 正在负责的文件。
## 规范审查结论
- 结论:`passed`
- 审查范围:架构、视觉、根因修复
- 说明:本次只调整 `.planning` 记忆层职责,没有破坏代码架构,也没有引入视觉改动;根因修复点是“项目状态分散、恢复入口不唯一”,不是做表面补丁。
- 标注:这是自审,不是独立复审
## Self-Check
PASSED

View File

@ -1,140 +0,0 @@
---
phase: 01-协作入口与项目记忆稳定化
plan: 03
type: execute
wave: 2
depends_on:
- 01-01
- 01-02
files_modified:
- .planning/ROADMAP.md
- .planning/STATE.md
- .planning/REQUIREMENTS.md
autonomous: true
gap_closure: true
requirements:
- COLL-01
- COLL-02
- ARCH-04
must_haves:
truths:
- "`.planning` 内部对 Phase 1 的完成状态只有一个结论:01-01 和 01-02 已完成,Phase 1 已闭环。"
- "新会话打开 `STATE.md` 时,能直接看到 Phase 1 已完成以及下一步应转入 Phase 2。"
- "`COLL-01`、`COLL-02`、`ARCH-04` 的追踪状态与 Phase 1 实际产物一致,不再停留在过期状态。"
artifacts:
- path: ".planning/ROADMAP.md"
provides: "Phase 1 计划清单、进度表和阶段完成状态"
contains: "01-01-PLAN.md"
- path: ".planning/STATE.md"
provides: "最新完成计划、阶段状态和下一步"
contains: "Latest completed plan"
- path: ".planning/REQUIREMENTS.md"
provides: "Phase 1 requirement traceability status"
contains: "COLL-01"
key_links:
- from: ".planning/phases/01-协作入口与项目记忆稳定化/01-01-SUMMARY.md"
to: ".planning/ROADMAP.md"
via: "完成产物回写计划状态"
pattern: "01-01-PLAN.md"
- from: ".planning/phases/01-协作入口与项目记忆稳定化/01-02-SUMMARY.md"
to: ".planning/STATE.md"
via: "最新完成计划与阶段状态同步"
pattern: "01-02-PLAN.md"
- from: ".planning/ROADMAP.md"
to: ".planning/REQUIREMENTS.md"
via: "Phase 1 完成状态驱动 requirement status"
pattern: "COLL-01|COLL-02|ARCH-04"
---
<objective>
只修复 Phase 1 在 `.planning` 内部的状态冲突,把已完成的 01-01、01-02 结果同步回路线图、状态页和需求追踪。
Purpose: 解决 `01-VERIFICATION.md` 指出的唯一 blocker,让 `.planning` 再次成为可信的项目恢复入口,符合 D-05、D-06,并收口 COLL-01、COLL-02、ARCH-04。
Output: 同步后的 `.planning/ROADMAP.md`、`.planning/STATE.md`、`.planning/REQUIREMENTS.md`,以及本次 gap closure 的执行总结预期。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/STATE.md
@.planning/ROADMAP.md
@.planning/REQUIREMENTS.md
@.planning/phases/01-协作入口与项目记忆稳定化/01-CONTEXT.md
@.planning/phases/01-协作入口与项目记忆稳定化/01-VERIFICATION.md
@.planning/phases/01-协作入口与项目记忆稳定化/01-01-SUMMARY.md
@.planning/phases/01-协作入口与项目记忆稳定化/01-02-SUMMARY.md
</context>
<tasks>
<task type="auto">
<name>Task 1: 回写 Phase 1 计划与状态页的真实完成状态</name>
<files>.planning/ROADMAP.md, .planning/STATE.md</files>
<read_first>
.planning/phases/01-协作入口与项目记忆稳定化/01-VERIFICATION.md
.planning/phases/01-协作入口与项目记忆稳定化/01-01-SUMMARY.md
.planning/phases/01-协作入口与项目记忆稳定化/01-02-SUMMARY.md
.planning/ROADMAP.md
.planning/STATE.md
.planning/phases/01-协作入口与项目记忆稳定化/01-CONTEXT.md
</read_first>
<acceptance_criteria>
- `.planning/ROADMAP.md` 把 `01-01-PLAN.md`、`01-02-PLAN.md` 都标记为已完成,`Plans` 数量与实际一致,Phase 1 和进度表状态同步收口。
- `.planning/STATE.md` 的 `Latest completed plan`、`Phase status`、`Last activity`、`Next Step` 与“Phase 1 已完成、下一步进入 Phase 2”一致。
- 文案只做状态同步,不补写 Phase 2 业务内容,不改 `.planning` 之外任何文件,符合 D-07。
</acceptance_criteria>
<action>依据 `01-VERIFICATION.md` 的 gap 和 `01-01/01-02-SUMMARY.md` 的真实完成结果,统一回写 `ROADMAP.md` 与 `STATE.md`。明确 01-01、01-02 已完成;如果 Phase 1 的所有计划都已完成,就把 Phase 1 标记为 complete,并把进度表、完成记录、下一步一起改成 Phase 2 启动前的状态。不要新造计划,不要保留“仍需等待 01-01”的旧结论。</action>
<verify>
<automated>rg -n "01-01-PLAN.md|01-02-PLAN.md|Phase 1: 协作入口与项目记忆稳定化|Phase status|Latest completed plan|Next Step|Phase 2" .planning/ROADMAP.md .planning/STATE.md</automated>
</verify>
<done>路线图和状态页不再把 Phase 1 同时写成“已完成”和“进行中”,新会话从 `STATE.md` 出发会得到正确的下一步。</done>
</task>
<task type="auto">
<name>Task 2: 对齐 Phase 1 需求追踪并定义本次 gap closure 总结输出</name>
<files>.planning/REQUIREMENTS.md</files>
<read_first>
.planning/REQUIREMENTS.md
.planning/ROADMAP.md
.planning/phases/01-协作入口与项目记忆稳定化/01-VERIFICATION.md
.planning/phases/01-协作入口与项目记忆稳定化/01-01-SUMMARY.md
.planning/phases/01-协作入口与项目记忆稳定化/01-02-SUMMARY.md
.planning/phases/01-协作入口与项目记忆稳定化/01-CONTEXT.md
</read_first>
<acceptance_criteria>
- `COLL-01`、`COLL-02`、`ARCH-04` 的 `Traceability` 状态与 Phase 1 完成状态一致,不再出现 Pending / In progress 的过期状态。
- `Phase 1 Coverage` 与 `Traceability` 对 01-01、01-02 的描述一致,能解释每个 requirement 是由哪些已完成计划收口。
- 执行总结要求明确:本次 gap closure 完成后生成 `01-03-SUMMARY.md`,说明同步了哪些状态和为什么能关闭 verification gap。
</acceptance_criteria>
<action>把 `REQUIREMENTS.md` 回写成已完成后的真实状态:`COLL-01` 对应 01-01,`COLL-02` 由 01-01 和 01-02 共同完成,`ARCH-04` 由 01-02 完成,并让状态与 Task 1 回写后的 Phase 1 结论保持一致。只同步 Phase 1 这三个 requirement,不顺手改其他阶段需求。执行完成后的总结里要专门记录“本次只做 `.planning` 状态同步,已关闭 01-VERIFICATION 的唯一 gap”。</action>
<verify>
<automated>rg -n "COLL-01|COLL-02|ARCH-04|Implemented|Completed|01-01-PLAN.md|01-02-PLAN.md|Phase 1" .planning/REQUIREMENTS.md</automated>
</verify>
<done>需求追踪不再滞后于真实执行结果,执行器也知道需要用 `01-03-SUMMARY.md` 记录本次 gap closure 的关闭依据。</done>
</task>
</tasks>
<verification>
先对照 `01-VERIFICATION.md` 的 gap 列表逐项核对,再运行两条 `rg` 命令确认:
1. `ROADMAP.md`、`STATE.md` 已统一反映 Phase 1 完成和 Phase 2 为下一步。
2. `REQUIREMENTS.md` 已把 `COLL-01`、`COLL-02`、`ARCH-04` 更新到与 Phase 1 一致的状态。
</verification>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`01-VERIFICATION.md` 中关于 “Phase 1 的目标、需求、成功标准和当前状态在 .planning 内部没有相互矛盾” 的失败项可以被关闭;新会话只看 `.planning` 就会得到一致结论:Phase 1 已完成,下一步进入 Phase 2。
</success_criteria>
<output>
After completion, create `.planning/phases/01-协作入口与项目记忆稳定化/01-03-SUMMARY.md`
</output>

View File

@ -1,41 +0,0 @@
# Phase 01 Plan 03: `.planning` 状态同步与 verification gap closure Summary
## Outcome
本次执行只做 `.planning` 内部状态同步,没有扩到业务代码或协作文档正文。核心结果是把 `ROADMAP.md`、`STATE.md`、`REQUIREMENTS.md` 全部回写到同一个结论:`01-01` 和 `01-02` 已完成,Phase 1 已闭环,下一步进入 Phase 2。
这次修掉的是 `01-VERIFICATION.md` 指出的唯一 blocker,也就是 `.planning` 内部还保留了“Phase 1 进行中”和“01-01 未完成”的过期状态,导致新会话无法从 `.planning` 得到唯一可信的恢复结论。
## Files Updated
- `.planning/ROADMAP.md`
- `.planning/STATE.md`
- `.planning/REQUIREMENTS.md`
## Verification
- `rg -n "01-01-PLAN.md|01-02-PLAN.md|Status|Complete|2/2|Phase 1" .planning/ROADMAP.md`
- `rg -n "Current focus|Current phase|Latest completed plan|Phase status|Next Step|Phase 2" .planning/STATE.md`
- `rg -n "COLL-01|COLL-02|ARCH-04|Completed by" .planning/REQUIREMENTS.md`
结果:Phase 1 的计划完成状态、恢复入口和 requirement traceability 已统一,不再出现 `Pending`、`In progress` 与已存在 Summary 冲突的情况。
## Deviations from Plan
None - 本次严格只同步 `.planning` 三个状态文件,并按计划补写执行总结,没有扩大范围。
## Risks
- 本次只关闭了 Phase 1 的状态矛盾,不等于 Phase 2 已经开始实现。
- 当前结论依赖 `.planning` 继续被当成唯一恢复入口;如果后续又把运行态结论写回其他零散文档,会再次造成记忆分叉。
## 规范审查结论
- 结论:`passed`
- 审查范围:架构、视觉、根因修复
- 说明:本次只同步 `.planning` 状态,不涉及业务架构和 UI;修的是状态真相不一致这个根因,不是补丁式遮盖。
- 标注:这是自审,不是独立复审
## Self-Check
PASSED

View File

@ -1,104 +0,0 @@
# Phase 1: 协作入口与项目记忆稳定化 - Context
**Gathered:** 2026-04-02 (assumptions mode)
**Status:** Ready for planning
<domain>
## Phase Boundary
固定当前项目的协作入口、分支与收口规则、以及 GSD 项目级记忆恢复入口,让新会话和协作者进入项目时可以按同一套规则接入主链,而不是继续依赖口头说明恢复上下文。本阶段不重做业务主链,也不扩展包装或入库能力。
</domain>
<decisions>
## Implementation Decisions
### 协作入口
- **D-01:** 新会话和协作者的统一入口固定为 `.planning`、`docs/workflow` 和外层 `AGENTS.md`,不新增第二套并行入口。
- **D-02:** `docs/workflow/团队协作模式.md`、`docs/workflow/模块边界说明.md`、`docs/workflow/主线同步操作手册.md` 作为进入 `furniture-3d-system/` 的第一层协作文档入口。
### 分支与收口规则
- **D-03:** 分支命名和主线收口规则视为锁定约束,继续沿用 `codex/<负责人>-<工作域>` 与 `codex/<负责人>-<工作域>-<任务简称>`。
- **D-04:** `main` 只由 `WuYongJie` 收口,`codex/xuxiake-packaging` 作为包装长期分支保留,长期共享分支同步 `main` 用 `merge`,临时个人任务分支才用 `rebase`。
### 项目记忆恢复
- **D-05:** `.planning/STATE.md` 作为“当前阶段、下一步、风险”的唯一快速恢复入口,`PROJECT.md`、`REQUIREMENTS.md`、`ROADMAP.md` 作为补充层。
- **D-06:** GSD 项目记忆更新以阶段文件和 `.planning` 结构为主,不把状态分散到零散说明文档里。
### 阶段边界
- **D-07:** Phase 1 只固定协作边界和记忆边界,不趁机重做业务主链结构。
- **D-08:** 现有主链和共享接缝在本阶段只做明确标注、引用和恢复入口固化,不提前改 `draft-group`、`intake`、`schema.prisma` 等敏感接缝。
### the agent's Discretion
本阶段中 GSD 文件的具体组织方式、说明文案细化顺序、以及后续 plan 的任务切分粒度,由 agent 在不突破以上边界的前提下决定。
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### 项目级记忆与路线
- `.planning/PROJECT.md` — 项目目标、核心价值、约束、关键决策
- `.planning/REQUIREMENTS.md` — Phase 1 对应的 `COLL-01`、`COLL-02`、`ARCH-04`
- `.planning/ROADMAP.md` — Phase 1 的目标、依赖和成功标准
- `.planning/STATE.md` — 当前项目状态恢复入口
### 协作文档
- `docs/workflow/团队协作模式.md` — 固定分工、分支规则、同步与收口流程
- `docs/workflow/模块边界说明.md` — 新平台主链、包装线、旧兼容链、共享接缝边界
- `docs/workflow/主线同步操作手册.md` — 长期分支 merge / 临时分支 rebase 的执行口径
### 全局规则
- `../AGENTS.md` — 全仓库级协作规则和进入项目的默认入口要求
### 代码库结构参考
- `.planning/codebase/STRUCTURE.md` — 目录组织和关键文件位置
- `.planning/codebase/CONVENTIONS.md` — 约定、命名和结构模式
- `.planning/codebase/STACK.md` — 当前技术栈和环境边界
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- `.planning/PROJECT.md`、`.planning/REQUIREMENTS.md`、`.planning/ROADMAP.md`、`.planning/STATE.md` 已经形成 GSD 的第一层项目记忆,可直接作为后续 plan 和 execute 的输入。
- `docs/workflow/团队协作模式.md`、`docs/workflow/模块边界说明.md`、`docs/workflow/主线同步操作手册.md` 已经覆盖协作规则、模块边界和同步口径,不需要从头再造。
- 根目录 `AGENTS.md` 已经把“进入 `furniture-3d-system/` 先读哪些文档”的规则写成全局入口。
### Established Patterns
- 当前项目已经形成“主线负责人收口、包装线协作、旧 CAD 兼容链保留”的组织模式。
- 当前仓库的工程结构仍以 `Route -> Service -> Repository -> Prisma` 为后端主模式,本阶段不需要动到这条结构线。
- 当前 GSD 配置是 `yolo + assumptions + parallelization enabled`,适合在少打断的前提下继续往下推进。
### Integration Points
- 后续 plan 需要围绕 `.planning`、`docs/workflow`、根级 `AGENTS.md` 三层入口做固化和必要修订。
- 如果后续需要把项目恢复入口暴露给新会话,关键接点就在 `.planning/STATE.md` 与 `AGENTS.md` 的引用关系。
- Phase 2 之后的业务推进,需要继承本阶段确认的协作规则和记忆入口,而不是重新定义。
</code_context>
<specifics>
## Specific Ideas
- 这个项目要“完全按 GSD-first 走”,不是继续用原生 Codex 的直推式节奏再顺便兼容 `.planning`。
- 当前体验目标不是重做业务,而是先让 GSD 真正成为项目推进的主工作流。
</specifics>
<deferred>
## Deferred Ideas
- 资产中心到草稿组的业务继续收口 —— 属于 Phase 2
- 包装方案与共享接缝的稳定化 —— 属于 Phase 3
- 正式入库与轻仓库闭环 —— 属于 Phase 4
- 核心服务继续拆分收口 —— 属于 Phase 5
</deferred>
---
*Phase: 01-协作入口与项目记忆稳定化*
*Context gathered: 2026-04-02*

View File

@ -1,36 +0,0 @@
# Phase 1: 协作入口与项目记忆稳定化 - Discussion Log (Assumptions Mode)
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions captured in CONTEXT.md — this log preserves the analysis.
**Date:** 2026-04-02
**Phase:** 01-协作入口与项目记忆稳定化
**Mode:** assumptions
**Areas analyzed:** 协作入口形态, 分支与收口规则, 项目记忆的唯一恢复源, Phase 1 的边界重点
## Assumptions Presented
### 协作入口形态
| Assumption | Confidence | Evidence |
|------------|-----------|----------|
| Phase 1 应该把统一入口固定为 `.planning` + `docs/workflow` + 外层 `AGENTS.md`,而不是再新增一套分散入口或依赖代码内注释。 | Confident | `D:\AI Code\3danzhuang\AGENTS.md`, `.planning/ROADMAP.md`, `.planning/STATE.md`, `.planning/PROJECT.md` |
### 分支与收口规则
| Assumption | Confidence | Evidence |
|------------|-----------|----------|
| Phase 1 需要把分支命名和主线收口规则视为锁定约束,不再把泛分支名或“谁都能直接并 main”当可选做法。 | Confident | `docs/workflow/团队协作模式.md`, `docs/workflow/主线同步操作手册.md`, `.planning/PROJECT.md` |
### 项目记忆的唯一恢复源
| Assumption | Confidence | Evidence |
|------------|-----------|----------|
| `.planning/STATE.md` 应作为“当前阶段、下一步、风险”的唯一快速恢复入口,`PROJECT.md` / `REQUIREMENTS.md` / `ROADMAP.md` 作为补充层。 | Confident | `.planning/ROADMAP.md`, `.planning/STATE.md`, `.planning/PROJECT.md` |
### Phase 1 的边界重点
| Assumption | Confidence | Evidence |
|------------|-----------|----------|
| Phase 1 的重点是固定协作边界和记忆边界,不是趁机重做主链结构;现有主链和共享接缝只需要被明确标注和引用。 | Likely | `.planning/PROJECT.md`, `docs/workflow/模块边界说明.md`, `src/components/platform/platform-navigation.ts`, `src/server/services/draft-group.service.ts`, `src/server/services/intake.service.ts`, `prisma/schema.prisma` |
## Corrections Made
No corrections — all assumptions confirmed by direct GSD-first推进决策。

View File

@ -1,108 +0,0 @@
---
phase: 01-协作入口与项目记忆稳定化
verified: 2026-04-02T11:59:45.1660327Z
status: passed
score: 6/6 must-haves verified
re_verification:
previous_status: gaps_found
previous_score: 5/6
gaps_closed:
- "Phase 1 的目标、需求、成功标准和当前状态在 `.planning` 内部没有相互矛盾。"
gaps_remaining: []
regressions: []
---
# Phase 1: 协作入口与项目记忆稳定化 Verification Report
**Phase Goal:** 新会话和协作者进入项目时,能按同一套规则接入主链,不再靠口头传递恢复上下文
**Verified:** 2026-04-02T11:59:45.1660327Z
**Status:** passed
**Re-verification:** Yes - after gap closure
## Goal Achievement
### Observable Truths
| # | Truth | Status | Evidence |
| --- | --- | --- | --- |
| 1 | 新协作者进入 `furniture-3d-system` 后,能从固定入口读到统一的分支命名与同步规则。 | ✓ VERIFIED | 根级 `AGENTS.md` 强制先读 3 份 workflow 文档;`团队协作模式.md` 和 `主线同步操作手册.md` 明确给出分支命名、`main` 收口人和同步命令。 |
| 2 | AI 会话不会再把协作规则读成多套互相矛盾的口径。 | ✓ VERIFIED | `AGENTS.md`、`团队协作模式.md`、`模块边界说明.md`、`主线同步操作手册.md` 对 `WuYongJie`、`codex/xuxiake-packaging`、`merge main` / `rebase main` 的表述一致。 |
| 3 | 包装长期分支和临时任务分支的 `merge` / `rebase` 规则可以直接从文档验证。 | ✓ VERIFIED | 两份 workflow 文档都明确“长期分支 `merge main`、临时任务分支 `rebase main`”,并给出可执行命令。 |
| 4 | 新 AI 会话只读 `.planning` 就能恢复当前阶段、下一步和主要风险。 | ✓ VERIFIED | `.planning/STATE.md` 直接给出 `Current focus`、`Current phase`、`Next Step`、`Risks`、`Recovery Path`。 |
| 5 | Phase 1 的目标、需求、成功标准和当前状态在 `.planning` 内部没有相互矛盾。 | ✓ VERIFIED | `ROADMAP.md` 已把 Phase 1 标为 `Complete`、计划清单为 `2/2`;`STATE.md` 已写明 Phase 1 complete 并转入 Phase 2;`REQUIREMENTS.md` Traceability 已同步为 `Completed by 01-01/01-02`。 |
| 6 | 项目记忆不会再分散到零散说明文档里,`STATE.md` 保持唯一快速恢复入口。 | ✓ VERIFIED | `PROJECT.md` 的 `Project Memory Model` 明确 `STATE.md` 是唯一快速恢复入口;`STATE.md` 的恢复路径只回链到 `ROADMAP.md`、`REQUIREMENTS.md`、`PROJECT.md`。 |
**Score:** 6/6 truths verified
### Required Artifacts
| Artifact | Expected | Status | Details |
| --- | --- | --- | --- |
| `D:\AI Code\3danzhuang\AGENTS.md` | 仓库级固定入口与强制阅读说明 | ✓ VERIFIED | 明确要求进入 `furniture-3d-system/` 前先读 3 份 workflow 文档。 |
| `docs/workflow/团队协作模式.md` | 角色分工、分支命名、收口规则 | ✓ VERIFIED | 明确角色边界、分支命名、主线收口与同步节奏。 |
| `docs/workflow/模块边界说明.md` | 主链、包装线、兼容链和共享接缝边界 | ✓ VERIFIED | 明确敏感接缝、禁止直改区和开工前入口。 |
| `docs/workflow/主线同步操作手册.md` | 长期分支 `merge` 与临时分支 `rebase` 口径 | ✓ VERIFIED | 明确命令、适用场景和禁用动作。 |
| `.planning/STATE.md` | 当前阶段、下一步、风险的快速恢复入口 | ✓ VERIFIED | 结构完整,且内容已回写到 Phase 2 准备态。 |
| `.planning/ROADMAP.md` | Phase 目标、依赖、成功标准和计划清单 | ✓ VERIFIED | Phase 1 完成状态、计划清单、执行说明与阶段目录 Summary 一致。 |
| `.planning/REQUIREMENTS.md` | 需求追踪与阶段映射 | ✓ VERIFIED | Phase 1 coverage 和 Traceability 已与 `01-01`、`01-02`、`01-03` 的状态同步。 |
| `.planning/PROJECT.md` | 项目核心价值、长期约束和记忆模型 | ✓ VERIFIED | 长期定义与运行态职责分离,没有重新抢 `STATE.md` 的入口职责。 |
### Key Link Verification
| From | To | Via | Status | Details |
| --- | --- | --- | --- | --- |
| `D:\AI Code\3danzhuang\AGENTS.md` | `docs/workflow/团队协作模式.md` | 进入项目前置阅读 | ✓ WIRED | 根级入口明确列出 3 份 workflow 文档。 |
| `docs/workflow/团队协作模式.md` | `docs/workflow/主线同步操作手册.md` | 同步规则引用 | ✓ WIRED | 文档正文直接回链并展开 `merge` / `rebase` 口径。 |
| `docs/workflow/模块边界说明.md` | `docs/workflow/团队协作模式.md` | 工作域与分支规则映射 | ✓ WIRED | 开头与“开工前最少要看什么”都要求先读团队协作模式。 |
| `.planning/STATE.md` | `.planning/ROADMAP.md` | 当前阶段与下一步恢复路径 | ✓ WIRED | `Recovery Path` 明确要求从 `STATE.md` 下钻 `ROADMAP.md`。 |
| `.planning/ROADMAP.md` | `.planning/REQUIREMENTS.md` | Requirement ID 覆盖 | ✓ WIRED | Phase 1 引用 `COLL-01`、`COLL-02`、`ARCH-04`,Requirements 中已有对应映射和完成状态。 |
| `.planning/PROJECT.md` | `.planning/STATE.md` | 项目记忆模型对齐 | ✓ WIRED | `PROJECT.md` 明确声明 `STATE.md` 是唯一快速恢复入口。 |
### Data-Flow Trace (Level 4)
| Artifact | Data Variable | Source | Produces Real Data | Status |
| --- | --- | --- | --- | --- |
| Documentation phase artifacts | N/A | Static markdown | N/A | SKIPPED - 本阶段是文档与项目记忆核验,不涉及运行时数据流。 |
### Behavioral Spot-Checks
| Behavior | Command | Result | Status |
| --- | --- | --- | --- |
| 协作入口规则可被直接检出 | `rg -n "团队协作模式|模块边界说明|主线同步操作手册|codex/<负责人>-<工作域>|codex/xuxiake-packaging|WuYongJie|merge main|rebase main"` | 关键命中全部存在,入口与口径一致 | ✓ PASS |
| `.planning` 恢复入口可被直接检出 | `rg -n "Current focus|Current phase|Latest completed plan|Phase status|Next Step|Recovery Path|COLL-01|COLL-02|ARCH-04"` | 当前阶段、风险、下一步和需求映射都能直接恢复 | ✓ PASS |
| 旧 blocker 是否关闭 | 对比 `01-01-SUMMARY.md`、`01-02-SUMMARY.md`、`01-03-SUMMARY.md` 与 `ROADMAP.md` / `STATE.md` / `REQUIREMENTS.md` | 已不存在“Summary 说完成,但 `.planning` 说未完成”的冲突 | ✓ PASS |
### Requirements Coverage
| Requirement | Source Plan | Description | Status | Evidence |
| --- | --- | --- | --- | --- |
| `COLL-01` | `01-01-PLAN.md` | 主线负责人和包装负责人拥有固定、明确、不歧义的分支命名规则 | ✓ SATISFIED | `AGENTS.md`、`团队协作模式.md`、`主线同步操作手册.md` 已统一分支命名和主线收口规则。 |
| `COLL-02` | `01-01-PLAN.md`, `01-02-PLAN.md` | 协作入口文档足够清晰,新的 AI 会话进入项目后能先读到正确规则 | ✓ SATISFIED | 外层入口文档和 `.planning` 恢复链已形成同一套接入路径,且状态不再冲突。 |
| `ARCH-04` | `01-02-PLAN.md` | GSD 需要能基于 `.planning` 文件在跨对话场景下恢复项目上下文 | ✓ SATISFIED | `STATE -> ROADMAP / REQUIREMENTS / PROJECT` 恢复链存在且状态一致。 |
### Anti-Patterns Found
| File | Line | Pattern | Severity | Impact |
| --- | --- | --- | --- | --- |
| None | - | 未发现 TODO、占位文案、空实现或新的状态分叉 | - | 本次复核范围内无 blocker / warning 级反模式。 |
### Human Verification Required
None. 本阶段目标是文档与 `.planning` 恢复链一致性,已可通过静态检查确认。
## 规范审查
- 结论:`passed`
- 标注:这是自审,不是独立复审
- 架构:本次核验确认协作入口、模块边界和 `.planning` 记忆模型没有互相打穿,也没有新增第二套并行入口。
- 视觉:本阶段没有 UI 改动,本次复核未发现视觉标准风险。
- 根因修复:之前的 blocker 是 `.planning` 真相不一致;现在 `ROADMAP.md`、`STATE.md`、`REQUIREMENTS.md` 已同步到同一完成结论,属于根因收口,不是表面补丁。
## Gaps Summary
旧 blocker 已关闭。Phase 1 现在同时满足两件事:协作者能从固定文档入口拿到同一套主链接入规则,新会话也能只靠 `.planning` 恢复当前阶段、下一步和风险,而且这两条链路已经没有内部状态冲突。因此本阶段目标已达成,可以判定 Phase 1 complete。
---
_Verified: 2026-04-02T11:59:45.1660327Z_
_Verifier: Claude (gsd-verifier)_

View File

@ -1,241 +0,0 @@
---
phase: 02-资产到草稿组主链收口
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- src/lib/open-product/platform-api.ts
- src/server/services/asset-center.service.ts
- src/server/services/asset-center.service.types.ts
- src/server/services/__tests__/asset-center.service.test.ts
- src/lib/open-product/single-board-desk.ts
- src/server/services/combination.service.ts
- src/server/services/combination.service.types.ts
- src/app/api/platform/schemas.ts
- src/server/services/__tests__/combination.service.test.ts
- src/lib/open-product/__tests__/single-board-desk.test.ts
autonomous: true
requirements:
- MAIN-01
- MAIN-02
must_haves:
truths:
- "平台负责人在资产中心可以稳定维护资产族与资产规格,并且自动组合直接消费这套维护结果。"
- "平台负责人在自动组合阶段看到的候选不再只是资产族摘要,而是可直接确认的实际组合关系。"
- "自动组合候选直接带出可驱动 3D 预览和摘要展示的数据,不必先进草稿组才第一次得到真实拼装结果。"
- "候选生成仍然以资产中心维护的 `AssetFamily / AssetVariant` 为数据真相,不回退到旧 `Product / SKU`。"
artifacts:
- path: "src/server/services/asset-center.service.ts"
provides: "资产族与规格款维护及审核后的主链服务"
contains: "listFamilies"
- path: "src/lib/open-product/platform-api.ts"
provides: "资产中心维护接口与自动组合消费接口"
contains: "createAssetFamily"
- path: "src/server/services/combination.service.types.ts"
provides: "预览就绪的自动组合候选 DTO 契约"
contains: "CombinationCandidateDTO"
- path: "src/server/services/combination.service.ts"
provides: "资产中心到候选列表的主链编排"
contains: "generateCandidates"
- path: "src/lib/open-product/single-board-desk.ts"
provides: "单板桌候选粒度与候选物化规则"
contains: "materializeSingleBoardDeskPreSku"
- path: "src/app/api/platform/schemas.ts"
provides: "自动组合接口输入输出所依赖的 schema 约束"
contains: "generateCombinationSchema"
key_links:
- from: "src/server/services/asset-center.service.ts"
to: "src/server/services/combination.service.ts"
via: "资产中心可维护结果直接供自动组合消费"
pattern: "AVAILABLE"
- from: "src/server/services/combination.service.ts"
to: "src/lib/open-product/single-board-desk.ts"
via: "候选生成后立即物化预览和摘要"
pattern: "materializeSingleBoardDeskPreSku"
- from: "src/server/services/combination.service.ts"
to: "src/server/repositories/asset-center.repository.ts"
via: "读取资产族与规格款"
pattern: "asset(Family|Variant)Repository"
- from: "src/app/api/platform/schemas.ts"
to: "src/server/services/combination.service.types.ts"
via: "接口契约与服务 DTO 对齐"
pattern: "generateCombinationSchema"
---
<objective>
把 `/auto-combinations` 的候选结果从“摘要列表”收口成“可直接确认的成品组合 + 预览数据”契约。
Purpose: 按 D-04、D-05、D-06、D-08、D-09、D-11 修根因,让候选阶段就携带足够的组合细节和预览能力,同时继续以资产中心数据为主链真相。
Output: 预览就绪的候选 DTO、单板桌候选规则收口,以及覆盖候选生成的自动化测试。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/02-资产到草稿组主链收口/02-CONTEXT.md
@docs/workflow/团队协作模式.md
@docs/workflow/模块边界说明.md
@src/app/api/auto-combinations/generate/route.ts
@src/server/services/combination.service.ts
@src/server/services/combination.service.types.ts
@src/lib/open-product/single-board-desk.ts
@src/lib/open-product/platform-view.ts
@src/server/services/pre-sku-materialization.service.ts
@src/lib/open-product/__tests__/single-board-desk.test.ts
<interfaces>
From `src/server/services/combination.service.types.ts`:
```ts
export interface GenerateCombinationInput {
seriesTemplateId: string
desktopFamilyIds: string[]
legFamilyIds?: string[]
connectorFamilyIds?: string[]
}
export interface CombinationCandidateDTO extends SingleBoardDeskCandidate {}
```
From `src/lib/open-product/single-board-desk.ts`:
```ts
export interface SingleBoardDeskCandidate {
key: string
defaultName: string
previewMode: 'summary-only'
slotSummary: Array<{ label: string; value: string }>
familySelection: {
desktopFamilyId: string
legGroupFamilyId: string | null
legRoles: Record<'frontLeft' | 'frontRight' | 'rearLeft' | 'rearRight', string | null>
connectorFamilyIds: string[]
}
estimatedCost: number | null
statusTag: '待确认'
}
export function materializeSingleBoardDeskPreSku(input: {
template: SingleBoardDeskTemplate
candidate: SingleBoardDeskCandidate
variants: OpenProductAssetVariant[]
}): MaterializedSingleBoardDeskPreSku
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: 收口资产中心维护链路与可消费验证</name>
<files>src/lib/open-product/platform-api.ts, src/server/services/asset-center.service.ts, src/server/services/asset-center.service.types.ts, src/server/services/__tests__/asset-center.service.test.ts</files>
<read_first>
.planning/phases/02-资产到草稿组主链收口/02-CONTEXT.md
src/lib/open-product/platform-api.ts
src/server/services/asset-center.service.ts
src/server/services/asset-center.service.types.ts
src/server/services/__tests__/asset-center.service.test.ts
src/app/auto-combinations/page.tsx
src/server/services/combination.service.ts
</read_first>
<acceptance_criteria>
- 资产中心现有“资产族 / 规格款维护”链路有明确可执行验证,能证明主线负责人可以维护并产出可被自动组合直接消费的 `AVAILABLE` 数据,符合 `MAIN-01`。
- 如果现有维护链路存在阻断主链消费的问题,本任务要把修复范围收敛在资产中心服务/API/DTO,不把包装、草稿组、正式入库混进来。
- 自动化测试至少覆盖“资产族/规格款可列出、可创建或更新、状态可进入自动组合消费范围”中的关键主链行为,不允许只在计划里口头认定“已经稳定”。
</acceptance_criteria>
<behavior>
- Test 1: 资产中心服务返回的资产族/规格款在状态满足条件时可被自动组合页面筛选进可用集合。
- Test 2: 维护后的资产族/规格款字段口径与自动组合使用的字段一致,例如类型、状态、connectionRoles、modelUrl 等。
- Test 3: 如果某类资产缺少主链必需字段,服务层会给出明确错误或不可用状态,而不是让自动组合阶段再兜底。
</behavior>
<action>先验证并收口 `MAIN-01`:把资产中心维护链路和自动组合消费链路明确连起来。优先复用现有 `asset-center.service.ts` 与 `platform-api.ts`,补足必要的服务层测试和 DTO 对齐,让“资产可维护、且能被后续主链直接使用”成为可验证事实。如果当前代码已经大体稳定,则任务重点放在补验证和去掉隐含前提;如果存在阻断点,则只修资产中心主链消费相关根因,不顺手扩到页面交互或其他模块。</action>
<verify>
<automated>npm test -- --runTestsByPath src/server/services/__tests__/asset-center.service.test.ts</automated>
</verify>
<done>Phase 2 的 `MAIN-01` 不再只是前提假设,而是有明确验证和必要收口动作支撑的可执行任务。</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: 定义预览就绪的候选契约与失败测试</name>
<files>src/server/services/combination.service.types.ts, src/lib/open-product/__tests__/single-board-desk.test.ts, src/server/services/__tests__/combination.service.test.ts</files>
<read_first>
.planning/phases/02-资产到草稿组主链收口/02-CONTEXT.md
src/server/services/combination.service.types.ts
src/lib/open-product/single-board-desk.ts
src/server/services/pre-sku-materialization.service.ts
src/lib/open-product/__tests__/single-board-desk.test.ts
src/server/services/__tests__/draft-group.service.test.ts
</read_first>
<acceptance_criteria>
- 候选 DTO 按 D-05、D-11 明确表达“实际组合关系 + 预览所需快照”,至少覆盖组合明细、`previewNodes`、状态/警告、摘要字段,而不是只保留 `summary-only`。
- 新增或改造的测试先表达失败预期:候选生成结果必须直接包含预览数据,并且数据来源仍然是资产中心的 `AssetFamily / AssetVariant`,符合 D-03、D-08。
- 测试命名和断言能直接说明单板桌候选的桌面、腿位、连接件关系,不把“腿组”继续当成唯一粒度,符合 D-09。
</acceptance_criteria>
<behavior>
- Test 1: `combinationService.generateCandidates()` 返回的每条候选都包含可供 3D 预览复用的 `previewNodes` 和组合摘要。
- Test 2: 单板桌候选会把桌面和四个腿位拆成实际组合关系,而不是只返回“桌腿组”粗粒度描述。
- Test 3: 当资产中心缺少唯一可用规格款时,候选仍返回可解释的状态/警告,而不是默默丢失预览信息。
</behavior>
<action>先把候选契约写清楚,再补测试约束。围绕 D-05、D-08、D-09、D-11 扩展 `CombinationCandidateDTO`,让执行者在实现前就知道候选必须直接携带预览所需数据。测试要覆盖服务层与纯规则层两侧:规则层证明粒度提升到真实组合关系,服务层证明资产中心数据可直接产出预览就绪候选。不要在这一任务里改页面代码,也不要把正式入库或包装字段塞进候选契约。</action>
<verify>
<automated>npm test -- --runTestsByPath src/lib/open-product/__tests__/single-board-desk.test.ts src/server/services/__tests__/combination.service.test.ts</automated>
</verify>
<done>候选契约和测试都已明确表达“候选即预览”的目标,且失败时能准确指出是候选粒度、预览快照还是资产规格款解析出了问题。</done>
</task>
<task type="auto" tdd="true">
<name>Task 3: 实现候选粒度升级与自动组合服务收口</name>
<files>src/lib/open-product/single-board-desk.ts, src/server/services/combination.service.ts, src/server/services/combination.service.types.ts, src/app/api/platform/schemas.ts, src/server/services/__tests__/combination.service.test.ts, src/lib/open-product/__tests__/single-board-desk.test.ts</files>
<read_first>
src/lib/open-product/single-board-desk.ts
src/server/services/combination.service.ts
src/app/api/auto-combinations/generate/route.ts
src/app/api/platform/schemas.ts
src/server/services/pre-sku-materialization.service.ts
src/lib/open-product/platform-view.ts
</read_first>
<acceptance_criteria>
- `combinationService.generateCandidates()` 按 D-04、D-06、D-11 返回可直接驱动左侧预览的候选数据,且字段口径和 `materializeSingleBoardDeskPreSku()` 一致。
- 单板桌候选不再固定为 `previewMode: 'summary-only'` 的摘要结果,而是显式带出组合明细、摘要、预览节点、状态和警告,符合 D-05、D-09。
- 路由 schema 仍只接收主链需要的输入;不把包装、正式产品或旧 CAD 真相混进来,符合 D-01、D-03、D-14。
</acceptance_criteria>
<behavior>
- Test 1: 使用资产中心中的桌面族、桌腿族、连接件族生成候选时,返回值包含 `previewNodes`、组合摘要和状态字段。
- Test 2: 某个腿位或连接件缺少唯一规格款时,候选结果带出 warning/status,而不是直接崩溃。
- Test 3: 接口 schema 仍然只接受 `seriesTemplateId`、资产族选择等主链参数,不接受包装或正式入库字段。
</behavior>
<action>基于 Task 1 的契约,把单板桌候选从“列表摘要”升级成“可直接确认的组合快照”。优先复用 `materializeSingleBoardDeskPreSku()` 和现有资产规格款解析逻辑,而不是再造第二套预览拼装机制,符合 D-08。必要时允许在 `single-board-desk.ts` 内抽公共 helper,但要保持纯规则层纯净;`combination.service.ts` 负责读资产中心数据、调用规则层并返回 DTO;`schemas.ts` 只维护接口输入约束,不做业务拼装判断。</action>
<verify>
<automated>npm test -- --runTestsByPath src/lib/open-product/__tests__/single-board-desk.test.ts src/server/services/__tests__/combination.service.test.ts</automated>
</verify>
<done>自动组合接口已经能稳定返回“可直接确认、可直接预览”的候选结果,后续前端 agent 只需消费 DTO,不需要再反推组合规则或自己拼预览数据。</done>
</task>
</tasks>
<verification>
执行后至少跑通单板桌规则测试和组合服务测试,确认候选粒度、预览快照、资产规格款来源三件事同时成立。
</verification>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`MAIN-01` 与 `MAIN-02` 在自动组合侧达成:资产中心数据可直接生成可确认的组合候选,候选本身就带出主链预览所需数据,不再让草稿组成为第一次看到真实拼装效果的唯一入口。
</success_criteria>
<output>
After completion, create `.planning/phases/02-资产到草稿组主链收口/02-01-SUMMARY.md`
</output>

View File

@ -1,44 +0,0 @@
---
phase: 02-资产到草稿组主链收口
plan: 01
subsystem: combination
tags: [asset-center, auto-combinations, preview, testing]
requires:
- phase: 01-协作入口与项目记忆稳定化
provides: 协作入口与项目记忆稳定
provides:
- 资产中心维护结果可被历史自动组合主链消费
- 单板桌候选预览契约基线
- 组合服务与规则层的自动化验证
affects: [phase-02, asset-center, historical-auto-combinations]
requirements-completed: [MAIN-01, MAIN-02]
completed: 2026-04-02
---
# Phase 2 Plan 01 Summary
**这轮先把“资产中心可维护”与“候选可预览”连成一条历史阶段基线,让 Phase 2 的主链不再只是摘要列表。**
## Accomplishments
- 补齐并验证了 `asset-center(资产中心)` 维护结果可直接被历史 `auto-combinations(自动组合)` 链消费的口径。
- 收口了单板桌候选的预览与摘要契约,让候选结果不再只是粗粒度摘要。
- 为 `combination.service(组合服务)`、`single-board-desk(单板桌规则)` 和 `asset-center(资产中心)` 相关服务补了关键自动化测试。
## Verification
- 已执行:`npm test -- --runTestsByPath src/server/services/__tests__/asset-center.service.test.ts`
- 已执行:`npm test -- --runTestsByPath src/server/services/__tests__/combination.service.test.ts`
- 已执行:`npm test -- --runTestsByPath src/lib/open-product/__tests__/single-board-desk.test.ts`
- 结果:通过
## Risks
- 本总结对应的是 Phase 2 当时的历史主链基线,当前现行入口已演进为 `assisted-assembly(辅助装配) / manual assembly(手动拼装)`,不要再把这里的 `auto-combinations(自动组合)` 直接当成今天的页面入口。
## Review
- 结论:`passed`
- 审查范围:架构、视觉、根因修复
- 标注:这是自审,不是独立复审

View File

@ -1,195 +0,0 @@
---
phase: 02-资产到草稿组主链收口
plan: 02
type: execute
wave: 2
depends_on:
- 02-01
files_modified:
- src/server/services/draft-group.service.types.ts
- src/server/services/pre-sku-materialization.service.ts
- src/server/services/draft-group.service.ts
- src/app/api/platform/schemas.ts
- src/server/services/__tests__/draft-group.service.test.ts
- ../docs/前端UI改动清单.md
autonomous: true
requirements:
- MAIN-02
- MAIN-03
must_haves:
truths:
- "平台负责人把候选送入草稿组后,草稿组里保存的 `PreSku` 结果与候选阶段看到的组合/预览口径一致。"
- "草稿组里的每个预 SKU 都能保存候选摘要、预览快照和后续接缝状态,不会在主链交接时丢字段。"
- "前端 agent 拿到明确交接要求后,可以按固定契约完成“点候选即预览”的页面改动,而不是自行猜后端数据。"
artifacts:
- path: "src/server/services/pre-sku-materialization.service.ts"
provides: "候选到 PreSku 的一致性物化逻辑"
contains: "buildPreparedPreSkus"
- path: "src/server/services/draft-group.service.ts"
provides: "自动组合结果进入草稿组的主链编排"
contains: "createFromAutoCombination"
- path: "src/server/services/draft-group.service.types.ts"
provides: "草稿组明细、PreSku 摘要与接缝状态 DTO"
contains: "DraftGroupPreSkuDTO"
- path: "../docs/前端UI改动清单.md"
provides: "自动组合页候选预览联动的前端交接要求"
contains: "自动组合候选即预览"
key_links:
- from: "src/server/services/draft-group.service.ts"
to: "src/server/services/pre-sku-materialization.service.ts"
via: "候选入草稿组时复用同一物化口径"
pattern: "buildPreparedPreSkus"
- from: "src/server/services/pre-sku-materialization.service.ts"
to: "src/lib/open-product/single-board-desk.ts"
via: "候选物化与预览节点生成"
pattern: "materializeSingleBoardDeskPreSku"
- from: "../docs/前端UI改动清单.md"
to: "src/server/services/draft-group.service.types.ts"
via: "前端按 DTO 消费候选预览与草稿组摘要"
pattern: "previewNodes|displaySummary|assemblyResultStatus"
---
<objective>
把“候选 -> 草稿组 -> PreSku”这段主链收口成同一口径,并把自动组合页的前端联动需求明确交接给前端 workflow。
Purpose: 按 D-06、D-10、D-11、D-12、D-13 修复主链交接不一致问题,避免候选阶段和草稿组阶段各说各话。
Output: 一致的候选入草稿组物化逻辑、草稿组 DTO 收口,以及更新后的前端 UI 交接清单。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/02-资产到草稿组主链收口/02-CONTEXT.md
@.planning/phases/02-资产到草稿组主链收口/02-01-PLAN.md
@src/server/services/draft-group.service.ts
@src/server/services/draft-group.service.types.ts
@src/server/services/pre-sku-materialization.service.ts
@src/app/api/platform/schemas.ts
@src/app/draft-groups/[id]/DraftGroupDetailClient.tsx
@src/components/platform/ModelPreviewPanel.tsx
@src/components/platform/CompositeAssetPreview.tsx
@../docs/前端UI改动清单.md
<interfaces>
From `src/server/services/draft-group.service.types.ts`:
```ts
export interface DraftGroupCandidateInput {
key: string
defaultName: string
familySelection: {
desktopFamilyId: string
legGroupFamilyId?: string | null
legRoles: Partial<
Record<'frontLeft' | 'frontRight' | 'rearLeft' | 'rearRight', string | null | undefined>
>
connectorFamilyIds: string[]
}
}
export interface DraftGroupPreSkuDTO {
id: string
sourceCandidateKey: string
previewNodes: unknown[]
displaySummary: Record<string, unknown> | null
assemblyResultStatus: AssemblyResultStatus | null
assemblyWarnings: string[]
}
```
From `src/server/services/pre-sku-materialization.service.ts`:
```ts
class PreSkuMaterializationService {
async buildPreparedPreSkus(
input: CreateDraftGroupFromAutoCombinationInput,
draftGroupId: string,
): Promise<{
seriesTemplateCode: string
preparedPreSkus: Prisma.PreSkuUncheckedCreateInput[]
}>
}
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: 统一候选入草稿组的物化口径与测试</name>
<files>src/server/services/draft-group.service.types.ts, src/server/services/pre-sku-materialization.service.ts, src/server/services/draft-group.service.ts, src/app/api/platform/schemas.ts, src/server/services/__tests__/draft-group.service.test.ts</files>
<read_first>
.planning/phases/02-资产到草稿组主链收口/02-01-PLAN.md
src/server/services/draft-group.service.ts
src/server/services/draft-group.service.types.ts
src/server/services/pre-sku-materialization.service.ts
src/server/services/__tests__/draft-group.service.test.ts
src/app/api/platform/schemas.ts
</read_first>
<acceptance_criteria>
- 草稿组接收的候选输入和 Plan `02-01` 输出契约一致,入草稿组后保存的 `previewNodes`、摘要、状态、警告不再和候选阶段脱节,符合 D-10、D-11、D-12。
- `createFromAutoCombination()` 继续只落 `DraftGroup -> PreSku`,不引入正式产品或包装方案直存逻辑,符合 D-01、D-10、D-14。
- 测试覆盖多候选保存、四个腿位拆分、摘要/预览口径一致和异常提示,不让“候选能看、草稿组丢字段”再次回归,符合 D-05、D-12。
</acceptance_criteria>
<behavior>
- Test 1: 选择多个候选创建草稿组后,每个 `PreSku` 保存的 `previewNodes`、摘要和状态与候选阶段一致。
- Test 2: 混搭腿位候选写入草稿组时,四个腿位仍分别保留,不会回退成单一腿组摘要。
- Test 3: 缺少可用规格款时,草稿组明细保留 warning/status,后续接缝状态可见。
</behavior>
<action>基于 Plan `02-01` 的候选契约,把候选入草稿组的物化过程收口到同一套口径。优先复用或下沉公共物化 helper,避免 `combination.service.ts` 与 `pre-sku-materialization.service.ts` 各自维护一套组合解释。必要时扩展 `DraftGroupCandidateInput` 和 `createDraftGroupSchema` 以承接预览就绪候选,但服务端仍要自行校验关键字段,不信任前端拼装结果。不要在本任务里把包装字段做成必填,也不要越界改草稿组详情 UI。</action>
<verify>
<automated>npm test -- --runTestsByPath src/server/services/__tests__/draft-group.service.test.ts src/server/services/__tests__/combination.service.test.ts</automated>
</verify>
<done>候选进入草稿组后,`PreSku` 保存结果与候选阶段同源同口径,主链在自动组合和草稿组之间不再出现“重新解释一次候选”的断层。</done>
</task>
<task type="auto">
<name>Task 2: 把自动组合页联动需求写入前端交接清单</name>
<files>../docs/前端UI改动清单.md</files>
<read_first>
../docs/前端UI改动清单.md
.planning/phases/02-资产到草稿组主链收口/02-CONTEXT.md
.planning/phases/02-资产到草稿组主链收口/02-01-PLAN.md
src/app/auto-combinations/AutoCombinationsClient.tsx
src/components/platform/ModelPreviewPanel.tsx
src/components/platform/CompositeAssetPreview.tsx
</read_first>
<acceptance_criteria>
- 按 D-13 把前端要做的事写成清晰交接项,而不是直接在当前 plan 里实现 UI。
- 清单明确写出“候选即预览”的交互目标、候选卡片需要展示的成品组合粒度、左侧预览消费的后端字段,以及联调时如何判断是前端问题还是后端问题,符合 D-06、D-13。
- 文档明确标注:实际页面改动需走前端 workflow / 前端 agent,不在本阶段主链计划里直接写 UI 代码。
</acceptance_criteria>
<action>更新 `../docs/前端UI改动清单.md`,新增一段只针对 `/auto-combinations` 的交接要求:候选列表必须展示实际组合关系;点击候选时左侧复用现有 `ModelPreviewPanel` / `CompositeAssetPreview` 消费后端返回的 `previewNodes`;创建草稿组时提交的候选字段必须和 Plan `02-01`、本计划 Task 1 对齐;如果联调失败,要按“接口字段缺失/口径不一致归后端,展示结构和交互归前端”分流。只写需求和联调边界,不直接改页面代码。</action>
<verify>
<automated>rg -n "自动组合候选即预览|实际组合关系|previewNodes|前端 workflow|联调失败" ../docs/前端UI改动清单.md</automated>
</verify>
<done>前端 agent 已有清晰、可执行的交接清单,后续 UI 落地不需要再回头猜候选字段、预览字段和责任边界。</done>
</task>
</tasks>
<verification>
执行后至少跑通草稿组服务测试,并用文档关键字校验确认前端交接要求已落盘且责任边界明确。
</verification>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`MAIN-02` 与 `MAIN-03` 在主链交接侧达成:候选结果能稳定收口到草稿组并保存为一致的 `PreSku` 摘要/预览数据,前端改动也已被清楚移交给前端 workflow,而不是混在后端计划里含糊处理。
</success_criteria>
<output>
After completion, create `.planning/phases/02-资产到草稿组主链收口/02-02-SUMMARY.md`
</output>

View File

@ -1,93 +0,0 @@
---
phase: 02-资产到草稿组主链收口
plan: 02
subsystem: api
tags: [draft-group, auto-combinations, pre-sku, preview, testing]
requires:
- phase: 02-资产到草稿组主链收口
provides: 自动组合候选预览契约
provides:
- 候选到草稿组的统一物化逻辑
- 缺规格款与多规格款场景下可持续流转的 PreSku 摘要
- 自动组合页前端联调交接清单
affects: [phase-03, packaging, auto-combinations-ui]
tech-stack:
added: []
patterns:
- 单板桌候选和草稿组共享同一套物化 helper
- 用 warning/status 保留异常场景,而不是在主链中途直接拦截
key-files:
created:
- src/server/services/single-board-desk-materialization.ts
- .planning/phases/02-资产到草稿组主链收口/02-02-SUMMARY.md
modified:
- src/server/services/combination.service.ts
- src/server/services/pre-sku-materialization.service.ts
- src/server/services/__tests__/draft-group.service.test.ts
- docs/前端UI改动清单.md
key-decisions:
- "候选阶段和草稿组阶段不再各自解释单板桌候选,统一复用 single-board-desk-materialization helper。"
- "缺规格款和多规格款不再阻断草稿组创建,而是保留装配状态与 warnings 继续进入 PreSku。"
patterns-established:
- "候选主链模式:generateCandidates 与 buildPreparedPreSkus 共用一套物化与诊断逻辑。"
- "交接模式:前端只消费后端契约,不自己补口径;联调失败按字段/展示边界分流。"
requirements-completed: [MAIN-02, MAIN-03]
duration: 45min
completed: 2026-04-02
---
# Phase 2 Plan 02 Summary
**自动组合候选和草稿组 PreSku 现在共用一套物化真相,异常候选也能带着状态与警告继续进入主链。**
## Performance
- **Duration:** 45 min
- **Started:** 2026-04-02T20:40:00+08:00
- **Completed:** 2026-04-02T21:23:50+08:00
- **Tasks:** 2
- **Files modified:** 5
## Accomplishments
- 抽出 `single-board-desk-materialization.ts`,让自动组合和草稿组都复用同一套模板构建、规格款映射和 warning 诊断逻辑。
- 草稿组创建不再因为“缺规格款 / 多规格款”直接失败,而是把 `assemblyResultStatus`、`assemblyWarnings`、`previewNodes`、`displaySummary` 一起保存进 `PreSku`。
- 更新了 `docs/前端UI改动清单.md`,把“候选即预览”的字段契约、页面目标和联调责任边界固定下来。
## Task Commits
未创建提交。当前仓库仍保持本地未提交工作状态。
## Files Created/Modified
- `src/server/services/single-board-desk-materialization.ts` - 单板桌候选共享物化 helper
- `src/server/services/combination.service.ts` - 自动组合候选改为复用共享 helper
- `src/server/services/pre-sku-materialization.service.ts` - 草稿组 PreSku 物化改为复用共享 helper
- `src/server/services/__tests__/draft-group.service.test.ts` - 覆盖缺规格款/多规格款继续收口场景
- `docs/前端UI改动清单.md` - 固定自动组合候选即预览交接清单
## Decisions Made
- 用统一 helper 解决根因,避免 `combination.service` 和 `pre-sku-materialization.service` 再次分叉。
- 保留“双层 warning”:既保留装配层 warning,也保留更可读的业务提示,前端直接展示即可。
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
- 草稿组旧测试假设“异常候选必须拦截”,与当前主链目标冲突;已改成验证 warning/status 持续保留。
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Phase 2 的两个 plan 已执行完成,主链已经能从候选稳定收口到草稿组。
- 下一步可以进入 Phase 3,专注包装接缝稳定化,不再回头补候选和草稿组之间的口径问题。
---
*Phase: 02-资产到草稿组主链收口*
*Completed: 2026-04-02*

View File

@ -1,138 +0,0 @@
# Phase 2: 资产到草稿组主链收口 - Context
**Gathered:** 2026-04-02
**Status:** Ready for planning
<domain>
## Phase Boundary
把新平台主链中“资产中心 -> 自动组合 -> 草稿组”这一段收口到符合真实业务目标的状态:资产族与规格款能作为组合输入,自动拼装能按系列规则生成合法候选,用户可以围绕候选结果确认组合并进入草稿组。当前阶段聚焦主线,不负责包材窗和包装方案实现,也不直接推进正式入库。
</domain>
<decisions>
## Implementation Decisions
### 主线范围与协作边界
- **D-01:** Phase 2 只做主线的资产中心、自动组合、草稿组链路收口,不把 `包材窗`、`包装方案` 纳入当前实现范围。
- **D-02:** 包材窗和包装方案明确属于同事 `xuxiake` 的包装域;主线只保留后续对接所需的数据接缝,不代做包装功能。
- **D-03:** 本阶段继续遵守新平台主链与旧 CAD 兼容链分离原则;自动拼装可复用旧 3D 预览能力,但不能让旧 `Product / SKU / Panel` 反向变成新主链数据真相。
### 自动拼装的业务目标
- **D-04:** 自动拼装的目标不是“生成摘要候选后再慢慢确认”,而是:用户选择某个系列,再选择一个或多个板材/组件,系统按该系列规则生成合法组合列表。
- **D-05:** 合法组合列表必须显式表达实际组合关系,例如“桌面A + 桌腿B*4”,而不是只停留在“桌面板资产族 + 桌腿组资产族”的粗粒度摘要。
- **D-06:** 用户点击某一条合法组合后,左侧 3D 预览区应立即按该组合自动拼装并正确展示对应模型结果;这属于主线自动拼装体验的一部分,不应延后到草稿组才首次出现。
### 当前代码真相与收口方向
- **D-07:** 当前 `/auto-combinations` 已具备“系列模板 + 资产族选择 + 候选列表生成”的基础,但左侧仍是配置摘要,不是候选驱动的 3D 预览。
- **D-08:** 当前系统已具备组合预览底座:`PreSku.previewNodes`、`ModelPreviewPanel`、`CompositeAssetPreview` 已能渲染多模型拼装预览,应优先复用,不重造第二套 3D 预览机制。
- **D-09:** 当前候选生成规则只锁定 `single-board-desk`,且主要是“桌面板 x 桌腿组”的资产族展开;Phase 2 需要围绕真实业务目标,把候选粒度、规则表达和预览联动往“可直接确认的成品组合”推进。
### 数据与对象流转
- **D-10:** 新平台主链的对象流转继续保持为:`AssetFamily / AssetVariant -> 自动组合候选 -> DraftGroup -> PreSku`,不能跳过草稿组直接落正式 `Product / SKU`。
- **D-11:** 候选阶段应尽早产出足够驱动预览的数据,避免出现“候选能看列表但不能看真实拼装,必须先进草稿组才能首次预览”的断层。
- **D-12:** 草稿组继续作为多条候选结果的收口容器,但不再承担“第一次看到真实拼装效果”的唯一入口职责。
### 阶段边界
- **D-13:** 本阶段优先做根因收口,不做 UI 美化型任务;如果需要前端落地改动,只把需求写入 `D:\AI Code\3danzhuang\docs\前端UI改动清单.md`,由前端 agent 实现。
- **D-14:** 本阶段不趁机扩写正式入库、轻仓库或包装增强内容;这些分别属于后续 Phase 3 / 4。
### the agent's Discretion
- 自动拼装在实现上先从“单板桌”模板收口,还是先补通通用候选预览协议,由 agent 根据现有代码复用度决定。
- 组合规则是先在 service 层细化,还是先在 `open-product` 规则层细化,由 agent 在不破坏分层的前提下决定。
- 为了让前端 agent 接手 UI,哪些交互差异只记录需求、不在当前阶段直接实现,由 agent 判断。
</decisions>
<specifics>
## Specific Ideas
- 用户期望的自动拼装行为是:
- 选择系列,例如 `单板桌系列`
- 选择部件,例如 `桌面A`
- 系统按系列规则生成合法列表,例如:
- `桌面A + 桌腿A*4`
- `桌面A + 桌腿B*4`
- `桌面A + 桌腿C*4`
- 点击其中一条后,左侧 3D 自动拼好并正确展示该组合
- 图里的 `包材窗` 和 `包装方案` 不归主线负责,Phase 2 不把这两个模块纳入执行范围。
- 当前项目真实现状是“自动组合页先出候选,草稿组详情页才看 3D”,而目标是“自动组合页点候选即看 3D”。
</specifics>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### 项目级约束
- `.planning/PROJECT.md` — 项目目标、长期约束、主线与兼容链分离原则
- `.planning/ROADMAP.md` — Phase 2 的目标、依赖、成功标准
- `.planning/REQUIREMENTS.md` — `MAIN-01`、`MAIN-02`、`MAIN-03` 的需求定义
- `.planning/STATE.md` — 当前阶段、下一步、风险恢复入口
### 协作与边界
- `../AGENTS.md` — 外层仓库全局规则、协作入口、前端/UI 交接规则
- `docs/workflow/团队协作模式.md` — 主线、包装线、兼容链的协作口径
- `docs/workflow/模块边界说明.md` — 新平台主链、包装协作、旧 CAD 兼容链和共享接缝边界
### 自动组合主链现状
- `src/app/auto-combinations/page.tsx` — 自动组合页主入口
- `src/app/auto-combinations/AutoCombinationsClient.tsx` — 当前候选生成页面与左侧区域现状
- `src/app/api/auto-combinations/generate/route.ts` — 自动组合生成接口入口
- `src/server/services/combination.service.ts` — 当前候选生成编排服务
- `src/server/services/combination.service.types.ts` — 当前候选 DTO 结构
- `src/lib/open-product/single-board-desk.ts` — 单板桌模板的候选生成与预览节点物化规则
- `src/lib/open-product/assembly-contract.ts` — 单板桌槽位与角色约束
- `src/server/services/series-template.service.ts` — 当前模板初始化和模板规则来源
- `src/server/repositories/series-template.repository.ts` — 模板持久化读取方式
### 草稿组与预览现状
- `src/server/services/draft-group.service.ts` — 自动组合结果进入草稿组的主链服务
- `src/server/services/pre-sku-materialization.service.ts` — 候选物化为 `PreSku` 与 `previewNodes` 的关键逻辑
- `src/server/services/draft-group.service.types.ts` — `PreSku` 预览与包装输入结构
- `src/app/draft-groups/[id]/DraftGroupDetailClient.tsx` — 当前“点击条目切换左侧 3D”的已实现页面
- `src/components/platform/ModelPreviewPanel.tsx` — 平台侧预览容器
- `src/components/platform/CompositeAssetPreview.tsx` — 组合资产预览组件
- `src/components/platform/CompositeAssetPreviewCanvas.tsx` — 组合 3D 画布实现
- `src/lib/open-product/platform-view.ts` — `previewNodes` / BOM / 安装预览读取逻辑
### 数据模型
- `prisma/schema.prisma` — `AssetFamily`、`AssetVariant`、`SeriesTemplate`、`DraftGroup`、`PreSku` 数据模型边界
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- `combinationService.generateCandidates()` 已经把模板、资产族和候选生成串起来,可作为 Phase 2 的候选规则主入口。
- `materializeSingleBoardDeskPreSku()` 已经能基于唯一规格款生成 `previewNodes`、BOM、安装步骤预览,这套物化逻辑可复用到“候选即预览”链路。
- `ModelPreviewPanel` + `CompositeAssetPreview` 已经具备多模型装配预览能力,不需要为自动组合页再造新的 3D 预览组件。
### Established Patterns
- 新平台主链沿用 `Route -> Service -> Repository -> Prisma`,候选规则与物化逻辑应继续留在 service/lib 规则层,不把业务判断塞回页面。
- 当前模板体系以 `SeriesTemplate` 为规则源,`previewRule`、`slotSchema`、`recommendationRule` 已是现有扩展点。
- 当前草稿组链路已经证明“候选结果 -> PreSku -> 3D 预览”可跑通,说明系统不是缺 3D 能力,而是缺自动组合页的联动收口。
### Integration Points
- `/api/auto-combinations/generate` 是候选生成的后端接入点,后续若要让候选直接驱动预览,这里或其下游 DTO 是关键接缝。
- `DraftGroupService.createFromAutoCombination()` 与 `PreSkuMaterializationService` 是候选落草稿组的主接缝,后续要保证候选预览数据和草稿组预览数据口径一致。
- 前端如果要实现“点候选即看 3D”,最直接的页面接点是 `AutoCombinationsClient.tsx` 左侧区域。
</code_context>
<deferred>
## Deferred Ideas
- 包材窗、包装方案和包装成本/体积/材料扩展 —— 属于 Phase 3
- 正式入库、成品落库、轻仓库收口 —— 属于 Phase 4
- 旧 CAD 兼容链的装配编辑器增强 —— 只作为复用参考,不在本阶段单独推进
- 自动拼装的更通用系列模板扩展 —— 先收口 `single-board-desk`,其余模板后续再扩
</deferred>
---
*Phase: 02-资产到草稿组主链收口*
*Context gathered: 2026-04-02*

View File

@ -1,64 +0,0 @@
# Phase 2: 资产到草稿组主链收口 - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions captured in CONTEXT.md — this log preserves the source discussion and code reconnaissance.
**Date:** 2026-04-02
**Phase:** 02-资产到草稿组主链收口
**Mode:** direct capture from user clarification + code reconnaissance
**Areas analyzed:** 自动拼装业务目标, 主线与包装域边界, 自动组合当前实现, 草稿组与 3D 预览现状
## User Clarifications Captured
### 包装域边界
| Clarification | Confidence | Source |
|------------|-----------|----------|
| 图里的 `包材窗` 和 `包装方案` 由同事负责,主线不用操心这两块实现。 | Confirmed | User explicit instruction on 2026-04-02 |
### 自动拼装目标
| Clarification | Confidence | Source |
|------------|-----------|----------|
| 用户希望先选系列,再选板材/组件,系统按系列规则生成合法组合列表。 | Confirmed | User explicit instruction on 2026-04-02 |
| 合法组合应显示成真实可理解的组合项,例如 `桌面A + 桌腿B*4`。 | Confirmed | User example on 2026-04-02 |
| 点击某一条组合后,左侧 3D 预览区要立即自动拼好并正确展示该组合。 | Confirmed | User explicit instruction on 2026-04-02 |
## Code Reconnaissance Findings
### 自动组合入口
| Finding | Confidence | Evidence |
|------------|-----------|----------|
| 当前主入口是 `/auto-combinations`,用户选择的是系列模板和资产族,不是直接选择具体规格款。 | Confident | `src/app/auto-combinations/page.tsx`, `src/app/auto-combinations/AutoCombinationsClient.tsx` |
### 候选生成现状
| Finding | Confidence | Evidence |
|------------|-----------|----------|
| 当前后端只锁定 `single-board-desk` 模板,候选由“桌面板资产族 x 桌腿组资产族”生成。 | Confident | `src/server/services/combination.service.ts`, `src/lib/open-product/single-board-desk.ts` |
| 当前候选更偏“摘要候选”,还不是用户想要的“点一条就看真实拼装”的候选体验。 | Confident | `AutoCombinationsClient.tsx`, `single-board-desk.ts` |
### 3D 预览现状
| Finding | Confidence | Evidence |
|------------|-----------|----------|
| 当前系统已经具备组合 3D 预览能力,底座在 `previewNodes` + `ModelPreviewPanel` + `CompositeAssetPreview`。 | Confident | `pre-sku-materialization.service.ts`, `ModelPreviewPanel.tsx`, `CompositeAssetPreview.tsx` |
| 现有“点击条目切换左侧 3D”发生在草稿组详情页,而不是自动组合页。 | Confident | `src/app/draft-groups/[id]/DraftGroupDetailClient.tsx` |
### 对象流转
| Finding | Confidence | Evidence |
|------------|-----------|----------|
| 当前主线对象流转是 `自动组合候选 -> DraftGroup -> PreSku -> 后续入库`。 | Confident | `draft-group.service.ts`, `pre-sku-materialization.service.ts`, `prisma/schema.prisma` |
## Corrections Made
- 修正了“自动拼装还没有 3D 底子”的粗判断。真实情况是 3D 预览底子已经有,只是放在草稿组阶段,不在自动组合页。
- 修正了“包装可能要一起考虑”的范围判断。根据用户明确说明,`包材窗` 和 `包装方案` 在 Phase 2 不属于主线执行范围。
## Open Technical Gaps Identified
- 候选 DTO 目前是否足够直接驱动自动组合页的左侧 3D 预览,还需要在规划阶段具体拆任务。
- 现有 `single-board-desk` 规则还比较粗,如何把“桌腿组”细化成用户理解的“桌腿A/B/C*4”显示与规则输出,需要在计划中明确。
- 自动组合页如果要实现点候选即看 3D,会涉及前端联动;当前工作流只负责把需求和后端/数据结构口径定清楚。
## Scope Reminder
- 本阶段不实现包材窗和包装方案。
- 本阶段不直接推进正式入库和轻仓库。
- 本阶段优先收口自动组合到草稿组的主链真相。

View File

@ -1,163 +0,0 @@
---
phase: 03-包装接缝稳定化
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- src/server/services/pre-sku-packaging.service.ts
- src/server/services/packaging-plan-calculator.ts
- src/server/services/intake.service.ts
- src/server/services/__tests__/draft-group.service.test.ts
- src/server/services/__tests__/intake.service.test.ts
- src/app/api/pre-skus/[id]/packaging/route.test.ts
autonomous: true
requirements:
- MAIN-04
must_haves:
truths:
- "PreSku 的包装结果保存后,读取、试算、正式入库承接的是同一套 `boxes / costSummary / processDraft` 真相结构。"
- "主线不会再同时维护旧 `items / resultSummary` 和新 `boxes[]` 两套包装入口口径。"
- "正式入库继续以“存在有效包装箱”作为准入前提,前端页面或包装线任务不能绕开这条规则。"
artifacts:
- path: "src/server/services/pre-sku-packaging.service.ts"
provides: "PreSku 包装上下文、试算、保存的主编排"
contains: "savePackagingPlan|previewPackagingPlan|getPackagingContext"
- path: "src/server/services/packaging-plan-calculator.ts"
provides: "包装箱、材料、成本、重量、体积统一计算"
contains: "materializePackagingPlanForPersistence"
- path: "src/server/services/intake.service.ts"
provides: "正式入库对包装结果的承接"
contains: "formalizeDraftGroup"
key_links:
- from: "src/server/services/pre-sku-packaging.service.ts"
to: "src/server/services/packaging-plan-calculator.ts"
via: "保存与试算统一走同一套包装物化逻辑"
pattern: "materializePackagingPlanForPersistence"
- from: "src/server/services/intake.service.ts"
to: "prisma/schema.prisma"
via: "PreSkuPackagingPlan -> PackagedItem 正式承接"
pattern: "PackagedItem|PreSkuPackagingPlan"
- from: "src/app/api/pre-skus/[id]/packaging/route.ts"
to: "src/app/api/platform/schemas.ts"
via: "包装接口输入必须继续锁定为 boxes / costSummary / processDraft / notes"
pattern: "savePreSkuPackagingPlanSchema"
---
<objective>
把 `PreSku` 包装结果的保存、试算和正式入库承接彻底收口成同一条主线真相,并用测试守住回归。
Purpose: 按 `MAIN-04` 修根因,避免包装结果“能保存但不能稳定承接”、或者后续又被旧结构和页面占位逻辑带偏。
Output: 更稳的包装保存/试算/入库承接测试闭环,以及明确只认新结构的主线口径。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/03-包装接缝稳定化/03-CONTEXT.md
@src/server/services/pre-sku-packaging.service.ts
@src/server/services/packaging-plan-calculator.ts
@src/server/services/intake.service.ts
@src/app/api/pre-skus/[id]/packaging/route.ts
@src/app/api/platform/schemas.ts
@src/server/services/__tests__/draft-group.service.test.ts
@src/server/services/__tests__/intake.service.test.ts
<interfaces>
From `src/server/services/draft-group.service.types.ts`:
```ts
export interface SavePreSkuPackagingPlanInput {
sourceType?: 'PRODUCT' | 'PRE_SKU' | 'DRAFT_GROUP'
sourceRefId?: string | null
status?: string
boxes: SavePreSkuPackagingPlanBoxInput[]
costSummary?: Partial<PreSkuPackagingCostSummaryDTO> | null
processDraft?: Record<string, unknown> | null
notes?: string | null
}
```
From `src/server/services/intake.service.ts`:
```ts
function ensurePreSkuReadyForFormalization(preSku, draftGroupId: string)
function normalizePackagingBoxes(value: unknown)
function normalizePackagingSummary(value: unknown, boxCount: number)
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: 锁定包装保存、试算与读取只认新结构</name>
<files>src/server/services/pre-sku-packaging.service.ts, src/server/services/packaging-plan-calculator.ts, src/app/api/pre-skus/[id]/packaging/route.test.ts, src/server/services/__tests__/draft-group.service.test.ts</files>
<read_first>
src/server/services/pre-sku-packaging.service.ts
src/server/services/packaging-plan-calculator.ts
src/app/api/pre-skus/[id]/packaging/route.ts
src/app/api/platform/schemas.ts
src/server/services/__tests__/draft-group.service.test.ts
src/app/api/pre-skus/[id]/packaging/route.test.ts
</read_first>
<acceptance_criteria>
- `src/server/services/__tests__/draft-group.service.test.ts` 包含“包装试算 / 保存 / 读取”围绕 `boxes`、`costSummary`、`processDraft` 的断言,不再依赖旧 `items / resultSummary` 入口。
- `src/app/api/pre-skus/[id]/packaging/route.test.ts` 能证明 `PUT` 非法旧结构会被 schema 拦截,合法新结构返回 `200`。
- `src/server/services/pre-sku-packaging.service.ts` 中保存与试算继续共用 `materializePackagingPlanForPersistence(`,没有出现第二套手工汇总逻辑。
</acceptance_criteria>
<action>补齐并收紧包装接口与服务测试:明确 `PUT /api/pre-skus/[id]/packaging` 只接受 `boxes / costSummary / processDraft / notes`,旧 `items / resultSummary` 形态必须校验失败;确认 `previewPackagingPlan()` 和 `savePackagingPlan()` 都继续复用 `materializePackagingPlanForPersistence()`,并在测试里固定断言返回与持久化结果中的 `boxes[0].materials`、`estimatedWeightKg`、`estimatedCost`、`costSummary.totalCost`、`costSummary.totalWeightKg`。</action>
<verify>
<automated>npm test -- --runTestsByPath src/app/api/pre-skus/[id]/packaging/route.test.ts src/server/services/__tests__/draft-group.service.test.ts</automated>
</verify>
<done>包装保存、试算、读取这三段都固定在新结构上,后续包装 UI 再接入时不会把旧结构重新带回来。</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: 锁定正式入库对包装结果的准入和承接</name>
<files>src/server/services/intake.service.ts, src/server/services/__tests__/intake.service.test.ts</files>
<read_first>
src/server/services/intake.service.ts
src/server/services/__tests__/intake.service.test.ts
prisma/schema.prisma
src/server/services/draft-group.service.ts
</read_first>
<acceptance_criteria>
- `src/server/services/__tests__/intake.service.test.ts` 包含“无有效包装箱不能入库”和“有 `boxes[]` 时会生成 `PackagedItem`”的断言。
- `src/server/services/intake.service.ts` 仍通过 `hasValidPackagingBoxes` 或等价显式逻辑判断准 SKU 是否可正式入库,没有新增页面兜底绕过。
- `src/server/services/intake.service.ts` 创建 `PackagedItem` 时 payload 内包含 `boxNumber`、`outerSizeMm`、`materials`、`costSummary`。
</acceptance_criteria>
<action>收紧正式入库测试和承接口径:明确 `ensurePreSkuReadyForFormalization()` 必须继续以“存在有效包装箱”为准入条件;在 `intake.service.test.ts` 固定断言 `formalizeDraftGroup()` 对没有 `boxes[]` 的 `PreSkuPackagingPlan` 直接报错,对有 `boxes[]` 的 `PreSkuPackagingPlan` 会创建 `PackagedItem`,并把 `boxNumber`、`outerSizeMm`、`materials`、`costSummary` 写入 payload。</action>
<verify>
<automated>npm test -- --runTestsByPath src/server/services/__tests__/intake.service.test.ts</automated>
</verify>
<done>包装结果到正式入库的承接链被测试守住,后续即使包装线继续开发,也不容易把主线准入规则改坏。</done>
</task>
</tasks>
<verification>
至少跑通包装路由测试、草稿组包装服务测试和正式入库测试,确认包装结果在保存、试算、入库三段中的结构一致。
</verification>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`MAIN-04` 在主线侧被收口为:`PreSku` 的包装结果能稳定保存、稳定试算、稳定承接到正式入库,而且这条链只认一套新结构。
</success_criteria>
<output>
After completion, create `.planning/phases/03-包装接缝稳定化/03-01-SUMMARY.md`
</output>

View File

@ -1,80 +0,0 @@
---
phase: 03-包装接缝稳定化
plan: 01
subsystem: api
tags: [packaging, pre-sku, intake, tests, boxes]
requires:
- phase: 02-资产到草稿组主链收口
provides: 候选到 PreSku 的稳定主链
provides:
- 包装保存/试算/入库承接测试闭环
- 正式入库对包装结果的准入保护
affects: [phase-04, packaging-ui, intake]
tech-stack:
added: []
patterns:
- 包装新结构只认 boxes/costSummary/processDraft
- 正式入库以有效包装箱为唯一准入前提
key-files:
created:
- .planning/phases/03-包装接缝稳定化/03-01-SUMMARY.md
modified:
- src/server/services/__tests__/intake.service.test.ts
key-decisions:
- "修测试而不是重改主线逻辑,因为当前真实问题是测试底座落后于已存在的入库承接实现。"
patterns-established:
- "包装链守护模式:路由、草稿组包装服务、正式入库三段一起跑回归测试。"
requirements-completed: [MAIN-04]
duration: 20min
completed: 2026-04-02
---
# Phase 3 Plan 01 Summary
**包装保存、试算和正式入库承接现在由同一组测试守住,主线继续只认 `boxes / costSummary / processDraft` 新结构。**
## Performance
- **Duration:** 20 min
- **Started:** 2026-04-02T21:50:00+08:00
- **Completed:** 2026-04-02T22:10:00+08:00
- **Tasks:** 2
- **Files modified:** 1
## Accomplishments
- 补齐了 `intake.service.test.ts` 的事务 mock,使其覆盖当前真实的板件、孔位、五金、包装方案与包装成品承接流程。
- 跑通包装路由、草稿组包装服务、正式入库三组关键测试,确认主线仍只认新包装结构。
- 固定了正式入库承接断言:`PackagedItem` payload 必须带 `boxNumber / outerSizeMm / materials / costSummary`。
## Task Commits
未创建提交。当前仓库仍保持本地未提交工作状态。
## Files Created/Modified
- `src/server/services/__tests__/intake.service.test.ts` - 补齐正式入库事务 mock 和包装承接断言
## Decisions Made
- 继续保留现有主线实现,不为了“让测试过”去回退已存在的 `panel / productHardware / packagingPlan / packagedItem` 承接逻辑。
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
- `intake.service.test.ts` 的 mock 没有覆盖新增的事务写入点,导致测试先于业务逻辑报错;已修正为与当前实现对齐。
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- 包装结果主线承接已经被测试守住,可以继续做协作边界和占位入口口径收口。
---
*Phase: 03-包装接缝稳定化*
*Completed: 2026-04-02*

View File

@ -1,145 +0,0 @@
---
phase: 03-包装接缝稳定化
plan: 02
type: execute
wave: 1
depends_on:
- 03-01
files_modified:
- docs/workflow/模块边界说明.md
- docs/workflow/团队协作模式.md
- docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md
- ../docs/前端UI改动清单.md
- src/app/draft-groups/[id]/DraftGroupDetailClient.tsx
autonomous: true
requirements:
- COLL-03
must_haves:
truths:
- "包装线默认能读共享接缝,但不能把 `draft-group / intake / schema.prisma` 当作自己的自由改动区。"
- "当前草稿组详情页里的包装区只是主线占位入口,不冒充最终包装工作区。"
- "前端 agent 和包装 agent 拿到的交接口径一致,不会一边按旧结构做,一边按新结构收口。"
artifacts:
- path: "docs/workflow/模块边界说明.md"
provides: "共享接缝与禁止越界边界"
contains: "包装协作边界"
- path: "docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md"
provides: "给同事 AI 的固定对接口径"
contains: "不要碰什么"
- path: "../docs/前端UI改动清单.md"
provides: "前端对草稿组包装区和自动组合联调的固定说明"
contains: "包装占位|真正包装工作区"
key_links:
- from: "src/app/draft-groups/[id]/DraftGroupDetailClient.tsx"
to: "../docs/前端UI改动清单.md"
via: "主线占位入口与最终包装工作区职责分离"
pattern: "包装方案|占位"
- from: "docs/workflow/团队协作模式.md"
to: "docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md"
via: "团队协作规则与同事 AI 固定口径一致"
pattern: "codex/xuxiake-packaging"
---
<objective>
把包装线和主线的共享接缝规则、占位入口口径和前端联调边界固定下来,避免后续协作再次混乱。
Purpose: 按 `COLL-03` 修根因,避免共享文件被误改、草稿组占位页被误当正式包装工作区、以及前端/包装 agent 围绕旧结构继续开发。
Output: 更新后的协作文档、对同事 AI 的固定说明,以及必要时更明确的主线占位文案。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/03-包装接缝稳定化/03-CONTEXT.md
@docs/workflow/团队协作模式.md
@docs/workflow/模块边界说明.md
@docs/workflow/同事交接模板.md
@docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md
@../docs/前端UI改动清单.md
@src/app/draft-groups/[id]/DraftGroupDetailClient.tsx
<interfaces>
From `src/app/draft-groups/[id]/DraftGroupDetailClient.tsx`:
```tsx
<AccordionSection
title="包装方案"
description="按准 SKU 保存包装占位,后续包装模块可继续增强。"
>
```
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: 固定共享接缝协作文档口径</name>
<files>docs/workflow/团队协作模式.md, docs/workflow/模块边界说明.md, docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md</files>
<read_first>
docs/workflow/团队协作模式.md
docs/workflow/模块边界说明.md
docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md
.planning/phases/03-包装接缝稳定化/03-CONTEXT.md
src/server/services/pre-sku-packaging.service.ts
src/server/services/intake.service.ts
</read_first>
<acceptance_criteria>
- `docs/workflow/模块边界说明.md` 明确写出 `draft-group.service.ts`、`draft-group.repository.ts`、`intake.service.ts`、`prisma/schema.prisma`、`src/app/draft-groups/[id]` 是包装线“可读但不默认直改”的共享接缝。
- `docs/workflow/团队协作模式.md` 明确写出主线当前包装区只是承接口,不是包装线独立顶层系统。
- `docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md` 明确写出当前草稿组包装区是最小联通版占位,包装线不要把它当最终工作区重写主线契约。
</acceptance_criteria>
<action>统一三份文档口径:明确包装线默认工作域是 `src/app/api/pre-skus/[id]/packaging`、`src/server/services/packaging.service.ts`、`src/server/repositories/packaging.repository.ts` 及包装文档目录;共享接缝固定列出 `draft-group.service.ts`、`draft-group.repository.ts`、`intake.service.ts`、`prisma/schema.prisma`、`src/app/draft-groups/[id]`;文档中直接写明草稿组详情页包装区只是“主线最小联通版占位入口”,最终包装工作区由包装线独立增强,但不能改坏 `PreSkuPackagingPlan`、`boxes[]`、`costSummary`、`processDraft` 现有契约。</action>
<verify>
<automated>rg -n "最小联通版占位|共享接缝|可读但不默认直改|boxes\\[\\]|processDraft" docs/workflow/团队协作模式.md docs/workflow/模块边界说明.md docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md</automated>
</verify>
<done>团队文档、同事 AI 说明和主线当前实现口径一致,后续协作不会再因为“这个页面是不是最终包装工作区”而跑偏。</done>
</task>
<task type="auto">
<name>Task 2: 固定前端占位与联调边界说明</name>
<files>../docs/前端UI改动清单.md, src/app/draft-groups/[id]/DraftGroupDetailClient.tsx</files>
<read_first>
../docs/前端UI改动清单.md
src/app/draft-groups/[id]/DraftGroupDetailClient.tsx
docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md
</read_first>
<acceptance_criteria>
- `../docs/前端UI改动清单.md` 包含“草稿组详情包装区只是占位入口,不是真正包装工作区”的明确交接说明。
- 如果修改 `DraftGroupDetailClient.tsx`,其文案必须直接出现“占位”或等价明确表达,避免前端误解为最终包装模块。
- 文档中明确区分:结构契约问题归后端 / 主线,视觉和交互落地归前端或包装 agent。
</acceptance_criteria>
<action>在 `../docs/前端UI改动清单.md` 追加草稿组详情页包装区说明:当前页面只负责按 `PreSku` 保存最小包装占位并验证主线能读写 `PreSkuPackagingPlan`,不是最终包装子工作区;联调时如果发现 `boxes[] / costSummary / processDraft / packagingInput` 缺失或与正式入库承接不一致,归后端主线;如果只是布局、动效、表单体验、材料选择器界面问题,归前端或包装 agent。视现有文案是否足够,再决定是否在 `DraftGroupDetailClient.tsx` 的包装区描述里补一句“后续包装模块继续增强,此处仅为主线占位入口”。</action>
<verify>
<automated>rg -n "占位入口|真正包装工作区|boxes\\[\\]|costSummary|processDraft|packagingInput" ../docs/前端UI改动清单.md src/app/draft-groups/[id]/DraftGroupDetailClient.tsx</automated>
</verify>
<done>前端和包装线后续接手时会先知道当前页面只是占位入口,避免直接在主线页面里堆出完整包装系统。</done>
</task>
</tasks>
<verification>
通过文档关键字校验和必要的页面文案检查,确认共享接缝口径、占位入口口径和联调边界都已落盘。
</verification>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`COLL-03` 在主线侧被收口为:包装线可以继续开发,但不会默认直改共享接缝,也不会把主线占位页误当成最终包装系统。
</success_criteria>
<output>
After completion, create `.planning/phases/03-包装接缝稳定化/03-02-SUMMARY.md`
</output>

View File

@ -1,91 +0,0 @@
---
phase: 03-包装接缝稳定化
plan: 02
subsystem: api
tags: [collaboration, packaging, docs, boundaries, ui-handoff]
requires:
- phase: 03-包装接缝稳定化
provides: 包装新结构已稳定承接
provides:
- 包装线共享接缝协作口径
- 草稿组包装区占位入口说明
- 前端/包装 agent 联调分流规则
affects: [phase-04, packaging-collab, frontend-handoff]
tech-stack:
added: []
patterns:
- 共享接缝先对齐再改
- 主线占位入口不冒充最终包装工作区
key-files:
created:
- .planning/phases/03-包装接缝稳定化/03-02-SUMMARY.md
modified:
- docs/workflow/团队协作模式.md
- docs/workflow/模块边界说明.md
- docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md
- ../docs/前端UI改动清单.md
- src/app/draft-groups/[id]/DraftGroupDetailClient.tsx
key-decisions:
- "草稿组详情页包装区明确定义为主线占位入口,不再允许被误解为最终包装工作区。"
- "包装线补 UI/材料选择/最低成本方案时,不默认落在 draft-group/intake/schema 这些共享接缝里实现。"
patterns-established:
- "协作分流模式:结构契约问题归主线后端,视觉与交互问题归前端或包装 agent。"
requirements-completed: [COLL-03]
duration: 20min
completed: 2026-04-02
---
# Phase 3 Plan 02 Summary
**包装线和主线的共享接缝规则、占位入口口径和联调边界现在已经固定到文档和页面文案里。**
## Performance
- **Duration:** 20 min
- **Started:** 2026-04-02T22:10:00+08:00
- **Completed:** 2026-04-02T22:30:00+08:00
- **Tasks:** 2
- **Files modified:** 5
## Accomplishments
- 在团队协作文档里明确了包装线默认边界,以及草稿组详情页包装区只是主线最小联通版占位入口。
- 在给同事 AI 的包装底座说明里明确了:不要把主线占位区当成最终包装工作区去重写共享契约。
- 在前端交接清单里补了草稿组包装区的固定口径和联调分流规则。
## Task Commits
未创建提交。当前仓库仍保持本地未提交工作状态。
## Files Created/Modified
- `docs/workflow/团队协作模式.md` - 固定包装线与主线协作边界
- `docs/workflow/模块边界说明.md` - 固定共享接缝和占位入口说明
- `docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md` - 固定给同事 AI 的接缝口径
- `../docs/前端UI改动清单.md` - 固定前端联调分流规则
- `src/app/draft-groups/[id]/DraftGroupDetailClient.tsx` - 页面文案明确为主线包装占位
## Decisions Made
- 页面文案只做最小必要修改,不直接把草稿组详情页改造成完整包装工作区。
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
- 无代码级阻塞;主要工作是把已经确认的边界从口头/会话规则落盘到正式协作文档。
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Phase 3 已完成,包装接缝和协作边界已经稳定。
- 下一步可以进入 Phase 4,专注正式入库与轻仓库闭环,而不是再回头解释包装接缝规则。
---
*Phase: 03-包装接缝稳定化*
*Completed: 2026-04-02*

View File

@ -1,137 +0,0 @@
# Phase 3: 包装接缝稳定化 - Context
**Gathered:** 2026-04-02
**Status:** Ready for planning
<domain>
## Phase Boundary
本阶段只做“包装线与主线共享接缝”的稳定化,不直接代做同事负责的包装方案业务本体。目标是让 `PreSku` 的包装结果能够稳定保存、稳定传递到正式入库,同时把共享文件的改动边界和协作规则固定住,避免后面主线和包装线互相打架。
</domain>
<decisions>
## Implementation Decisions
### 范围与职责
- **D-01:** Phase 3 聚焦 `MAIN-04` 与 `COLL-03`,只收口包装接缝、保存链路、入库承接和协作边界。
- **D-02:** 包装方案 UI、包装材料选择、最低成本方案、工艺图组合占位仍由同事 `xuxiake` 负责;主线当前不代做这些业务层实现。
- **D-03:** 本阶段不回头扩写自动组合规则、资产中心规则或旧 CAD 兼容链。
### 当前后端真相
- **D-04:** `PreSkuPackagingPlan` 已经是新平台包装结果真相对象,主结构锁定为 `sourceType / sourceRefId / status / boxes / costSummary / processDraft / notes`。
- **D-05:** `GET /api/pre-skus/[id]/packaging` 返回 `packagingPlan + packagingInput`,前端不应自己重拼包装输入。
- **D-06:** `PUT /api/pre-skus/[id]/packaging` 只接受新结构;旧的 `items / resultSummary` 不能再作为新平台入口契约。
- **D-07:** 正式入库已经按 `boxes[]` 生成 `PackagedItem`,说明包装结果到入库承接链已存在,不需要再发明第二条包装落库路径。
### 共享接缝与敏感区
- **D-08:** 当前最敏感的共享接缝固定是:`draft-group.service.ts`、`draft-group.repository.ts`、`intake.service.ts`、`prisma/schema.prisma`、`src/app/draft-groups/[id]`。
- **D-09:** 这些接缝文件后续如果要被包装线修改,必须先在任务里写清“为什么非改不可”,不能因为包装 UI 要求就直接重写主线对象结构。
- **D-10:** Phase 3 如果要做主线改动,优先做“边界稳定”和“契约补齐”,不做包装线本体的体验优化。
### 状态推进规则
- **D-11:** `PreSku` 保存包装结果后,草稿组状态通过 `draftGroupStateService` 在 `PACKAGING / READY_FOR_INTAKE / FORMALIZED` 之间推进;这条状态链必须继续保持单一真相。
- **D-12:** 正式入库前的准入规则继续依赖“是否存在有效包装箱”,不能被包装线 UI 绕开。
### 协作规则
- **D-13:** `xuxiake` 长期包装分支固定为 `codex/xuxiake-packaging`,同步 `main` 固定用 `merge main`;这不是建议,是固定协作口径。
- **D-14:** 包装线默认可以读共享接缝,但不默认拥有这些文件的自由修改权。
- **D-15:** 主线负责人后续收口包装改动时,优先按小任务合并或手工收口,不一次吞并长期积压改动。
### the agent's Discretion
- Phase 3 先做“文档和计划收口”,还是先补主线侧接缝防护测试,由 agent 根据现有代码脆弱点决定。
- 如果发现共享接缝已经有明显架构风险,是否先插一个小 plan 做守护性重构,由 agent 在不越过包装业务本体的前提下决定。
</decisions>
<specifics>
## Specific Ideas
- 包装底座当前已经具备:
- `packagingInput` 自动派生
- `boxes[]` 保存与试算
- 从资产中心读取 `PACKAGING` 资产族与规格款
- 正式入库按 `boxes[]` 生成 `PackagedItem`
- 当前草稿组详情页只是“最小联通版包装占位”,不是最终包装工作区。
- 当前真正容易反复打架的,不是接口有没有,而是:
- 包装线会不会反向改 `DraftGroup / PreSku / Intake` 契约
- 主线会不会在未对齐情况下继续扩共享模型
- 前端会不会为了联调顺手兜底旧结构
</specifics>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### 项目级约束
- `.planning/PROJECT.md`
- `.planning/ROADMAP.md`
- `.planning/REQUIREMENTS.md`
- `.planning/STATE.md`
### 协作与边界
- `../AGENTS.md`
- `docs/workflow/团队协作模式.md`
- `docs/workflow/模块边界说明.md`
- `docs/workflow/主线同步操作手册.md`
- `docs/workflow/同事交接模板.md`
- `docs/workflow/同事本地AGENTS模板.md`
- `docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md`
### 主线侧包装接缝
- `src/server/services/draft-group.service.ts`
- `src/server/services/draft-group.service.types.ts`
- `src/server/services/pre-sku-packaging.service.ts`
- `src/server/services/packaging-plan-calculator.ts`
- `src/server/services/draft-group-state.service.ts`
- `src/server/services/intake.service.ts`
- `src/server/repositories/draft-group.repository.ts`
- `src/app/api/pre-skus/[id]/packaging/route.ts`
- `src/app/api/pre-skus/[id]/packaging/preview/route.ts`
- `src/app/api/pre-skus/[id]/packaging/material-options/route.ts`
- `src/app/api/platform/schemas.ts`
- `src/app/draft-groups/[id]/DraftGroupDetailClient.tsx`
- `src/lib/open-product/platform-api.ts`
### 数据模型
- `prisma/schema.prisma`
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- `preSkuPackagingService` 已经把包装上下文读取、材料资产读取、保存、试算串起来。
- `materializePackagingPlanForPersistence()` 已经把箱内项、包装材料、重量、体积、成本汇总逻辑收口成单独计算层。
- `intakeService.formalizeDraftGroup()` 已经消费 `packagingPlan.boxes` 并生成正式 `PackagingPlan + PackagedItem`。
### Established Patterns
- 新平台主链继续遵守 `Route -> Service -> Repository -> Prisma`。
- 包装结果在 `PreSkuPackagingPlan` 落真相,正式入库再承接,不允许直接绕过 `PreSku` 去创建正式包装成品。
- 包装状态推进由主线状态服务统一处理,不靠前端页面自己判断。
### Integration Points
- `draft-group.service.ts` 是包装线和主线之间最核心的服务接缝。
- `DraftGroupDetailClient.tsx` 是当前最小联通前端接点,但不是最终包装工作区。
- `schema.prisma` 中的 `PreSku / PreSkuPackagingPlan / PackagedItem / ProductSKU` 是一组联动模型,任一处字段调整都会影响上下游。
</code_context>
<deferred>
## Deferred Ideas
- 包装子工作区最终 UI 设计与交互实现
- 最低成本方案生成算法
- 工艺图组合真正实现
- 正式产品级 `PackagingPlan` 进一步重构
- 包装域的更完整成本、体积、材料扩展(作为 v2 的 `PACK-01 / PACK-02`)
</deferred>
---
*Phase: 03-包装接缝稳定化*
*Context gathered: 2026-04-02*

View File

@ -1,57 +0,0 @@
# Phase 3: 包装接缝稳定化 - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions captured in CONTEXT.md — this log preserves the source discussion and code reconnaissance.
**Date:** 2026-04-02
**Phase:** 03-包装接缝稳定化
**Mode:** direct capture from code reconnaissance + existing workflow docs
**Areas analyzed:** 包装协作边界, 共享接缝, 包装保存链路, 正式入库承接
## User Clarifications Captured
### 职责边界
| Clarification | Confidence | Source |
|------------|-----------|----------|
| 包装和包装方案由同事负责,主线当前不用代做。 | Confirmed | User explicit instruction on 2026-04-02 |
| 主线后续如果需要动前端,不由当前 agent 直接做,而是写到前端交接文档。 | Confirmed | User explicit instruction on 2026-04-02 |
## Code Reconnaissance Findings
### 包装底座现状
| Finding | Confidence | Evidence |
|------------|-----------|----------|
| `preSkuPackagingService` 已经提供包装上下文读取、材料规格读取、保存和试算能力。 | Confident | `src/server/services/pre-sku-packaging.service.ts` |
| 包装成本、重量、体积计算已经独立收口到 `materializePackagingPlanForPersistence()`。 | Confident | `src/server/services/packaging-plan-calculator.ts` |
| 包装接口当前走 `GET/PUT /api/pre-skus/[id]/packaging`,并复用 `savePreSkuPackagingPlanSchema`。 | Confident | `src/app/api/pre-skus/[id]/packaging/route.ts`, `src/app/api/platform/schemas.ts` |
### 主线共享接缝
| Finding | Confidence | Evidence |
|------------|-----------|----------|
| 草稿组详情页当前已经能保存包装占位,但这是最小联通版,不是最终包装工作区。 | Confident | `src/app/draft-groups/[id]/DraftGroupDetailClient.tsx` |
| 入库服务当前直接消费 `PreSku.packagingPlan.boxes` 并生成 `PackagedItem`。 | Confident | `src/server/services/intake.service.ts` |
| `schema.prisma` 里 `PreSku`、`PreSkuPackagingPlan`、`PackagedItem`、`ProductSKU` 已形成联动模型。 | Confident | `prisma/schema.prisma` |
### 协作风险
| Finding | Confidence | Evidence |
|------------|-----------|----------|
| 当前最大的协作风险不是“包装底座缺失”,而是包装线误改 `draft-group / intake / schema.prisma` 这些共享接缝。 | Confident | workflow docs + current code topology |
| 当前文档已经明确这些文件不是包装线默认自由修改区,但 `.planning` 里还缺少 Phase 3 的正式上下文。 | Confident | `docs/workflow/模块边界说明.md`, `.planning/STATE.md` |
## Corrections Made
- 修正了“Phase 3 可能要继续搭包装底座”的判断。真实情况是底座已存在,下一步重点应是接缝稳定化和协作边界固化。
- 修正了“包装问题主要在 UI 层”的判断。真实更敏感的是共享模型、状态推进与正式入库承接链。
## Open Technical Gaps Identified
- 共享接缝是否还缺守护性测试,特别是包装保存后草稿组状态推进与正式入库承接之间的回归保护。
- 草稿组详情页当前的最小包装占位是否需要进一步标注“仅占位,不是真正包装工作区”,避免包装线误把它当最终入口。
- `REQUIREMENTS.md` 中 Phase 2 已完成项还需要回写状态,避免后续规划误判主链进度。
## Scope Reminder
- 本阶段不直接做包装方案业务 UI。
- 本阶段不直接做最低成本算法。
- 本阶段不扩写自动组合和资产中心。
- 本阶段优先收口共享接缝、状态链和协作边界。

View File

@ -1,164 +0,0 @@
---
phase: 04-正式入库与轻仓库闭环
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- src/server/services/intake.service.ts
- src/server/services/intake.service.types.ts
- src/server/services/__tests__/intake.service.test.ts
- src/app/api/platform/schemas.ts
- src/app/intake/IntakeClient.tsx
- ../docs/前端UI改动清单.md
autonomous: true
requirements:
- MAIN-05
must_haves:
truths:
- "正式入库继续只从 `DraftGroup / PreSku` 进入,不允许绕过草稿组直接造正式 `Product / SKU`。"
- "`formalizeDraftGroup()` 是 Phase 4 的正式入库唯一主链入口,补强应围绕现有实现收口,而不是另起第二套流程。"
- "正式入库结果必须稳定落成 `Product / ProductSKU / PackagingPlan / PackagedItem`,并且对失败场景给出明确可测试的校验。"
artifacts:
- path: "src/server/services/intake.service.ts"
provides: "正式入库编排、校验、正式对象创建"
contains: "formalizeDraftGroup|ensurePreSkuReadyForFormalization"
- path: "src/server/services/__tests__/intake.service.test.ts"
provides: "正式入库主链回归保护"
contains: "formalizeDraftGroup"
- path: "src/app/api/platform/schemas.ts"
provides: "正式入库输入 DTO 校验"
contains: "formalizeDraftGroupSchema"
key_links:
- from: "src/app/intake/IntakeClient.tsx"
to: "src/server/services/intake.service.ts"
via: "正式入库页提交的字段必须与服务层契约一致"
pattern: "formalizeDraftGroup"
- from: "src/server/services/intake.service.ts"
to: "prisma/schema.prisma"
via: "正式入库承接到 Product / ProductSKU / PackagingPlan / PackagedItem"
pattern: "productSKU|packagingPlan|packagedItem"
---
<objective>
把草稿组/准 SKU 到正式 `Product / SKU` 的入库链收口成稳定、可验证、可回归的主线能力。
Purpose: 按 `MAIN-05` 修根因,避免正式入库虽然“能跑通”,但在多准 SKU、挂已有 SPU、包装承接、失败校验这些关键场景下仍不稳定。
Output: 更稳的正式入库服务契约、关键测试覆盖,以及必要的前端联调说明。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/04-正式入库与轻仓库闭环/04-CONTEXT.md
@.planning/phases/03-包装接缝稳定化/03-CONTEXT.md
@src/app/intake/page.tsx
@src/app/intake/IntakeClient.tsx
@src/app/api/intake/formalize/route.ts
@src/app/api/platform/schemas.ts
@src/lib/open-product/platform-api.ts
@src/server/services/intake.service.ts
@src/server/services/intake.service.types.ts
@src/server/services/__tests__/intake.service.test.ts
@prisma/schema.prisma
<interfaces>
From `src/server/services/intake.service.types.ts`:
```ts
export interface FormalizeDraftGroupInput {
draftGroupId: string
targetMode: 'CREATE_PRODUCT' | 'ATTACH_PRODUCT'
productId?: string
modelNumber?: string
productName?: string
skuItems: Array<{
preSkuId: string
skuCode?: string
skuName?: string
initialStock?: number
warehouseLocationId?: string | null
}>
}
```
From `src/server/services/intake.service.ts`:
```ts
async formalizeDraftGroup(input: FormalizeDraftGroupInput, user: AuthUser)
function ensurePreSkuReadyForFormalization(preSku, draftGroupId: string)
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: 收紧正式入库校验与服务回归测试</name>
<files>src/server/services/intake.service.ts, src/server/services/intake.service.types.ts, src/server/services/__tests__/intake.service.test.ts</files>
<read_first>
src/server/services/intake.service.ts
src/server/services/intake.service.types.ts
src/server/services/__tests__/intake.service.test.ts
prisma/schema.prisma
</read_first>
<acceptance_criteria>
- `intake.service.test.ts` 覆盖“新建 SPU”“挂已有 SPU”“同一次入库重复选择同一 preSku”“已 FORMALIZED / ARCHIVED preSku 不可重复入库”“无有效包装箱不可入库”等关键场景。
- `intake.service.ts` 中正式入库失败原因继续在 service 层显式校验,而不是让 route 或页面兜底。
- 成功入库时,测试能证明会创建正式 `ProductSKU`,并承接包装成品与状态推进结果。
</acceptance_criteria>
<action>补齐并收紧 `formalizeDraftGroup()` 的回归测试,优先围绕 Phase 4 真正影响主链稳定性的场景:单次多准 SKU 正式入库、挂已有 SPU 时的合法性、重复选择同一 `preSkuId` 的拦截、已经 `FORMALIZED` 或 `ARCHIVED` 的准 SKU 拦截、无有效包装箱拦截,以及成功后 `draftGroup` 状态的推进。必要时小幅重构 `intake.service.ts` 的局部逻辑,让校验和对象创建更可测试,但不改变“从 `PreSku` 承接正式对象”的主真相。</action>
<verify>
<automated>npm test -- --runTestsByPath src/server/services/__tests__/intake.service.test.ts</automated>
</verify>
<done>正式入库主链具备稳定可回归的失败/成功断言,后续执行 Phase 4 其他计划时不会反复担心基础入库逻辑漂移。</done>
</task>
<task type="auto">
<name>Task 2: 固定正式入库前端联调契约</name>
<files>src/app/api/platform/schemas.ts, src/app/intake/IntakeClient.tsx, ../docs/前端UI改动清单.md</files>
<read_first>
src/app/api/platform/schemas.ts
src/app/intake/IntakeClient.tsx
src/lib/open-product/platform-api.ts
../docs/前端UI改动清单.md
</read_first>
<acceptance_criteria>
- `formalizeDraftGroupSchema` 与 `IntakeClient` 实际提交字段保持一致,不出现“页面可填但接口不认”或“接口要求但页面没传”的漂移。
- 如果发现需要前端 UI 配合,只追加到 `../docs/前端UI改动清单.md`,不在本计划里直接大改前端实现。
- 入库页当前文案或交互若会误导用户绕过主链,应在最小范围内修正或明确记录给前端 agent。
</acceptance_criteria>
<action>核对 `IntakeClient`、`platform-api.ts`、`formalizeDraftGroupSchema` 三处正式入库契约是否完全一致;如果存在缺口,只做主链所需的最小修正。任何需要独立前端 agent 处理的体验问题,统一写进 `../docs/前端UI改动清单.md`,例如:多准 SKU 预览切换、成功态跳转反馈、挂已有 SPU 时的推荐选择体验等。</action>
<verify>
<automated>rg -n "formalizeDraftGroup|targetMode|warehouseLocationId|initialStock|skuItems" src/app/intake/IntakeClient.tsx src/app/api/platform/schemas.ts src/lib/open-product/platform-api.ts ../docs/前端UI改动清单.md</automated>
</verify>
<done>正式入库页和后端契约一致,前端需要继续做的内容也被明确分流给 UI agent。</done>
</task>
</tasks>
<verification>
至少跑通正式入库服务测试,并用关键字校验确认前端联调契约一致。
</verification>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`MAIN-05` 在主线侧被收口为:草稿组/准 SKU 能稳定转成正式 `Product / SKU`,并且这条链有足够测试与契约保护。
</success_criteria>
<output>
After completion, create `.planning/phases/04-正式入库与轻仓库闭环/04-01-SUMMARY.md`
</output>

View File

@ -1,87 +0,0 @@
---
phase: 04-正式入库与轻仓库闭环
plan: 01
subsystem: api
tags: [intake, formalization, validation, tests, ui-handoff]
requires:
- phase: 03-包装接缝稳定化
provides: 包装结果新结构与正式入库承接已稳定
provides:
- 正式入库关键失败场景回归保护
- 正式入库前后端契约收紧
- 前端正式入库联调口径补充
affects: [phase-04, intake, warehouse, frontend-handoff]
tech-stack:
added: []
patterns:
- 正式入库前置校验继续留在 service 层
- ATTACH_PRODUCT / CREATE_PRODUCT 契约前移到 schema
key-files:
created:
- .planning/phases/04-正式入库与轻仓库闭环/04-01-SUMMARY.md
modified:
- src/app/api/platform/schemas.ts
- src/server/services/__tests__/intake.service.test.ts
- ../docs/前端UI改动清单.md
key-decisions:
- "不重写 intake 主链,而是补齐正式入库关键失败场景测试与 schema 契约。"
- "前端 UI 继续交给独立 agent,主线只补正式入库联调口径。"
patterns-established:
- "正式入库守护模式:service 回归测试 + schema 契约校验 + 前端联调清单三段一起收口。"
requirements-completed: [MAIN-05]
duration: 20min
completed: 2026-04-02
---
# Phase 4 Plan 01 Summary
**正式入库主链现在补上了更关键的失败场景保护,并把挂已有 SPU / 新建 SPU 的契约前移到了 schema。**
## Performance
- **Duration:** 20 min
- **Started:** 2026-04-02T23:10:00+08:00
- **Completed:** 2026-04-02T23:30:00+08:00
- **Tasks:** 2
- **Files modified:** 3
## Accomplishments
- 补齐了正式入库关键失败场景测试,覆盖目标产品不存在、库位不存在、新建 SPU 模式等主链风险点。
- 收紧了 `formalizeDraftGroupSchema`,明确 `ATTACH_PRODUCT` 必须传 `productId`,`CREATE_PRODUCT` 不允许混传 `productId`。
- 在前端交接清单里补了正式入库联调口径,明确两种入库模式要分开确认,且多准 SKU 首入库要按一次提交的主链口径联调。
## Task Commits
未创建提交。当前仓库仍保持本地未提交工作状态。
## Files Created/Modified
- `src/app/api/platform/schemas.ts` - 收紧正式入库 schema 契约
- `src/server/services/__tests__/intake.service.test.ts` - 补齐正式入库关键回归测试
- `../docs/前端UI改动清单.md` - 补正式入库的前端联调说明
## Decisions Made
- 继续保留 `intakeService.formalizeDraftGroup()` 作为正式入库唯一主链入口,不为 Phase 4 另起第二套正式入库流程。
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
- 无阻塞性问题;现有 intake 主链本身可用,主要缺口在关键测试覆盖和 schema 契约没有前移。
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- `MAIN-05` 已做第一轮正式收口,正式入库主链更稳定。
- 下一步可以进入 `04-02`,收口轻仓库 overview 和入库后结果可见性闭环。
---
*Phase: 04-正式入库与轻仓库闭环*
*Completed: 2026-04-02*

View File

@ -1,137 +0,0 @@
---
phase: 04-正式入库与轻仓库闭环
plan: 02
type: execute
wave: 1
depends_on:
- 04-01
files_modified:
- src/server/services/warehouse.service.ts
- src/server/services/warehouse.service.types.ts
- src/server/repositories/warehouse.repository.ts
- src/app/api/warehouse/route.ts
- src/app/warehouse/WarehouseClient.tsx
- ../docs/前端UI改动清单.md
autonomous: true
requirements:
- MAIN-06
must_haves:
truths:
- "轻仓库仍是正式入库后的轻量闭环页,不是完整 WMS。"
- "轻仓库必须能直接看见正式入库产出的正式 SKU、包装成品和基础库位结果。"
- "任何轻仓库补强都应优先复用现有 overview 聚合链,而不是另外再造一套仓库读取入口。"
artifacts:
- path: "src/server/services/warehouse.service.ts"
provides: "轻仓库概览聚合与库位能力"
contains: "getOverview|createLocation"
- path: "src/app/warehouse/WarehouseClient.tsx"
provides: "轻仓库概览页骨架"
contains: "正式 SKU 库存|包装成品库存|库位管理"
- path: "src/server/services/warehouse.service.types.ts"
provides: "轻仓库 overview DTO"
contains: "WarehouseOverviewDTO"
key_links:
- from: "src/server/services/intake.service.ts"
to: "src/server/services/warehouse.service.ts"
via: "正式入库产物必须在轻仓库 overview 中可见"
pattern: "productSKU|packagedItem"
- from: "src/app/warehouse/page.tsx"
to: "src/server/services/warehouse.service.ts"
via: "轻仓库页面直接消费 overview 聚合结果"
pattern: "getOverview"
---
<objective>
把轻仓库收口成“正式入库后马上能检查结果”的稳定闭环,而不是只剩一个概念页。
Purpose: 按 `MAIN-06` 修根因,避免正式入库虽然生成了正式对象,但轻仓库里信息不全、字段不稳、无法真正承担验收入口。
Output: 更完整的轻仓库 overview 契约、必要的数据补强,以及前端联调说明。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/04-正式入库与轻仓库闭环/04-CONTEXT.md
@src/app/warehouse/page.tsx
@src/app/warehouse/WarehouseClient.tsx
@src/app/api/warehouse/route.ts
@src/app/api/warehouse/locations/route.ts
@src/server/services/warehouse.service.ts
@src/server/services/warehouse.service.types.ts
@src/server/repositories/warehouse.repository.ts
@src/server/services/intake.service.ts
@../docs/前端UI改动清单.md
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: 收紧轻仓库 overview 契约</name>
<files>src/server/services/warehouse.service.ts, src/server/services/warehouse.service.types.ts, src/server/repositories/warehouse.repository.ts</files>
<read_first>
src/server/services/warehouse.service.ts
src/server/services/warehouse.service.types.ts
src/server/repositories/warehouse.repository.ts
src/server/services/intake.service.ts
prisma/schema.prisma
</read_first>
<acceptance_criteria>
- `WarehouseOverviewDTO` 能稳定表达库位、正式 SKU、包装成品三组核心结果,不靠前端猜字段。
- `warehouse.service.ts` 返回的正式 SKU 和包装成品信息足够支撑“入库后检查结果”这一目标,例如正式对象名、型号/编码、库存、库位、基础尺寸/重量等。
- 如果发现仓库读取层存在明显空洞,补在 service/repository 层,不把业务拼装塞回页面组件。
</acceptance_criteria>
<action>审视 `getOverview()` 当前聚合出来的字段是否足够支撑轻仓库闭环。优先修复“正式入库后结果可见但信息不完整”的根因,例如包装成品缺少关联编码、正式 SKU/包装成品的展示字段不统一、读取链在 service 层混杂页面兜底假设等。若需要新增测试,可就地补到合适的 service/repository 测试文件,保证 overview 契约稳定。</action>
<verify>
<automated>rg -n "getOverview|skuStock|packagedItems|warehouseLocation" src/server/services/warehouse.service.ts src/server/services/warehouse.service.types.ts src/server/repositories/warehouse.repository.ts</automated>
</verify>
<done>轻仓库后端 overview 契约更完整,正式入库后的关键结果可以稳定读取并继续被页面消费。</done>
</task>
<task type="auto">
<name>Task 2: 固定轻仓库页面与前端分工边界</name>
<files>src/app/api/warehouse/route.ts, src/app/warehouse/WarehouseClient.tsx, ../docs/前端UI改动清单.md</files>
<read_first>
src/app/api/warehouse/route.ts
src/app/warehouse/WarehouseClient.tsx
../docs/前端UI改动清单.md
src/server/services/warehouse.service.types.ts
</read_first>
<acceptance_criteria>
- `/warehouse` 当前页面不会再暗示“完整 WMS 已完成”,而是明确属于轻仓库闭环页。
- 若需要前端继续增强检索、切换、布局、动效等体验,只记录到 `../docs/前端UI改动清单.md`。
- 页面如果存在明显与后端 overview 契约不一致的展示问题,只做主链所需的最小修正。
</acceptance_criteria>
<action>核对 `WarehouseClient` 与 `warehouseService.getOverview()` 的字段契约和页面口径,确保轻仓库始终表达“正式结果查看与基础验收”而不是完整仓储系统。任何独立前端增强项,例如更强检索、筛选、卡片样式、移动端交互、成功回流引导等,只写入 `../docs/前端UI改动清单.md`,让 UI agent 继续处理。</action>
<verify>
<automated>rg -n "轻仓库|正式 SKU|包装成品|WMS|库位" src/app/warehouse/WarehouseClient.tsx ../docs/前端UI改动清单.md</automated>
</verify>
<done>轻仓库页面与服务契约口径一致,同时把需要 UI agent 继续处理的前端需求从主线逻辑里分流出去。</done>
</task>
</tasks>
<verification>
至少完成轻仓库 overview 契约检查,并用关键字验证页面和前端交接文档口径一致。
</verification>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`MAIN-06` 在主线侧被收口为:正式产品与 SKU 的基础结果可以在轻仓库稳定查看,并形成入库后的轻量管理闭环。
</success_criteria>
<output>
After completion, create `.planning/phases/04-正式入库与轻仓库闭环/04-02-SUMMARY.md`
</output>

View File

@ -1,92 +0,0 @@
---
phase: 04-正式入库与轻仓库闭环
plan: 02
subsystem: api
tags: [warehouse, overview, sku, packaged-item, tests, ui-handoff]
requires:
- phase: 04-正式入库与轻仓库闭环
provides: 正式入库主链关键契约已收紧
provides:
- 轻仓库 overview 读取链补强
- 包装成品关联 SKU 编码可见
- 轻仓库服务测试底座
affects: [phase-04, warehouse, frontend-handoff]
tech-stack:
added: []
patterns:
- 轻仓库读取优先走 repository
- overview 契约由测试守住
key-files:
created:
- .planning/phases/04-正式入库与轻仓库闭环/04-02-SUMMARY.md
- src/server/services/__tests__/warehouse.service.test.ts
modified:
- src/server/repositories/sku.repository.ts
- src/server/repositories/draft-group.repository.ts
- src/server/services/warehouse.service.ts
- ../docs/前端UI改动清单.md
key-decisions:
- "先补正式入库后最直接可见的仓库闭环缺口:包装成品必须能显示关联 SKU 编码。"
- "轻仓库 overview 的正式 SKU 读取改为走 repository,不继续在 service 层直接查 Prisma。"
patterns-established:
- "轻仓库守护模式:repository 聚合 + service 映射 + overview 测试 + 前端联调清单。"
requirements-completed: [MAIN-06]
duration: 20min
completed: 2026-04-02
---
# Phase 4 Plan 02 Summary
**轻仓库现在能把包装成品和正式 SKU 对得上了,并且 overview 读取链补上了测试。**
## Performance
- **Duration:** 20 min
- **Started:** 2026-04-02T23:30:00+08:00
- **Completed:** 2026-04-02T23:50:00+08:00
- **Tasks:** 2
- **Files modified:** 5
## Accomplishments
- 给轻仓库补了 `warehouse.service.test.ts`,用测试固定正式 SKU 与包装成品 overview 的核心映射结果。
- 补强了包装成品读取关系,轻仓库现在会返回包装成品对应的正式 `skuCode`,方便入库后核对。
- 把正式 SKU 仓库总览读取从 service 内直查 Prisma 调整为走 `skuRepository`,让分层更稳。
- 在前端交接清单里补了“包装成品列表要能看见关联正式 SKU 编码”的联调要求。
## Task Commits
未创建提交。当前仓库仍保持本地未提交工作状态。
## Files Created/Modified
- `src/server/repositories/sku.repository.ts` - 新增轻仓库正式 SKU overview 读取方法
- `src/server/repositories/draft-group.repository.ts` - 包装成品读取补上关联 SKU
- `src/server/services/warehouse.service.ts` - 轻仓库 overview 映射补全 `skuCode`
- `src/server/services/__tests__/warehouse.service.test.ts` - 新增轻仓库服务测试
- `../docs/前端UI改动清单.md` - 补轻仓库联调说明
## Decisions Made
- 先修“入库后核对信息不完整”这个根因,而不是扩写更重的仓储动作。
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
- 无阻塞性问题;当前最直接的闭环缺口是包装成品 DTO 里 `skuCode` 一直为 `null`,已修正。
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- `MAIN-06` 已完成第一轮正式收口,轻仓库现在更适合作为正式入库后的基础验收页。
- 下一步可以进入 `04-03`,固定主线收口检查单和 `merge / cherry-pick / 手工整合` 决策规则。
---
*Phase: 04-正式入库与轻仓库闭环*
*Completed: 2026-04-02*

View File

@ -1,129 +0,0 @@
---
phase: 04-正式入库与轻仓库闭环
plan: 03
type: execute
wave: 1
depends_on:
- 04-01
files_modified:
- docs/workflow/团队协作模式.md
- docs/workflow/主线同步操作手册.md
- docs/workflow/同事交接模板.md
- docs/workflow/模块边界说明.md
autonomous: true
requirements:
- COLL-04
must_haves:
truths:
- "主线收口人收到同事分支后,必须能按固定检查单完成检查,而不是继续靠临场判断。"
- "对协作者分支的决策必须能明确落到 `merge / cherry-pick / 手工整合` 三种口径之一。"
- "收口规则必须继续服务当前主线和包装协作边界,不产生第二套平级入口文档。"
artifacts:
- path: "docs/workflow/团队协作模式.md"
provides: "团队协作总规则"
contains: "merge / cherry-pick / 手工整合"
- path: "docs/workflow/主线同步操作手册.md"
provides: "分支同步与收口执行口径"
contains: "冲突|merge main|rebase main"
- path: "docs/workflow/同事交接模板.md"
provides: "协作者提交分支时的固定交接材料"
contains: "本次只改了什么|验证|风险"
key_links:
- from: "docs/workflow/团队协作模式.md"
to: "docs/workflow/模块边界说明.md"
via: "检查分支是否越过模块边界或改了共享接缝"
pattern: "共享接缝|包装协作边界"
- from: "docs/workflow/同事交接模板.md"
to: "docs/workflow/主线同步操作手册.md"
via: "交接材料与同步/并线动作要能对上"
pattern: "merge|rebase|冲突"
---
<objective>
把主线负责人收同事分支的流程收口成固定、可复用、AI 也能照着执行的规则。
Purpose: 按 `COLL-04` 修根因,避免包装同事后续继续提交分支时,主线收口仍然因为“怎么检查、什么时候 merge、什么时候 cherry-pick”不固定而反复混乱。
Output: 更新后的协作文档、主线收口检查单、三选一决策口径,以及更明确的交接模板。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/04-正式入库与轻仓库闭环/04-CONTEXT.md
@docs/workflow/团队协作模式.md
@docs/workflow/主线同步操作手册.md
@docs/workflow/模块边界说明.md
@docs/workflow/同事交接模板.md
@docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md
</context>
<tasks>
<task type="auto">
<name>Task 1: 固定主线收口检查单与三选一决策表</name>
<files>docs/workflow/团队协作模式.md, docs/workflow/主线同步操作手册.md, docs/workflow/模块边界说明.md</files>
<read_first>
docs/workflow/团队协作模式.md
docs/workflow/主线同步操作手册.md
docs/workflow/模块边界说明.md
.planning/phases/04-正式入库与轻仓库闭环/04-CONTEXT.md
</read_first>
<acceptance_criteria>
- 文档里明确列出主线收口前最少检查项:是否同步最新 `main`、是否超出任务范围、是否碰共享接缝、是否有最小验证、是否带交接说明。
- 文档里明确写出什么情况直接 `merge`、什么情况 `cherry-pick`、什么情况手工整合、什么情况暂不收口。
- 文档里明确把 Phase 4 的主线风险写出来:不能把包装逻辑误带进正式入库主链,不能把旧兼容链重新当作主链真相。
</acceptance_criteria>
<action>在现有协作文档入口内补一套主线收口检查规则,不新增第二套入口。重点补充:主负责人收同事分支时必须先看任务范围、共享接缝、验证记录、交接说明;当改动集中且无敏感接缝时可直接 `merge`,当改动里混有敏感接缝或非目标提交时优先 `cherry-pick`,当提交质量一般但目标功能仍要收口时采用手工整合;如果缺验证、范围失控、碰敏感接缝但未说明原因,则暂不收口。</action>
<verify>
<automated>rg -n "cherry-pick|手工整合|检查单|共享接缝|暂不收口|merge" docs/workflow/团队协作模式.md docs/workflow/主线同步操作手册.md docs/workflow/模块边界说明.md</automated>
</verify>
<done>主线收口不再依赖口头经验,后续人和 AI 都能按同一套规则判断如何收同事分支。</done>
</task>
<task type="auto">
<name>Task 2: 收紧同事交接模板,让 AI 可直接读懂</name>
<files>docs/workflow/同事交接模板.md</files>
<read_first>
docs/workflow/同事交接模板.md
docs/workflow/团队协作模式.md
docs/workflow/模块边界说明.md
</read_first>
<acceptance_criteria>
- `同事交接模板.md` 至少要求同事说明:本次只改什么、明确没改什么、是否碰共享接缝、验证跑了什么、风险/待主线继续什么。
- 模板语言足够让 AI 直接提取信息,不依赖大量上下文猜测。
- 模板口径与当前固定分支名、共享接缝规则一致,不再出现 `feature/packaging` 等旧口径。
</acceptance_criteria>
<action>把 `docs/workflow/同事交接模板.md` 收紧成 AI 也能直接解析的固定交接格式,显式要求协作者在提交分支时同步写明:目标分支名、任务范围、未做范围、共享接缝是否触达、最小验证结果、风险和需要主线负责人决定的事项。确保模板与 `codex/xuxiake-packaging` 及其任务分支口径一致。</action>
<verify>
<automated>rg -n "codex/xuxiake-packaging|共享接缝|验证|没改|风险|目标分支" docs/workflow/同事交接模板.md</automated>
</verify>
<done>同事后续给主线的交接材料更标准,主线收口和 AI 辅助检查都会更稳定。</done>
</task>
</tasks>
<verification>
通过文档关键字校验,确认主线收口检查单、三选一决策规则和交接模板都已落盘且口径一致。
</verification>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`COLL-04` 在主线侧被收口为:主线负责人可以在同事提交分支后按固定规则完成检查,并决定 `merge / cherry-pick / 手工整合`。
</success_criteria>
<output>
After completion, create `.planning/phases/04-正式入库与轻仓库闭环/04-03-SUMMARY.md`
</output>

View File

@ -1,90 +0,0 @@
---
phase: 04-正式入库与轻仓库闭环
plan: 03
subsystem: docs
tags: [collaboration, merge, cherry-pick, handoff, intake, warehouse]
requires:
- phase: 04-正式入库与轻仓库闭环
provides: 正式入库与轻仓库闭环边界已固定
provides:
- 主线收口检查单
- merge/cherry-pick/手工整合 决策口径
- AI 可读的交接模板增强
affects: [phase-04, collaboration, packaging-collab]
tech-stack:
added: []
patterns:
- 协作者分支收口规则并入现有 workflow 入口
- 交接模板显式要求写清范围、共享接缝、验证与建议收口方式
key-files:
created:
- .planning/phases/04-正式入库与轻仓库闭环/04-03-SUMMARY.md
modified:
- docs/workflow/团队协作模式.md
- docs/workflow/主线同步操作手册.md
- docs/workflow/模块边界说明.md
- docs/workflow/同事交接模板.md
key-decisions:
- "不新建第二套平级协作文档入口,而是在既有 workflow 文档里补主线收口规则。"
- "主线收口默认按检查单决定 merge / cherry-pick / 手工整合,不再继续靠临场判断。"
patterns-established:
- "协作收口守护模式:交接模板 -> 收口检查单 -> 决策规则 三段闭环。"
requirements-completed: [COLL-04]
duration: 20min
completed: 2026-04-02
---
# Phase 4 Plan 03 Summary
**主线收同事分支的流程现在已经写成固定规则了,后面人和 AI 都能按同一套口径判断怎么收。**
## Performance
- **Duration:** 20 min
- **Started:** 2026-04-02T23:50:00+08:00
- **Completed:** 2026-04-03T00:10:00+08:00
- **Tasks:** 2
- **Files modified:** 4
## Accomplishments
- 在团队协作总文档里补了主线收口检查单,以及 `merge / cherry-pick / 手工整合 / 暂不收口` 的固定判断口径。
- 在主线同步操作手册里补了“主线负责人怎么收同事分支”的执行说明。
- 在模块边界说明里补了共享接缝被改动时的收口提醒,避免直接整支 `merge`。
- 在同事交接模板里补了 AI 更容易读懂的固定字段,包括目标分支、本次只改/没改、是否碰共享接缝、风险和建议主线怎么收。
## Task Commits
未创建提交。当前仓库仍保持本地未提交工作状态。
## Files Created/Modified
- `docs/workflow/团队协作模式.md` - 固定主线收口检查单和三选一决策规则
- `docs/workflow/主线同步操作手册.md` - 补主线负责人收同事分支的操作口径
- `docs/workflow/模块边界说明.md` - 补共享接缝改动时的收口提醒
- `docs/workflow/同事交接模板.md` - 补 AI 可读的固定交接字段
## Decisions Made
- 把 `COLL-04` 落在现有 workflow 入口里,而不是再发明一份新的并线说明文档。
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
- 无阻塞性问题;主要工作是把已明确的协作原则落成可执行规则。
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- `COLL-04` 已完成第一轮正式收口,后续同事提交包装分支时,主线有固定收口口径可依。
- Phase 4 的三条计划已全部完成第一轮收口,后续可转向 Phase 5,或按需要补 Phase 4 的更细化验证。
---
*Phase: 04-正式入库与轻仓库闭环*
*Completed: 2026-04-02*

View File

@ -1,137 +0,0 @@
# Phase 4: 正式入库与轻仓库闭环 - Context
**Gathered:** 2026-04-02
**Status:** Ready for planning
<domain>
## Phase Boundary
本阶段只收口新平台主链的最后一段:把草稿组中的 `PreSku` 结果稳定转成正式 `Product / SKU`,并在入库后通过轻仓库完成基础查看与验收闭环。同时补齐主线负责人检查协作者分支并决定 `merge / cherry-pick / 手工整合` 的固定收口口径。本阶段不代做包装方案本体,不扩成完整 WMS,也不把旧 CAD 兼容链重新拉回新主链真相。
</domain>
<decisions>
## Implementation Decisions
### 正式入库主链
- **D-01:** Phase 4 继续以 `DraftGroup / PreSku` 为正式入库唯一前置,不允许跳过草稿组,也不允许让旧 `Product / SKU` 反向主导新平台组合阶段。
- **D-02:** 正式入库沿用现有 `formalizeDraftGroup()` 主链收口,核心方向是补稳定性、可检查性和测试覆盖,不重起第二套入库流程。
- **D-03:** `PreSku` 正式入库前仍必须满足“属于当前草稿组、未正式入库、未归档、且存在有效包装箱”这组准入规则;包装结果继续只认 Phase 3 已锁定的新结构。
- **D-04:** 入库后的正式结果以 `Product / ProductSKU / PackagingPlan / PackagedItem` 为正式承接对象,但这些正式对象只承接来自 `PreSku` 的已确认结果,不作为前置规划阶段的数据真相。
### 轻仓库闭环
- **D-05:** 轻仓库定位继续保持“基础验收与查看闭环”,只管理正式 SKU、包装成品、库存数量和基础库位,不在本阶段扩写完整出入库流水、调拨或盘点能力。
- **D-06:** 轻仓库页面和接口优先复用现有 `warehouseService.getOverview()` 与库位创建能力,重点收口正式入库后的可见性、字段完整性和主链闭环体验。
- **D-07:** 轻仓库展示应直接反映正式入库产物,包括正式 SKU、包装成品和库位挂接结果,避免用户入库后还要再录一遍仓库基础数据。
### 协作者分支收口
- **D-08:** Phase 4 必须把 `COLL-04` 从“原则”提升为“可执行规则”,让主线负责人收到同事分支后,能按固定检查单决定 `merge / cherry-pick / 手工整合`。
- **D-09:** 收口规则至少要覆盖:是否基于最新 `main`、是否超出任务范围、是否碰共享接缝、是否附带最小验证、是否带交接说明、是否影响正式入库/轻仓库主链。
- **D-10:** 对包装同事分支的收口,继续坚持“小任务收口优先”,默认不一次吞并长期积压改动;如果改动落入敏感接缝,优先 `cherry-pick` 或手工整合,而不是直接整支合并。
### 范围边界
- **D-11:** 包装方案本体、包装材料选择、最低成本方案、工艺图组合仍属于同事 `xuxiake` 的工作域;主线本阶段只承接包装结果,不代做包装业务实现。
- **D-12:** 前端/UI 不由当前工作流直接实现;如果 Phase 4 需要前端配合,只把需求写到 `D:\AI Code\3danzhuang\docs\前端UI改动清单.md`。
- **D-13:** 本阶段优先修根因和闭环缺口,不顺手扩写旧 CAD 兼容链,也不把路由层变成业务实现层。
### the agent's Discretion
- 是否先补正式入库链的测试和数据收口,再补轻仓库展示细节,由 agent 根据当前缺口排序决定。
- `COLL-04` 的收口规则最终落在单独文档、现有协作文档补章,还是两者组合,由 agent 在不增加第二套入口的前提下决定。
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### 项目级约束
- `.planning/PROJECT.md` — 项目目标、主链定义、前端边界、协作约束
- `.planning/ROADMAP.md` — Phase 4 的目标、依赖、成功标准
- `.planning/REQUIREMENTS.md` — `MAIN-05`、`MAIN-06`、`COLL-04` 的需求定义
- `.planning/STATE.md` — 当前阶段恢复入口与风险
### 既有阶段上下文
- `.planning/phases/02-资产到草稿组主链收口/02-CONTEXT.md` — `AssetFamily / AssetVariant -> DraftGroup / PreSku` 主链边界
- `.planning/phases/03-包装接缝稳定化/03-CONTEXT.md` — 包装新结构、共享接缝和协作边界
- `.planning/phases/03-包装接缝稳定化/03-01-SUMMARY.md` — 包装结果保存/试算/入库承接已被测试守住
- `.planning/phases/03-包装接缝稳定化/03-02-SUMMARY.md` — 包装占位入口与协作边界已固定
### 协作与收口规则
- `../AGENTS.md` — 全仓库协作入口、规范审查和团队协作固定口径
- `docs/workflow/团队协作模式.md` — 主线/包装线职责、分支规则、并线原则
- `docs/workflow/模块边界说明.md` — 主线、包装、旧兼容链与共享接缝边界
- `docs/workflow/主线同步操作手册.md` — 长期分支 `merge main` 与临时任务分支 `rebase main` 的固定口径
- `docs/开发指南/包装方案增强/新平台包装底座-同事AI对接说明-20260331.md` — 包装线对接主线时必须遵守的结构和接缝说明
### 正式入库现状
- `src/app/intake/page.tsx` — 正式入库页面入口与数据装配
- `src/app/intake/IntakeClient.tsx` — 正式入库编辑流程、选草稿组/选准 SKU/入库去向
- `src/app/api/intake/formalize/route.ts` — 正式入库接口入口
- `src/app/api/platform/schemas.ts` — 正式入库与库位 DTO 校验结构
- `src/lib/open-product/platform-api.ts` — 前端调用正式入库与库位接口的统一入口
- `src/server/services/intake.service.ts` — 正式入库编排、准入校验、正式对象创建
- `src/server/services/intake.service.types.ts` — 正式入库输入输出 DTO
- `src/server/services/__tests__/intake.service.test.ts` — 正式入库与包装承接现有测试底座
### 轻仓库现状
- `src/app/warehouse/page.tsx` — 轻仓库页面入口
- `src/app/warehouse/WarehouseClient.tsx` — 轻仓库概览、库位创建、正式 SKU/包装成品展示
- `src/app/api/warehouse/route.ts` — 轻仓库概览接口
- `src/app/api/warehouse/locations/route.ts` — 库位接口
- `src/server/services/warehouse.service.ts` — 轻仓库概览与库位服务
- `src/server/services/warehouse.service.types.ts` — 轻仓库 DTO
- `src/server/repositories/warehouse.repository.ts` — 基础库位仓储
### 数据模型与共享对象
- `prisma/schema.prisma` — `DraftGroup`、`PreSku`、`Product`、`ProductSKU`、`PackagingPlan`、`PackagedItem`、`WarehouseLocation` 数据边界
- `src/server/repositories/draft-group.repository.ts` — 草稿组与准 SKU 读取接缝
- `src/server/repositories/product.repository.ts` — 正式产品推荐与承接对象读取
- `src/server/repositories/sku.repository.ts` — 正式 SKU 结构与查询接缝
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- `intakeService.formalizeDraftGroup()` 已经具备从 `PreSku` 创建正式 `Product / ProductSKU / PackagingPlan / PackagedItem` 的主链能力,可直接作为 Phase 4 主入口继续收口。
- `WarehouseService.getOverview()` 已经把库位、正式 SKU、包装成品聚合成一个总览 DTO,适合作为轻仓库最小闭环底座。
- `IntakeClient` 和 `WarehouseClient` 已有基础页面骨架,说明 Phase 4 不是从零搭页面,而是补闭环、补字段、补检查规则。
### Established Patterns
- 主线后端继续遵守 `Route -> Service -> Repository -> Prisma`,Phase 4 不应把入库或仓库规则塞回 Route 或页面。
- 包装结果入库承接继续以 `boxes[]` 为唯一有效输入,不能为了兼容旧口径重新放开旧结构。
- 轻仓库现在是“轻量管理页”,不是完整 WMS;这是本阶段已明确的产品边界,不应被计划误扩。
### Integration Points
- `draftGroupService.getById()` 提供正式入库页所需的 `DraftGroupDetailDTO`,是 `PreSku -> 正式入库` 的前置读取接缝。
- `intake.service.ts` 是 `PreSku -> Product / SKU / 包装成品` 的核心编排层,任何正式入库补强都应围绕这里分层推进。
- `warehouse.service.ts` 和 `/warehouse` 页面是入库后结果可见性的主接点;Phase 4 若发现闭环缺口,应优先在这里收口。
- `docs/workflow/*.md` 是 `COLL-04` 固定口径的正式入口,新的收口规则应并入现有入口,而不是再造第二套平级文档入口。
</code_context>
<specifics>
## Specific Ideas
- 用户已明确:包装和包装方案由同事负责,本阶段不要回头代做包装本体。
- 用户已明确:前端/UI 由独立 agent 处理;主线如果需要 UI 配合,只记录需求到 `D:\AI Code\3danzhuang\docs\前端UI改动清单.md`。
- 当前真正需要补的是“正式入库完成后是否真的形成可检查闭环”和“同事分支来了以后主线怎么稳定收口”,而不是重新定义业务主链。
</specifics>
<deferred>
## Deferred Ideas
- 包装方案本体增强、最低成本方案、工艺图组合 — 继续留在包装域
- 完整 WMS 能力(出入库流水、调拨、盘点)— 后续阶段或新需求
- 旧 CAD 兼容链增强 — 保持可用,但不属于本阶段闭环
- 主链核心服务进一步大拆分 — 留到 Phase 5 继续做可维护性收口
</deferred>
---
*Phase: 04-正式入库与轻仓库闭环*
*Context gathered: 2026-04-02*

View File

@ -1,61 +0,0 @@
# Phase 4: 正式入库与轻仓库闭环 - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-04-02
**Phase:** 04-正式入库与轻仓库闭环
**Areas discussed:** 正式入库主链、轻仓库闭环、协作者分支收口
---
## 正式入库主链
| Option | Description | Selected |
|--------|-------------|----------|
| 沿用现有正式入库主链补稳定性 | 复用 `formalizeDraftGroup()`、补校验和测试,不重起第二套流程 | ✓ |
| 新起独立正式入库流程 | 绕开现有主链重新设计一套正式入库实现 | |
| 先扩旧产品链再回接新主链 | 让旧 `Product / SKU` 兼容链重新承担主链前置职责 | |
**User's choice:** 自动模式采用推荐项,继续沿用现有正式入库主链补稳定性。
**Notes:** 已从当前代码确认 `intake.service.ts` 能把 `PreSku` 承接为正式 `Product / ProductSKU / PackagingPlan / PackagedItem`,因此 Phase 4 重点是闭环收口,不是重写主链。
---
## 轻仓库闭环
| Option | Description | Selected |
|--------|-------------|----------|
| 维持轻仓库定位,只补正式结果可见性 | 继续只做库位、正式 SKU、包装成品和基础库存查看 | ✓ |
| 直接扩成完整 WMS | 本阶段加入复杂出入库、调拨、盘点等能力 | |
| 把轻仓库并回正式入库页 | 不单独保留轻仓库概览页 | |
**User's choice:** 自动模式采用推荐项,维持轻仓库定位,只补正式结果可见性。
**Notes:** 当前 `/warehouse` 已经具备概览和库位创建底座,符合“轻仓库”定位;扩成完整 WMS 属于明显 scope creep。
---
## 协作者分支收口
| Option | Description | Selected |
|--------|-------------|----------|
| 固定主线收口检查单和三选一决策规则 | 明确 `merge / cherry-pick / 手工整合` 的判断标准 | ✓ |
| 继续沿用口头原则 | 只保留文档里的抽象原则,收口时临场判断 | |
| 让协作者自己决定如何并入主线 | 放弃主线收口人的固定检查职责 | |
**User's choice:** 自动模式采用推荐项,固定主线收口检查单和三选一决策规则。
**Notes:** 文档侦察确认 `COLL-04` 目前只有原则,没有可执行检查单;Phase 4 需要把这部分补成稳定流程。
---
## the agent's Discretion
- 计划阶段可决定先补正式入库测试闭环还是先补轻仓库字段闭环,但都必须服务 `MAIN-05 / MAIN-06`。
- `COLL-04` 最终落在单独操作手册还是并入现有协作文档章节,可在不增加第二入口的前提下决定。
## Deferred Ideas
- 包装方案本体增强与包装工作区 UI
- 完整 WMS 能力
- 旧 CAD 兼容链增强

View File

@ -1,104 +0,0 @@
---
phase: 05-主链可维护性收口
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- src/server/services/assembly.service.ts
- src/server/services/panel.service.ts
- src/server/services/planning.service.ts
- src/server/services/*.types.ts
- src/server/services/__tests__/*
autonomous: true
requirements:
- ARCH-01
must_haves:
truths:
- "主链核心服务继续按编排、校验、映射、规则等职责拆分,新逻辑不能重新堆回单个膨胀 service。"
- "拆分优先围绕已有热点和现有拆分模式继续推进,不做一次性全仓库大重构。"
artifacts:
- path: "src/server/services/assembly.service.ts"
provides: "旧兼容链装配主服务"
contains: "findByProductId|bulkUpdateSteps|generate"
- path: "src/server/services/panel.service.ts"
provides: "板件主服务"
contains: "findByProductId|bulkSaveAssemblySemantics|create|update"
- path: "src/server/services/planning.service.ts"
provides: "规划主服务"
contains: "createTemplate|updateTemplate|createWorkspace|generateCandidates"
key_links:
- from: "src/server/services/assembly.service.ts"
to: "src/server/services/assembly-generation.service.ts"
via: "继续抽离生成与编排职责"
pattern: "generate"
- from: "src/server/services/panel.service.ts"
to: "src/server/services/panel-scope.service.ts"
via: "继续压缩权限与范围判断到 scope/validation 层"
pattern: "scope"
---
<objective>
继续把主链热点 service 拆到职责更清楚的状态,避免后续需求再次堆回大文件。
Purpose: 按 `ARCH-01` 修根因,建立“新增逻辑该放哪”的可预测结构。
Output: 一轮继续拆分后的主链 service 结构和必要测试。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/phases/05-主链可维护性收口/05-CONTEXT.md
@src/server/services/assembly.service.ts
@src/server/services/assembly-generation.service.ts
@src/server/services/assembly-scope.service.ts
@src/server/services/panel.service.ts
@src/server/services/panel-scope.service.ts
@src/server/services/panel-validation.service.ts
@src/server/services/planning.service.ts
@src/server/services/planning-generation.service.ts
@src/server/services/planning-scope.service.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: 继续压缩已拆但未拆透的热点 service</name>
<files>src/server/services/assembly.service.ts, src/server/services/panel.service.ts, src/server/services/planning.service.ts</files>
<read_first>
src/server/services/assembly.service.ts
src/server/services/panel.service.ts
src/server/services/planning.service.ts
对应的 *.scope/*.validation/*.mapper/*.generation 文件
</read_first>
<acceptance_criteria>
- 继续从主服务里抽出明确职责,不把新逻辑反向塞回去。
- 拆分后主服务只保留高层编排、少量胶水逻辑和对外入口。
- 至少补或更新一组相关测试,证明拆分没有改变外部行为。
</acceptance_criteria>
<action>从当前仍偏大的 `assembly.service.ts`、`panel.service.ts`、`planning.service.ts` 里找最明显的一段职责块继续下沉到已有的 scope / validation / mapper / generation / internal 文件,保持 API 不变,重点让“新增逻辑该放哪”更明确。</action>
<verify>
<automated>npm test -- --runTestsByPath <补实际测试文件></automated>
</verify>
<done>热点 service 继续瘦身,结构样板更清楚。</done>
</task>
</tasks>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`ARCH-01` 在当前热点主链 service 上继续落地,后续新增逻辑位置更可预测。
</success_criteria>
<output>
After completion, create `.planning/phases/05-主链可维护性收口/05-01-SUMMARY.md`
</output>

View File

@ -1,63 +0,0 @@
---
phase: 05-主链可维护性收口
plan: 01
subsystem: service
tags: [planning, service-layer, validation, draft-snapshot, tests]
requires:
- phase: 04-正式入库与轻仓库闭环
provides: 主链功能闭环已先稳定
provides:
- planning.service 继续瘦身
- 规划模板/分类校验职责下沉
- 草稿选中资产最小快照口径固定
affects: [phase-05, planning, architecture]
tech-stack:
added: []
patterns:
- planning 主服务只保留编排
- validation 负责模板定义与分类存在性校验
- internal helper 负责草稿快照裁剪
key-files:
created:
- .planning/phases/05-主链可维护性收口/05-01-SUMMARY.md
- src/server/services/planning-validation.service.ts
modified:
- src/server/services/planning.service.ts
- src/server/services/planning.service.internal.ts
- src/server/services/__tests__/planning.service.test.ts
key-decisions:
- "先继续收 planning.service,而不是同时扩到 assembly/panel,避免这轮 Phase 5 扩范围。"
- "把模板定义校验和分类存在性校验下沉到 planning-validation.service。"
- "把草稿 selectedAssets 的最小持久化快照提成 internal helper,避免主服务重复拼装。"
patterns-established:
- "planning 收口模式:scope 控权限,generation 管候选生成,validation 管模板/分类校验,internal 管 JSON/快照胶水。"
requirements-progressed: [ARCH-01]
completed: 2026-04-03
---
# Phase 5 Plan 01 Summary
**这一轮先把 `planning.service` 再瘦一层,让主服务更像编排层,而不是继续混着做输入校验和快照拼装。**
## Accomplishments
- 新增 `planning-validation.service.ts`,把模板定义解析校验和分类存在性校验从 `planning.service.ts` 下沉出去。
- 在 `planning.service.internal.ts` 增加草稿 `selectedAssets` 最小快照 helper,统一只保留 `id / assetType / code / name`。
- `planning.service.ts` 现在主要保留模板、工作台、草稿的高层编排,职责边界更清楚。
- 补了 `planning.service.test.ts`,验证模板创建时的分类校验/字段修剪,以及草稿保存时的最小快照持久化口径。
## Verification
- 已执行:`npm test -- --runTestsByPath src/server/services/__tests__/planning.service.test.ts`
- 结果:通过(4/4)
## Risks
- 这轮只继续收口了 `planning.service.ts`,`assembly.service.ts` 和 `panel.service.ts` 仍属于 Phase 5 后续可继续瘦身的热点。
- 未跑全量 `type-check` 和全量测试;本轮验证只覆盖了规划服务相关路径。
## Review
- 结论:`passed`
- 审查范围:`planning.service` 分层、最小测试、是否属于根因修复
- 标注:这是自审,不是独立复审

View File

@ -1,93 +0,0 @@
---
phase: 05-主链可维护性收口
plan: 02
type: execute
wave: 1
depends_on:
- 05-01
files_modified:
- src/server/services/intake.service.ts
- src/server/services/warehouse.service.ts
- src/server/repositories/*.ts
- src/app/api/**/*.ts
- docs/workflow/模块边界说明.md
autonomous: true
requirements:
- ARCH-02
- ARCH-03
must_haves:
truths:
- "旧 CAD 兼容链继续可用,但不会反向成为新平台主链的数据真相。"
- "新增主线后端能力继续遵守 Route -> Service -> Repository -> Prisma。"
artifacts:
- path: "src/server/services/intake.service.ts"
provides: "正式入库主链"
contains: "formalizeDraftGroup"
- path: "src/server/services/warehouse.service.ts"
provides: "轻仓库主链"
contains: "getOverview"
- path: "docs/workflow/模块边界说明.md"
provides: "新旧链边界约束"
contains: "不混主链和兼容链"
---
<objective>
继续把新旧链边界和分层约束收紧,避免 Phase 4 收好的主链又被旧链和直查逻辑侵入。
Purpose: 按 `ARCH-02`、`ARCH-03` 修根因,守住主链数据真相和统一分层。
Output: 一轮边界/分层收口后的代码与必要说明。
</objective>
<execution_context>
@D:/AI Code/3danzhuang/.codex/get-shit-done/workflows/execute-plan.md
@D:/AI Code/3danzhuang/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/phases/05-主链可维护性收口/05-CONTEXT.md
@src/server/services/intake.service.ts
@src/server/services/warehouse.service.ts
@src/app/api/intake/formalize/route.ts
@src/app/api/warehouse/route.ts
@src/app/api/warehouse/locations/route.ts
@docs/workflow/模块边界说明.md
</context>
<tasks>
<task type="auto">
<name>Task 1: 收掉主链里的分层残留点和边界风险</name>
<files>src/server/services/intake.service.ts, src/server/services/warehouse.service.ts, src/server/repositories/*.ts, src/app/api/**/*.ts</files>
<read_first>
src/server/services/intake.service.ts
src/server/services/warehouse.service.ts
相关 route 和 repository
</read_first>
<acceptance_criteria>
- 能回收到 repository 的直接数据访问尽量回收,不继续散落在 service / route。
- 正式入库、轻仓库等新平台主链不反向依赖旧兼容链真相。
- 若发现边界口径需要补,补到现有边界文档,不另开平级入口。
</acceptance_criteria>
<action>检查并继续收掉 `intake / warehouse` 以及相关 route/repository 中仍然不够干净的分层残留点;如果发现旧兼容链对象正在反向影响新主链决策,优先做最小结构收口,并把必要的边界提醒补到现有文档。</action>
<verify>
<automated>npm test -- --runTestsByPath <补实际测试文件></automated>
</verify>
<done>主链的分层和边界更稳,Phase 4 收好的能力不容易再被结构漂移带坏。</done>
</task>
</tasks>
<review>
执行完成后必须单独输出一次规范审查结论,参考 `docs/workflow/规范审查清单.md`,固定包含:
- 结论:`passed` / `passed with warnings` / `rejected`
- 审查范围:架构、视觉、根因修复
- 标注:`这是自审,不是独立复审`
</review>
<success_criteria>
`ARCH-02`、`ARCH-03` 在主链关键接点上继续落地,新旧链边界和统一分层保持可预测。
</success_criteria>
<output>
After completion, create `.planning/phases/05-主链可维护性收口/05-02-SUMMARY.md`
</output>

View File

@ -1,59 +0,0 @@
---
phase: 05-主链可维护性收口
plan: 02
subsystem: boundary-and-layering
tags: [intake, warehouse, prisma, material-library, assisted-assembly, gsd-memory]
requires:
- phase: 05-主链可维护性收口
provides: 05-01 已完成第一轮 service 拆分
- phase: 05-主链可维护性收口
provides: 05-03 已把包装支线资料库规则并回主线
provides:
- intake 主链继续回收到 repository 分层
- 主仓库 prisma schema 与当前代码口径重新对齐
- 新旧链边界文档回到当前手动拼装主链
affects: [phase-05, intake, warehouse, prisma-schema, planning-memory]
requirements-progressed: [ARCH-02, ARCH-03]
completed: 2026-04-11
---
# Phase 5 Plan 02 Summary
**这轮把 `05-03(包装支线资料库并线)` 留下的结构尾巴收掉:一边把 `intake(正式入库)` 的分层残留继续回收到 `repository(仓储层)`,一边把主仓库里落后的 `schema.prisma(数据库模型)` 补回到当前真实代码口径。**
## Accomplishments
- `intake.service.ts(正式入库服务)` 不再继续直接查 `prisma(数据库)` 里的 `Product / SKU`,已回收到 `productRepository(产品仓储)` 与 `skuRepository(SKU 仓储)`。
- 新增 `skuRepository.findByProductIdAndSkuCode(...)`,让正式入库唯一编码生成继续遵守 `Route -> Service -> Repository -> Prisma` 分层。
- `docs/workflow/模块边界说明.md` 已改回当前真实主线:
- 当前入口是 `assisted-assembly(辅助装配)`
- 当前装配主链是 `manual assembly(手动拼装)`
- 旧 `auto-combinations(自动组合)` 只按历史兼容链理解
- 主仓库 `prisma/schema.prisma(数据库模型)` 已补齐到与当前代码一致:
- `surfaceOptionId(贴面类型)` 可空
- `weightKg(整板重量)`
- `SubstrateFinish(基材-花色关联)`
- `Material.code(花色编码)`
- 主仓库已补回缺失迁移:
- `20260411143000_merge_packaging_branch_material_library(包装支线资料库并线迁移)`
- `material.service.ts(花色服务)`、`asset-center.service.ts(资产中心服务)`、`open-platform-demo-seed.service.ts(演示数据服务)` 等配套代码与测试已和新口径对齐。
- `platform-navigation(平台导航)`、`draft-groups(草稿组)`、`intake(正式入库)` 页面里的旧“生成候选”话术已回收到当前“完成手动拼装”口径。
## Verification
- `npx prisma validate`
- `npm run type-check`
- `npm test -- --runInBand --runTestsByPath src/server/services/__tests__/intake.service.test.ts src/server/services/__tests__/warehouse.service.test.ts src/server/services/__tests__/open-platform-demo-seed.service.test.ts src/lib/open-product/__tests__/assembly-workspace.test.ts src/app/assisted-assembly/AssistedAssemblyClient.test.tsx src/app/__tests__/assisted-assembly-page.test.tsx`
结果:全部通过。
## Risks
- `5 字段唯一约束` 的最终业务规则仍未定稿;本轮只把当前主线收回到“代码、迁移、文档一致”,没有擅自拍板最终重复判定真相。
- 当前工作区仍然是混合现场,包含前端重写相关改动和未跟踪目录;本总结只代表 `05-02` 这轮结构收口已完成,不代表整仓库已经干净可直接发布。
## Review
- 结论:`passed with warnings`
- 审查范围:架构、视觉、根因修复
- 标注:这是自审,不是独立复审

View File

@ -1,47 +0,0 @@
---
phase: 05-主链可维护性收口
plan: 03
type: execute
wave: inserted
depends_on:
- 05-01
files_modified:
- prisma/schema.prisma
- src/server/services/asset-center.service.ts
- src/server/services/material.service.ts
- src/app/asset-center/material-library/**
- src/lib/open-product/platform-api.ts
- .planning/PROJECT.md
- .planning/ROADMAP.md
- .planning/REQUIREMENTS.md
- .planning/STATE.md
autonomous: true
requirements:
- ARCH-01
- COLL-04
must_haves:
truths:
- "包装支线里的资料库业务真相先并回主线,但不把未拍板的 5 字段唯一约束冒充已定稿。"
- "GSD 项目记忆必须回到当前真实主线,不能继续把旧自动组合入口当现行入口。"
artifacts:
- path: "docs/会话交接-20260411-包装支线并线与资料库收口.md"
provides: "这轮并线收了什么、留了什么"
contains: "5 字段唯一约束后续专题"
- path: ".planning/STATE.md"
provides: "当前真实恢复入口"
contains: "05-03"
---
<objective>
把包装支线里的基础资料库规则和迁移安全地并回主线,并顺手把 GSD 项目记忆校正到当前真实业务主线。
Purpose: 收掉当前主线和包装支线之间已经拍板的资料库真相,同时修复 `.planning` 记忆滞后问题。
Output: 可运行的主线代码、测试环境部署结果、可恢复的 GSD 项目记忆。
</objective>
<success_criteria>
- 包装支线资料库规则并回主线后,主线仍可验证、可部署。
- 测试环境完成部署并确认基础资料库与辅助装配主链没有回退。
- `.planning` 的核心文件重新描述当前真实主线,而不是继续停在旧的自动组合话术。
</success_criteria>

View File

@ -1,54 +0,0 @@
---
phase: 05-主链可维护性收口
plan: 03
subsystem: material-library
tags: [packaging-merge, material-library, prisma, deploy, gsd-memory]
requires:
- phase: 05-主链可维护性收口
provides: 05-01 已完成第一轮服务拆分收口
provides:
- 包装支线资料库规则并回主线
- 基材 / 花色迁移脚本与测试
- GSD 项目记忆刷新到当前真实主线
affects: [phase-05, material-library, packaging-merge, planning-memory]
requirements-progressed: [ARCH-01, COLL-04]
completed: 2026-04-11
---
# Phase 5 Plan 03 Summary
**这轮先把包装支线中已经拍板的资料库真相并回主线,再把 GSD 项目记忆刷新到当前真实主线。**
## Accomplishments
- 已把包装支线里的 `material-library(基础资料库)` 规则并回主线,包括:
- `surfaceOptionId(贴面类型)` 可空
- `weightKg(整板重量)`
- `SubstrateFinish(基材-花色关联)`
- `BC-XXXX(基材编码)` / `HS-XXXX(花色编码)` 自动生成
- 花色同名仅按 `isActive(启用)` 判重
- 右侧详情驱动面板、临时新建花色 / 分类、缩略图上传过渡能力
- 已新增 `Prisma(数据库)` 迁移,并在测试环境手工落库,确认数据库结构与主线代码一致。
- 已补资料库、资产中心、辅助装配、旧订单生成装配的关键测试,并重新跑通。
- 已把 `.planning` 的核心记忆从旧 `auto-combinations(自动组合)` 主线,校正到当前 `assisted-assembly(辅助装配) / manual assembly(手动拼装)` 主线。
## Verification
- `npm run type-check`
- `npm test -- --runInBand --runTestsByPath src/app/asset-center/material-library/MaterialLibraryClient.test.tsx src/server/repositories/__tests__/material.repository.test.ts src/server/services/__tests__/material.service.test.ts src/server/services/__tests__/asset-center.substrate.service.test.ts src/server/services/__tests__/asset-center.service.test.ts`
- `npm test -- --runInBand --runTestsByPath src/app/assisted-assembly/AssistedAssemblyClient.test.tsx src/app/__tests__/assisted-assembly-page.test.tsx src/components/platform/__tests__/AssemblyWorkbench.test.tsx src/lib/open-product/__tests__/assembly-workspace.test.ts`
- `npm test -- --runInBand --runTestsByPath src/app/api/products/[id]/assembly/generate-from-order/__tests__/route.test.ts`
- 测试环境部署成功,`PM2(进程管理)` 进程 `furniture-3d-system-wyj-test` 在线
- 真实页面最小冒烟通过:资料库页可打开、辅助装配页未回退到旧自动组合文案
## Risks
- `5 字段唯一约束` 的最终业务规则仍未定稿,本轮只做了可运行的并线收口,不等于这项真相已经拍板完成。
- 测试环境数据库来自旧库快照,本轮迁移采用手工执行 `migration.sql(迁移脚本)` 的方式落库,后续如需长期治理,应补一条更正式的基线说明。
## Review
- 结论:`passed with warnings`
- 审查范围:架构、视觉、根因修复
- 标注:这是自审,不是独立复审

View File

@ -1,127 +0,0 @@
# Phase 5: 主链可维护性收口 - Context
**Gathered:** 2026-04-03
**Status:** Ready for planning
<domain>
## Phase Boundary
本阶段只处理“主链以后还能不能继续稳着长”的结构问题:继续把核心服务按编排、校验、映射、规则拆开,继续压住旧 CAD 兼容链对新平台主链的反向侵入,并把新增主线能力继续收在 `Route -> Service -> Repository -> Prisma` 结构里。本阶段不再重做 Phase 4 功能闭环,不扩新业务范围,也不代做前端改版。
</domain>
<decisions>
## Implementation Decisions
### 服务拆分方向
- **D-01:** Phase 5 继续沿用“从膨胀服务里抽出 scope / validation / mapper / generation / materialization”的拆分路线,而不是一次性大重构全仓库。
- **D-02:** 已经开始拆分的主链服务优先继续收口,包括 `assembly.service.ts`、`panel.service.ts`、`planning.service.ts`,以及仍可能继续膨胀的 `intake.service.ts`、`warehouse.service.ts`。
- **D-03:** 新拆分应优先服务“职责可预测”,让后续新增逻辑知道该放哪,而不是仅仅为了文件变多。
### 新旧链边界
- **D-04:** 新平台主链的数据真相继续锁在 `AssetFamily / AssetVariant / DraftGroup / PreSku -> Product / SKU` 这条链上,旧 `CAD / Panel / Hole / ProductHardware / AssemblyStep` 只负责兼容维护和承接,不回头主导新平台规划阶段。
- **D-05:** 如果正式入库或轻仓库需要复用旧链对象,只允许作为“正式承接结果”或“兼容展示结果”使用,不允许反向把旧对象当成新平台前置真相。
- **D-06:** 与旧链相关的 service 如果继续扩写,必须明确是兼容链能力,不能伪装成新平台主链 service。
### 分层约束
- **D-07:** Phase 5 要继续把 service 内直接查 Prisma 的残留点收回 repository,或者至少集中到已有 repository 能力里。
- **D-08:** Route 继续只做收参、鉴权、返回;如果发现 Route 或页面开始兜底主业务规则,要优先回收。
- **D-09:** 本阶段优先做“结构收口”,不为追求表面整洁去回退已经跑通的主链能力。
### the agent's Discretion
- 先拆哪一个主链 service,由 agent 按当前膨胀度和风险排序决定。
- 是否把 Phase 5 拆成“服务继续拆分”和“边界/分层收口”两条 plan,由 agent 决定。
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### 项目级约束
- `.planning/PROJECT.md` — 项目目标、主链与旧链并存约束
- `.planning/ROADMAP.md` — Phase 5 的目标、依赖、成功标准
- `.planning/REQUIREMENTS.md` — `ARCH-01`、`ARCH-02`、`ARCH-03` 的需求定义
- `.planning/STATE.md` — 当前阶段恢复入口
### 前序阶段结果
- `.planning/phases/04-正式入库与轻仓库闭环/04-01-SUMMARY.md` — 正式入库主链已收口到什么程度
- `.planning/phases/04-正式入库与轻仓库闭环/04-02-SUMMARY.md` — 轻仓库 overview 已收口到什么程度
- `.planning/phases/04-正式入库与轻仓库闭环/04-03-SUMMARY.md` — 协作收口规则已固定
### 协作与边界
- `../AGENTS.md` — 全仓库标准与固定流程
- `docs/workflow/模块边界说明.md` — 新平台主链、包装线、旧 CAD 兼容链边界
- `docs/workflow/团队协作模式.md` — 主线职责、协作分流和收口规则
### 当前服务拆分现状
- `src/server/services/assembly.service.ts`
- `src/server/services/assembly-generation.service.ts`
- `src/server/services/assembly-scope.service.ts`
- `src/server/services/assembly.mapper.ts`
- `src/server/services/panel.service.ts`
- `src/server/services/panel-scope.service.ts`
- `src/server/services/panel-validation.service.ts`
- `src/server/services/panel.mapper.ts`
- `src/server/services/planning.service.ts`
- `src/server/services/planning-generation.service.ts`
- `src/server/services/planning-scope.service.ts`
- `src/server/services/planning.mapper.ts`
- `src/server/services/planning.service.internal.ts`
- `src/server/services/intake.service.ts`
- `src/server/services/warehouse.service.ts`
### 相关仓储与边界接点
- `src/server/repositories/sku.repository.ts`
- `src/server/repositories/draft-group.repository.ts`
- `src/server/repositories/warehouse.repository.ts`
- `src/app/api/intake/formalize/route.ts`
- `src/app/api/warehouse/route.ts`
- `src/app/api/warehouse/locations/route.ts`
- `prisma/schema.prisma`
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- `assembly.service.ts` 已经开始把生成、scope、mapper 分出去,说明 Phase 5 不需要从零定义拆分模式。
- `panel.service.ts` 已经开始复用 `panelScopeService`、`panelValidationService`、`panel.mapper.ts`。
- `planning.service.ts` 已经开始复用 `planningGenerationService`、`planningScopeService`、`planning.mapper.ts` 与 `planning.service.internal.ts`。
### Established Patterns
- 当前仓库已经形成“主服务 + scope/validation/mapper/helper”的拆分样式,Phase 5 应继续沿这条线推进。
- `draft-group.service.ts` 与 `combination.service.ts` 已经比前面更轻,说明“先拆膨胀热点,再稳接口”这条路径是可行的。
- Phase 4 已证明:如果不主动收口,`intake / warehouse` 这种主链服务会继续自然膨胀。
### Integration Points
- `intake.service.ts` 是 `PreSku -> Product / SKU / PackagingPlan / PackagedItem` 的正式承接中心,是主链结构风险高点。
- `warehouse.service.ts` 现在已比之前更干净,但仍是 Phase 5 里要继续盯的主链读模型服务。
- `assembly.service.ts`、`panel.service.ts`、`planning.service.ts` 是“已拆但未完全拆透”的典型点,适合继续作为 Phase 5 的结构样板。
</code_context>
<specifics>
## Specific Ideas
- 用户一直要求“地基打好”,Phase 5 对应的就是继续打地基,而不是再开新功能。
- 当前最该避免的不是功能缺失,而是 Phase 4 完成功能后,主链又慢慢回到“大文件继续堆逻辑”的状态。
</specifics>
<deferred>
## Deferred Ideas
- 新功能扩展
- 大规模前端视觉改版
- 包装方案本体增强
- 完整 WMS 能力
</deferred>
---
*Phase: 05-主链可维护性收口*
*Context gathered: 2026-04-03*

View File

@ -1,59 +0,0 @@
# Phase 5: 主链可维护性收口 - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-04-03
**Phase:** 05-主链可维护性收口
**Areas discussed:** 服务拆分方向、新旧链边界、分层约束
---
## 服务拆分方向
| Option | Description | Selected |
|--------|-------------|----------|
| 延续已有拆分路线,继续从膨胀服务抽 scope/validation/mapper/generation | 小步稳定收口,降低对现有主链的冲击 | ✓ |
| 一次性大重构整个 service 层 | 风险高,容易把 Phase 4 刚收稳的主链打散 | |
| 先不拆服务,只靠文档约束 | 结构风险仍会继续累积 | |
**User's choice:** 自动模式采用推荐项,继续沿已有拆分路线推进。
**Notes:** 当前仓库已经出现 `assembly-generation.service.ts`、`panel-scope.service.ts`、`planning.mapper.ts` 这类拆分产物,说明这条路已被验证。
---
## 新旧链边界
| Option | Description | Selected |
|--------|-------------|----------|
| 继续压住旧 CAD 兼容链,只允许作为兼容承接层存在 | 保持新平台主链数据真相清晰 | ✓ |
| 让旧链对象重新参与新平台前置规划 | 会重新混淆主链真相 | |
| 暂时不管边界,只先拆代码文件 | 容易拆完后仍然业务真相混乱 | |
**User's choice:** 自动模式采用推荐项,继续压住旧链边界。
**Notes:** ROADMAP 和模块边界文档都已经把这点写成长期约束。
---
## 分层约束
| Option | Description | Selected |
|--------|-------------|----------|
| 继续把 service 内直查 Prisma 的残留点回收到 repository | 让后续新增逻辑位置可预测 | ✓ |
| 接受 service 里零散直查 Prisma | 会持续削弱分层稳定性 | |
| 只在 Route 层补注释说明 | 不能解决结构漂移 | |
**User's choice:** 自动模式采用推荐项,继续把残留直查 Prisma 收回 repository。
**Notes:** Phase 4 的 warehouse 已经证明这种回收方式是低风险且有效的。
---
## the agent's Discretion
- Phase 5 先从哪一个主链热点 service 下手,由执行阶段再按风险排序。
## Deferred Ideas
- 新业务能力扩展
- 大规模前端改版

View File

@ -7,10 +7,9 @@
# ============================================
# 阶段 1: 基础镜像
# ============================================
FROM node:18-alpine AS base
FROM node:24-alpine AS base
# 使用阿里云镜像源加速(中国网络优化)
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
# 保留官方 Alpine 源,避免镜像站同步滞后导致 apk 包 404。
# ============================================
# 阶段 2: 依赖安装 (deps)
@ -71,6 +70,11 @@ FROM base AS builder
ARG DATABASE_URL="postgresql://user:password@localhost:5432/placeholder_db?schema=public"
ARG NEXTAUTH_URL=""
ARG NEXT_PUBLIC_APP_URL=""
ARG S3_ENDPOINT="http://localhost:9000"
ARG S3_PUBLIC_DOMAIN="http://localhost:9000/furniture-3d-models"
ARG S3_BUCKET="furniture-3d-models"
ARG S3_ACCESS_KEY="build-only"
ARG S3_SECRET_KEY="build-only"
# Prisma 引擎缓存配置
ARG PRISMA_ENGINE_CACHE_FILE_NAME="libquery_engine-linux-musl-openssl-3.0.x.so.node"
@ -82,6 +86,12 @@ ENV PRISMA_SCHEMA_ENGINE_CACHE_FILE_NAME=${PRISMA_SCHEMA_ENGINE_CACHE_FILE_NAME}
ENV DATABASE_URL=${DATABASE_URL}
ENV NEXTAUTH_URL=${NEXTAUTH_URL}
ENV NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL}
ENV S3_ENDPOINT=${S3_ENDPOINT}
ENV S3_PUBLIC_DOMAIN=${S3_PUBLIC_DOMAIN}
ENV S3_BUCKET=${S3_BUCKET}
ENV S3_ACCESS_KEY=${S3_ACCESS_KEY}
ENV S3_SECRET_KEY=${S3_SECRET_KEY}
ENV PUPPETEER_SKIP_DOWNLOAD=true
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
ENV PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING=1
@ -237,4 +247,4 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/api/health || exit 1
# 启动应用
CMD ["node", "server.js"]
CMD ["node", "server.js"]

View File

@ -1,10 +1,11 @@
# 家具开品平台
# 由壹点「产品开发平台」
> 最后更新:2026-04-07
> 当前默认主线:`资产中心 -> 自动组合 -> 草稿组 -> 包装方案 -> 正式入库 -> 轻仓库`
> 最后更新:2026-04-15
> 当前默认主线:`资产中心 -> 辅助装配 -> 草稿组 -> 正式入库 -> 轻仓库`
这是当前正在持续开发的主项目。
旧 `CAD -> 装配编辑 -> 装配步骤 -> 3D 回放` 链继续保留,但它属于兼容链,不再代表当前业务主线。
包装结果当前挂在草稿组、`PreSku` 和轻仓库承接口里,不作为独立顶层页面阶段。
## 开始前先读
@ -16,8 +17,8 @@
## 当前项目目标
- 用资产族和规格款作为开品基础数据
- 按系列规则生成自动组合候选
- 让用户在草稿组里确认组合、预览、包装承接
- 在辅助装配工作台完成组合、吸附、3D 预览和保存前校验
- 让用户在草稿组里确认组合、预览,并承接包装相关信息
- 再进入正式入库和轻仓库
- 保留旧 CAD 链服务历史数据、兼容导入、装配编辑和回放
@ -50,7 +51,7 @@ npm test
### 新平台主链
- `/asset-center`
- `/auto-combinations`
- `/assisted-assembly`
- `/draft-groups`
- `/intake`
- `/warehouse`
@ -74,11 +75,11 @@ npm test
- `docs/项目总览.md`
- `docs/README.md`
- `docs/agent-memory/README.md`
- `docs/部署指南.md`
- `docs/运维指南.md`
## 说明
- 一次性阶段报告、测试报告和旧设计稿已清理,不再占用项目入口。
- 前端交接单以工作区根目录 `docs/前端UI改动清单.md` 为准,不看仓库内旧副本。
- 项目内不再维护前端交接单、会话交接或 `agent-memory` 这类台账入口。
- 历史文档里如果还出现 `auto-combinations`、`自动组合`、`自动拼装`,统一按旧称呼理解,不再代表当前主线入口。

View File

@ -0,0 +1,30 @@
# 供应链脚本监控 Agent
每台运行脚本的机器部署一份 Agent。Agent 只主动向产品平台发起 HTTP 请求,不需要为被监控机器开放入站端口。
## 接入
1. 在供应链中心的“脚本监控”页面点击“接入机器”,生成独立的 `agentId` 和 `agentToken`。
2. 复制本目录到目标机器,把 `config.example.json` 复制为 `config.json`,填入凭据、内外网地址和脚本任务说明。
3. 先运行 `node agent.mjs` 确认上报,再使用 `pm2 start ecosystem.config.cjs` 常驻。
需要在平台点击“立即运行”的脚本,必须在 `config.json` 对应任务中显式配置
`"allowManualRun": true`。平台只会下发脚本 `key`,Agent 始终执行本机配置里预存的
`command`,不会接收平台传来的任意命令。Agent 默认每 5 秒主动领取一次执行请求,
因此内网机器不需要开放入站端口。
`connectionMode` 支持:
- `intranet`:只走内网地址。
- `internet`:只走公网地址,生产必须使用 HTTPS。
- `auto`:优先内网,失败后自动尝试公网。
## 记录真实运行结果
对于需要记录开始时间、耗时、退出码和输出的任务,用包装器代替原命令:
```bash
node /opt/furniture-script-monitor/run-script.mjs supply-chain-archive-sync
```
包装器执行 `config.json` 中同一 `key` 的 `command`,并在开始与结束时上报。Agent Token 只保存在目标机器的 `config.json`,该文件已被仓库忽略。

View File

@ -0,0 +1,410 @@
import { createHash } from 'node:crypto'
import { execFileSync, spawn } from 'node:child_process'
import { existsSync, readFileSync, statSync } from 'node:fs'
import { hostname, platform, release } from 'node:os'
import { dirname, resolve } from 'node:path'
import { fileURLToPath, pathToFileURL } from 'node:url'
export const AGENT_VERSION = '1.0.0'
const AGENT_DIR = dirname(fileURLToPath(import.meta.url))
function requiredText(value, label) {
if (typeof value !== 'string' || !value.trim()) throw new Error(`缺少 ${label}`)
return value.trim()
}
export function loadConfig(configPath = process.env.SCRIPT_MONITOR_CONFIG || resolve(AGENT_DIR, 'config.json')) {
if (!existsSync(configPath)) {
throw new Error(`配置文件不存在:${configPath},请复制 config.example.json 为 config.json`)
}
const config = JSON.parse(readFileSync(configPath, 'utf8'))
config.agentId = requiredText(config.agentId, 'agentId')
config.agentToken = requiredText(config.agentToken, 'agentToken')
config.connectionMode = String(config.connectionMode || 'auto').toLowerCase()
if (!['intranet', 'internet', 'auto'].includes(config.connectionMode)) {
throw new Error('connectionMode 必须是 intranet、internet 或 auto')
}
config.reportIntervalSec = Math.max(15, Number(config.reportIntervalSec) || 60)
config.requestTimeoutMs = Math.max(1000, Number(config.requestTimeoutMs) || 10000)
config.commandPollIntervalSec = Math.min(60, Math.max(2, Number(config.commandPollIntervalSec) || 5))
config.scripts = Array.isArray(config.scripts) ? config.scripts : []
return config
}
function normalizeCenterUrl(value) {
if (typeof value !== 'string' || !value.trim()) return null
const url = new URL(value.trim())
if (!['http:', 'https:'].includes(url.protocol)) throw new Error(`不支持的中心地址协议:${url.protocol}`)
return url.toString().replace(/\/$/, '')
}
export function resolveCenterEndpoints(config) {
const intranet = normalizeCenterUrl(config.centerUrls?.intranet)
const internet = normalizeCenterUrl(config.centerUrls?.internet)
const endpoints = []
if (config.connectionMode !== 'internet' && intranet) endpoints.push({ kind: 'INTRANET', url: intranet })
if (config.connectionMode !== 'intranet' && internet) endpoints.push({ kind: 'INTERNET', url: internet })
if (endpoints.length === 0) throw new Error('当前连接模式没有可用的 centerUrls 地址')
return endpoints
}
export async function postToCenter(config, route, payload) {
let lastError = null
for (const endpoint of resolveCenterEndpoints(config)) {
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), config.requestTimeoutMs)
try {
const response = await fetch(`${endpoint.url}${route}`, {
method: 'POST',
signal: controller.signal,
headers: {
'content-type': 'application/json',
authorization: `Bearer ${config.agentToken}`,
'x-script-monitor-agent-id': config.agentId,
},
body: JSON.stringify({
...payload,
...(route.endsWith('/report')
? { connectionKind: payload.connectionKind ?? endpoint.kind }
: {}),
}),
})
if (!response.ok) {
const message = await response.text().catch(() => '')
throw new Error(`HTTP ${response.status}${message ? `:${message.slice(0, 240)}` : ''}`)
}
return { endpoint, body: await response.json().catch(() => null) }
} catch (error) {
lastError = error
} finally {
clearTimeout(timeout)
}
}
throw lastError ?? new Error('没有可用的中心地址')
}
function hashKey(value) {
return createHash('sha256').update(value).digest('hex').slice(0, 20)
}
function redactCommand(command) {
return String(command || '')
.replace(/([A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|KEY)[A-Z0-9_]*=)([^\s]+)/gi, '$1[REDACTED]')
.replace(/(https?:\/\/[^:/\s]+:)[^@/\s]+@/gi, '$1[REDACTED]@')
.slice(0, 2000)
}
function readPm2() {
try {
const raw = execFileSync('pm2', ['jlist'], { encoding: 'utf8', timeout: 5000 })
return JSON.parse(raw)
} catch {
return []
}
}
function scanPm2() {
return readPm2().map((processInfo) => {
const name = String(processInfo.name || '未命名 PM2 进程')
const status = processInfo.pm2_env?.status === 'online'
? 'RUNNING'
: processInfo.pm2_env?.status === 'stopped'
? 'DISABLED'
: processInfo.pm2_env?.status === 'errored'
? 'FAILED'
: 'UNKNOWN'
return {
key: `pm2:${name}`,
name,
taskDescription: '',
jobType: 'PM2',
schedule: null,
commandPreview: redactCommand(processInfo.pm2_env?.pm_exec_path || ''),
expectedIntervalSec: null,
status,
isEnabled: status !== 'DISABLED',
canManualRun: false,
lastStartedAt: processInfo.pm2_env?.pm_uptime
? new Date(processInfo.pm2_env.pm_uptime).toISOString()
: null,
lastResultSummary: status === 'RUNNING'
? `PM2 在线,PID ${processInfo.pid || '-'}`
: `PM2 状态:${processInfo.pm2_env?.status || 'unknown'}`,
}
})
}
function parseCronLine(line) {
const value = line.trim()
if (!value || value.startsWith('#')) return null
const special = value.match(/^@(reboot|yearly|annually|monthly|weekly|daily|hourly)\s+(.+)$/)
const regular = value.match(/^(\S+\s+\S+\s+\S+\s+\S+\s+\S+)\s+(.+)$/)
const schedule = special ? `@${special[1]}` : regular?.[1]
const command = special?.[2] ?? regular?.[2]
if (!schedule || !command) return null
const tag = command.match(/#\s*([\w.-]{3,})\s*$/)?.[1]
const name = tag || command.split(/\s+/).filter(Boolean).slice(-1)[0] || 'cron task'
return {
key: `cron:${hashKey(`${schedule}\n${command}`)}`,
name,
taskDescription: '',
jobType: 'CRON',
schedule,
commandPreview: redactCommand(command.replace(/\s+#.*$/, '')),
expectedIntervalSec: null,
status: 'UNKNOWN',
isEnabled: true,
canManualRun: false,
}
}
function scanCron() {
if (platform() === 'win32') return []
try {
const raw = execFileSync('crontab', ['-l'], { encoding: 'utf8', timeout: 5000 })
return raw.split(/\r?\n/).map(parseCronLine).filter(Boolean)
} catch {
return []
}
}
function checkProcess(name) {
try {
if (platform() === 'win32') {
const output = execFileSync('tasklist', ['/FI', `IMAGENAME eq ${name}`], { encoding: 'utf8', timeout: 4000 })
return output.toLowerCase().includes(String(name).toLowerCase())
}
execFileSync('pgrep', ['-f', '--', name], { stdio: 'ignore', timeout: 4000 })
return true
} catch {
return false
}
}
function checkConfiguredScript(script) {
if (script.enabled === false) return { status: 'DISABLED', summary: '监控已停用' }
const check = script.resultCheck
if (!check) return { status: 'UNKNOWN', summary: null }
if (check.type === 'file') {
try {
const stat = statSync(requiredText(check.path, 'resultCheck.path'))
const ageSec = Math.max(0, Math.round((Date.now() - stat.mtimeMs) / 1000))
const fresh = !check.maxAgeSec || ageSec <= Number(check.maxAgeSec)
const largeEnough = check.minBytes == null || stat.size >= Number(check.minBytes)
return {
status: fresh && largeEnough ? 'SUCCESS' : 'FAILED',
summary: `文件 ${stat.size} B,${ageSec} 秒前更新`,
}
} catch (error) {
return { status: 'FAILED', summary: `文件检查失败:${error.message}` }
}
}
if (check.type === 'systemd') {
try {
const service = requiredText(check.service, 'resultCheck.service')
const state = execFileSync('systemctl', ['is-active', service], { encoding: 'utf8', timeout: 4000 }).trim()
return { status: state === 'active' ? 'RUNNING' : 'FAILED', summary: `systemd:${state}` }
} catch {
return { status: 'FAILED', summary: 'systemd 服务未运行' }
}
}
if (check.type === 'process') {
const running = checkProcess(requiredText(check.name, 'resultCheck.name'))
return { status: running ? 'RUNNING' : 'FAILED', summary: running ? '进程正在运行' : '未找到目标进程' }
}
return { status: 'UNKNOWN', summary: '未识别的结果检查类型' }
}
function normalizeConfiguredScript(script) {
const key = requiredText(script.key, 'scripts[].key')
const result = checkConfiguredScript(script)
return {
key,
name: requiredText(script.name, `scripts[${key}].name`),
taskDescription: String(script.taskDescription || '').slice(0, 2000),
jobType: String(script.jobType || 'OTHER').toUpperCase(),
schedule: script.schedule ? String(script.schedule).slice(0, 240) : null,
commandPreview: redactCommand(script.commandPreview || script.command || ''),
expectedIntervalSec: script.expectedIntervalSec ? Number(script.expectedIntervalSec) : null,
status: result.status,
isEnabled: script.enabled !== false,
canManualRun: script.allowManualRun === true && Boolean(script.command),
lastResultSummary: result.summary,
}
}
export function collectScripts(config) {
const byKey = new Map()
if (config.autoDiscover?.cron !== false) {
for (const script of scanCron()) byKey.set(script.key, script)
}
if (config.autoDiscover?.pm2 !== false) {
for (const script of scanPm2()) byKey.set(script.key, script)
}
for (const script of config.scripts) {
const normalized = normalizeConfiguredScript(script)
byKey.set(normalized.key, { ...(byKey.get(normalized.key) || {}), ...normalized })
}
return [...byKey.values()]
}
export async function reportSnapshot(config) {
const scripts = collectScripts(config)
const result = await postToCenter(config, '/api/internal/script-monitor/report', {
hostname: hostname(),
os: `${platform()} ${release()}`,
agentVersion: AGENT_VERSION,
reportIntervalSec: config.reportIntervalSec,
scripts,
})
console.log(`[script-monitor] 已通过${result.endpoint.kind === 'INTRANET' ? '内网' : '公网'}上报 ${scripts.length} 个脚本`)
}
function keepTail(current, chunk, limit = 12000) {
const next = current + chunk.toString()
return next.length > limit ? next.slice(-limit) : next
}
async function reportManualRun(config, command, input) {
return postToCenter(config, '/api/internal/script-monitor/run', {
commandId: command.id,
scriptKey: command.scriptKey,
scriptName: command.scriptName,
taskDescription: command.taskDescription || '',
jobType: String(command.jobType || 'OTHER').toUpperCase(),
...input,
})
}
export async function executeManualCommand(config, command) {
const script = config.scripts.find((item) => item.key === command.scriptKey)
const startedAt = new Date()
const externalRunId = `manual:${command.id}`
if (!script?.command || script.allowManualRun !== true) {
const message = 'Agent 配置未开放该脚本的手动运行权限'
await reportManualRun(config, command, {
externalRunId,
status: 'FAILED',
trigger: 'manual',
startedAt: startedAt.toISOString(),
finishedAt: new Date().toISOString(),
durationMs: 0,
exitCode: 1,
resultSummary: message,
errorTail: message,
})
return
}
await reportManualRun(config, command, {
externalRunId,
status: 'RUNNING',
trigger: 'manual',
startedAt: startedAt.toISOString(),
})
let outputTail = ''
let errorTail = ''
try {
const child = spawn(script.command, {
cwd: script.cwd ? resolve(script.cwd) : process.cwd(),
env: process.env,
shell: true,
stdio: ['ignore', 'pipe', 'pipe'],
})
child.stdout.on('data', (chunk) => {
outputTail = keepTail(outputTail, chunk)
process.stdout.write(chunk)
})
child.stderr.on('data', (chunk) => {
errorTail = keepTail(errorTail, chunk)
process.stderr.write(chunk)
})
const exitCode = await new Promise((resolveExit, reject) => {
child.once('error', reject)
child.once('close', (code) => resolveExit(code ?? 1))
})
const finishedAt = new Date()
await reportManualRun(config, command, {
externalRunId,
status: exitCode === 0 ? 'SUCCESS' : 'FAILED',
trigger: 'manual',
startedAt: startedAt.toISOString(),
finishedAt: finishedAt.toISOString(),
durationMs: finishedAt.getTime() - startedAt.getTime(),
exitCode,
resultSummary: exitCode === 0 ? '手动运行成功' : `手动运行失败,退出码 ${exitCode}`,
outputTail,
errorTail,
})
} catch (error) {
const finishedAt = new Date()
await reportManualRun(config, command, {
externalRunId,
status: 'FAILED',
trigger: 'manual',
startedAt: startedAt.toISOString(),
finishedAt: finishedAt.toISOString(),
durationMs: finishedAt.getTime() - startedAt.getTime(),
exitCode: 1,
resultSummary: `手动运行启动失败:${error.message}`,
errorTail: error.message,
})
}
}
export async function pollManualCommand(config) {
const result = await postToCenter(
config,
'/api/internal/script-monitor/commands/claim',
{},
)
const command = result.body?.data?.command
if (!command) return false
await executeManualCommand(config, command)
return true
}
async function main() {
const config = loadConfig()
const run = async () => {
try {
await reportSnapshot(config)
} catch (error) {
console.error('[script-monitor] 上报失败:', error.message)
}
}
let commandBusy = false
const poll = async () => {
if (commandBusy) return
commandBusy = true
try {
await pollManualCommand(config)
} catch (error) {
console.error('[script-monitor] 手动任务轮询失败:', error.message)
} finally {
commandBusy = false
}
}
await run()
await poll()
const timer = setInterval(run, config.reportIntervalSec * 1000)
const commandTimer = setInterval(poll, config.commandPollIntervalSec * 1000)
const stop = () => {
clearInterval(timer)
clearInterval(commandTimer)
process.exit(0)
}
process.on('SIGINT', stop)
process.on('SIGTERM', stop)
}
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
main().catch((error) => {
console.error('[script-monitor] 启动失败:', error.message)
process.exit(1)
})
}

View File

@ -0,0 +1,79 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { executeManualCommand } from './agent.mjs'
function config(overrides = {}) {
return {
agentId: 'agent-1',
agentToken: 'secret-token',
connectionMode: 'internet',
centerUrls: { internet: 'https://monitor.example.test' },
requestTimeoutMs: 5000,
scripts: [{
key: 'local-check',
name: '本机检查',
taskDescription: '验证手动触发',
jobType: 'MANUAL',
allowManualRun: true,
command: `${JSON.stringify(process.execPath)} -e "console.log('manual-ok')"`,
}],
...overrides,
}
}
function command() {
return {
id: 'command-1',
scriptKey: 'local-check',
scriptName: '本机检查',
taskDescription: '验证手动触发',
jobType: 'MANUAL',
}
}
test('manual command executes only the local configured command and reports its result', async () => {
const previousFetch = globalThis.fetch
const reports = []
globalThis.fetch = async (_url, init) => {
reports.push(JSON.parse(init.body))
return new Response(JSON.stringify({ success: true, data: { ok: true } }), {
status: 200,
headers: { 'content-type': 'application/json' },
})
}
try {
await executeManualCommand(config(), command())
} finally {
globalThis.fetch = previousFetch
}
assert.equal(reports.length, 2)
assert.equal(reports[0].status, 'RUNNING')
assert.equal(reports[1].status, 'SUCCESS')
assert.equal(reports[1].exitCode, 0)
assert.match(reports[1].outputTail, /manual-ok/)
assert.equal(reports[1].commandId, 'command-1')
})
test('manual command is rejected when allowManualRun is not explicitly enabled', async () => {
const previousFetch = globalThis.fetch
const reports = []
globalThis.fetch = async (_url, init) => {
reports.push(JSON.parse(init.body))
return new Response(JSON.stringify({ success: true, data: { ok: true } }), {
status: 200,
headers: { 'content-type': 'application/json' },
})
}
const blockedConfig = config()
blockedConfig.scripts[0].allowManualRun = false
try {
await executeManualCommand(blockedConfig, command())
} finally {
globalThis.fetch = previousFetch
}
assert.equal(reports.length, 1)
assert.equal(reports[0].status, 'FAILED')
assert.match(reports[0].resultSummary, /未开放/)
})

View File

@ -0,0 +1,36 @@
{
"agentId": "PASTE_AGENT_ID_HERE",
"agentToken": "PASTE_AGENT_TOKEN_HERE",
"agentName": "仓库服务器 01",
"connectionMode": "auto",
"centerUrls": {
"intranet": "http://10.0.0.20:3001",
"internet": "https://cp.jiansun.vip"
},
"reportIntervalSec": 60,
"commandPollIntervalSec": 5,
"requestTimeoutMs": 10000,
"autoDiscover": {
"cron": true,
"pm2": true
},
"scripts": [
{
"key": "supply-chain-archive-sync",
"name": "供应链产品档案同步",
"taskDescription": "同步聚水潭商品档案、仓库和供应商扩展字段",
"jobType": "CRON",
"schedule": "*/15 * * * *",
"expectedIntervalSec": 900,
"allowManualRun": true,
"command": "npm run sync:supply-chain-product-archive",
"cwd": "/opt/furniture-3d-system-prod/current",
"resultCheck": {
"type": "file",
"path": "/opt/furniture-3d-system-prod/shared/logs/product-archive-sync.log",
"maxAgeSec": 1800,
"minBytes": 1
}
}
]
}

View File

@ -0,0 +1,15 @@
module.exports = {
apps: [{
name: 'furniture-script-monitor-agent',
script: 'agent.mjs',
cwd: __dirname,
instances: 1,
exec_mode: 'fork',
autorestart: true,
watch: false,
env: {
NODE_ENV: 'production',
SCRIPT_MONITOR_CONFIG: `${__dirname}/config.json`,
},
}],
}

View File

@ -0,0 +1,82 @@
import { spawn } from 'node:child_process'
import { randomUUID } from 'node:crypto'
import { resolve } from 'node:path'
import { loadConfig, postToCenter } from './agent.mjs'
function keepTail(current, chunk, limit = 12000) {
const next = current + chunk.toString()
return next.length > limit ? next.slice(-limit) : next
}
async function sendRun(config, script, input) {
return postToCenter(config, '/api/internal/script-monitor/run', {
scriptKey: script.key,
scriptName: script.name,
taskDescription: script.taskDescription || '',
jobType: String(script.jobType || 'OTHER').toUpperCase(),
...input,
})
}
async function main() {
const scriptKey = process.argv[2]
if (!scriptKey) throw new Error('用法:node run-script.mjs <script-key>')
const config = loadConfig()
const script = config.scripts.find((item) => item.key === scriptKey)
if (!script) throw new Error(`配置中不存在脚本:${scriptKey}`)
if (!script.command) throw new Error(`脚本 ${scriptKey} 未配置 command`)
const externalRunId = `${scriptKey}:${randomUUID()}`
const startedAt = new Date()
await sendRun(config, script, {
externalRunId,
status: 'RUNNING',
trigger: 'wrapper',
startedAt: startedAt.toISOString(),
}).catch((error) => {
console.warn('[script-monitor-run] 开始状态上报失败,不阻断原脚本:', error.message)
})
let outputTail = ''
let errorTail = ''
const child = spawn(script.command, {
cwd: script.cwd ? resolve(script.cwd) : process.cwd(),
env: process.env,
shell: true,
stdio: ['ignore', 'pipe', 'pipe'],
})
child.stdout.on('data', (chunk) => {
outputTail = keepTail(outputTail, chunk)
process.stdout.write(chunk)
})
child.stderr.on('data', (chunk) => {
errorTail = keepTail(errorTail, chunk)
process.stderr.write(chunk)
})
const exitCode = await new Promise((resolveExit, reject) => {
child.once('error', reject)
child.once('close', (code) => resolveExit(code ?? 1))
})
const finishedAt = new Date()
await sendRun(config, script, {
externalRunId,
status: exitCode === 0 ? 'SUCCESS' : 'FAILED',
trigger: 'wrapper',
startedAt: startedAt.toISOString(),
finishedAt: finishedAt.toISOString(),
durationMs: finishedAt.getTime() - startedAt.getTime(),
exitCode,
resultSummary: exitCode === 0 ? '脚本执行成功' : `脚本退出码 ${exitCode}`,
outputTail,
errorTail,
}).catch((error) => {
console.warn('[script-monitor-run] 运行结果上报失败,保留原退出码:', error.message)
})
process.exitCode = exitCode
}
main().catch((error) => {
console.error('[script-monitor-run] 执行失败:', error.message)
process.exitCode = 1
})

View File

@ -0,0 +1,257 @@
{
"version": 1,
"generatedAt": "2026-10-26T08:52:26.453Z",
"sourceFile": "各省发货占比.xlsx",
"totalShipment": 10983200,
"rowCount": 31,
"rows": [
{
"province": "安徽",
"shipmentCount": 426181,
"totalShipment": 10983200,
"shipmentShare": 0.038803,
"region": "一区",
"regionShare": 0.214
},
{
"province": "江苏",
"shipmentCount": 800646,
"totalShipment": 10983200,
"shipmentShare": 0.072897,
"region": "一区",
"regionShare": 0.214
},
{
"province": "上海",
"shipmentCount": 325564,
"totalShipment": 10983200,
"shipmentShare": 0.029642,
"region": "一区",
"regionShare": 0.214
},
{
"province": "浙江",
"shipmentCount": 797004,
"totalShipment": 10983200,
"shipmentShare": 0.072566,
"region": "一区",
"regionShare": 0.214
},
{
"province": "北京",
"shipmentCount": 290861,
"totalShipment": 10983200,
"shipmentShare": 0.026482,
"region": "二区",
"regionShare": 0.485
},
{
"province": "福建",
"shipmentCount": 467880,
"totalShipment": 10983200,
"shipmentShare": 0.0426,
"region": "二区",
"regionShare": 0.485
},
{
"province": "广东",
"shipmentCount": 1820992,
"totalShipment": 10983200,
"shipmentShare": 0.165798,
"region": "二区",
"regionShare": 0.485
},
{
"province": "河北",
"shipmentCount": 424406,
"totalShipment": 10983200,
"shipmentShare": 0.038641,
"region": "二区",
"regionShare": 0.485
},
{
"province": "河南",
"shipmentCount": 546529,
"totalShipment": 10983200,
"shipmentShare": 0.04976,
"region": "二区",
"regionShare": 0.485
},
{
"province": "湖北",
"shipmentCount": 378469,
"totalShipment": 10983200,
"shipmentShare": 0.034459,
"region": "二区",
"regionShare": 0.485
},
{
"province": "湖南",
"shipmentCount": 386336,
"totalShipment": 10983200,
"shipmentShare": 0.035175,
"region": "二区",
"regionShare": 0.485
},
{
"province": "江西",
"shipmentCount": 271676,
"totalShipment": 10983200,
"shipmentShare": 0.024736,
"region": "二区",
"regionShare": 0.485
},
{
"province": "山东",
"shipmentCount": 626419,
"totalShipment": 10983200,
"shipmentShare": 0.057034,
"region": "二区",
"regionShare": 0.485
},
{
"province": "天津",
"shipmentCount": 115475,
"totalShipment": 10983200,
"shipmentShare": 0.010514,
"region": "二区",
"regionShare": 0.485
},
{
"province": "广西",
"shipmentCount": 397325,
"totalShipment": 10983200,
"shipmentShare": 0.036176,
"region": "三区",
"regionShare": 0.251
},
{
"province": "贵州",
"shipmentCount": 242963,
"totalShipment": 10983200,
"shipmentShare": 0.022121,
"region": "三区",
"regionShare": 0.251
},
{
"province": "黑龙",
"shipmentCount": 166557,
"totalShipment": 10983200,
"shipmentShare": 0.015165,
"region": "三区",
"regionShare": 0.251
},
{
"province": "吉林",
"shipmentCount": 135735,
"totalShipment": 10983200,
"shipmentShare": 0.012358,
"region": "三区",
"regionShare": 0.251
},
{
"province": "辽宁",
"shipmentCount": 257754,
"totalShipment": 10983200,
"shipmentShare": 0.023468,
"region": "三区",
"regionShare": 0.251
},
{
"province": "山西",
"shipmentCount": 177480,
"totalShipment": 10983200,
"shipmentShare": 0.016159,
"region": "三区",
"regionShare": 0.251
},
{
"province": "陕西",
"shipmentCount": 281956,
"totalShipment": 10983200,
"shipmentShare": 0.025672,
"region": "三区",
"regionShare": 0.251
},
{
"province": "四川",
"shipmentCount": 532179,
"totalShipment": 10983200,
"shipmentShare": 0.048454,
"region": "三区",
"regionShare": 0.251
},
{
"province": "云南",
"shipmentCount": 341852,
"totalShipment": 10983200,
"shipmentShare": 0.031125,
"region": "三区",
"regionShare": 0.251
},
{
"province": "重庆",
"shipmentCount": 222288,
"totalShipment": 10983200,
"shipmentShare": 0.020239,
"region": "三区",
"regionShare": 0.251
},
{
"province": "甘肃",
"shipmentCount": 144419,
"totalShipment": 10983200,
"shipmentShare": 0.013149,
"region": "四区",
"regionShare": 0.05
},
{
"province": "海南",
"shipmentCount": 148380,
"totalShipment": 10983200,
"shipmentShare": 0.01351,
"region": "四区",
"regionShare": 0.05
},
{
"province": "内蒙",
"shipmentCount": 166636,
"totalShipment": 10983200,
"shipmentShare": 0.015172,
"region": "四区",
"regionShare": 0.05
},
{
"province": "宁夏",
"shipmentCount": 47493,
"totalShipment": 10983200,
"shipmentShare": 0.004324,
"region": "四区",
"regionShare": 0.05
},
{
"province": "青海",
"shipmentCount": 39031,
"totalShipment": 10983200,
"shipmentShare": 0.003554,
"region": "四区",
"regionShare": 0.05
},
{
"province": "西藏",
"shipmentCount": 441,
"totalShipment": 10983200,
"shipmentShare": 0.00004,
"region": "未分区",
"regionShare": 0.0002471046689489402
},
{
"province": "新疆",
"shipmentCount": 2273,
"totalShipment": 10983200,
"shipmentShare": 0.000207,
"region": "未分区",
"regionShare": 0.0002471046689489402
}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -59,10 +59,13 @@ docker-compose -f docker-compose.lightweight.yml up -d
### 4. 初始化数据库
```bash
# 进入应用容器执行迁移
docker exec -it furniture3d-app npx prisma db push
docker exec -it furniture3d-app npx prisma migrate deploy
# 或在本地执行(需要数据库可访问)
npx prisma db push
npx prisma migrate deploy
# 迁移后验证主数据地基
npm run db:verify-master-data:all:strict
```
### 5. 访问系统
@ -134,19 +137,22 @@ JWT_EXPIRES_IN=7d
### 首次部署
```bash
# 推送 schema 到数据库
npx prisma db push
# 应用 Prisma migration,保留 _prisma_migrations 历史
npx prisma migrate deploy
# 生成 Prisma Client
npx prisma generate
# 验证主数据迁移、视图、约束和 JSON 边界
npm run db:verify-master-data:all:strict
```
### 后续更新(Schema 变更)
```bash
# 开发环境:直接推送
npx prisma db push
# 仅本地临时开发环境可用;脚本会拒绝 production、非本机库和疑似生产库名
npm run prisma:push
# 生产环境:使用迁移(推荐)
# 生产环境:必须使用迁移
npx prisma migrate deploy
```
@ -272,10 +278,13 @@ docker-compose -f docker-compose.prod.yml build app
docker-compose -f docker-compose.prod.yml up -d app
# 执行数据库迁移(如有)
docker exec -it furniture3d-app npx prisma db push
docker exec -it furniture3d-app npx prisma migrate deploy
docker exec -it furniture3d-app npm run db:verify-master-data:all:strict
```
### 重要提示
- 更新前请先备份数据库
- 测试环境验证后再更新生产环境
- 保留旧版本镜像以便回滚
- 生产/准生产禁止用 `prisma db push` 替代迁移;否则会绕过 `_prisma_migrations`,后续无法证明数据库结构来源。
- 仓库内 `npm run prisma:push` 只作为本地开发保护入口:默认只允许 `localhost / 127.0.0.1 / ::1`,拒绝 `NODE_ENV=production`、`VERCEL_ENV=production` 和疑似生产库名。

View File

@ -1,7 +1,7 @@
# 家具开品平台文档导航
# 由壹点「产品开发平台」文档导航
> 最后更新:2026-04-07
> 这份导航只突出当前有效入口,历史阶段文档不再和现行入口混排。
> 最后更新:2026-04-15
> 当前主线入口:`/asset-center -> /assisted-assembly -> /draft-groups -> /intake -> /warehouse`
## 当前必读入口
@ -16,15 +16,12 @@
### 项目现状
- [项目总览](./项目总览.md)
- [长期记忆导航](./agent-memory/README.md)
- [长期决策日志](./agent-memory/decision-log.md)
- [长期待办与阻塞](./agent-memory/open-issues.md)
### 业务与架构
- [认证系统设计](./架构设计/auth-system-design.md)
- [权限系统设计](./架构设计/permission-design-v2.md)
- [新平台一期实施计划](./架构设计/新平台一期-资产中心与自动拼装实施计划-20260331.md)
- [新平台一期实施计划(历史文件名保留“自动拼装”字样)](./架构设计/新平台一期-资产中心与自动拼装实施计划-20260331.md)
### 开发与运维
@ -53,4 +50,9 @@
2. [团队协作模式](./workflow/团队协作模式.md)
3. [模块边界说明](./workflow/模块边界说明.md)
4. [主线同步操作手册](./workflow/主线同步操作手册.md)
5. [agent-memory/README](./agent-memory/README.md)
## 当前说明
- 部分历史文档或文件名仍保留“自动拼装 / 自动组合 / auto-combinations”等旧称呼;当前一律按“辅助装配(assisted-assembly)”理解。
- 这份导航只保留有效入口;具体任务边界以当前任务单、交接摘要、已确认协作信息和实际代码现状为准。
- `main` 继续由 `WuYongJie` 负责收口。

View File

@ -18,8 +18,9 @@
## 当前主线口径
- 默认主线:`资产中心 -> 自动组合 -> 草稿组 -> 包装方案 -> 正式入库 -> 轻仓库`
- 默认主线:`资产中心 -> 辅助装配 -> 草稿组 -> 正式入库 -> 轻仓库`
- 旧 CAD 装配链:兼容链,不是默认主线
- 历史文档或文件名里的 `自动组合`、`自动拼装`、`auto-combinations` 只按旧称呼理解,不再代表当前页面入口或系列资料口径。
## 文件分工

View File

@ -2,8 +2,9 @@
| 日期 | 决策 | 原因 | 影响范围 | 状态 |
|---|---|---|---|---|
| 2026-05-18 | 旧“自动组合 / 自动拼装 / 固定模板”口径降级为历史兼容称呼,当前主线统一为“资产中心 -> 辅助装配 -> 草稿组 -> 正式入库 -> 轻仓库” | 系列资料和长期记忆里残留旧模板描述,会误导资产中心和后续 AI 把已取消的自动拼装当成当前入口 | `AssetSeries` 资料、`docs/agent-memory/*`、后续 AI 会话上下文 | 已生效 |
| 2026-04-07 | 文档入口收口到“当前主线入口 + 历史参考分层”,删除重复副本和明显过时文档 | 旧入口混有过时阶段总结、旧 UI 方案和重复清单,容易把人和 AI 带回旧主线 | 根 README、仓库 README、docs 导航、agent-memory、重复旧文档 | 已生效 |
| 2026-04-07 | 项目长期记忆主目标从“CAD 装配编辑”切回“资产中心 -> 自动组合 -> 草稿组 -> 包装方案 -> 正式入库 -> 轻仓库” | 当前业务主线已经变化,如果不先修正 agent-memory,后续 AI 会持续沿用错误目标 | `docs/agent-memory/*` 与后续所有 AI 会话上下文 | 已生效 |
| 2026-04-07 | 项目长期记忆主目标从“CAD 装配编辑”切回当时的新平台开品链 | 当时业务主线已经变化,如果不先修正 agent-memory,后续 AI 会持续沿用更早的 CAD 目标 | `docs/agent-memory/*` 与后续所有 AI 会话上下文 | 已被 2026-05-18 决策替代 |
| 2026-03-13 | 协作流程收敛为“直接开发 + 规范审查闸门” | 避免运行时协作策略干扰主线开发,把硬规则稳定落到每次交付的固定步骤里 | 全项目默认协作方式、交付口径、审查流程 | 已生效 |
| 2026-03-05 | SKU详情页/装配编辑器节点解析统一到“模型真实节点映射 + canonical 别名解析”,并将 3D 拾取容差改为“中心命中优先 + 最近命中优先” | 修复 SKU 详情与装配编辑器点选规则不一致、折角线锚点错位,以及点选漂移导致的误选/漏选 | `SKUDetailClient.workspace`、`install-order-node-resolution`、`model-viewer-pick-tolerance` | 已生效 |
| 2026-02-27 | 装配编辑器本轮收口:修复可见乱码文案、步骤编辑器响应式布局、孔位高亮坐标解算改为“panel/modelRoot 双坐标根候选择优” | 解决页面可用性问题并降低孔位错绑/爆炸状态漂移 | `AssemblyEditorWorkspace`、`AssemblyStepEditor`、`HoleOverlay2D` | 已生效 |

View File

@ -1,6 +1,6 @@
# 待办与阻塞(长期)
最后更新:2026-04-07
最后更新:2026-05-18
## P0
@ -10,10 +10,10 @@
- 证据:`src/app/asset-center/AssetCenterClient.tsx`、工作区根目录 `docs/前端UI改动清单.md`
- 状态:处理中
- 问题:自动组合、草稿组、包装承接这条主线还在持续联调。
- 问题:辅助装配、草稿组、正式入库这条主线还在持续联调。
- 责任角色:主线负责人 / 前端 / 包装负责人
- 下一步:继续验证候选预览、草稿组承接、包装占位和正式入库之间的真实业务闭环
- 证据:`src/app/auto-combinations`、`src/app/draft-groups`、`src/app/intake`
- 下一步:继续验证辅助装配保存、草稿组承接、包装占位和正式入库之间的真实业务闭环
- 证据:`src/app/assisted-assembly`、`src/app/draft-groups`、`src/app/intake`
- 状态:处理中
- 问题:文档和 AI 记忆已经做过一次清理,但后续如果不持续维护,仍会再次回到多口径状态。

View File

@ -1,12 +1,13 @@
# 项目上下文(长期)
最后更新:2026-04-07
最后更新:2026-05-18
## 项目目标
- 当前主线目标:用资产中心沉淀资产数据,再通过自动组合、草稿组、包装承接、正式入库和轻仓库形成开品闭环。
- 当前默认主线:`资产中心 -> 自动组合 -> 草稿组 -> 包装方案 -> 正式入库 -> 轻仓库`
- 当前主线目标:用资产中心沉淀资产数据,再通过辅助装配、草稿组、正式入库和轻仓库形成开品闭环。
- 当前默认主线:`资产中心 -> 辅助装配 -> 草稿组 -> 正式入库 -> 轻仓库`
- 旧 `CAD -> 装配编辑 -> 装配步骤 -> 回放` 链继续保留,但属于兼容链。
- 历史 `自动组合 / 自动拼装 / auto-combinations` 只作为旧称呼或兼容资料理解,不再代表当前主链入口。
## 当前硬约束
@ -19,7 +20,7 @@
## 当前项目最容易出问题的地方
- 资产命名、连接角色、系列规则继续出现自由输入,导致脏数据进入主线
- 自动组合候选与 3D 预览结果不一致
- 辅助装配保存、草稿组承接与正式入库资料同步不一致
- 包装承接口和主线真相混层
- 旧兼容链的历史逻辑反向污染新平台主线

View File

@ -0,0 +1,109 @@
# 1688代发异常事项:退货地址推送接口
## 交付与启用
本接口供外部系统按 SPU 推送退货地址。正式站发布包含该接口的版本并配置独立密钥后,对方即可按本文调用。
部署人员在生产运行环境配置 `DROPSHIP_RETURN_ADDRESS_API_KEY`,使用独立随机密钥(建议至少32个随机字节),由双方通过安全渠道交接;密钥不进入 Git、对接文档、URL 或普通日志。未配置时接口返回503。无需提供数据库账号,无需平台用户登录。
## 请求
- 方法:`POST`
- 正式地址:`https://cp.jiansun.vip/api/external/supply-chain/dropship-return-addresses`
- 本地联调:`http://localhost:3001/api/external/supply-chain/dropship-return-addresses`
- 请求头:`Content-Type: application/json`、`x-api-key: <对接密钥>`
- 每批1–100条;单条推送也使用 `items` 数组。
```json
{
"items": [
{
"spu": "DF-C1YMJ0009",
"returnAddress": "收件人:张三;电话:138xxxxxxxx;地址:江苏省……"
}
]
}
```
| 字段 | 必填 | 规则 |
| --- | --- | --- |
| `spu` | 是 | 1–120位字母、数字、下划线或连字符;自动去除首尾空格并转为大写 |
| `returnAddress` | 是 | 去除首尾空格后1–500字;收件人、电话、详细地址合并为一段文字 |
仅接收上述字段。额外传入发票类型、安装方式等字段,该条会失败,不会改动这些字段。
调用示例(密钥由调用方运行环境注入):
```bash
curl --request POST \
'https://cp.jiansun.vip/api/external/supply-chain/dropship-return-addresses' \
--header 'Content-Type: application/json' \
--header "x-api-key: ${DROPSHIP_RETURN_ADDRESS_API_KEY}" \
--data '{"items":[{"spu":"DF-C1YMJ0009","returnAddress":"收件人:张三;电话:138xxxxxxxx;地址:江苏省……"}]}'
```
## 写入规则
1. 一SPU一退货地址,同SPU下所有SKU共用;按档案中的DF代发范围核验,不受当月业绩筛选影响。
2. SPU必须已存在于产品档案。没有维护记录时自动创建维护记录;不会创建产品档案。
3. 新地址覆盖该SPU原有地址(包括人工填写的地址),以数据库实际处理顺序为准。
4. 只更新退货地址和维护人/时间,保留发票类型、整装/拆装;新增维护记录时其他字段使用原有空值默认值。
5. 空地址拒绝,不能通过这个接口清空地址。重复推送同一地址返回 `UNCHANGED`,不重复写入或变更时间。
6. 同批重复SPU(含大小写/首尾空格差异)的重复项全部失败,其余条目继续处理;每批按输入顺序返回结果。
7. 每条独立保存,某条失败不撤销已经成功的条目。实际地址变化写入1688代发异常事项操作日志,标记“外部退货地址同步”,包含前后值与请求编号。
8. 当前页面默认只显示有业绩SPU;无业绩产品推送成功后,切换“全部SPU”查看。
## 响应
HTTP200表示批次已处理,**仍须检查 `data.failedCount` 和每条 `status`**。
```json
{
"success": true,
"data": {
"total": 2,
"successCount": 1,
"failedCount": 1,
"updatedCount": 1,
"unchangedCount": 0,
"results": [
{
"index": 1,
"spu": "DF-C1YMJ0009",
"status": "UPDATED",
"updatedAt": "2026-10-24T04:00:00.000Z"
},
{
"index": 2,
"spu": "DF-MISSING",
"status": "FAILED",
"code": "SPU_NOT_FOUND",
"message": "产品档案中不存在该代发 SPU"
}
]
},
"meta": {
"requestId": "服务端请求编号",
"timestamp": "2026-10-24T04:00:00.000Z"
}
}
```
`index` 从1开始,对应请求数组顺序;`UPDATED` 表示新增或更新成功,`UNCHANGED` 表示地址已一致。两种均计入 `successCount`。时间使用UTC ISO8601格式。
| 每条失败码 | 含义与处理 |
| --- | --- |
| `INVALID_ITEM` | 字段格式、地址为空/过长或包含额外字段;修正数据后重推 |
| `DUPLICATE_SPU` | 同批存在重复SPU;去重后重推 |
| `SPU_NOT_FOUND` | 不存在对应DF代发档案;核对SPU或等待档案同步后重推 |
| `WRITE_FAILED` | 保存失败;重试该条,持续失败时附 `meta.requestId` 联系管理员 |
| HTTP状态 | 含义 |
| --- | --- |
| 200 | 批次已处理,检查逐条结果 |
| 400 | 非法JSON、缺少items、空批次、超过100条或多余顶层字段;整批不写入 |
| 401 | 缺少密钥或密钥错误;整批不写入 |
| 503 | 服务端未配置对接密钥;整批不写入 |
| 500 | 系统错误;提供请求编号排查 |
网络超时或500时可能已有部分条目保存。可重推相同地址;如果期间出现更新,应重新取得上游最新地址再推送,避免旧批次覆盖新地址。调用方不应并发推送同一SPU。

View File

@ -0,0 +1,153 @@
# 财务系统店铺密码直接显示契约
状态:生产接口已上线;2026-09-09 按当前明文存储规则修正 `passwordConfigured`。
## 存储事实与授权边界
- 密码真相源是 `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 当成操作者。服务凭据仅保存在财务后端,禁止交给浏览器。
- 不提供密码列表或批量导出。财务页面可在已通过自身页面权限校验后,自动读取当前可见页最多 50 个账号;CSV 和共享镜像仍只读取 `passwordConfigured`。复制操作继续走单账号接口。上游审计记录的用户 ID 是专用财务服务的身份断言,未授权请求中的 ID 不可信,`serviceId=null`。
## HTTP 契约
生产配置完成后的地址:
`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、请求体、请求头中传待读取密码。
请求头:
```http
Authorization: Bearer <专用服务令牌>
Content-Type: application/json
```
请求体(严格拒绝额外字段,最大 1024 字节,1 秒内读取完毕):
```json
{ "requesterId": "finance-user-123", "purpose": "view" }
```
`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 相同。不会在响应头中放密码。
错误体:
```json
{ "requestId": "<UUID>", "error": { "code": "FORBIDDEN", "message": "请求被拒绝" } }
```
| HTTP | code | 含义 |
|---|---|---|
| 403 | FORBIDDEN | 服务认证失败、非 HTTPS、非法 ID/JSON/用途、额外参数或非 POST |
| 404 | PASSWORD_NOT_FOUND | 账号不存在或没有可用密码,两者不区分 |
| 429 | RATE_LIMITED | 任一限流窗口超限 |
| 503 | UNAVAILABLE | 密钥/解密、数据库、限流或审计不可用;不返回内部异常 |
审计或数据库故障优先返回 503,绝不在无法持久化成功审计时返回密码。数据库完全不可用时无法保存该次失败审计,由网关仅记录追踪 ID、状态与时间供运维关联。成功审计表示服务器已授权并生成响应,不等同于浏览器已经复制成功。
## 限流和超时
- 单账号数据库滚动窗口:服务全部账号 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 秒和总请求截止,防止慢请求占用。
- 入口网关还需按来源 IP 对全部请求(包括未授权请求)限制为 60 次/分钟,返回 429 JSON 和 `no-store`;这项入口防滥用配置属于上线门禁。应用层数据库限流负责已认证的敏感读取。
## 环境变量(仅名称及要求)
| 名称 | 所在系统与要求 |
|---|---|
| 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 容器以只读扫描测试期间日志 |
## HTTPS、日志和缓存上线门禁
1. 财务后端至公开入口及跨主机的所有代理链路必须使用验证证书的 HTTPS,禁止 HTTP 跳转、跳过证书验证、明文 FRP/跨主机上游。当前 PR 不修改生产 TLS 拓扑;上线前逐跳核实,不满足即不启用令牌。
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 的绑定参数和错误参数记录。
6. 财务后端同样禁用正文采集、缓存及异常对象打印。浏览器在进入已授权页面后只读取当前可见页,避免 localStorage/sessionStorage/Service Worker 持久化;页面隐藏或离开时清除页面密码,不把密码放入通知、埋点或消息队列。
## 视图与审计迁移
初始迁移:`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` 的当前定义为 `passwordCiphertext <> ''`。修复迁移仅替换这个布尔表达式,并在事务内核对原字段逐行内容、视图所有者和 ACL 未变;不读取或输出密码。
- 明文迁移已由独立脚本完成。本状态修复不改任何密码或银行账号数据,也不增加普通只读账号权限。审计数据至少保留 365 天。
迁移前由 DBA 只读确认目标库、视图定义/所有者/ACL、字段顺序、行数与唯一性、密码聚合、普通读取账号和其成员角色权限。若生产仍以本次基线验收,COUNT 与 DISTINCT id 必须均为 958;若业务新增/删除则暂停并重新确认基线,不改数据凑数。
备份与执行顺序(生产必须在明确批准后执行):
1. 备份当前 `main`/部署 SHA、现有视图 DDL/ACL/所有者及 `_prisma_migrations` 状态。生成目标库 `pg_dump -Fc` 备份并验证可读取;备份进入生产主机权限 0700 目录,文件 0600,不进入 Git 或共享日志。备份可能含密文,按敏感材料管理。
2. 本次不修改任何密码、银行卡号或服务令牌;备份不得进入 Git 或普通日志。
3. 执行明文状态修复迁移,视图短时获得 DDL 锁(最多等 5 秒,执行预算 30 秒);不回放未审查的其他迁移。
4. 在同一只读快照比较迁移前后原字段行集、顺序/类型、行数/唯一性和权限;运行 `scripts/sql/check-store-account-password-reveal.sql`,确认最后字段 boolean,报告 true/false/null 聚合。预计当前数据为 957/1/0,但必须执行后实测。
5. 按项目主线发布流程部署,完成逐跳 TLS、网关日志/限流/缓存检查后再配令牌。生产虚构账号的创建、调用与删除属于额外生产写入,需单独批准;不要调用真实账号作为测试。
回滚:先移除财务令牌阻止新调用并回退应用 SHA。新增状态列与审计表可安全保留以避免丢失审计;旧应用不依赖它们。需要完整结构回退时,经 DBA 检查依赖后,在事务中以备份 DDL 重建旧视图并恢复原所有者/ACL(PostgreSQL 不允许 `CREATE OR REPLACE VIEW` 删除尾列,禁止随意 `DROP ... CASCADE`)。审计记录保留,不直接删表;按项目规则处理已应用迁移历史,禁止直接删除 `_prisma_migrations` 记录或用 `db push`。任何完整结构回退先在隔离库演练。
## 密钥轮换
服务令牌:在受限凭据系统生成新随机令牌;旧值暂放 `STORE_ACCOUNT_FINANCE_TOKEN_PREVIOUS`,新值放主变量,重启上游;更新财务后端,使用虚构账号验收;移除旧值并重启。两值对应同一服务身份,审计/额度不变。怀疑泄露时立即撤销旧值,宁可短暂拒绝请求。
网关凭据:暂停此接口,在网关及上游同时更新 `STORE_ACCOUNT_REVEAL_EDGE_TOKEN` 后验收并恢复;不支持旧值并行。不得将网关凭据与服务令牌设成相同值。
历史兼容密钥:当前明文规则不需要为新值轮换数据密钥。若确认数据库中不存在 `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 项已有违规,均位于本次未修改模块;不得据此声称全仓库通过。
- 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;状态字段与当前明文存储规则对齐,专用认证、审计和限流边界不变。

View File

@ -0,0 +1,689 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>安装方式 / 工厂质检归档布局草图</title>
<style>
:root {
--page: #eef2f5;
--panel: #f7f9fb;
--surface: #ffffff;
--soft: #edf2f5;
--line: #dce5ea;
--text: #14212a;
--muted: #687885;
--faint: #9aa8b3;
--ink: #263b45;
--ink-2: #4d6672;
--accent: #365a66;
--active: #23313a;
--green: #2d7c57;
--orange: #d99142;
--shadow: 0 24px 70px rgba(24, 37, 46, 0.12);
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
background:
radial-gradient(circle at 18% 12%, rgba(54, 90, 102, 0.12), transparent 25%),
linear-gradient(180deg, #fbfcfd 0%, var(--page) 100%);
color: var(--text);
font-family:
"Microsoft YaHei",
"PingFang SC",
"Noto Sans CJK SC",
sans-serif;
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 72px;
padding: 0 28px;
background: rgba(255, 255, 255, 0.82);
border-bottom: 1px solid var(--line);
backdrop-filter: blur(18px);
}
.brand {
display: flex;
align-items: center;
gap: 12px;
font-weight: 800;
}
.brand-mark {
display: grid;
width: 34px;
height: 34px;
place-items: center;
border-radius: 12px;
background: var(--text);
color: #fff;
font-size: 18px;
}
.top-actions {
display: flex;
align-items: center;
gap: 12px;
}
.search {
width: 260px;
border-radius: 16px;
background: var(--soft);
color: var(--faint);
padding: 12px 16px;
font-size: 14px;
}
.avatar {
display: grid;
width: 40px;
height: 40px;
place-items: center;
border-radius: 999px;
background: #fff;
border: 1px solid var(--line);
color: var(--muted);
font-weight: 700;
}
.shell {
display: grid;
grid-template-columns: 196px minmax(0, 1fr) 236px;
gap: 18px;
min-height: calc(100vh - 72px);
padding: 20px 18px 24px;
}
.left-nav,
.right-nav {
border-radius: 26px;
background: rgba(255, 255, 255, 0.72);
border: 1px solid rgba(220, 229, 234, 0.86);
box-shadow: 0 18px 48px rgba(31, 47, 56, 0.08);
}
.left-nav {
display: flex;
flex-direction: column;
gap: 8px;
padding: 22px 14px;
}
.nav-item {
border-radius: 15px;
color: #40515b;
padding: 13px 14px;
font-size: 14px;
}
.nav-item.active {
background: #fff;
color: var(--active);
font-weight: 800;
box-shadow: 0 14px 32px rgba(24, 37, 46, 0.08);
}
.workspace {
min-width: 0;
border-radius: 30px;
padding: 14px;
background: rgba(235, 240, 244, 0.78);
border: 1px solid rgba(214, 224, 230, 0.92);
}
.workspace-card {
overflow: hidden;
min-height: calc(100vh - 132px);
border-radius: 26px;
background: var(--surface);
box-shadow: var(--shadow);
}
.workspace-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
min-height: 94px;
padding: 22px 26px;
border-bottom: 1px solid var(--line);
}
.workspace-head h1 {
margin: 0 0 8px;
font-size: 22px;
letter-spacing: -0.02em;
}
.workspace-head p {
margin: 0;
color: var(--muted);
font-size: 14px;
}
.toolbar {
display: flex;
align-items: center;
gap: 10px;
}
.button {
border-radius: 15px;
padding: 13px 18px;
background: var(--accent);
color: #fff;
font-weight: 800;
font-size: 14px;
box-shadow: 0 16px 34px rgba(54, 90, 102, 0.2);
}
.button.ghost {
background: transparent;
color: var(--faint);
box-shadow: none;
}
.archive-layout {
display: grid;
grid-template-columns: 224px minmax(430px, 1fr) 382px;
min-height: calc(100vh - 226px);
}
.style-panel {
border-right: 1px solid var(--line);
padding: 22px 18px;
background: #fbfcfd;
}
.eyebrow {
margin: 0 0 12px;
color: var(--faint);
font-size: 11px;
font-weight: 800;
letter-spacing: 0.2em;
text-transform: uppercase;
}
.style-card {
margin-bottom: 12px;
border-radius: 18px;
padding: 15px;
background: #fff;
border: 1px solid var(--line);
}
.style-card.active {
border-color: var(--active);
background: #eef3f5;
}
.style-title {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 8px;
font-size: 16px;
font-weight: 800;
}
.pill {
border-radius: 999px;
background: var(--ink-2);
color: #fff;
padding: 4px 10px;
font-size: 12px;
}
.style-card p {
margin: 0;
color: var(--muted);
font-size: 13px;
line-height: 1.6;
}
.memo {
margin-top: 18px;
border-radius: 20px;
padding: 16px;
background: var(--panel);
color: var(--muted);
font-size: 13px;
line-height: 1.65;
}
.media-panel {
display: flex;
min-width: 0;
flex-direction: column;
background: #eef3f6;
}
.media-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
padding: 18px 20px;
border-bottom: 1px solid var(--line);
}
.media-search {
flex: 1;
border-radius: 16px;
background: #fff;
color: var(--faint);
padding: 13px 16px;
font-size: 14px;
}
.type-tabs {
display: flex;
gap: 8px;
}
.type-tab {
border-radius: 999px;
padding: 10px 14px;
background: #fff;
color: var(--muted);
font-size: 13px;
font-weight: 800;
border: 1px solid var(--line);
}
.type-tab.active {
background: var(--active);
color: #fff;
border-color: var(--active);
}
.media-scroll {
min-height: 0;
overflow-y: auto;
padding: 20px;
}
.media-grid {
display: grid;
grid-template-columns: repeat(3, minmax(140px, 1fr));
gap: 14px;
}
.media-card {
overflow: hidden;
min-height: 176px;
border-radius: 22px;
background: #fff;
border: 1px solid rgba(220, 229, 234, 0.9);
box-shadow: 0 14px 34px rgba(31, 47, 56, 0.08);
}
.media-card.active {
border-color: var(--active);
box-shadow: 0 20px 46px rgba(31, 47, 56, 0.16);
}
.thumb {
height: 112px;
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.35), transparent 42%),
linear-gradient(135deg, #d3dbe0, #bfcbd1);
}
.media-card:nth-child(2) .thumb {
background: linear-gradient(135deg, #cfc2b4, #856d57);
}
.media-card:nth-child(3) .thumb {
background: linear-gradient(135deg, #d4e3e5, #78949d);
}
.media-card:nth-child(4) .thumb {
background: linear-gradient(135deg, #ded5cd, #aa8466);
}
.media-card:nth-child(5) .thumb {
background: linear-gradient(135deg, #dfe2e6, #939fa8);
}
.media-card:nth-child(6) .thumb {
background: linear-gradient(135deg, #e7ded6, #b9855b);
}
.media-info {
padding: 12px 13px 14px;
}
.media-info strong {
display: block;
overflow: hidden;
margin-bottom: 6px;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 14px;
}
.media-info span {
color: var(--muted);
font-size: 12px;
}
.detail-panel {
display: flex;
min-width: 0;
flex-direction: column;
border-left: 1px solid var(--line);
background: #fff;
}
.detail-head {
padding: 24px 26px 16px;
}
.detail-head .eyebrow {
margin-bottom: 10px;
}
.detail-head h2 {
margin: 0;
font-size: 24px;
line-height: 1.25;
}
.preview {
margin: 0 26px;
overflow: hidden;
border-radius: 24px;
background:
linear-gradient(145deg, rgba(255, 255, 255, 0.24), transparent 42%),
linear-gradient(135deg, #cfc2b4, #7b624f);
min-height: 330px;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.45);
}
.detail-meta {
margin: 18px 26px 0;
border-radius: 22px;
background: var(--panel);
padding: 18px;
}
.meta-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
padding: 9px 0;
color: var(--muted);
font-size: 13px;
}
.meta-row strong {
color: var(--text);
}
.detail-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin: 18px 26px 26px;
}
.small-action {
border-radius: 15px;
background: #fff;
border: 1px solid var(--line);
color: var(--ink);
padding: 12px;
text-align: center;
font-size: 13px;
font-weight: 800;
}
.right-nav {
align-self: start;
padding: 16px;
}
.right-title {
padding: 12px 12px 16px;
border-bottom: 1px solid var(--line);
}
.right-title h3 {
margin: 0 0 6px;
font-size: 18px;
}
.right-title p {
margin: 0;
color: var(--muted);
font-size: 12px;
}
.doc-list {
display: flex;
flex-direction: column;
gap: 10px;
padding-top: 14px;
}
.doc-item {
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 15px;
padding: 12px 14px;
background: #fff;
border: 1px solid var(--line);
color: #243641;
font-weight: 700;
}
.doc-item.active {
background: #dfe8ee;
border-color: #c8d5dd;
}
.doc-count {
border-radius: 999px;
background: #fff;
color: var(--muted);
min-width: 28px;
padding: 4px 9px;
text-align: center;
font-size: 12px;
}
@media (max-width: 1440px) {
.shell {
grid-template-columns: 170px minmax(0, 1fr) 212px;
}
.archive-layout {
grid-template-columns: 206px minmax(360px, 1fr) 340px;
}
.media-grid {
grid-template-columns: repeat(2, minmax(150px, 1fr));
}
}
@media (max-width: 1100px) {
.shell,
.archive-layout {
grid-template-columns: 1fr;
}
.left-nav,
.right-nav {
display: none;
}
.style-panel,
.detail-panel {
border: 0;
}
}
</style>
</head>
<body>
<header class="topbar">
<div class="brand">
<div class="brand-mark">J</div>
<span>由壹点「产品开发平台」</span>
</div>
<div class="top-actions">
<div class="search">搜索组件...</div>
<div class="avatar">L</div>
</div>
</header>
<main class="shell">
<aside class="left-nav">
<div class="nav-item">首页概览</div>
<div class="nav-item">资产中心</div>
<div class="nav-item">辅助装配</div>
<div class="nav-item">草稿组</div>
<div class="nav-item active">正式产品</div>
<div class="nav-item">仓库管理</div>
</aside>
<section class="workspace">
<div class="workspace-card">
<div class="workspace-head">
<div>
<h1>归档型工作区布局草图</h1>
<p>安装方式、工厂质检可以共用这套结构:左边选款式,中间看素材,右边看当前详情。</p>
</div>
<div class="toolbar">
<div class="button">上传到当前栏</div>
<div class="button ghost">下载当前</div>
</div>
</div>
<div class="archive-layout">
<aside class="style-panel">
<p class="eyebrow">Style</p>
<div class="style-card active">
<div class="style-title">款式一 <span class="pill">26</span></div>
<p>摔包图片 18 份<br />摔包视频 8 份</p>
</div>
<div class="style-card">
<div class="style-title">款式二 <span class="pill">0</span></div>
<p>当前还没有工厂质检资料</p>
</div>
<div class="style-card">
<div class="style-title">款式三 <span class="pill">0</span></div>
<p>跟随 SKU 型号自动出现</p>
</div>
<div class="memo">
这里不再堆太多说明,只保留款式和数量。后续图片再多,也只在中间素材墙滚动。
</div>
</aside>
<section class="media-panel">
<div class="media-head">
<div class="media-search">搜索文件名、日期、备注...</div>
<div class="type-tabs">
<div class="type-tab active">摔包图片 18</div>
<div class="type-tab">摔包视频 8</div>
</div>
</div>
<div class="media-scroll">
<div class="media-grid">
<article class="media-card active">
<div class="thumb"></div>
<div class="media-info">
<strong>正面摔包-01.jpg</strong>
<span>今天 14:20 · 已归档</span>
</div>
</article>
<article class="media-card">
<div class="thumb"></div>
<div class="media-info">
<strong>侧面摔包-02.jpg</strong>
<span>今天 14:19 · 已归档</span>
</div>
</article>
<article class="media-card">
<div class="thumb"></div>
<div class="media-info">
<strong>箱角状态-03.jpg</strong>
<span>昨天 18:06 · 已归档</span>
</div>
</article>
<article class="media-card">
<div class="thumb"></div>
<div class="media-info">
<strong>包装封口-04.jpg</strong>
<span>昨天 17:55 · 已归档</span>
</div>
</article>
<article class="media-card">
<div class="thumb"></div>
<div class="media-info">
<strong>复检记录-05.jpg</strong>
<span>04/25 · 已归档</span>
</div>
</article>
<article class="media-card">
<div class="thumb"></div>
<div class="media-info">
<strong>外箱状态-06.jpg</strong>
<span>04/25 · 已归档</span>
</div>
</article>
</div>
</div>
</section>
<aside class="detail-panel">
<div class="detail-head">
<p class="eyebrow">Preview</p>
<h2>正面摔包-01.jpg</h2>
</div>
<div class="preview"></div>
<div class="detail-meta">
<div class="meta-row"><span>所属款式</span><strong>款式一</strong></div>
<div class="meta-row"><span>资料类型</span><strong>摔包图片</strong></div>
<div class="meta-row"><span>当前数量</span><strong>18 份</strong></div>
<div class="meta-row"><span>上传时间</span><strong>今天 14:20</strong></div>
</div>
<div class="detail-actions">
<div class="small-action">放大预览</div>
<div class="small-action">删除当前</div>
</div>
</aside>
</div>
</div>
</section>
<aside class="right-nav">
<div class="right-title">
<h3>图片与文档</h3>
<p>这里只负责切换工作区,不再挤进主内容里。</p>
</div>
<div class="doc-list">
<div class="doc-item">产品实拍 <span class="doc-count">9</span></div>
<div class="doc-item">安装方式 <span class="doc-count">0</span></div>
<div class="doc-item active">工厂质检 <span class="doc-count">26</span></div>
<div class="doc-item">仓库抽检 <span class="doc-count">0</span></div>
</div>
</aside>
</main>
</body>
</html>

View File

@ -0,0 +1,778 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>开品参考填写预览</title>
<style>
:root {
--page: #f8f9fa;
--shell: #eef3f7;
--card: #ffffff;
--soft: #f7f9fb;
--line: #dce3ea;
--text: #111c27;
--muted: #617182;
--faint: #8c98a5;
--dark: #405766;
--active: #202a33;
--blue-soft: #e9f3ff;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-width: 1360px;
height: 100vh;
overflow: hidden;
background: var(--page);
color: var(--text);
font-family:
Manrope,
Inter,
"PingFang SC",
"Microsoft YaHei",
Arial,
sans-serif;
font-size: 13px;
}
button,
input,
textarea {
font: inherit;
}
.top-nav {
position: fixed;
inset: 0 0 auto 0;
z-index: 20;
height: 64px;
display: flex;
align-items: center;
padding: 0 32px;
border-bottom: 1px solid rgba(0, 0, 0, 0.04);
background: rgba(255, 255, 255, 0.92);
backdrop-filter: blur(24px);
}
.brand {
display: flex;
min-width: 172px;
align-items: center;
gap: 10px;
color: #2d3335;
text-decoration: none;
}
.brand-mark {
width: 36px;
height: 36px;
border-radius: 6px;
display: grid;
place-items: center;
background: #0d1520;
color: #fff;
font-size: 18px;
font-weight: 900;
}
.brand strong {
display: block;
font-size: 13px;
font-weight: 900;
line-height: 1;
}
.brand span {
display: block;
margin-top: 5px;
font-size: 10px;
font-weight: 700;
color: rgba(90, 96, 98, 0.55);
}
.divider {
width: 1px;
height: 32px;
margin: 0 28px;
background: rgba(0, 0, 0, 0.05);
}
.nav-links {
display: flex;
align-items: center;
gap: 6px;
}
.nav-link {
height: 40px;
display: inline-flex;
align-items: center;
gap: 8px;
border-radius: 6px;
padding: 0 12px;
color: rgba(90, 96, 98, 0.72);
font-size: 13px;
font-weight: 700;
text-decoration: none;
}
.nav-link.active {
background: rgba(0, 0, 0, 0.04);
color: #2d3335;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
}
.nav-icon {
width: 15px;
height: 15px;
border: 1.7px solid currentColor;
border-radius: 4px;
opacity: 0.75;
}
.top-tools {
margin-left: auto;
display: flex;
align-items: center;
gap: 12px;
border-left: 1px solid rgba(0, 0, 0, 0.04);
padding-left: 24px;
}
.round,
.user {
height: 38px;
border: 1px solid rgba(15, 23, 42, 0.08);
background: #fff;
color: #5a6062;
}
.round {
width: 38px;
border-radius: 50%;
}
.user {
display: flex;
align-items: center;
gap: 8px;
border-radius: 999px;
padding: 0 14px 0 10px;
}
.avatar {
width: 28px;
height: 28px;
border-radius: 50%;
display: grid;
place-items: center;
background: #eef1f4;
color: #7b858f;
font-weight: 800;
}
.main {
height: calc(100vh - 64px);
margin-top: 64px;
overflow: hidden;
padding: 0 0 16px 16px;
background: var(--page);
}
.split {
height: 100%;
display: flex;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.72);
border-radius: 32px;
background: var(--shell);
box-shadow: 0 18px 44px rgba(27, 45, 58, 0.08);
}
.content {
min-width: 0;
flex: 1;
overflow-y: auto;
padding: 12px;
}
.content-inner {
min-height: 100%;
border: 1px solid var(--line);
border-radius: 14px;
background: var(--card);
padding: 12px;
}
.info-card {
border: 1px solid var(--line);
border-radius: 12px;
background: #fff;
padding: 12px;
}
.section-title {
margin: 0 0 12px;
font-size: 15px;
font-weight: 900;
}
.top-summary {
display: grid;
grid-template-columns: 220px minmax(0, 1fr);
gap: 16px;
}
.image-box {
height: 174px;
border: 1px solid var(--line);
border-radius: 12px;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.45), rgba(222, 229, 235, 0.18)),
#f3f6f9;
display: grid;
place-items: center;
overflow: hidden;
}
.desk {
position: relative;
width: 154px;
height: 86px;
}
.desk::before {
content: "";
position: absolute;
left: 5px;
right: 5px;
top: 14px;
height: 28px;
border-radius: 6px;
transform: perspective(120px) rotateX(6deg);
background: #6a452e;
box-shadow: inset 0 -9px 0 rgba(0, 0, 0, 0.2);
}
.leg {
position: absolute;
top: 40px;
width: 8px;
height: 50px;
background: #3b281e;
}
.leg.one {
left: 24px;
}
.leg.two {
left: 62px;
}
.leg.three {
right: 62px;
}
.leg.four {
right: 24px;
}
.field-grid {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 22px 36px;
align-content: start;
}
.field span,
.line-field span {
display: block;
margin-bottom: 6px;
color: var(--muted);
font-size: 12px;
}
.field input,
.field textarea,
.line-field input,
.line-field textarea {
width: 100%;
border: 0;
background: transparent;
color: #071522;
outline: none;
padding: 0;
font-weight: 700;
}
.field input::placeholder,
.field textarea::placeholder {
color: #a2acb7;
font-weight: 500;
}
.soft-input {
min-height: 30px;
display: flex;
align-items: center;
border: 1px solid var(--line);
border-radius: 10px;
background: #f8fafc;
padding: 0 10px;
}
.soft-input input {
height: 28px;
}
.select-pill {
height: 32px;
display: inline-flex;
align-items: center;
gap: 8px;
border: 1px solid var(--line);
border-radius: 12px;
background: #f3f6f9;
padding: 0 10px;
font-weight: 800;
}
.badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 24px;
height: 18px;
border-radius: 999px;
background: #425966;
color: #fff;
font-size: 11px;
font-weight: 900;
}
.divider-line {
margin: 16px 0 12px;
height: 1px;
background: var(--line);
}
.line-grid {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 18px 34px;
}
.line-field.wide-2 {
grid-column: span 2;
}
.line-field.wide-3 {
grid-column: span 3;
}
.textarea-box {
min-height: 54px;
border: 1px solid var(--line);
border-radius: 10px;
background: #f8fafc;
padding: 9px 10px;
}
.textarea-box textarea {
min-height: 36px;
resize: vertical;
line-height: 1.5;
}
.sub-card {
margin-top: 14px;
border: 1px solid var(--line);
border-radius: 12px;
background: #fff;
overflow: hidden;
}
.sub-head {
padding: 12px;
border-bottom: 1px solid var(--line);
}
.sub-head h2 {
margin: 0;
font-size: 15px;
font-weight: 900;
}
.sub-head p {
margin: 5px 0 0;
color: var(--muted);
font-size: 12px;
}
.reference-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
.reference-table th,
.reference-table td {
border-bottom: 1px solid var(--line);
padding: 10px 12px;
text-align: left;
vertical-align: top;
}
.reference-table th {
color: var(--muted);
font-weight: 800;
background: #f8fafc;
}
.mini-actions {
display: flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.mini-btn {
height: 26px;
border: 1px solid var(--line);
border-radius: 7px;
background: #f7f9fb;
color: #334b5f;
padding: 0 9px;
font-size: 11px;
font-weight: 800;
}
.mini-btn.primary {
border-color: var(--dark);
background: var(--dark);
color: #fff;
}
.mini-btn.danger {
color: #c73646;
}
.bottom-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 12px;
}
.btn {
height: 38px;
border: 1px solid var(--line);
border-radius: 10px;
background: #f7f9fb;
color: var(--text);
padding: 0 18px;
font-weight: 800;
}
.btn.primary {
border-color: var(--dark);
background: var(--dark);
color: #fff;
}
.right-panel {
width: 224px;
min-width: 224px;
max-width: 224px;
overflow-y: auto;
border-left: 1px solid var(--line);
background: #fff;
padding: 12px;
}
.side-card {
min-height: 100%;
border: 1px solid var(--line);
border-radius: 14px;
background: #fff;
overflow: hidden;
}
.side-head {
padding: 14px 12px;
border-bottom: 1px solid var(--line);
}
.side-head h2 {
margin: 0;
font-size: 16px;
font-weight: 900;
}
.side-head p {
margin: 7px 0 0;
color: var(--muted);
font-size: 11px;
}
.node-list {
display: grid;
gap: 8px;
padding: 10px;
}
.node {
height: 38px;
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid var(--line);
border-radius: 10px;
background: #fff;
color: #334b5f;
padding: 0 10px;
}
.node.active {
background: #e6eef5;
color: #27495f;
}
.node.dark {
border-color: var(--active);
background: var(--active);
color: #fff;
}
.count {
min-width: 24px;
height: 24px;
display: grid;
place-items: center;
border-radius: 999px;
background: #f6f8fa;
color: var(--muted);
font-weight: 900;
font-size: 12px;
}
.dark .count {
background: #40515f;
color: #fff;
}
</style>
</head>
<body>
<header class="top-nav">
<a class="brand" href="#">
<div class="brand-mark">J</div>
<div>
<strong>壹点产品开发平台</strong>
<span>正式产品</span>
</div>
</a>
<div class="divider"></div>
<nav class="nav-links">
<a class="nav-link" href="#"><i class="nav-icon"></i>首页概览</a>
<a class="nav-link" href="#"><i class="nav-icon"></i>资产中心</a>
<a class="nav-link" href="#"><i class="nav-icon"></i>辅助装配</a>
<a class="nav-link" href="#"><i class="nav-icon"></i>草稿组</a>
<a class="nav-link active" href="#"><i class="nav-icon"></i>正式产品</a>
<a class="nav-link" href="#"><i class="nav-icon"></i>仓库管理</a>
</nav>
<div class="top-tools">
<button class="round" aria-label="通知"></button>
<button class="round" aria-label="切换主题"></button>
<button class="user"><span class="avatar">L</span>LocalAdmin⌄</button>
</div>
</header>
<main class="main">
<section class="split">
<div class="content">
<div class="content-inner">
<section class="info-card">
<h1 class="section-title">开品参考填写</h1>
<div class="top-summary">
<div class="image-box">
<div class="desk">
<span class="leg one"></span>
<span class="leg two"></span>
<span class="leg three"></span>
<span class="leg four"></span>
</div>
</div>
<div class="field-grid">
<div class="field">
<span>开品名称</span>
<div class="soft-input"><input value="胡桃木色窄边书桌" /></div>
</div>
<div class="field">
<span>临时编号</span>
<div class="soft-input"><input value="KP20260428-001" /></div>
</div>
<div class="field">
<span>品牌</span>
<input value="基本盘" />
</div>
<div class="field">
<span>适配品牌</span>
<div class="select-pill">基本盘 <i class="badge">+3</i> ▼</div>
</div>
<div class="field">
<span>当前阶段</span>
<input value="立项前" />
</div>
<div class="field">
<span>商品类目</span>
<input value="书桌" />
</div>
<div class="field">
<span>销售类目</span>
<input value="书桌" />
</div>
<div class="field">
<span>设计师</span>
<input value="徐霞客" />
</div>
<div class="field">
<span>企划负责人</span>
<input value="徐霞客" />
</div>
<div class="field">
<span>年份</span>
<input value="2026" />
</div>
</div>
</div>
<div class="divider-line"></div>
<div class="line-grid">
<div class="line-field wide-2">
<span>竞品链接 / 参考链接</span>
<div class="soft-input"><input value="https://example.com/reference-desk" /></div>
</div>
<div class="line-field">
<span>目标价格带</span>
<input value="待评审确认" />
</div>
<div class="line-field">
<span>尺寸</span>
<input value="140/60/78" />
</div>
<div class="line-field">
<span>材质</span>
<input value="胡桃木色 / 橡胶木方向" />
</div>
<div class="line-field">
<span>是否急开</span>
<input value="否" />
</div>
<div class="line-field wide-3">
<span>开品理由</span>
<div class="textarea-box">
<textarea>看中竞品窄边桌面比例和轻量化视觉,适合基础盘补一个中低价位书桌款。</textarea>
</div>
</div>
</div>
</section>
<section class="sub-card">
<div class="sub-head">
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px">
<div>
<h2>参考资料</h2>
<p>这里上传前期参考文档、截图整理和竞品资料,不放 SKU,也不放完整基础信息。</p>
</div>
<button class="mini-btn primary">上传文档</button>
</div>
</div>
<table class="reference-table">
<thead>
<tr>
<th style="width: 80px">序号</th>
<th style="width: 220px">文档名称</th>
<th style="width: 150px">类型</th>
<th>备注</th>
<th style="width: 150px">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>竞品截图整理.pdf</td>
<td>参考文档</td>
<td>主图、侧面图、结构截图,供产品资料和立项评审参考。</td>
<td>
<div class="mini-actions">
<button class="mini-btn">下载</button>
<button class="mini-btn danger">删除</button>
</div>
</td>
</tr>
<tr>
<td>2</td>
<td>目标价格带.xlsx</td>
<td>价格资料</td>
<td>竞品价格、尺寸、卖点和链接初步整理。</td>
<td>
<div class="mini-actions">
<button class="mini-btn">下载</button>
<button class="mini-btn danger">删除</button>
</div>
</td>
</tr>
<tr>
<td>3</td>
<td>参考外观图.zip</td>
<td>图片资料</td>
<td>外观图片、局部结构图、材质参考图。</td>
<td>
<div class="mini-actions">
<button class="mini-btn">下载</button>
<button class="mini-btn danger">删除</button>
</div>
</td>
</tr>
</tbody>
</table>
<div class="bottom-actions">
<button class="btn">保存草稿</button>
<button class="btn primary">保存开品参考</button>
</div>
</section>
</div>
</div>
<aside class="right-panel">
<div class="side-card">
<div class="side-head">
<h2>图片与文档</h2>
<p>右侧按分类切换工作区</p>
</div>
<div class="node-list">
<button class="node active"><span>开品参考</span><i class="count">0</i></button>
<button class="node"><span>产品资料</span><i class="count">1</i></button>
<button class="node"><span>设计卖点</span><i class="count">1</i></button>
<button class="node"><span>立项评审</span><i class="count">0</i></button>
<button class="node dark"><span>基础信息</span><i class="count">锁</i></button>
<button class="node"><span>样品评审</span><i class="count">锁</i></button>
<button class="node"><span>产品实拍</span><i class="count">锁</i></button>
<button class="node"><span>安装方式</span><i class="count">锁</i></button>
<button class="node"><span>包装细节</span><i class="count">锁</i></button>
</div>
</div>
</aside>
</section>
</main>
</body>
</html>

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