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

5.5 KiB
Raw Blame History

OneTalk DOM 二次卡片采集重构设计

目标与边界

二次采集以 DOM-only reader 替代 React/Fiber reader,并独立保存不带 kind 的命名补全字段。它不判断也不覆盖基础消息 orderinquiryproduct 或其它 kind;补全可先于基础消息保存,后续读取或实时发布再由完整复合 identity 关联。

renderedCard 是 exact-shape、versioned 公共值,至少包含一个已批准字段,但绝不含 kind、模板 ID、DOM path、class、data-expinfo、HTML、style、React props、URL query/fragment 或动作参数:

type OneTalkRenderedCard = {
    version: 1;
    title?: string;
    image?: string;
    price?: string;
    minimumOrder?: string;
    purchaseQuantity?: string;
    requirementText?: string;
    reference?: string;
    productCount?: { display: string; value: number | null };
    status?: string;
    total?: string;
    deliveryDate?: string;
    shippingAddress?: string;
};

字段是可选的,因不同模板本来显示不同内容;空对象、未知键、无安全字段或不符合模板映射的卡 fail closed。图片仍须为 approved host、HTTPS、无 query/fragment 的 URL。

数据流与所有权

DOM card + selected conversation
  -> structure-based field reader in MAIN
  -> generic named renderedCard observation
  -> page bridge -> SW ledger -> rendered.card.observed -> server JSONB
  -> base-message read/create/update by composite identity
  -> OneTalkCenterMessage.renderedCard

MAIN 是 DOM 结构和可见节点读取的唯一 ownershared contract 拥有 OneTalkRenderedCard exact shape、URL/text guards、fingerprint/clone/equalityserver repository 拥有独立事实的 first-content-wins/conflictread projection 是唯一将 base message 与 generic supplement 组合为 Mind 输出的 owner。

严格 identity

读取前后必须是同一 single selected conversation。卡和最近 wrapper 的 data-expinfo 都必须解码,messageId 必须合法且相等;任一已出现 cid/conversationCode/externalConversationId 为空、非法或冲突即拒绝。卡 metadata 缺 conversation ID 时,只能用读前后一致的 selected conversation 补足。任何 cardType/messageType 只可作为诊断与模板采样证据,永不进入 payload 或作为业务类型。

结构化命名字段

模板 ID 和路径仅可用于运行时调试或 fixture 采样,绝不能作为采集准入、业务类型或字段映射的门槛。reader 以恰好一个可见文本锚点选择内部字段解析器:订单=订单状态,询盘=详细需求,商品=最小订购量/Minimum order;任一类别锚点重复、零个或多个类别同时命中时 fail closed。该分发不产生或传输业务 kind。所有卡只从已验证图片 owner 取第一张安全图片为单值 image。询盘从可见 .description-container 读取保留换行的 requirementText,并将商品、采购数量与 ID 分别投影为 title/imagepurchaseQuantityreference。订单读取可见“订单”为 title5 products 这类直接数量展示从同一局部容器的前置兄弟图片区取代表图,绝不扫描整卡而误取顶部箭头图标;再映射已审核的 productCountstatustotaldeliveryDateshippingAddress。其它未识别行省略。商品在同一 .dx-event-node owner 内以三段 normalized MOQ label/value/unit 锚定,读取其所属可见 TextView 序列中的价格、标题和安全图片;MOQ 公开为原样组合字符串(如 500 Pieces)。动作、促销/服务标签与数组字段不投影。

读取每个 DOM text/image 前都排除 templatehiddenaria-hidden=truedisplay:nonevisibility:hidden 祖先。无法识别或不安全的单个字段省略;没有任何可投影字段才 fail closed。图片仍须为安全 URL,且不安全可选图片组不能阻断已验证的文本字段。

Contract、数据库与公开组合

OneTalkRenderedCardContent 保留 legacy rendered_inquiry/product/order 仅为已存数据的读取兼容,新生产 reader 只写无 kind 的 OneTalkRenderedCard。页面 bridge 解码、wire 与 ledger 的 observation content 只允许该 generic objecttyped 历史内容不可重发。OneTalkCenterMessage.renderedCard?: OneTalkRenderedCard 同样无 kind,基础 content 原样保留。

repository 继续独立 insert/duplicate/conflict;基础消息先到或补全先到均按 [channelAccountId, conversationId, messageId] 关联。legacy typed supplement 保留旧 public projection;新 generic supplement 只附加顶层 renderedCard。本次不把 send.result 扩展为补全 transport。

数据库 migration 保留 0015 的四列删除,替换 0016 中的 temporary rendered_dom_card check:兼容 legacy typed kinds,或允许无 kindversion=1 的 generic object。迁移不得 DELETE、TRUNCATE、回填或重建表。

验证与回滚

fixture 必须覆盖未知模板 ID 仍可按已识别 DOM 结构采集、无模板 metadata 与 view-name="TextView" 的订单/商品结构、空结构拒绝、hidden nodes、identity 非法/冲突、unsafe images、DOM mutation 重读、generic supplement/base 先后顺序、legacy read compatibility 与 exact Center decoder。真实 Chrome 仅做无点击/无刷新验证,且须加载本次构建才能宣称 bridge/ACK/DB runtime 通过。

generic rows 出现后,回滚 schema check 前由用户在任务外处理 generic rows;本任务不自动删除 supplement。