Files
atlas-seller-kit/docs/reviews/implementation-readiness-open-items.md
T
2026-09-28 17:34:33 +08:00

8.2 KiB
Raw Blame History

实现前待决与待落地事项

本文记录 API 契约和数据库 DDL 评审后尚未闭环的事项。它不是新的产品需求;除明确标为“已决策”的条目外,实施对应模块前必须先确认或补齐。最后复核:2026-09-02。

使用规则

  • 已决策,待落地:产品或技术方向已确认,但 DDL、接口契约或实现规则尚未同步,编码前补齐即可。
  • 待确认:会改变用户可见行为或数据语义,不能由开发自行猜测。
  • 实现约束:已经确认,后续代码、测试和文档必须遵守。

P0:开始实现前必须闭环

1. 幂等请求与 Transactional Outbox

状态:已决策,DDL 与基础架构文档已落地;待 NestJS 实现和集成测试。

所有可重试的关键写操作需要幂等保护;所有会触发异步工作的业务写入需要 Transactional Outbox。基础迁移已新增以下两张工作空间隔离表,并已纳入 RLS、清理策略和 Worker 设计:

  • idempotency_requests:按用户、工作空间、规范化请求路径和 Idempotency-Key 保存请求摘要、处理状态、HTTP 响应摘要、关联资源及过期时间。至少保留 24 小时;建议 48 小时。相同键而请求摘要不同返回 409 IDEMPOTENCY_KEY_REUSED。
  • outbox_events:在同一数据库事务中随业务数据和 operations 记录写入待投递事件;独立 Dispatcher 读取、投递 BullMQ 并重试。事件投递必须按“至少一次”设计,消费者必须可重入;不得将 API Key、Cookie、预签名 URL 等秘密写入事件载荷。

首批必须接入的场景:店铺创建/凭据验证、上传完成后的媒体处理、采集素材转存、首次上架发布、在线商品同步、AI 生成任务。普通草稿保存可在首期只使用乐观锁。完整设计和验收项见 ../architecture/transactional-outbox-and-idempotency.md。

2. offer_id(Ozon 货号)策略

状态:2026-09-22 修订决策;AI 填报尚未实现。

  • 采集进入采集箱时,型号(model)和 offer_id 都允许为空。
  • model 是商品通用字段;offer_id 是当前平台下、每个 SKU 的字段。用户可手动填写,也可在进入编辑页后点击“AI 填报”,由规则与 AI 生成并直接填入表单。
  • 采集入箱本身不填写货号;AI 生成以 Atlas 稳定商品标识、已确认类目和实际 SKU 规格为依据,可使用来源信息提炼语义片段,但不得直接复用来源 SKU 作为目标平台货号。
  • 首次上架前,model 必填;每个被选中 SKU 的 offer_id 必填,且在目标门店内唯一。生成值和手填值接受相同的保存及发布校验。
  • 导入成功后,该次平台 SKU 发布记录使用的 offer_id 作为历史事实,不允许通过“重新上架”改变;后续编辑在线商品属于另一套平台商品更新能力。

详细的主动生成、表单回填及冲突处理见 ../product/ai-autofill-plan.md。

P1:实现对应模块前修正

1. SKU 与平台发布配置的同商品归属

状态:M3.0 已落地数据库约束(2026-09-09);发布任务、在线镜像等后续表必须复用该归属链。

product_variant_platform_listings 同时引用平台发布配置(profile)和通用 SKU(variant),但必须强制:variant.product_id = profile.product_id。同一条归属链也适用于属性、报价、发布任务项和在线镜像映射。

2026-09-28:迁移 043~045 已补齐报价的同商品、同 profile、同门店/平台归属,以及已选候选属于当前报价的复合外键;确认快照不可更新。跨 SKU 和跨租户测试已通过。

实施时优先选用可维护的复合外键/冗余 product_id 加一致性约束;如 PostgreSQL 外键表达能力不足,再使用约束触发器。不得只依赖服务层校验。

2. 类目切换后未映射属性的持久化

状态:M3.0 已落地持久化表(2026-09-09);类目切换写入与恢复操作随编辑草稿保存实现。

