LISTING-AI-IMPLEMENTATION-PLAN.md 33 KB

Listing AI 优化工作台实施计划

状态:已实施并完成首轮全量验收(2026-08-21)
适用仓库:Saas-voc 前端、Saas-voc-server 后端
本文是实施契约;实际落地、全量数据结果和保留门禁见 LISTING-AI-IMPLEMENTATION-REPORT-2026-08-21.md。任何字段、路由、表结构或验收口径变更,都应先修改本文并记录原因。

0. 2026-08-21 AI 评分目标纠偏(本节优先于旧 MVP 描述)

产品目标确认是真实 Listing AI 评分,不是“规则评分后由 AI 生成优化文案”。因此新增 listing-jd-ai-v1,并保留 listing-jd-v2 作为规则基线:

  • AI 不自由输出 0–100 总分,只对固定原子标准返回 unknown/fail/weak/pass/strong、原文证据、理由和置信度;
  • 服务端按冻结权重将等级映射为分数、校验范围并求和,模型无权修改维度、权重和总分;
  • 标题、核心卖点、详情和规格使用“确定性结构分 + AI 语义分”;当前文本模型不具备已验证的视觉能力,主图维度暂沿用结构规则分并明确标注,不能宣称完成视觉评分;
  • 正常读取使用 sourceHash + rubricVersion + model + promptVersion 稳定键;同键命中既有结果时不再次调用模型;模型、Prompt 或权重变化必须发布新版本,不覆盖旧结果;
  • AI 评分与 AI 优化是两条独立链路。本次 AI 评分不生成标题、卖点或详情候选,不创建优化版本;
  • 本次修正和真实验收最多选择 10 个商品。未选择的其余商品继续展示 listing-jd-v2 规则分,并在 UI 标注“规则评分,尚未执行 AI 评分”;
  • 当前发布门禁将单个 AI 评分任务限制为 10 个商品,禁止用筛选范围对 824 个商品直接发起模型调用。

0.1 listing-jd-ai-v1 固定权重

维度 确定性结构分 AI 语义分 合计
标题 8 搜索意图 4、信息层级 3、差异化 3、事实与合规 2 20
核心卖点 4 客户收益 4、具体可信 4、差异化 3、购买顾虑 3、一致性 2 20
主图 20(仅数量、主图标记、URL、重复和顺序) 0;视觉语义等待多模态模型 20
详情 6 完整度 4、结构可读性 3、卖点证据 3、顾虑处理 2、一致性 2 20
规格 10 决策有效性 3、命名清晰 2、类目完整性 3、参数一致性 2 20

等级映射固定为:fail=0%weak=35%pass=70%strong=100%unknown 不伪装为通过,维度标记 partial。最终分数保留 0.5 分精度。相同输入绕过缓存重复评分时,整体漂移超过 2 分即不允许扩大样本。

1. 目标、范围与明确不做的事

1.1 MVP 目标

针对京东授权店铺当前 API 返回的全部商品,完成一个可恢复、可审计、可解释的 Listing 评分批任务,并提供:

  • 商品评分总览、筛选、分页和批量发起任务;
  • 单商品标题、核心卖点、主图、详情、规格五个维度的原文、证据、得分和优化建议;
  • AI 建议版本的生成、对比、采纳和审计;
  • 批任务进度、成功/部分/阻塞/失败数量、单项失败原因和重试;
  • 不在浏览器持有 JD/Parse 密钥,不把缺失数据伪装成 0 分。

页面上显示的商品总数必须来自同步摘要/API,不得写死 614、821 或本次实测的 824

1.2 MVP 不做

  • 不调用京东写接口,不自动发布标题、详情或图片;
  • 不把 AI 建议直接覆盖源 Listing,只生成版本草稿;
  • 不把卖家 Listing 文本当作 VOC 用户证据;
  • 不声称做了图片审美/视觉语义评分。当前 AI 网关只有文本消息能力,MVP 主图评分仅使用数量、顺序、主图标记、URL 可访问性等确定性规则;
  • 不接天猫、抖音、拼多多。样例图的平台 tab 仅京东可用,其余显示“即将支持”且不可点击发起任务;
  • 不重写现有 VOC、行动、验证链路;
  • 不让 Angular 直连目标 Parse 或 JD SP-API。

