2171 Commits

Author SHA1 Message Date
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
8a95dbcbf0 fix(download-center): correct error code enum and failAllActive return shape 2026-09-22 17:40:21 +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
居居
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
687e80b6cd feat(oa-board): 流程状态筛选新增「全部」口径
流程状态下拉新增「全部」:overallStatus=__all__ 时服务端与客户端过滤均跳过状态限制,包含已驳回、已终止、已取消及无状态标记数据;默认口径(进行中+已完成)不变,导出明细同口径生效。
2026-09-22 11:24:16 +08:00
居居
7b5f66dfcf fix(supply-chain): 固化人员信息界面配置 2026-09-22 10:33:18 +08:00
Codex
f94286464b feat(supplier-recommended): add image similarity search 2026-09-22 10:17:35 +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
191e050374 fix(sync): drop server-only marker imports from the archive sync dependency chain 2026-09-21 20:58:07 +08:00
WuYongJie
fd3b451af9 chore(sync): log full stack on enrichment failures 2026-09-21 20:34:25 +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
407946d5d2 chore(sync): record server-only and uuid in the deploy lock file 2026-09-21 19:15:24 +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
c4943e6b04 fix(product-archive): include stat_date in ops owners group by to unblock daily sync 2026-09-21 17:35:36 +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
ce48ab7944 fix(1688): show zero for missing cost values 2026-09-21 11:42:05 +08:00
WuYongJie
073437c8bf 安装演示工作台 5 项 bug 根因修复接手 2026-09-21 11:02:32 +08:00
WuYongJie
d4778d29e9 feat(intake): extend barcode import permissions 2026-09-21 10:54:14 +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
7d8f6d3848 fix(supply-chain): restore 1688 data menu order 2026-09-18 15:53:36 +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
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
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
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
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
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
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
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
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