Files
trade-message-center/.trellis/tasks/09-16-onetalk-dom-secondary-card-collection/prd.md
T

11 KiB
Raw Blame History

OneTalk DOM 二次卡片采集重构

Goal

删除现有 OneTalk 二次卡片采集实现,以已渲染 DOM 为唯一生产源,稳定采集所有具严格消息归属的卡片可公开字段,并沿既有受控 bridge、ledger、Bright supplement 入库链路独立发送。二次数据不决定业务类型;Mind 在读取或发布基础消息时按同一复合身份关联这些字段,基础 kind 保持为第一次采集的事实。

Confirmed facts

  • 用户已在真实 OneTalk 页面只读展示四类卡。每一类都可从 .session-rich-content.card 定位最近 .message-item-wrapper;卡内首个 [data-expinfo] 与 wrapper 的 data-expinfo 都有相同的 messageId,未观察到冲突或缺失。
  • 历史样本曾显示询盘、商品、订单和报价;最新真实只读样本证明 card metadata 不稳定:报价样本有 cardType=8,用户指定订单卡没有 cardType,只呈现 messageType=1 和通用 TextView 模板。卡内与 wrapper 的 message ID 一致,但当前样本两边都没有会话 ID。
  • TradeBridge 的 oneTalkProductCardFromElement() 已验证可复用的消息归属模式:从卡内和 wrapper 读取 data-expinfo,消息或会话 ID 冲突时丢弃。其商品 URL gate 仅适合商品字段投影,不能用于询盘、订单或报价。
  • 当前仓库仍有旧的 rendered-card reader task;本任务以用户最新确认的 DOM-only 范围为准,不能重新引入 React/Fiber root traversal、固定扫描预算、MutationObserver/timer fallback 或依赖原始 React props 的采集。
  • 旧 MAIN observer 依赖 react-card-reader.ts 从 Fiber 提取卡片字段以及 directionsentAtMs base evidencepage bridge 与 Service Worker ledger 当前强制这些 evidence 与 observation 一一对应。服务端 rendered.card.observed wire 和独立 supplement repository 并不携带它们,且补全的 durable-first commit 不等待基础消息 ACK。
  • 当前数据库 onetalk_rendered_card_content 以复合消息身份存储补全 JSONB;旧 kind check 仅接受 typed supplement,须兼容扩展为新的 type-agnostic snapshot。
  • onetalk_rendered_card_content 的复合主键(账号、会话、消息)、rendered_card_contentrendered_card_content_fingerprintrendered_card_observed_at_ms 是本任务保留的持久化形状。页面观察时间和指纹服务于重传匹配/幂等;它们不出现在 Mind-facing content。
  • 用户确认保留 conflict_count,删除 first_confirmed_atlast_observed_atlast_conflicting_fingerprintlast_conflict_observed_at_msconflict_count 在同一复合身份收到不同内容快照时递增;删除 last_conflicting_* 后仍保留首内容不可覆盖、conflict ACK 和总冲突计数,但不再持久化最近冲突的指纹或时间。
  • 用户将自行删除历史 supplement。本任务不包含历史数据删除、重算或迁移回填;schema migration 不得执行 DELETETRUNCATE 或重建表。
  • 用户确认二次采集数据完全脱离类型:它可以在基础消息尚未入账时独立持久化。后续读取或发布基础消息时,按复合 identity 关联已存的二次快照;第一次采集的基础 kind 是唯一类型事实源。
  • 用户确认二次补全 JSON 不得有 kindrendered_dom_card 与裸 texts[]/imageUrls[] 都不适合作为公开业务数据。二次数据使用单值 image,不允许 images/productImages 数组;询盘采购数量必须是 purchaseQuantity 字符串,不允许 attributes;商品 MOQ 必须是原样组合字符串 minimumOrder,不允许对象;不允许 promotionTexts。商品标题只可在 MOQ 锚定的同一 owner 内相对已识别价格读取。
  • 已证实的 OneTalk 实时样本只有通用 dx-card-wrappertpl-wrapperTextView 等结构;当前没有 title/price 等稳定 class 或 attribute 的运行时证据。新增命名字段前必须对每种真实模板采集并审核其 selector/class/path 证据;无稳定证据时不能把任意 TextView 或第 N 个文本猜成 title。
  • 商品卡的运行时 root-relative 证据已校正:data-tpl-id=4305043349523tpl-wrapper root 下,title=0.1.2.0.0、price=0.1.3.1.0.0.0、minimumOrder=0.1.4.0.0/0.1.4.1.0/0.1.4.2.0、promotion=0.1.5.1.0、images=0.1.0.0.0.0/0.1.1.0.0.0/0.1.5.0.0.0。此前采样路径按 card 根记录,传给 tpl root 时少了首层 0,会 fail closed;路径只用于 MAIN DOM reader 内部选择,不能持久化或跨 bridge。
  • 用户切换的询盘卡提供第二份模板证据:data-tpl-id=3593828979407.list-item 的 item image/text 子树位于 2.0.0.02.0.0.1.description-container 是需求正文的稳定 DOM owner,位于 3.0.3.0.0;两个 .dx-event-node 操作位于 4.04.1。其它 TextView 字段的语义尚未从脱敏结构确定,不能按路径猜为 title、数量或询盘号。
  • 询盘卡 root-relative 映射为:title=0.2.0.0.1.0.0、item image owner=0.2.0.0、purchaseQuantity=0.3.0.0、requirementText=0.3.0.3.0.0、reference=0.3.0.6.1.0。该模板只在可见 详细需求 表明询盘结构时,输出无 kind{ title?, image?, purchaseQuantity?, requirementText?, reference? };不上传静态“询盘”、详细需求采购数量ID:等 UI label 或操作参数。
  • 订单卡 root-relative 映射为:data-tpl-id=3620167990752title=0.0.0.0(可见“订单”)、5 products 数量展示的同级前置图片 owner(不含顶部订单图标)、productCount=0.0.2.1.1.0.0status/total/deliveryDate/shippingAddress=0.0.3.0.1.0/0.0.3.2.1.0/0.0.3.5.1.0/0.0.3.8.1.0。该模板输出无 kind{ title?, image?, productCount?, status?, total?, deliveryDate?, shippingAddress? },不上传未识别 label/value 行或动作参数。