2. 已验证的输入事实

  1. JD GET /sp-product/v0/products 返回动态总数,本次为 824。
  2. GET /sp-product/v0/products/{productId}?scene=pop 已成功;productId 必须参与 MD5 签名。
  3. 目标 Parse class 资源为 {JD_TARGET_PARSE_URL}/classes/<ClassName>;挂载前缀根 GET 的 404 不能作为不可用证据。
  4. 目标 Parse 偶发 HTTP 403 / Parse code 119,有限重试可恢复;count=1 在当前托管环境不可靠。
  5. 导入器当前仍只读取 page=1,且 --limit 上限为 20,只能做联调,不能承担全量生产同步。
  6. JD 商品详情已观察到标题、品牌、类目、描述、主图元数据、属性、SKU、尺寸重量、价格与履约字段。
  7. 现有 Angular 的 DomesticDatasetService 加载的是 VOC 快照,不是本店实时 Listing 目录;不能用它替代 Listing 源数据。
  8. 现有 listing-api.service.ts 是停用的 Amazon 旧边界,不复用。
  9. 现有 AI 调用发生在浏览器,且网关 schema 只接受字符串消息。824 商品批处理必须移到服务端 worker。

复测原始结论见 JD-PRODUCT-DETAIL-PARSE-VERIFICATION-2026-08-21.md

3. 总体调用链