切换目标平台类目后,旧类目中无法映射的属性值必须作为 Atlas 草稿保留,供用户查看、复用或手动处理,不能只出现在一次 API response 的 warning 中。

可选落地方式:

  • 新增 product_unmapped_attributes,保存原类目/属性标识、名称快照、原值、原因、保留时间;或
  • 在 product_platform_profiles 增加有明确 JSON Schema 的 unmapped_attributes_draft。

选择后需要同步商品编辑 API 的读取、保存和类目切换响应。

3. 乐观锁的统一实施

状态:M3.1 已使用产品/profile 双 revision 条件更新、事务回滚和集成测试落地;后续编辑资源不得绕过该规则。

revision 不依赖数据库触发器自动递增。所有可编辑资源的更新必须由统一 Repository/数据访问层执行:

UPDATE ...
SET ..., revision = revision + 1
WHERE id = :id AND revision = :expected_revision;

受影响行数为 0 时,服务层读取当前版本摘要并返回 409 VERSION_CONFLICT。必须为并发更新和重复提交编写集成测试;禁止由各业务开发者手写、遗漏该规则。

4. 来源平台、发布目标平台与页头上下文

状态:已决策,待平台能力模型落地。

platforms 需要显式表达以下能力,不能由名称或零散 JSON 约定猜测:

  • can_be_source:可作为采集来源,例如 1688、淘宝、天猫、Ozon;
  • can_be_target:可作为商品发布目标,例如首期 Ozon;
  • can_be_header_context:可出现在 ERP 页头平台切换器。

页头只展示已启用、且 can_be_header_context = true 的目标平台;来源信息仍可保存其平台标识。实现时可以使用三个布尔列,或统一的 capabilities JSONB,但后者必须在架构文档中给出固定 JSON Schema、默认值和校验规则。

5. 在线商品同步中的“未见”与归档

状态:已确认(2026-09-26)。 维护者选择以下规则,在线商品全量同步按此执行。

规则如下:

  1. 一次成功的全量同步中未见某在线 SKU:仅首次写入 missing_since;不归档。
  2. 之后连续第二次成功全量同步仍未见,或 missing_since 已超过 7 天:写入 archived_at,列表默认隐藏但可筛选查看。
  3. 同步失败、部分同步或被取消:绝不修改 missing_since、archived_at。
  4. 该 SKU 随后的成功同步再次出现:清空 missing_since 与 archived_at,恢复正常展示。

P2:契约一致性优化

1. 删除的同步与异步响应

状态:已决策,待统一约定更新。

  • 同步软删除成功返回 204 No Content。
  • 涉及大量素材/衍生文件清理、需要异步任务的删除或清空操作允许返回 202 Accepted 与 operation。

应将此例外写入 docs/api/conventions.md,并在每个异步删除接口明确说明。

2. 平台市场、语言和币种能力的 JSON Schema

状态:待技术设计确认。

目前平台 API 返回的 default_market_code、default_locale 等字段可暂存于 platforms.capabilities,但必须定义统一 Schema,至少覆盖:默认市场、可用市场、默认语言、可用语言、可用币种,以及上述来源/目标/页头能力。

如平台能力会被频繁查询、筛选或由后台维护,后续可拆分为平台市场/语言/币种关联表;首期不得让各模块自行解释任意 JSON 字段。

3. 修改门店 Client ID 的边界

状态:待产品确认。

当前 API 契约禁止编辑 Client ID,这比最初“编辑店铺”需求更严格。二选一:

  • 保持禁止:Client ID 变化视为新增授权,用户新建门店后停用/删除旧门店;
  • 允许替换:要求同时提交新的 Client ID 与 API Key,服务端原子加密替换、清除验证状态并重新验证,验证失败不得使旧密钥与新 Client ID 形成混合状态。

无论选哪种,都不得只允许单独改 Client ID。

关闭记录

事项关闭时,在对应小节补充“确认日期、最终决策、影响的 SQL/API/测试文件”。不要直接删除历史结论,以保留设计决策轨迹。