Requirements

R1. Hard-delete legacy collection production path

  • 删除被 DOM reader 取代的旧二次采集生产路径、其专属分类/读取逻辑及不再可达的诊断、测试辅助和配置;不得保留双写、运行时 feature flag 或静默 fallback。
  • 不改变基础消息采集、历史同步、授权、bridge envelope、ledger ACK、补全独立提交、冲突和发布语义,除非当前代码证据证明 DOM 采集契约必须做兼容变更。

R2. Shared strict DOM identity and type-agnostic projection

  • .session-rich-content.card 的卡内 [data-expinfo] 和最近 .message-item-wrapper[data-expinfo] 读取消息归属。两个位置都存在时,规范化后的 messageId 必须相等;会话 ID 同时存在时也必须相等;缺失、无效或冲突时不得发送。
  • 所有具合法 identity 的卡共享上述归属解析器。二次采集投影为无 kind 的 type-agnostic named card fields:键只能来自已验证的 DOM selector/class/template 映射,值是受限文本、数量、展示价格、促销文本或安全图片;不含 data-expinfo 原文、样式、HTML、React props、URL query/fragment、动作参数或业务分类。
  • 二次采集不得从卡自身 cardTypemessageType 或模板 ID 推断或输出 orderinquiryproduct 或任何其它 kind;reader 仅可由单个可见锚点内部选择字段解析器:订单状态详细需求最小订购量/Minimum order。零个或多个锚点同时命中都不发送,合法 identity 即可独立写 ledger 和发送 frame,基础消息尚未入账不是门槛。
  • 新内容走当前 rendered.card.observed 发送与 ACK 链路,且每次 observation 按当前复合消息身份和内容指纹保持幂等。
  • 为完成旧采集硬删除,移除只为 Fiber 提供的 MAIN baseEvidence 依赖;DOM reader 不得从 CSS 方向、页面账户、原始 metadata 或其他猜测来源伪造 direction / sentAtMs。该字段不进入 server wire,且不能成为独立 supplement 发送门槛。