源 Parse EcomAuth(服务端读取最新 JD token)
  -> JdTokenProvider
  -> JdSpClient(path/query/header 统一签名、超时、错误分类、限流)
  -> JdProductClient(列表分页 / 商品详情 / SKU / 库存)
  -> ListingCatalogSyncWorker(分页、详情并发、断点、幂等、raw hash)
  -> 目标 Parse ProductDetail 等源快照(现有兼容写入)
  -> ListingSourceRepository(归一化为内部 DTO)
  -> ListingScoreJobWorker
       -> DeterministicRuleEngine(结构基线、覆盖率、硬规则证据)
       -> ListingAiRubricJudge(固定原子标准、等级与证据)
       -> AI Gateway(语义裁判;temperature=0;严格 JSON)
       -> ServerScoreComposer(固定权重映射与总分计算)
       -> ListingScoreRepository / ListingVersionRepository
  -> /api/listing-ai/*(RBAC + Zod + cursor pagination)
  -> Angular ListingAiApiService
  -> Workbench / Product / Versions / Tasks 页面

关键决策:AI 对固定语义标准作结构化判定,服务端确定性计算数值分。 AI 失败时不覆盖既有 listing-jd-v2 规则分;AI 成功后以独立 listing-jd-ai-v1 结果展示,且必须显示模型、Prompt 版本、评分类型和证据。

4. 信息架构与页面流程

4.1 路由

路由 页面类型 职责
/listing-ai/workbench Hub 商品总览、评分分布、筛选、批量评分入口
/listing-ai/workbench/:productId Entity 单商品预览、五维评分、证据、建议和采纳
/listing-ai/versions Workspace 优化版本列表、状态、创建人、源快照和 diff
/listing-ai/tasks Workspace 批任务列表、进度、错误数、重试入口
/listing-ai/tasks/:jobId Entity 任务明细、各商品状态、错误与重试

app.routes.ts 中新增一个受现有 protectedRoutes 保护的 listing-ai 父路由,不修改现有路由含义,不删除旧 route。

4.2 工作台布局

  1. 顶部 page-header:标题、数据更新时间、京东状态、保存筛选、发起评分任务。
  2. 平台切换:京东 active;其他平台 disabled + “即将支持”,不制造空数据。
  3. 指标卡:商品总数、可评分、已评分、部分/阻塞、失败、平均分。每个指标带口径 tooltip。
  4. page-toolbar:搜索、类目、上下架、评分状态、分数区间、数据完整度、更新时间、排序。
  5. data-table-shell:商品、标题、类目、SKU 数、数据完整度、总分、五维分、rubric 版本、最后评分时间、状态、操作。
  6. 批量操作只传筛选快照或选中 ID,不把 824 条完整记录塞进 request body。
  7. 分页使用 cursor,默认 25,最大 100;浏览器不预载全量数据。

4.3 单商品页

  • 左栏:移动端只读预览,HTML 详情必须先清洗;图片失败显示占位和原因。
  • 右栏:board-tabs 五个维度:标题、核心卖点、主图、详情、规格。
  • 每个 tab 使用统一结构:当前内容、覆盖度、规则证据、扣分项、AI 建议、来源字段路径、源快照时间。
  • “采用此版本”只写入 ListingVersion.status=adopted 和审计事件,不调用京东发布。
  • 缺失字段显示 ,同时显示 not_returnedemptyparse_failed,不能显示为 0。

4.4 任务页

  • 状态机:queued -> running -> completed | partial | failed | cancelled
  • 单商品状态:queued -> rules_scored -> ai_pending -> scored | partial | blocked | failed
  • 展示总量必须来自创建任务时冻结的 scope 解析结果;进度为 terminal item 数 / frozen total。
  • MVP 使用 2–5 秒 polling;先不引入 SSE/WebSocket。页面刷新后凭 jobId 恢复。

5. JD 字段接收与内部 DTO

后端先保留受控 raw JSON 与 hash,再生成稳定 DTO。前端只接收 DTO,不感知 JD 原始层级。

内部字段 JD 实际路径 类型/处理 缺失语义
productId productInfo.productId;fallback 列表 productId String,禁止 JS number 身份比较 blocked
shopId 授权记录/同步上下文 String blocked
title productInfo.productTitle.title -> productInfo.productName trim String empty
titleBrandName productInfo.productTitle.titleBrandName String/null not_returned
brand.id/name productInfo.brandInfo.* ID 转 String not_returned
categoryIds categoryDetail.thirdCategoryId, lastCategoryId String[],去重 not_returned
price.jd/cost productInfo.priceInfo.* number/null,不用 0 代替 null not_returned
descriptions.desktopHtml productDetailDesc.desc 原始 HTML 仅服务端留存;输出 sanitized HTML empty
descriptions.mobileHtml productDetailDesc.mobileDesc 同上 empty
features[] productInfo.features[].key/value 丢弃全空项,保留顺序 empty
attributes[] productInfo.goodsAttrInfos[] attrId/name/values empty
images[] material.mainImages[].imageInfoList[] 展平、按 orderSort 排序、去 URL 重复 empty
images[].isPrimary primaryFlag boolean/null not_returned
images[].gptFlag gptFlag boolean/null;不推导审美分 not_returned
skus[] skuList[] skuId String、名称、价格、库存、属性、状态 empty
dimensions length/width/height/weight number/null + 明确单位配置 not_returned
logistics productInfo.logisticsInfo 白名单字段 not_returned
afterService productInfo.afterServiceInfo, to7ReturnFlag 白名单归一化 not_returned
sourceModifiedAt productInfo.modifiedTime epoch 校验后 ISO not_returned
sourceHash 归一化前关键内容 canonical JSON SHA-256 必填

API DTO 草案

interface ListingProductSummaryDto {
  productId: string;
  shopId: string;
  platform: 'jd';
  title: string | null;
  imageUrl: string | null;
  categoryIds: string[];
  itemStatus: string | null;
  skuCount: number | null;
  sourceUpdatedAt: string | null;
  syncedAt: string;
  sourceHash: string;
  coverage: { percent: number; missing: string[]; status: 'eligible' | 'partial' | 'blocked' };
  latestScore: ListingScoreSummaryDto | null;
}

DTO 中 ID 一律 String;金额仍用 number 是展示折中,后续涉及财务计算再升级 decimal 字符串。

6. 评分模型

6.1 结果结构

type ListingDimension = 'title' | 'selling_points' | 'images' | 'description' | 'specifications';

interface ListingDimensionScoreDto {
  dimension: ListingDimension;
  score: number | null;
  maxScore: number;
  coverage: number;
  status: 'scored' | 'partial' | 'blocked';
  evidence: Array<{
    ruleId: string;
    fieldPath: string;
    outcome: 'pass' | 'fail' | 'unknown';
    delta: number;
    message: string;
  }>;
  suggestions: string[];
}
  • 默认五维各 20 分,总分 100;权重只存在 rubric 配置,不散落在组件。
  • overallScore 只在所有必需维度满足 rubric 的最低覆盖门槛时计算。
  • coveragequality score 是两条独立指标。没有详情不能得“详情 0 分”,而应是 score=null,status=blocked
  • rubric 从 listing-jd-v1 开始;发现 v1 将 JD 传输控制标记误判为重复卖点后,未原地修改,而是发布 listing-jd-v2 并完成全量重算,v1 继续保留审计。

6.2 第一版规则边界

维度 可确定性评分 仅建议/待确认
标题 是否存在、长度、品牌/品类/型号覆盖、重复片段、异常符号、SKU 冲突 类目禁限词词库需业务确认
核心卖点 features 数量、非空、重复、规格覆盖、服务信息覆盖 京东是否有独立卖点字段需更多样本确认
主图 图片数、主图标识、顺序、URL 结构/可访问、重复 URL 构图、清晰度、文字占比需多模态模型,MVP 不评分
详情 desktop/mobile 是否存在、清洗后文本长度、结构、两端一致性、危险标签 内容说服力由 AI 建议,不直接决定数值分
规格 SPU/SKU 属性数量、必填规格覆盖、尺寸重量、属性一致性 类目必填属性清单需要规则数据源

在 20 商品 shadow benchmark 完成前,不把暂定阈值写死为产品真理。阈值由 rubric JSON 管理,并在样本评审后冻结 v1。

7. 后端设计

7.1 模块边界

新增 src/modules/listing-ai/,不把评分任务塞进现有 analysis_run。现有 analysis_run 的类型和生命周期服务于 VOC 洞察,强行复用会污染语义和权限。

实际落地结构(小文件按职责合并,避免无意义拆分):

src/modules/listing-ai/
  domain.ts
  schemas.ts
  routes.ts
  listing-ai.service.ts
  clients/
    jd-token.provider.ts
    jd-sp.client.ts
    jd-product.client.ts
  normalization/
    domestic-dataset.adapter.ts
    jd-listing.normalizer.ts
    html-sanitizer.ts
  repositories/
    in-memory-listing-ai.repository.ts
    parse-rest-listing-ai.repository.ts
    postgres-listing-ai.repository.ts
  scoring/
    rule-engine.ts

canonicalHash 与五维 v1 rubric 同置于 rule-engine.ts,批任务领取/恢复置于 ListingAiService,全量目录 worker 作为显式 scripts/sync-jd-listings.ts 运行。这样保留清晰边界,同时不为单一实现增加空包装层。

postgresparse_rest 两种 storage driver 都必须继续支持,则 repository 定义接口,并各有实现;local demo 使用显式内存实现,不在 route 里写条件分支。

7.2 全量商品同步必须先工程化

现有 tools/jd-import-products.mjs 仅保留为联调工具。生产同步必须满足:

  1. 从 page 1 循环到 API 返回的实际末页,不依据历史总数;
  2. 记录每页请求、返回条数、productId 去重数和 pagination 元数据;
  3. 详情请求使用配置化并发(初始 3),遇限流/5xx 指数退避并尊重 retry-after;
  4. access token 每批获取最新值,失效时只允许一次受控刷新/重取;
  5. 使用 (shopId, productId) 作为身份,现有兼容业务键 jd:${shopId}:${productId} 保留;
  6. 每条源记录写 sourceHashsourceModifiedAtsyncedAtdetailStatus 和错误分类;
  7. checkpoint 保存 page、最后成功 productId、统计值;worker 重启可恢复;
  8. upsert 幂等,相同 sourceHash 不重复触发评分;
  9. ProductDetailProductAsinSkuMappingJdStockSnapshot 兼容写入先保留;迁移完成前不删类、不改业务键;
  10. 完成度以本次冻结的 productId 清单和逐条终态计算,不使用当前异常的 Parse count。

7.3 持久化实体

Postgres 下一迁移文件建议为 migrations/007_listing_ai.sql

实体/表 主键与唯一约束 关键字段
voc.listing_source_snapshot unique (workspace_id, platform, shop_id, product_id, source_hash) normalized_json, raw_ref/raw_json, detail_status, source_modified_at, observed_at
voc.listing_score_job unique (workspace_id, idempotency_key) scope_json, rubric_version, source_cutoff, totals, status, checkpoint, timestamps
voc.listing_score_item unique (job_id, product_id, source_hash) status, attempts, score_result_id, error_code, error_detail_redacted
voc.listing_score_result unique (workspace_id, product_id, source_hash, rubric_version) overall_score nullable, coverage, dimension_json, model_info, prompt_version
voc.listing_version unique (workspace_id, product_id, version_no) base_source_hash, content_json, status, created_by, adopted_at

所有写操作追加现有 audit log:listing.score.requestedlisting.score.retriedlisting.version.createdlisting.version.adopted

Parse REST driver 对应类名使用 VocListingSourceSnapshotVocListingScoreJobVocListingScoreItemVocListingScoreResultVocListingVersion,字段语义与 Postgres 对齐。已有外部目标类 ProductDetail 等只作为 JD 源镜像,不直接承担 SaaS 工作区/RBAC 结果存储。

7.4 AI 执行

  • worker 调用 FmodeAiClient,前端不再逐商品请求 /api/ai/chat/completions
  • prompt 输入只包含白名单后的 Listing DTO、规则失败项和 rubric 版本;
  • 返回必须通过 Zod schema;失败记录 ai_invalid_output 并可重试;
  • AI 返回只产生建议和候选版本,不修改规则 evidence 或 numeric score;
  • 结果记录 model、promptVersion,job 记录 requestHash;当前上游未稳定返回 usage,正式 AI 放量前在可用时补充耗时/token usage 指标,任何情况下都不记录 token/密钥;
  • LISTING_AI_CONCURRENCYLISTING_AI_MAX_ITEMS_PER_JOB 配置并发和单任务硬预算;超限在入队前返回 429,未配置模型时规则结果保留且任务为 partial,不无限重试。跨任务每日总预算仍是全量 AI 发布门禁。

8. 后端 API 契约

统一前缀:/api/listing-ai。沿用现有 AuthenticationMiddleware、WorkspaceAccessService、Zod、ApiError、audit,不创造第二套鉴权。

8.1 商品目录

GET /api/listing-ai/products?workspaceId=&platform=jd&limit=25&cursor=&search=&categoryId=&itemStatus=&scoreStatus=&minScore=&maxScore=&coverageStatus=&sort=
{
  "items": [],
  "nextCursor": null,
  "summary": {
    "sourceTotal": 824,
    "eligible": 0,
    "scored": 0,
    "partial": 0,
    "blocked": 0,
    "failed": 0,
    "lastCatalogSyncAt": "2026-08-21T00:00:00.000Z"
  }
}

sourceTotal 来自最近一次成功同步摘要,不来自不可靠的 Parse count。

GET /api/listing-ai/products/:productId?workspaceId=&platform=jd

返回 sourcecoveragelatestScoreversionsSummary。默认不返回完整 raw JD JSON。

8.2 评分任务

POST /api/listing-ai/score-jobs
Idempotency-Key: <client generated UUID>
Content-Type: application/json

{
  "workspaceId": "demashi",
  "platform": "jd",
  "scope": {
    "mode": "filter",
    "filter": { "itemStatus": "on_shelf", "scoreStatus": "unscored" }
  },
  "rubricVersion": "listing-jd-v1",
  "includeAiSuggestions": true
}

也支持 scope.mode=selected,但 productIds 上限 100;全量必须用 filter scope。创建时服务端冻结 productId + sourceHash 清单,返回 202。

GET  /api/listing-ai/score-jobs?workspaceId=&status=&limit=&cursor=
GET  /api/listing-ai/score-jobs/:jobId?workspaceId=
GET  /api/listing-ai/score-jobs/:jobId/items?workspaceId=&status=&limit=&cursor=
POST /api/listing-ai/score-jobs/:jobId/retry
POST /api/listing-ai/score-jobs/:jobId/cancel

8.3 评分与版本

GET  /api/listing-ai/products/:productId/scores/latest?workspaceId=&rubricVersion=
GET  /api/listing-ai/products/:productId/versions?workspaceId=&limit=&cursor=
POST /api/listing-ai/products/:productId/versions
GET  /api/listing-ai/versions/:versionId?workspaceId=
POST /api/listing-ai/versions/:versionId/adopt

创建版本 body 必须包含 baseSourceHashbaseScoreResultId,若源商品已变化则返回 409 listing_source_changed,防止采纳过期建议。

8.4 错误码与 HTTP

HTTP error 场景
400 invalid_request Zod 校验失败
401 unauthenticated 未登录/会话失效
403 workspace_forbidden 无工作区权限
404 listing_product_not_found / score_job_not_found 资源不存在
409 listing_source_changed / score_job_not_retryable / idempotency_conflict 状态或幂等冲突
422 listing_source_incomplete 明确请求单品评分但必要源数据缺失
429 listing_ai_budget_exceeded AI 并发/预算限制
502 jd_upstream_error / ai_upstream_error 上游明确失败
503 listing_storage_unavailable 目标存储不可用

列表中的单商品失败不得让整批 GET 500;它应成为 item 的终态和脱敏错误码。

9. 前端设计与文件变更

9.1 新增文件

src/modules/listing-ai/
  listing-ai.routes.ts
  listing-ai.shared.scss
  models/listing-ai.models.ts
  services/listing-ai-api.service.ts
  services/listing-ai-api.service.spec.ts
  services/listing-ai-workbench.store.ts
  pages/workbench/listing-ai-workbench.component.{ts,html}
  pages/product/listing-ai-product.component.{ts,html}
  pages/versions/listing-version-list.component.{ts,html}
  pages/tasks/listing-score-job-list.component.{ts,html}
  pages/task-detail/listing-score-job-detail.component.{ts,html}
  components/listing-mobile-preview/*
  components/listing-score-summary/*
  components/listing-dimension-panel/*

source evidence 已合并到统一维度面板,version diff 已合并到版本页;页面共享样式集中在 listing-ai.shared.scss。API service 单测覆盖参数与写请求,页面/API 流程由后端 route 集成测试、全量 Angular suite 和 production build 共同验收。

新组件使用 standalone + OnPush。状态用 Angular signals/RxJS 组合的小型 feature store;MVP 不为了单模块引入 NgRx。

9.2 修改文件

文件 修改内容
src/app/app.routes.ts lazy load listing-ai 子路由,沿用 protectedRoutes
src/app/core/config/runtime-config.ts 增加 listingAiPath,默认 /api/listing-ai
src/modules/shared/components/navigation/navigation.component.ts 在“行动”或独立组中增加 Listing AI 工作台、版本、任务入口
src/app/core/services/index.ts 若该项目继续使用 barrel,则导出新 API service;否则不改
proxy.conf.json 只有现有 /api 代理不能覆盖时才改;优先不改

9.3 组件复用

必须优先复用 COMPONENT-INDEX.md 中已有的:page-headerpage-toolbarsummary-metric-carddata-table-shellcontent-cardboard-tabsentity-property-list、loading/empty/error、neu-buttonsplit-panelproduct-identity

不引入 Angular Material、ng-zorro、Tailwind 或第二套图标库。若现有组件缺能力,先用向后兼容的 Input/slot 扩展,并给现有用例回归测试。

10. 后端文件变更

10.1 新增

  • 上文 src/modules/listing-ai/**
  • migrations/007_listing_ai.sql
  • test/listing-ai.routes.test.ts
  • test/listing-ai.rule-engine.test.ts
  • test/jd-sp-client.signature.test.ts
  • scripts/sync-jd-listings.ts(全量/显式同步,支持 checkpoint/resume,不在启动时偷偷同步);
  • scripts/score-listings.tsscripts/verify-listing-rollout.ts

worker 的冻结 scope、执行、重试、恢复和 stale 行为由 listing-ai.routes.test.ts 以真实 service/repository 流程覆盖,没有创建只重复同一流程的空壳 listing-ai.worker.test.ts

10.2 修改

文件 修改内容
src/config/env.ts 增加 JD/目标 Parse/worker 并发/预算配置;按 storage/worker 条件校验
src/app.ts 注入 listing repositories/service,挂载 /api/listing-ai
src/local-app.ts 注入 local demo repository,保证本地启动不因无 JD 凭据崩溃
src/server.tssrc/local-server.ts 注入正确 storage repository;评分任务由 service 启动恢复,进程不执行隐式 JD 全量同步
src/db/migrations.ts 只有 readiness 表清单为硬编码时才加入新表检查
.env.example 只增加变量名和占位说明,不填真实值
package.json 增加 sync:jd-listings / score:jd-listings / verify:listing-rollout 显式脚本

10.3 删除

第一阶段不删除任何现有文件、route、表或 Parse classtools/jd-import-products.mjs 在服务端同步达到字段、幂等和故障恢复对等后才能标记 deprecated;再经过一个发布周期才讨论删除。

11. 承重墙:禁止直接触碰

  1. 认证与权限:不绕过 /api 认证中间件、WorkspaceAccessService 和权限名;生产不能启用 disabled auth。
  2. 身份键:不把 JD productId 当 number,不改变 jd:${shopId}:${productId} 兼容业务键,不用 legacy asin 取代它。
  3. 数据语义:不把 Listing 文本当用户 VOC;不把 missing/null 变成 0;不把库存推导为销量。
  4. 密钥边界:AppSecret、MasterKey、access/refresh token 不进 Angular、assets、URL、localStorage、日志或 API response。
  5. 现有生命周期:不扩大/修改现有 analysis_run enum 来承载 Listing 批任务;不破坏 insight -> decision -> action -> validation 链路。
  6. UI 体系:不引入新框架,不改 AI 视觉报告全局样式块,不用页面私有 CSS 复制已有组件。
  7. 源数据:保留 raw/normalized 分层和 sourceHash;优化版本不覆盖源快照。
  8. 外部写操作:MVP 禁止 JD 发布、上下架、改价、改库存。
  9. 路由兼容:只增加 child routes,不改已有 URL 的含义或重定向。
  10. 上游判活:不以 Parse base root 404 或异常 count 作为失败真值;必须探测 class 资源。

12. 实施顺序与 PR 边界

PR-0:已完成的连接修复

  • .gitignore
  • getProduct path 签名;
  • itemStatus schema 归一化;
  • JD/Parse 诊断脚本与复测文档。

PR-1:后端源目录与全量同步

  • 服务端 token/sign/client;
  • 全分页、详情并发、checkpoint、幂等;
  • source snapshot repository/API;
  • 20 商品 shadow run,再执行全量目录同步;
  • 不含 AI、不含页面。

PR-2:版本化规则引擎

  • v1 rubric、五维规则、coverage、证据;
  • 20 个样本由产品/运营人工复核并冻结阈值;
  • 单商品评分 API;
  • 不含批量 AI。

PR-3:批任务与 AI 建议

  • job/item/result/version 表;
  • worker、幂等、恢复、预算、Zod 输出校验;
  • 先跑 20,再跑 100,满足门禁后才跑当前全量;
  • 记录成本和耗时基线后再定义 SLA。

PR-4:Angular 工作台

  • 先接真实 API 合约和错误态;
  • 工作台 -> 商品页 -> 任务页 -> 版本页;
  • 不用 mock 数量覆盖真实 summary;
  • 做 keyboard、窄屏、空态、partial 状态回归。

PR-5:全量验收与灰度

  • 新建全量 job,冻结当时实际商品清单;
  • 运维监控、失败重试、重启恢复、安全扫描;
  • 只开放给指定 workspace/role;
  • 通过 go/no-go 后再扩大访问。

13. 测试与成功判定

13.1 连接与同步

  • 签名单测固定验证:path productId 在签名串字段中但不在 query 中;
  • 同一真实商品 getProduct HTTP 200,旧变体稳定复现 99904000013
  • 遍历所有页,页面汇总等于本次同步返回的动态 total;
  • productId 去重后无重复、无空 ID;
  • 每个冻结 productId 有 available|empty|failed 明确详情状态;
  • 相同源数据重复同步,业务记录数不增长且 sourceHash 不变;
  • 进程中断后从 checkpoint 恢复,不从头制造重复;
  • 精确 403/code119 有限重试,永久 403 不被掩盖;
  • readiness 请求 class 资源,不请求 base root。

13.2 规则与 AI

  • 相同 sourceHash + rubricVersion 重跑得到完全相同 numeric score/evidence;
  • 必需字段缺失时 score=null/blocked,而不是 0;
  • 每个扣分项能定位 ruleId 和 JD/DTO fieldPath;
  • AI 超时、429、5xx、坏 JSON 时规则分仍保存,item 为 partial;
  • AI 建议不修改规则分,不覆盖源快照;
  • sourceHash 变化后旧版本明确标为 stale;
  • 20 商品人工复核通过后才能冻结 rubric v1;
  • 20 -> 100 -> 全量规则评分逐级放量,每级统计错误、重试与耗时;AI 全量成本仍受预算门禁。

13.3 API

  • 所有 endpoint 有 Zod 输入校验和 workspace RBAC;
  • 同一 Idempotency-Key + 同一 body 返回同一 job;同 key 不同 body 返回 409;
  • cursor 不重复、不漏项,limit 最大 100;
  • cancel/retry 只允许合法状态;
  • 任务重启后总量、terminal 数与 item 明细一致;
  • response 不含 raw token、MasterKey、AppSecret、完整 raw JD payload;
  • audit log 包含发起、重试、取消、版本创建和采纳。

13.4 前端

  • UI 商品总数完全来自 API,代码与模板中不存在 614/821/824;
  • 首屏只拉 summary + 当前页,不把全量商品载入浏览器;
  • filter/sort/page 状态进入 URL,刷新可恢复;
  • loading、empty、error、partial、blocked、stale 均有不同展示;
  • polling 在终态/离开页面时停止,无重复订阅;
  • 单商品页五个 tab 的证据、建议、版本引用同一 sourceHash;
  • “采用”只改变内部版本状态,浏览器网络中没有 JD 写请求;
  • 京东以外平台不可发起任务;
  • ng build、Listing API service 单测和后端关键路由集成测试通过。

13.5 安全与发布门禁

  • git check-ignore docs/JD-CONNECTION-CONFIG.local.md 命中;
  • Git 历史/工作树、dist、source map、浏览器请求和日志扫描无真实凭据;
  • HTML 详情经过 allowlist sanitizer,无 script/event handler/javascript URL;
  • 图片只通过安全 URL 策略展示,失败不执行任意协议;
  • 生产 API_AUTH_MODE=parse,跨 workspace 访问用例返回 403;
  • worker concurrency、retry、timeout 均有上限;AI 通过显式开关且未获预算批准时不执行;
  • 824(或当时动态总数)个冻结 item 最终全部进入 terminal 状态,无 orphan;
  • completed 仅表示全部成功;混合结果必须是 partial
  • 未完成性能基线前不承诺“几分钟完成”。在 20/100 商品实测后,将吞吐、P95 和成本阈值补入 rubric/release 文档。

14. 风险、待确认项与决策责任

风险/问题 当前处理 谁确认/何时阻塞
Parse 间歇 403 精确错误有限重试 + 指标告警 连续失败超过重试预算时阻塞全量
Parse count 不可靠 使用同步摘要和冻结 ID 清单 后端实现时验证
JD 限流/分页边界未知 3 并发起步,20/100 shadow run 全量前必须有实测
核心卖点真实字段口径 先以 features/服务/规格归一化 产品与运营冻结 rubric 前确认
类目禁限词/必填属性缺数据源 v1 标 unknown,不擅自扣分 业务提供规则数据后进入 v2
图片视觉质量 MVP 不评分视觉语义 多模态网关接入后单独设计
AI 成本和速率 分级放量、预算门禁 全量任务前批准预算
HTML XSS/外链图片 服务端清洗 + URL 策略 安全测试不通过则阻塞上线
商品同步期间变化 job 冻结 productId+sourceHash 变化商品标 stale,另建任务

15. 回滚策略

  • 新路由可由 feature flag/导航权限隐藏;回滚前端不删除后端结果。
  • worker 可停止领取新任务;单实例重启会扫描并恢复持久化的 queued/running 任务。多实例 lease 是正式横向扩容前的门禁,见实施报告。
  • 新表/Parse class 为旁路新增,不修改旧 VOC 表;回滚应用版本无需 drop table。
  • rubric v1 不原地改;错误规则发布 v2 并重算,保留旧结果审计。
  • 外部 ProductDetail 兼容镜像在新 source repository 稳定一个发布周期前不移除。

16. 开发开始前的 Go / No-Go 清单

只有以下条件同时满足才开始 PR-1,而不是直接画页面:

  • 本机凭据文件已忽略;
  • getProduct 400 的真实错误码、错误消息和签名字段已记录;
  • getProduct 修复后返回真实详情;
  • 目标 Parse class 路由读写成功,root 404 语义澄清;
  • itemStatus schema mismatch 已修复;
  • 前后端边界、API DTO、表/类、路由和文件清单已写入本文;
  • 承重墙、验收点、风险和回滚已写入本文;
  • 产品/运营确认 v1 五维权重、标题阈值、卖点口径;
  • Postgres + parse_rest 两驱动均已实现;
  • 目标 Parse 间歇 403 使用仅针对 119 的有限退避重试;
  • AI 负责人确认 20/100/全量预算与并发。

前三个未决责任项可以与 PR-1 的纯同步工程并行,但在 PR-2 rubric 冻结和 PR-3 全量 AI 任务前必须关闭。