R3. Persist independent snapshots and compose them with base messages

  • 共享 contract、数据库补全 JSON 和 Mind-facing OneTalkCenterMessage 增加同一个可选 renderedCard 字段对象;它没有 kind,使用命名字段而不是裸文本/图片列表,位于基础消息旁,绝不覆盖基础 content 的 kind 或字段。
  • 服务器按复合 identity 独立保存/重传/ACK 快照;当 supplement 或基础消息任一方先到达时,之后的基础消息读取与 message.created/message.updated 投影都组合 renderedCard。不需要基础消息 ACK 或本地 first-candidate 才能提交二次数据。
  • schema migration 删除已批准四列,并将既有 JSONB kind check 兼容扩展为 legacy typed kinds 与无 kind 的 version-1 named supplement。新生产路径只生成无 kind 的 type-agnostic object;迁移不得执行 DELETETRUNCATE 或表重建。

R4. Regression and verification

  • 为共享 identity resolver、通用 DOM snapshot、身份冲突/缺失、supplement/基础消息双向先后顺序和公开组合建立回归测试;证明旧采集不再可运行。
  • 按扩展、contract、server 的变更范围运行定向测试、类型检查与构建;对真实 Chrome 只做无点击、无刷新验证,并将运行时证据与本地/数据库验证分开报告。

Out of scope

  • 不操作 OneTalk 页面,不发送消息,不主动同步,也不读取或输出凭据、原始 data-expinfo、完整 URL query 或用户隐私内容。
  • 不修改基础消息身份、历史 cursor、授权或正常文本/图片/文件消息采集。
  • 未经确认不删除或重写已经持久化的 rendered-card supplement 历史数据。

Acceptance criteria

  • 旧二次采集生产路径已被删除,运行时只有 DOM-only 路径能创建 rendered-card observation,且没有双写或 fallback。
  • 所有具严格 identity 的卡均通过同一个 resolver 创建 type-agnostic observation;任何 card type、message type、模板 ID 或文本位置均不参与业务类型判断;identity 冲突、缺失或无效都不发送。
  • 二次数据是经证据验证且不含 kind 的命名 DOM 字段,包含 title 等字段时必有对应 selector/class/template 证据与回归;不存在裸文本数组、raw DOM metadata、React props、敏感 URL query 或动作参数。
  • 每张已发送卡仅命中订单、询盘、商品三个解析器中的一个;订单、询盘、商品各自由 订单状态详细需求最小订购量/Minimum order 单独锚定,绝不以多个字段组合猜测类别。
  • 从独立 supplement 到数据库、基础消息读取与实时发布的关联有测试覆盖:补全先到与基础消息先到都得到同一命名 renderedCard;基础内容 kind 保持为第一次采集的事实,二次数据不携带或覆盖 business kind。
  • 相关测试、类型检查和构建通过;真实 Chrome 只读验证与数据库写入/ACK 验证结论分别记录。

Decisions

  • 二次数据完全 type-agnostic;所有合法 identity 的已渲染卡均可独立记录,无法安全投影的 DOM 值逐字段拒绝而非猜测。
  • 保留 rendered_card_content_fingerprintrendered_card_observed_at_msconflict_count;删除 first_confirmed_atlast_observed_atlast_conflicting_fingerprintlast_conflict_observed_at_ms
  • 历史 supplement 由用户在任务外自行清理;本任务不执行历史数据操作。