Files
lingxi/docs/评估/_调研/dewu-调研报告.md
T
Stev_Wang 6096568621 docs: 新增双项目整合评估、调研报告与技术栈架构定稿
- 新增双项目整合可行性评估 V0.1 rev.2:覆盖度矩阵、方案对比、目标架构、工作量/TCO、风险与决策清单
- 新增 dewu/sucaigongfang 两份调研报告(内部资料,平台敏感值已脱敏)
- 新增技术栈与架构定稿 V1.0:Node 24.15.0 / pnpm 11.4.0 / Prisma 7.10.1 / PostgreSQL 18 / Redis 7
- 更新 README 文档索引与仓库结构
2026-10-30 14:13:00 +08:00

727 lines
83 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# dewu-material-Downloader 调研报告
> **调研对象**:`参考项目/dewu-material-Downloader/`(应用名「得物素材助手」,v2.7.0,git HEAD `fe7646b`)。
> **密级**:内部;文中平台接口/签名等敏感信息已脱敏,原文见受控知识库。
> **调研方式**:只读通读源码与文档,交叉核对 git 历史、CI 配置与构建脚本;**未安装依赖、未运行构建、未修改参考项目**。凡文档与代码冲突处,以代码为准,并在文中注明文档已过时的位置。
> **路径简写**:下文 `dw/` = `参考项目/dewu-material-Downloader/`;`dw:line` 均指该仓库内文件行号。
> **数字口径**:LOC 用 `find <dir> -name '*.ts' -o -name '*.tsx'`(scripts 为 `*.mjs`/`*.cjs`)+ `wc -l`,口径在 §7.1 写明;文件数不含 `node_modules/`、`dist/`、`dist-electron/`、`release/`、`.cache/`、`.probe-material-page/`、`review/` 等产物或本地取证目录。
> **调研时间**:2026-10-30。
---
## 0. 结论速览
- **一句话定位**:这不是一个简单的「下载器」,而是得物「星光平台」软广素材的**桌面端运营工作台**——已从 v1 的「登录→抓素材→下视频」演进为「素材下载 + 商品查询 + 素材审核查询 + 批量送审/混剪送审 + DPLink 一键复制」五段闭环。
- **端形态**:Electron 44 单窗口桌面应用(Windows 为主力历史平台、macOS 为当前开发/验证机;Linux 有配置但未验证);`contextIsolation: true` + preload 白名单;React 19 + Semi Design 2 + Tailwind 3 + Playwright(`dw/package.json:3`、`dw/electron/main.ts:1`)。
- **成熟度**:v2.7.0;git 记录 **228 个提交 / 21 个 tag / 2026-05-23 → 2026-10-22**(约 5 个月;最后 tag 为 v2.4.0,2.5.0–2.7.0 未打 tag,仅有 release 提交);功能覆盖深、但**真机验证不均**(送审非混剪已端到端成功,混剪「是」分支未真机;素材审核页仍挂 BETA)。
- **质量基线**:TypeScript `strict` 全开、`tsc --noEmit` 是 CI 第一道门禁;**无单一测框架**(无 vitest/jest),但有 **32 个验证脚本、15,411 行、CI 三个 job**(`verify:ui` v2.7.0 记录 480 条通过、`verify:platform` 260 条通过;脚本内 `ok()` 断言点分别为 472/255);无 ESLint/Prettier 配置。
- **体量**:`src/` 19,120 行(67 个 ts/tsx)、`electron/` 17,345 行(45 个 ts)、`scripts/` 27,828 行(73 个 mjs/cjs)、`release-scripts/` 875 行(mjs/cjs,不含 .sh/README 的 622 行),合计 **65,168 行**;另有 CSS 3,675 行、docs 30 篇 Markdown 14,633 行。
- **最大的整合事实**:它只覆盖 **得物/星光一个平台**(全仓检索不到唯品会/vipshop 代码),且只覆盖 PRD 的 **P-01/P-04 与 P-05 的一部分**;**P-02 图片模板、P-03 视频 AI、P-06 多平台、P-07 资产管理完全不含**。新平台这两条产线必须另起,不能指望从本项目迁移。
- **Web 化的核心结论**:**「读」链路可以做到无浏览器**(签名 + 只读接口已验证,纯 Node 可复现);**「写」链路(送审)必须保留服务端浏览器自动化**(平台上传走页面第一方 SDK + `duToken` 网关,重放成本高且易踩风控);**Electron 外壳能力(内嵌 webview、本地文件路径、FTP Cookie 同步、safeStorage、自动更新、代码签名)全部重写或废弃**。
### 0.1 结论表(能力域 → Web 化结论)
| 能力域 | Web 化结论 | 关键原因 | 粗估人日 |
|---|---|---|---|
| 登录态(接口验证码登录 / 浏览器兜底 / Cookie 同步) | **服务端重写** | 单账号单会话;凭据是本机 `safeStorage` 加密文件 + FTP 静态共享;服务端需多用户账号归属与 KMS 加密 | 8–12 |
| 素材读取(软广素材库列表/详情、审核列表、枚举) | **改造复用** | 只读接口 + MD5 签名 + 信封解析已跑通,纯 Node 可复现;去掉 Electron 直接作为服务端模块 | 5–8 |
| 原始物料下载(含任务工作台 UI + 视频 CDN 直连 + 商品图/主视频) | **服务端重写** | 浏览器只用于「登录/兜底」;下载、MD5 去重、分批冷却可保留策略,但文件落盘改为对象存储/下载中心;UI 需按新设计系统重做 | 25–38 |
| 商品查询(SPU 解析 + 商品详情 + 素材 9 项) | **改造复用** | 商品详情是 SSR HTML 直取,不需要浏览器;链接短链解析可服务端做 | 12–18(含 UI) |
| 内嵌商品 H5 画布 + 净化模式 | **废弃** | 依赖 Electron `<webview>` + 移动端 UA + 持久分区;Web 端受 iframe/X-Frame-Options 限制,且价值低于「数据直取」 | 0(可选 3–5) |
| 素材审核页(列表/筛选/驳回原因/上传记录/CSV) | **改造复用** | 纯只读接口 + 平台枚举,无 Electron 依赖;UI 需按新设计系统重做 | 8–12 |
| 送审判定 / 命名解析 / 预检规则 | **直接复用** | 已是应用与门禁共用的纯逻辑(`scripts/lib/submit-naming.mjs` 等),零 Electron 依赖 | 3–5 |
| 批量送审(页面自动化 + 上传 + 幂等 + 回查 + 账本/审计) | **服务端重写** | 浏览器填表 + `setInputFiles` + 观察响应;服务端需浏览器池、大文件上传通道、会话串行;策略层可复用 | 26–37 |
| 混剪送审(双栏配对 + 源检索 + N 组提交) | **服务端重写(UI 改造复用)** | 纯逻辑已抽到 `scripts/lib/mix-submit.mjs`;「混剪=是」写链路尚未真机验证 | 14–22 |
| DPLink 一键复制 | **改造复用** | 纯算法 4/4 样本逐字符复现;仅两个只读接口 + 硬广凭据 | 5–8 |
| 应用设置 / 远端配置 / 日志 | **改造复用** | 配置分层、档位预设、白名单和签名机制可迁移;用户配置改服务端 DB | 7–10 |
| 自动更新 / 关于页 / 旧版本清理 / 窗口状态 | **废弃** | 桌面应用专属(Squirrel/ShipIt、`legacy-bundle`、BrowserWindow maximize) | 0–1 |
| 验证门禁与纯逻辑资产 | **改造复用** | 21 个 `scripts/lib/*.mjs` + fixtures + 32 个 verify 脚本,可直接转服务端单测/契约测试 | 6–10 |
> 人日为「分析员粗估、供排期参考」,含联调与验收、不含图片/视频 AI 产线;合计约 **115–190 人日**(逐项见 §8)。
---
## 1. 项目定位与业务链路
### 1.1 解决什么问题、给谁用
**业务背景**:得物「星光平台」是软广/KOL 投放的素材后台,素材库、审核、送审都在内网/H5 体系里,**没有面向内部的开放 API**,也没有批量下载能力。运营与剪辑的日常是:
1. 在软广视频库里逐条点「预览」,复制视频地址,手工记录 `媒体任务ID / 视频ID / SPUID / 投放类目 / 授权时间`;
2. 手工改名落盘,反复登录网页版导致桌面工具掉线;
3. 剪辑后回到素材池「KOL原生二创素材上传」抽屉,逐条填投放渠道、媒体任务ID、视频ID、SPUID、挂 140–190MB 成片、点提交;
4. 被拒后去审核列表逐条看驳回原因;
5. 投放前再去星光「商品生成链接」页手工选落地页、商品,复制 iOS/Android 唤端链接。
**目标用户**:得物软广素材运营、剪辑、投放优化师(小团队、共享少数星光账号)。产品一直是**内部效率工具**,不是多租户 SaaS——这决定了它大量假设「一台机器、一个人、一个账号」。
### 1.2 主链路(端到端)
```
① 登录星光(接口验证码登录 / 浏览器登录兜底 / 云端 Cookie 同步)
↓
② 选品
A. 素材下载页:输入 SPUID(可多个)→ 软广视频库筛选 → 提取素材
B. 商品查询页:输入 SPUID / 商品链接 / App 分享文本 → 商品详情 + 9 项原始素材
↓
③ 原始物料获取
A. 视频:按「媒体任务ID-视频ID-SPUID-类目-授权起-授权止.mp4」命名 → CDN 直连下载 → MD5 去重
B. 商品图/主视频:按「<下载目录>/商品素材/<spuId>-<货号>-<标题前40字>/」落盘
↓
④ 本地剪辑 / 混剪(应用外,本仓库不覆盖)
↓
⑤ 提审:命名解析 → md5 本地查重 → 预检弹窗 → 页面自动化逐条提交 → 超时只回查不重试
→ 已送审 md5 账本 + JSONL 审计
↓
⑥ 审核查询:审核中/通过/不通过/全部 → 驳回原因、审核人、视频直链、上传记录 xlsx、CSV 导出
↓
⑦ 投放配置:商品查询页复制 iOS UniversalLink / 安卓 DeepLink(DPLink)
```
**与旧版下载器的关键差异**(git 历史可证):v1.x 只有 ①③(登录 + 下载)以及更新/设置;v2.0 加商品查询页;v2.1 收敛设置/远端配置/更新验签;v2.2 升 Electron 44 并落地 mac 签名公证;v2.3 把「读」接口化(P1);v2.4 加素材审核(P2);v2.5 加素材送审(P4)+ 改名 + mac 原生更新;v2.6 加 DPLink;v2.7 改为任务工作台、加混剪送审/素材预览/过滤开关。**它已经是一个流程闭环产品,而不是单点脚本**。
### 1.3 与 PRD 1.1 问题的对应关系
| PRD 编号 | PRD 问题 | 本项目覆盖度 | 证据 / 说明 |
|---|---|---|---|
| P-01 | 原始物料:详情页图片逐张人工下载、分散存放 | **部分覆盖** | 商品查询页支持主图/详情图/主视频一键下载并按商品目录归档(`dw/electron/shop/materials.ts:24-26`);视频素材库支持按 SPUID 批量下载(`dw/electron/platform/materials.ts`)。**只覆盖得物单平台**,且无「本次选品物料库」概念 |
| P-02 | 图片:三规格 × 四层信息人工排版 | **不覆盖** | 全仓无 PSD 解析、无模板合成、无 800×800/1280×720/1080×1920 产出代码 |
| P-03 | 视频:依赖实拍与个人脚本经验 | **不覆盖** | 无脚本生成、无 AI 视频、无 TTS/画面组合 |
| P-04 | 命名提审:手工改名与登记,进度散落 | **覆盖** | 命名规范 + 末三段解析 + 预检弹窗 + 批量送审 + 审核状态跟踪 + md5 幂等 + 审计(`dw/electron/submit/*`、`dw/docs/送审命名规范与预检规则.md`) |
| P-05 | 账户搭建:DP、承接页逐条复制 | **部分覆盖(仅 DPLink)** | 只做「商品 → 投放唤端链接」拼接与复制,**不做 DP 编写、不做承接页字段批量替换**(PRD 中 DP 的具体口径也待业务确认) |
| P-06 | 多平台:资源与口径不统一 | **不覆盖** | 全仓检索 `唯品会/vipshop/vip.com` 无命中;平台地址只有星光 + 得物商品站 |
| P-07 | 资产管理:模板/脚本散落 | **不覆盖** | 无模板/脚本/Logo 资产库;仅有「落地页列表」与「商品库」两个只读数据源 |
> **结论**:本项目是**新平台「选品→原始物料→提审→投放链接」段的现成参考实现**,但不是「图片/视频产线」的参考实现;新平台需要「得物作业端(迁移+增强) + 图片/视频产线(新建) + 多平台抽象(新建)」三块并行。
---
## 2. 功能模块清单(全量)
> 以 `dw/src/App.tsx:240-306` 的侧边栏 6 个入口 + 代码内实际存在的能力为准;「实现状态」按代码与文档的真实完成度标注(完整 / BETA / Phase 1 待真机 / 仅配置 / 未实现)。
| 模块 | 页面/入口 | 核心功能点 | 实现状态 | 关键文件:行 | 依赖 Electron 能力 |
|---|---|---|---|---|---|
| **素材下载** | 侧边栏「素材下载」`/` | SPUID 筛选(多值、中文分隔归一化、填写后显示各 SPUID 素材数);按页数/按数量两种范围;提取「拦截→接口→DOM 兜底」三级路径并显示读取方式;两个下载前过滤开关(30 天内到期 / 未授权快手);素材预览;分批+并发+随机延迟+指数退避+冷却;MD5 去重;任务详情/重试历史;打开下载目录/系统通知/日志导出;新建任务(保留前置条件) | **完整**(v2.7.0 真机可用) | `dw/src/pages/DownloadPage/index.tsx:250`、`dw/electron/browser/manager.ts`、`dw/electron/downloader/manager.ts`、`dw/electron/platform/materials.ts` | 是:Playwright、文件系统、dialog、通知、剪贴板、`shell.openPath` |
| **商品查询** | 侧边栏「商品查询」`/shop` | 输入 SPUID / 商品链接 / App 分享文本;左栏内嵌得物 H5 商品页(移动 UA + 独立持久分区 + 净化模式 + 导航白名单 + 右键菜单);右栏 SSR 直取商品卡片(标题/价格/货号/SKU/主图/详情图/主视频/来源链接);9 项素材格;下载范围「主图+视频 / 全部」;逐文件进度与取消;DPLink 两个复制按钮 | **完整**(2026-10-22 摘除 BETA) | `dw/src/pages/ShopPage/index.tsx:426`、`dw/src/components/SpuCanvas.tsx`、`dw/electron/shop/session.ts:32-66`、`dw/electron/shop/detail.ts:11-27` | 是:`<webview>`、partition、dialog、文件系统 |
| **素材审核** | 侧边栏「素材审核」`/review` | 审核中/通过/不通过/全部 4 Tab + 总数;按素材ID、审核时间、上传时间筛选;列表(32 条/页,与网页版一致);详情弹窗(视频、字段、审核人);驳回原因富文本(白名单 AST,不 `dangerouslySetInnerHTML`);上传/送审记录(含 xlsx 预签名下载);勾选行导出 CSV;权限前置「建议而非硬拦」 | **BETA**(数据来自平台接口,界面仍按反馈调整) | `dw/src/pages/ReviewPage/index.tsx:627`、`dw/src/pages/ReviewPage/ReviewTable.tsx`、`dw/electron/platform/reviews.ts:43-48`、`dw/scripts/lib/review-csv.mjs` | 否(只读 HTTP);CSV 落盘用 dialog |
| **批量送审** | 素材审核页「批量送审 / 选文件夹」→ 预检弹窗 → 进度浮层 | 命名两模式解析(文件夹名恰好三段 / 文件名末三段);SPUID 回显;md5 流式查重 + 已送审账本;批内去重;就地改 ID / 批量补 SPUID / 冲突二选一;逐条开抽屉→选混剪=否→投放渠道→填 3 字段→`setInputFiles`→等上传→提交→观察响应;超时只回查;掉线停批;审计 JSONL | **完整**(非混剪链路 2026-10-16 端到端成功 1 条;离线门禁全绿) | `dw/src/pages/ReviewPage/SubmitPrecheckModal.tsx`、`dw/electron/submit/page.ts:47-110`、`dw/electron/submit/batch.ts:1-40`、`dw/electron/submit/verify.ts` | 是:Playwright、dialog |
| **混剪送审** | 素材审核页「混剪送审」→ 双栏配对弹窗 | 成品文件名只取末段 SPUID;右栏按 SPUID 自动检索平台素材/本机下载库;授权状态(有效/≤30天/已过期/未生效/长期/未知);N 组 `{xtTaskId, videoId}` 配对;提交前校验配对/渠道/SPUID 一致性/组数;页面自动化选「混剪=是」并回读组数 | **Phase 1 已落地,待真机**(「混剪=是」写链路未验证) | `dw/src/pages/ReviewPage/MixSubmitModal.tsx`、`dw/electron/submit/mix.ts`、`dw/electron/platform/source-search.ts`、`dw/scripts/lib/mix-submit.mjs` | 是:Playwright、dialog |
| **送审进度** | 全局浮层 + 页面卡片 | 逐条阶段(填表/上传/提交/回查)、成功/跳过/失败/未知、可中断(当前条跑完即停)、切页不中断、可按 md5 看审计尾部 | **完整** | `dw/src/pages/ReviewPage/SubmitProgressPopover.tsx`、`dw/src/hooks/useSubmitProgress.ts` | 是(经 IPC) |
| **DPLink 一键复制** | 商品查询页「复制 iOS DPLink / 复制安卓 DPLink」+ 设置「投放落地页」 | 拉落地页列表(含 `flag/_gsign/navControl`)、按 SPU 反查商品库 `cSpuId`、拼两层链接(内层整段 encode)、复制到剪贴板;硬广账号独立凭据、云端 `accounts.dplink` 分区、错误分类(未配置/无权限/未选落地页/不在商品库/网络) | **完整**(2.6.0;4 条样本 4/4 逐字符复现;依赖客户共享硬广账号) | `dw/src/pages/ShopPage/index.tsx:695-703`、`dw/electron/dplink/link.ts:39-41`、`dw/electron/dplink/account.ts:1-40` | 剪贴板(主进程 `clipboard`);凭据 safeStorage |
| **登录态** | 侧边栏底部状态、登录弹窗、设置「账号与同步」 | 软广账号:接口发码/登录(不启浏览器)、浏览器登录兜底、无头/有头切换、登录预检(启动检测)、退出(只清本机+忽略云端);硬广账号:独立加密槽 + 独立登录;云端 Cookie 同步 v2(soft/dplink 分区、HTTP 拉、FTP 推、写后回读) | **完整**(接口登录 `verify:login` 9/9 实测;云端同步为遗留架构) | `dw/src/components/SidebarLoginStatus.tsx:32-74`、`dw/src/components/LoginDialog.tsx`、`dw/electron/platform/auth.ts:1-70`、`dw/electron/browser/cookieSync.ts:1-40` | 是:Playwright、safeStorage、FTP、文件系统 |
| **配置管理** | 侧边栏「应用设置」`/config` | 分区清单:账号与同步 / 下载(路径、完成后自动打开、三档速度)/ 外观与启动(主题、启动最大化)/ 更新(启动检查、自动下载、退出安装)/ 投放落地页 / 诊断与日志(版本、日志、远端配置来源、忽略远端配置);未保存改动导航守卫;恢复默认 | **完整** | `dw/src/pages/ConfigPage/index.tsx:716-1180`、`dw/electron/config/default.ts:52-72`、`dw/electron/config/presets.ts:59-102` | 是:dialog、safeStorage、BrowserWindow |
| **远端配置** | 设置「诊断与日志 → 远端配置」 | Ed25519 验签、JSONC 解析、白名单+区间校验、revision 单调、三级回退(远端→缓存→内置)、紧急 `disabled`、送审总开关 `enableSubmitReview` | **完整**(线上 revision 2) | `dw/electron/config/remote.ts:1-40`、`dw/scripts/lib/remote-config-schema.mjs:32-80`、`dw/remote-config/app-config.json:26,72` | 否(HTTP GET) |
| **运行日志** | 设置「诊断与日志」、下载/送审页 | 内存缓冲 + 按日文件(保留 7 个)+ 导出 + 打开目录 + 跨天切换 | **完整** | `dw/electron/logger.ts:41-101` | 是:文件系统、shell |
| **自动更新** | 侧边栏「软件更新」`/update` | 检查/下载/安装/跳过此版本/恢复提醒;更新元数据 `.sig` Ed25519 验签;强制更新、最低版本、更新说明 Markdown 渲染;mac 走 Squirrel 原生替换 `.app` | **完整**(CI 签名+公证+装订;本机 ad-hoc 包点更新会失败属预期) | `dw/src/pages/UpdatePage/index.tsx`、`dw/electron/updater/manager.ts:56-90,185,274-301` | 是:electron-updater、shell、app 生命周期 |
| **关于页** | 侧边栏「关于应用」`/about` | 版本徽标、复制版本信息、日志入口、第三方许可清单/完整文本、企微反馈;**无更新逻辑**(跳更新页) | **完整** | `dw/src/pages/AboutPage/index.tsx:23-59` | 是:clipboard、shell、许可文件路径 |
| **应用外壳** | `App.tsx` | HashRouter 6 页、侧边栏、主题(亮/暗/跟随系统)、4 页骨架屏、错误边界、退出二次确认、崩溃兜底、单实例锁、窗口最小尺寸/启动最大化、`Cmd/Ctrl+,` 打开设置 | **完整** | `dw/src/App.tsx:240-306,437-442`、`dw/src/contexts/*`、`dw/electron/window/state.ts` | 是:BrowserWindow、Menu、app 事件 |
| **浏览器组件分发** | 首次启动/设置提示 | 三级检测:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` → 系统 Chrome → 自建服务器 manifest 下载;manifest Ed25519 验签 + sha256 + revision 精确匹配;无 CDN 回退(防无校验下载) | **完整**(曾有版本错配事故,已修并加门禁) | `dw/electron/browser/playwrightInstaller.ts:1-125,436`、`dw/scripts/lib/browser-manifest-trust.mjs` | 是:Chromium 下载/解压、文件系统 |
| **旧版本应用清理** | macOS 启动弹窗 | 改名后新旧 `.app` 并存,检测旧包→移入废纸篓/访达显示/7 天稍后提醒;路径二次校验防后门 | **完整**(仅 macOS) | `dw/electron/legacy/bundle.ts:33-64,60` | 是:shell、应用枚举 |
| **素材预览 / 过滤偏好** | 素材下载页 | 素材卡片首帧预览、已过滤卡片隐藏与计数、两个开关记 localStorage、DOM 兜底路径同一规则 | **完整**(门禁 43 条;真实数据过滤比例待真机) | `dw/src/components/MaterialPreview.tsx`、`dw/scripts/lib/material-filter.mjs`、`dw/docs/素材下载过滤开关-实现说明.md` | 否(渲染层) |
| **SPUID 素材数量即时查询** | 素材下载页筛选区 | 填完 SPUID 后显示各 SPUID 达人素材条数;最多 20 个、超出显式截断;单条失败不拖垮整批 | **完整** | `dw/src/components/SpuMaterialCounts.tsx`、`dw/electron/platform/materials.ts:112-140` | 否(只读接口) |
| **混剪 Excel/CSV 导入(Phase 2)** | 方案文档 | 批量导入清单 | **未实现(仅文档 + 模板 xlsx)** | `dw/docs/混剪素材批量上传-开发方案.md`、`dw/docs/混剪素材批量上传模板.xlsx` | — |
> **核对说明**:侧边栏只有 6 个一级入口,但「素材审核」页内聚合了 4 个写/读子能力(批量送审、混剪送审、上传明细、CSV 导出),菜单文字易让人低估其分量;「素材下载」页也不只是下载,而是任务工作台(提取、预览、过滤、结果分级)。另:PRD 语境里的「混剪素材」在本项目中不是下载分类,而是**审核页的混剪送审**(源素材检索 + N 组配对提交);下载侧只有「软广视频库素材」与「商品原始素材」两条链路。
---
## 3. 技术栈与架构
### 3.1 栈与关键版本
| 层 | 选型 | 版本(声明 → 实测) | 说明 |
|---|---|---|---|
| 渲染层 | React / react-dom | `^19.3.0` → 19.3.0 | 无全局状态库;Theme/LoginStatus/UnsavedChanges 三个 Context |
| 路由 | react-router-dom | `^6.20.0` → 6.30.3 | `HashRouter`(Windows 打包后 BrowserRouter 失效的历史修复) |
| UI | @douyinfe/semi-ui | `^2.103.0` → 2.103.0 | 2026-10 全量从 Ant Design 迁到 Semi(commit `bf350ae`);`@douyinfe/semi-vite-plugin` |
| 样式 | Tailwind CSS / PostCSS | `^3.4.0` → 3.4.19 | 大量页面级 CSS + Semi token 变量(`--semi-color-*`) |
| 图标 | @remixicon/react | `^4.9.0` → 4.9.0 | 统一图标库(`src/icons.tsx` 173 行) |
| Markdown | marked | `^18.1.0` → 18.1.0 | 发版说明渲染(`Changelog.tsx`) |
| 桌面 | Electron | `^44.7.0` | contextIsolation + preload;主进程 ESM 打包为 `dist-electron/main.js` |
| 浏览器自动化 | playwright | **`^1.40.0` → 1.60.0(⚠️ 漂移)** | 核心能力:登录、DOM 兜底、送审抽屉自动化、上传 |
| 构建 | Vite + vite-plugin-electron | 5.4.21 / 0.28 | 三份构建配置(frontend 配置为死文件) |
| 打包 | electron-builder | `^26.15.3` | Win NSIS / mac dmg+zip / Linux;`extraResources` 分发许可文本 |
| 更新 | electron-updater | `^6.8.9` | generic provider + 自建签名元数据 |
| 其他 | basic-ftp、adm-zip、fs-extra | 6.2.3 / 0.6.1 / 11.3.5 | Cookie FTP 同步、Chromium zip 解压 |
| 类型 | TypeScript | `^5.3.0` → 5.9.3 | `strict` / `noUnusedLocals` / `noUnusedParameters` / `noFallthroughCasesInSwitch` 全开(`dw/tsconfig.json`) |
### 3.2 进程模型与数据流
```
Renderer(React,HashRouter,6 页)
└─ window.electronAPI(preload.ts contextBridge,唯一通道)
├─ invoke/handle:87 条(请求-响应)
└─ on/send:14 条单向事件(进度/登录态/更新)
Main(Electron)
├─ ipc/handlers.ts(1822 行,模块级单例协调层,唯一组装点)
│ ├─ BrowserManager(Playwright 会话、登录、提取、送审编排,1962 行)
│ ├─ DownloadManager(下载、MD5、分批/并发/重试,516 行)
│ ├─ PlaywrightInstaller(浏览器分发,864 行)
│ ├─ configManager(三层配置:用户/档位/开发者 + 远端覆盖)
│ ├─ platform/*(只读接口层:client/http/sign/auth/materials/reviews/cache/authorization)
│ ├─ submit/*(写链路:page 自动化 / batch 执行器 / plan 会话 / verify 回查 / idempotency 账本 / audit)
│ ├─ shop/*(商品查询:webview 承载 / SSR 直取 / 素材下载)
│ ├─ dplink/*(硬广凭据 + 只读商品库/落地页 + 拼链)
│ └─ updater / logger / legacy / task-state / window-state
```
**文件落盘**:主进程是唯一写文件的地方——`userData/config.json`、`cookies.json`、`dplink-*.json`、`platform-cache/*`、`submit-audit.jsonl`、`submit-payloads.jsonl`、`remote-config.cache.json`、`logs/`、下载目录(默认 `~/Downloads/得物素材` 或用户指定)。渲染层无文件系统权限,所有落盘必须经 IPC(CSV 导出、日志导出、下载目录选择)。
**关键约束(详见 `dw/docs/新功能开发计划.md:455-475`)**:
1. **业务写只走页面自动化**,绝不直接调用写接口;唯一豁免是登录(`electron/platform/auth.ts` 只含两条硬编码路径且不重试);
2. **不另开浏览器 context**(平台单账号单会话,另开会被判新会话并踢掉自己);
3. **提交超时只回查、不重试**;
4. **筛选/取数失败显式失败**,不静默降级为「下载全部」;
5. 选择器不绑哈希类名(`previewBtn___*` 集中到 `legacySelectors.ts`,仅 DOM 兜底可用)。
### 3.3 IPC 契约(按域汇总,不逐条贴参数)
| 域 | 通道数 | 主要用途 | 事件 | 定义位置 |
|---|---|---|---|---|
| `browser:*` | 24 | 启停、登录预检/登录/退出、发码/验证码登录、切换无头、导航/分页/提取、保存 Cookie、浏览器组件安装 | `browser:loginStateChanged`、`browser:download:progress` | `dw/electron/ipc/channels.ts`、`dw/electron/preload.ts:5-100` |
| `submit:*` | 16 | 可用性、选文件/解析、md5 计算、起批、混剪选成品/检索/计算/起批、中断、状态快照、审计尾部、忘记已送审 | `submit:md5Progress`、`submit:batchProgress` | `dw/electron/preload.ts:190-240` |
| `update:*` | 11 | 检查/下载/安装/跳过/恢复/状态 + 5 个事件 | 进度/可用/无更新/已下载/错误 | `dw/electron/preload.ts:300-380` |
| `platform:*` | 10 | 签名自检、取素材页/详情、SPUID 数量、预览、审核列表、上传记录、枚举、标签树、账号详情 | — | `dw/electron/platform/*` |
| `dplink:*` | 9 | 硬广账号状态/发码/登录/复核/退出、落地页列表/读取/保存、按 SPU 拼链 | — | `dw/electron/dplink/*` |
| `download:*` | 8 | 开始/停止/统计/打开目录 + 4 个事件 | 进度/完成/汇总/状态 | `dw/electron/downloader/*` |
| `shop:*` | 6 | 承载配置、链接解析、商品详情、素材下载、取消 + 进度事件 | `shop:downloadProgress` | `dw/electron/shop/*` |
| `config:*` | 4 | 读取/保存/恢复默认/刷新远端配置 | — | `dw/electron/config/*` |
| `legacy:*` | 4 | 检测/移入废纸篓/访达显示/7 天稍后 | — | `dw/electron/legacy/bundle.ts` |
| `log:*` | 3 | 日志目录/导出/打开 | — | `dw/electron/logger.ts` |
| 其他 | 6 | 通知、剪贴板、许可、dialog、shell.openExternal、review 导出 CSV | — | `dw/electron/ipc/channels.ts` |
| **合计** | **101** | 87 invoke + 14 event | — | `dw/scripts/verify-channels.mjs` 断言唯一来源 |
> 通道名收口到 `IpcChannel` 常量表(`dw/electron/ipc/channels.ts:1`),`verify:channels` 静态守「handlers/preload 不得出现字符串字面量」——这是 2026-10 修掉的历史债(旧版 channels.ts 曾是死代码且含拼写错误)。
### 3.4 构建与发布链路
- **开发**:`npm run dev`(Vite + vite-plugin-electron 热重载);`postinstall` 是 `playwright install chromium --dry-run`,**装依赖不会装浏览器**。
- **构建**:`build:web`(licenses + `tsc` + `vite build`)与 `build[:win|:mac|:linux]`(再跑 electron-builder)。
- **CI**:三个 workflow——`ci.yml`(每次 push/PR:gates 20+ 步、ui-verify 480 条通过、package-size)、`build-release.yml`(仅 `workflow_dispatch`,Win 构建 + mac 签名/公证 + 产物断言 + 更新元数据签名 + GitHub Release + FTP)、`build-chromium.yml`(手动构建 Chromium 包 + manifest)。
- **发版唯一入口**:`npm run release`(`scripts/release.mjs`,改版本号 + 预检 + 门禁 + 推送 + `gh workflow run`),已移除 tag 触发后门。
- **签名/信任根**:远端配置 Ed25519、更新元数据 Ed25519、浏览器 manifest Ed25519 三把独立密钥;macOS Developer ID + 公证 + 装订(`verify:mac-sign` 断言产物本身)。
---
## 4. 数据模型与持久化
### 4.1 存储位置与形态(`app.getPath('userData')`)
| 数据 | 路径 | 形态 | 加密 | 说明 |
|---|---|---|---|---|
| 用户配置 | `config.json` | JSON | 字段级 safeStorage(`ftpPassword`/`cookieSyncKey` 加 `encrypted:` 前缀) | 明文可读;解析失败**静默回退默认**,不备份坏文件(排查坑) |
| 软广 Cookie | `cookies.json` | safeStorage 整文件加密 | 是 | 跨机器复制**解不开且静默解析失败**(双刃剑) |
| 硬广账号 | `dplink-account.json` | safeStorage 整文件 | 是 | 与软广凭据**完全隔离**,绝不注入浏览器上下文 |
| 硬广落地页设置 | `dplink-settings.json` | safeStorage 整文件 | 是 | 只在本机、不同步云端;旧明文首次读取自动迁移 |
| 平台枚举/标签树缓存 | `platform-cache/enums.json`、`label-tree.json` | JSON + `fetchedAt` | 否 | TTL 24 小时;过期仍返回并标 `stale`(离线可用) |
| 已送审 md5 账本 | `platform-cache/submitted-md5.json` | JSON `{version, md5s[], updatedAt}` | 否 | 无 TTL(100 年);原子写;审核列表回灌自愈 |
| 送审审计 | `submit-audit.jsonl` | JSONL 追加 | 否 | 每条结果/阶段/md5/groups;写失败不阻断送审 |
| 送审请求体快照 | `submit-payloads.jsonl` | JSONL 追加 | 脱敏后否 | 与同事「人工提交」做字段级对账 |
| 远端配置缓存 | `remote-config.cache.json` | JSON + `.sig` 校验后缓存 | 否 | 读缓存**重新验签**,防本地投毒 |
| 旧版本清理状态 | `legacy-bundle.json` | JSON | 否 | 已处理/稍后提醒(7 天) |
| 云 Cookie 上传临时文件 | `_cloud_cookies_upload.json` | JSON 明文 | 否 | 遗留 P1 风险:上传临时文件明文落盘 |
| 日志 | `logs/YYYY-MM-DD.log` | 文本,保留 7 个 | 否 | 内存缓冲 + 按日 + 跨天切换 |
| 本地操作偏好 | `localStorage` | `download.skipExpiring`、`download.skipUnauthorizedChannel`、`submit.lastSpuId`、最近 SPU 等 | 否 | 渲染层,生产 `file://` 下可能不可用,读写全 try/catch |
> **无 sqlite、无数据库**:全部 JSON/JSONL;缓存写入一律「临时文件 + rename」原子写(`dw/electron/platform/cache.ts:11-24`)。
### 4.2 配置项全表
**A. 用户项(`PersistedUserConfig`,`dw/electron/config/default.ts:16-72`)**
| 键 | 默认 | 含义 | 远端可覆盖 |
|---|---|---|---|
| `theme` | `light` | 亮/暗/跟随系统 | 否 |
| `downloadPath` | `''` → `~/Downloads/得物素材` | 素材保存目录 | 否 |
| `downloadPreset` | `balanced` | 下载速度档位(保守/均衡/快速) | 档位数值可覆盖 |
| `autoOpenDownloadDir` | `true` | 完成后打开下载目录 | 否 |
| `cookieCloudSync` | `true` | 登录态云同步开关 | 否 |
| `checkUpdateOnStart` | `true` | 启动检查更新 | 否 |
| `autoDownloadUpdate` | `false` | 自动后台下载新版本 | 否 |
| `installOnQuit` | `true` | 退出时安装已下载更新 | 否 |
| `ignoreRemoteConfig` | `false` | 忽略远端配置(逃生口) | 否 |
| `maximizeOnStart` | `true` | 启动最大化(≠全屏) | 否 |
**B. 档位参数(`downloadPresets`,`dw/electron/config/presets.ts:59-102`)**
| 参数 | 保守 | 均衡(默认) | 快速 | 远端允许区间 |
|---|---|---|---|---|
| `maxConcurrent` | 2 | 3 | 5 | 1–10 |
| `retryTimes` | 5 | 3 | 3 | 0–10 |
| `downloadTimeout`(秒) | 180 | 120 | 90 | 30–600 |
| `downloadDelayMin/Max`(秒) | 3/8 | 1/3 | 0/1 | 0–30 / 0–60 |
| `batchSize` | 3 | 5 | 10 | 1–50 |
| `batchCooldownMin/Max`(秒) | 20/45 | 10/20 | 3/8 | 0–300 / 0–600 |
| `retryBaseDelay/MaxDelay`(秒) | 5/120 | 3/60 | 2/30 | 1–60 / 10–300 |
**C. 远端可下发字段(`dw/scripts/lib/remote-config-schema.mjs:32-53`)**
| 字段 | 类型 | 说明 |
|---|---|---|
| `platform.baseUrl` | URL | 星光主站 |
| `platform.videoLibraryUrl` | URL | 软广视频库页面 |
| `platform.updateCheckUrl` | URL | 更新 feed |
| `platform.cookieCloudUrl` | URL | 云端 Cookie 读地址 |
| `platform.enableSubmitReview` | boolean(独立白名单) | **送审总开关**,内置默认 `true`,远端 `false` 一键止血 |
| `presets.{gentle,balanced,fast}.*` | number | 档位数值覆盖,超区间整份拒收 |
**D. 开发者项(`dw/electron/config/developer.ts:65-160`,不出现在 UI)**
| 键 | 值/默认 | 说明 |
|---|---|---|
| `baseUrl` / `videoLibraryUrl` / `spuDetailUrlTemplate` | 星光 + `fast.dewu.com/page/productDetail` | `spuDetailUrlTemplate` **不在远端白名单**(要改需同时改白名单并重签) |
| `updateCheckUrl` / `cookieCloudUrl` | `http://dw_update.xinsenidea.com/...` | **明文 HTTP**(历史债) |
| `cookieSyncKey` / `ftpHost` / `ftpPort` / `ftpUser` / `ftpPassword` / `ftpRemotePath` | **仓库内明文默认值**(`developer.ts:130,135`) | 🔴 严重:凭据仍在源码与 git 历史中,Web 化前必须轮换并改为服务端密钥管理 |
| `timeout` / `headless` | 30 秒 / `true` | 浏览器自动化内部参数,无 UI 开关 |
| `remoteConfigBootstrap.url/publicKey` | 远端配置地址 + Ed25519 公钥 | 信任根,**不可远端下发** |
| `updateMetadataPublicKey` | Ed25519 公钥 | 与远端配置是两把独立密钥 |
### 4.3 下载文件命名与路径规则
**视频素材(星光软广素材库)**:
```
{媒体任务ID}-{视频ID}-{SPUID}-{投放类目}-{授权开始日期}-{授权结束日期}.mp4
```
- 空值哨兵:SPUID → `无商品ID`;类目 → `未知类目`;开始 → `无开始日期`;结束 → `无限用日期`;
- `-`/`unknown`/空 均视为无值;长期授权 → `长期-长期`;
- 非法字符 `< > : " / \ | ? *` 替换为 `_`;
- 落盘:先写 `{name}.downloading{ext}` 临时文件 → 校验 MD5 → `rename` 原子转正。
- 证据:`dw/electron/downloader/utils.ts:11-20,24-37`、`dw/electron/downloader/manager.ts:430`(`-internal.aliyuncs.com` → `.aliyuncs.com` 内网转公网改写)。
**商品原始物料(商品查询页)**:
```
<下载目录>/商品素材/<spuId>-<货号>-<标题前40字>/
主图-01.jpg … 主图-06.jpg
详情图-<段落名>-01.jpg …
主视频.mp4
```
- 取的是 SSR 载荷里的**原图**(`cdn.poizon.com/pro-img/origin-img/...`),不是页面 `webimg.dewucdn.com` 的压缩衍生图;详情图在 `imageAndTextMap` 下;
- 证据:`dw/electron/shop/materials.ts:24-26`、`dw/electron/shop/detail.ts:11-27,242-280`。
**提审文件命名(与下载命名刻意不同)**:
- 模式 A(文件夹批量):文件夹名**恰好** `{媒体任务ID}-{视频ID}-{SPUID}`;
- 模式 B(单文件批量):文件名**末三段** = `{媒体任务ID}-{视频ID}-{SPUID}`,前缀(日期/版本/商品名/授权期/剪辑师)只展示不校验;
- **不兼容**应用自己下载的 `ID 在开头` 的命名 → 明确判识别失败并标红,不会误提交;
- 证据:`dw/docs/送审命名规范与预检规则.md:20-75`、`dw/scripts/lib/submit-naming.mjs`。
### 4.4 状态机
| 状态机 | 取值 | 说明/证据 |
|---|---|---|
| 登录态(渲染层) | `checking` / `logging_in` / `logged_in` / `not_logged_in` / `error` / `not_ready` | `dw/electron/browser/manager.ts:46-55` |
| 提取/下载任务 | `idle → extracting → downloading → done/stopped/failed`(渲染层 `currentStep 0..3`) | `dw/src/pages/DownloadPage/index.tsx:289,1562-1573` |
| 下载单文件结果 | `success / skipped / failed` + `retryHistory` | `dw/electron/downloader/manager.ts:288-339` |
| 送审行状态 | `pending / warning / conflict / invalid / duplicate / already_submitted / submitted / failed / unknown` | `dw/src/types/index.ts:511-520` |
| 送审批次结果 | `submitted / skipped / failed / unknown` | `dw/electron/submit/batch.ts:1-30`、`dw/src/types/index.ts:792-802` |
| 混剪授权状态 | `valid / soon / expired / not_started / longterm / unknown` | `dw/src/types/index.ts:629`、`dw/scripts/lib/mix-submit.mjs` |
| 审核状态(平台) | 数字:待审核=1 / 通过=2 / 拒绝=3;字符串 `toBeReviewed/reviewPassed/reviewFailed`;Tab 另加「全部=null」 | `dw/scripts/lib/platform-material-mapping.mjs:424-506`、`dw/docs/星光平台API接口文档.md:551` |
### 4.5 去重与校验
- **下载去重**:按内容 MD5(不是文件名)。下载前一次性扫描目录建 `md5 → path` 映射;命中则跳过并写明「经md5校验,本地已存在相同的文件」。已知性能债:**同步全量扫描 + 整文件读内存**(`dw/electron/downloader/manager.ts:174`、`dw/electron/downloader/utils.ts:78-100`;评估文档 §5.1 标 P0)。
- **送审去重**:只按 `md5`,双向同步——提交成功后立即记账;每次打开审核页/回查把平台审核行的 md5 并进账本(换机/清 userData 后能自愈)。平台侧同样按 md5 在「提交」步查重(`code:900` + msg 含 `md5/重复/已送审`),本地只是提前拦一次流量。
- **签名/完整性**:远端配置、更新元数据、浏览器 manifest 三处 Ed25519;安装包 sha512 由 electron-updater 校验;浏览器包 sha256 + revision 精确匹配。
- **上传预检**:原视频授权渠道(`authedMediaIds` 与投放渠道 `deliveryMediaIds` 求交集)、videoId↔xtTaskId 成对性、SPUID 回显、批内同名/同 md5、组数为 0 等。
---
## 5. 外部平台集成(重点)
### 5.1 星光平台(唯一外部平台)
**域名与鉴权**
| 项 | 值 | 证据 |
|---|---|---|
| 主站 | `https://material-manage.shizhuang-inc.com` | `dw/electron/config/developer.ts:119`(可由远端配置覆盖) |
| 商品站 | `https://fast.dewu.com/page/productDetail?spuId=…` | `dw/electron/config/developer.ts:124` |
| CDN | `h5cdn.dewu.com`、`delivery-platform.dewucdn.com`、`delivery-platform.oss-cn-shanghai.aliyuncs.com` | `dw/docs/星光平台API接口文档.md:19-24` |
| 凭据 | Cookie `materialAccessToken`(非 HttpOnly,会话 Cookie)→ 请求头 `authorization: <token>` | `dw/docs/星光平台API接口文档.md:54-62` |
| 未登录 | HTTP 401 + `{"code":700,"msg":"请先登录"}`;`hacking-kol` 族则是 HTTP 200 + `code:401` | 同上 §1.4 |
| 会话特性 | **单账号单会话**:网页版登录会顶掉应用 token;应用内所有操作复用同一个 Playwright context | `dw/docs/星光平台送审与审核查询-可行性评估.md:1-120` |
**登录方式(三条通道)**
1. **接口登录(主力,P1/P2 已上线)**:`POST /platform/user/send-code` → `POST /platform/user/login` → 响应体 `data.token`(实测长度 168);裸 Node 可完成,不启浏览器;`GET /platform/user/detail` 复核并取账号信息/权限菜单。实测 9/9(`dw/electron/platform/auth.ts:1-70`、`dw/docs/接口登录通道-实现说明.md:146-169`)。
2. **浏览器登录(兜底)**:有头 Chromium 打开登录页,轮询 120 秒 + 登录后 `detectLoginState()` 再确认;成功后切无头;换账号时先清本地 Cookie。
3. **云端 Cookie 同步(历史架构)**:HTTP GET `cookies.json`(带 `X-Sync-Key`)+ FTP 上传;v2 分区 `accounts.soft` / `accounts.dplink`,写时只替换自己的分区并回读校验;本地 `safeStorage` 加密。**明文 HTTP + FTP 无 TLS,且是「全体共享一个账号」的传输方式**(`dw/electron/browser/cookieSync.ts:1-40`)。
**Cookie 保活**:没有定时刷新/心跳。保活依赖三个检查点——① 启动时 `probeLoginState()` 无头校验;② 业务导航落到登录页时广播 `not_logged_in` 并抛错;③ 用户点侧边栏「重新登录」(清旧 Cookie 或以新 token 覆盖)。云端同步只发生在启动/登录/退出等节点,**不是持续保活机制**;平台无刷新 token 接口,token 失效只能重登。
**接口清单(实测/代码在用;完整 106 条静态枚举见 API 文档 §9.3)**
| 用途 | 方法 | 路径 | 证据 |
|---|---|---|---|
| 软广素材列表(含下载直链) | POST | `/api/v1/h5/delivery-platform/v1/platform/soft-material/material-library/list` | `dw/electron/platform/materials.ts:91`、API 文档 §2.1 |
| 素材全字段详情 | POST | `…/soft-material/material-library/detail` | `dw/electron/platform/materials.ts:177`、API 文档 §2.2 |
| 审核列表(状态/驳回原因/md5) | POST | `…/platform/self-material/list` | `dw/electron/platform/reviews.ts:43`、API 文档 §3.1 |
| 上传/送审记录(含明细 xlsx 预签名) | POST | `…/platform/self-material/publishRecord` | `dw/electron/platform/reviews.ts:44`、API 文档 §3.2 |
| 万能枚举(35 键:驳回原因/品牌/角色…) | GET | `…/platform/common/config-list` | `dw/electron/platform/reviews.ts:45`、API 文档 §5.1 |
| 标签树 | POST | `…/platform/material-label/label-tree` | `dw/electron/platform/reviews.ts:46`、API 文档 §3.3 |
| 账号信息 + 权限菜单 | GET | `…/platform/user/detail` | `dw/electron/platform/reviews.ts:48`、API 文档 §6 |
| 发短信验证码 | POST | `…/platform/user/send-code` | `dw/electron/platform/auth.ts:66` |
| 验证码登录 | POST | `…/platform/user/login` | `dw/electron/platform/auth.ts:68` |
| 媒体多清晰度详情 | POST | `/api/v1/h5/media-data-center/v1/media/query` | API 文档 §3.4(代码未接入) |
| SPUID 预检(标题/品牌/货号) | POST | `…/soft-material/kol-native/spuid-check` | API 文档 §4.1(送审预检数据源) |
| OSS 直传凭证 | POST | `/api/v2/h5/oss-platform/token/customize/stsToken/v2` | API 文档 §4.2(由页面 SDK 调用,应用不实现) |
| 登记 mediaId | POST | `/api/v1/h5/media-center/v1/media/create` | API 文档 §4.4(由页面 SDK 调用) |
| **提交审核(写)** | POST | `…/soft-material/kol-native/upload-derivative-material` | `dw/electron/submit/page.ts:47`、API 文档 §4.5 |
| DPLink 落地页列表 | GET | `…/platform/material-library/list-landing` | `dw/electron/dplink/link.ts:39`、DPLink 文档 §4.4 |
| DPLink 商品库反查 `cSpuId` | POST | `…/platform/material-library/list-commodities` | `dw/electron/dplink/link.ts:41`、DPLink 文档 §4.4 |
**签名/加密/remote-config**
- 每个请求带 `?sign=<32位hex>`:`md5(按 key 升序拼接的 "key" + serialize(value) + SALT)`,`SALT = <已脱敏;原值见受控文档>`;GET 覆盖 query、POST 覆盖 JSON body(`dw/scripts/lib/platform-sign.mjs:37,106`、`dw/electron/platform/sign.ts:18-36`)。
- **启动自校验**:`sign({})` 必须等于平台未登录时实际发出的签名(`fe26befc…`);盐变化时日志直接报「签名盐已变化」,避免把所有 401 误判成登录失效。
- 平台前端常量(`userId=1000000`、`bizSourceId=500041536`、上传 `appId=Jem7c…`、OSS bucket/prefix)来自前端硬编码,应用侧应配置化而非再硬编码(API 文档 §1.6)。
- 送审写链路**不重放接口**:用 `page.setInputFiles` 把文件交给平台自己的上传 SDK(含 md5/分片/OSS token/`duToken` 网关),应用只填表 + 点提交 + 观察响应。
- `remote-config` 是**应用自身**的运维配置(非星光),见 §4.2;三把 Ed25519 密钥分别守护远端配置、更新元数据、浏览器 manifest。
**CDN 直连下载与防限速**
- 下载不走浏览器:Node `http/https.get` 直连素材 `url`;把 `-internal.aliyuncs.com` 改写成 `.aliyuncs.com`(内网地址转公网);`downloadWithRetry` 指数退避 `min(base*2^n, max)`;随机延迟、分批、批间冷却、并发信号量;文件名/目录规则见 §4.3(`dw/electron/downloader/manager.ts:181-227,374-430`)。
- 商品图/视频下载同样直连 CDN,但只允许 `poizon.com / dewu.com / dewucdn.com` 三个域(IPC 入参防越权),带 `User-Agent` 与 Referer(`dw/electron/shop/materials.ts:82-95,212`)。
- 已识别的限速/风控线索:平台按账号会话统计;应用刻意把详情并发限制为 4、SPUID 数量查询串行、不并发轰炸(`dw/electron/platform/materials.ts:1-32,121-140`)。
**其他平台**
- **唯品会:无任何代码**(全仓检索 `唯品会/vipshop/vip.com` 零命中)。PRD 的 P-06 多平台能力在本项目完全没有参考实现;`sucaigongfang` 项目是否覆盖唯品会需由另一份调研确认(本报告不越界)。
### 5.2 服务端(无 Electron、多用户并发)复现评估
| 集成点 | 服务端复现可行性 | 改造点 | 结论 |
|---|---|---|---|
| 只读接口(素材/审核/枚举/标签) | **高**:纯 HTTPS + 签名 + 信封,Node 即可 | 把 `electron/platform/*` 抽成服务端 SDK;token 从「本机加密文件」改为「用户/账号凭据服务」;加缓存与限流 | **直接迁移(工作量小)** |
| 登录(发码/验证码) | **高**:不需要浏览器 | 服务端接收用户手机号/验证码;token 加密落库(KMS/信封加密);处理短信频控与失败码未知问题 | **可迁移,但账号归属需重设计** |
| 会话模型(单账号单会话) | **中**:技术可行,业务冲突 | 必须按「账号」串行化(同一账号同一时刻只允许一个任务),或为不同用户分配不同账号;Web 多用户共享一个 token 会互相顶号 | **最大架构约束之一** |
| 素材下载(CDN 直连) | **高** | 服务端可直接下载并转存对象存储;或下发签名 URL 让浏览器直下(省带宽);保留 MD5 去重与冷却策略,去重索引改 DB/Redis | **可迁移** |
| 商品详情 SSR 直取 | **高** | 服务端 HTTP GET + 解析 `__remixContext`,无需 UA/浏览器;短链解析放服务端(域名白名单) | **可迁移且更稳** |
| 内嵌商品 H5 / 净化 | **低** | Web 端 iframe 受 `X-Frame-Options/CSP` 限制;Electron `<webview>` 无对应物。替代:只保留数据卡片 / 服务端截图(成本高、收益低) | **建议废弃** |
| 送审写链路(页面自动化) | **中**:必须服务端浏览器 | 浏览器池 + 每账号一个 context + 上传通道(浏览器→服务端 140–190MB 或服务端代传 OSS)+ 队列/断点/回查/审计;风控暴露面从「单机」变成「机房 IP」 | **可做,但成本最高、风险最高** |
| DPLink | **高**:纯算法 + 两个只读接口 | 硬广账号凭据独立密钥槽;落地页配置存用户/项目级;错误分类保留 | **最划算的迁移项** |
| Cookie 云同步(HTTP+FTP) | **低**:架构应废弃 | 改为服务端凭据库(加密、审计、按用户/账号授权);FTP 与明文 HTTP 不应保留 | **废弃并重写** |
| 自动更新/浏览器分发/代码签名 | **不适用** | Web 无客户端分发;服务端浏览器镜像由部署流水线维护 | **废弃** |
| 本机文件/账本/审计 | **中** | 本地 JSON/JSONL → 数据库表;文件下载 → 对象存储 + 下载中心;路径语义(下载目录)改为「云端素材库/导出任务」 | **需数据模型重设计** |
**建议的 Web 目标形态(供后续架构评审)**:
1. **BFF + Worker 两层**:BFF 走只读接口与元数据;Worker 承担 Playwright 浏览器任务(送审、登录兜底),按账号加分布式锁,任务状态落 Redis/DB,前端用 SSE/WebSocket 订阅进度(替代 IPC 事件)。
2. **上传走「浏览器/客户端 → OSS 直传」**:服务端只发 STS 临时凭证并登记 mediaId,避免 140MB 文件绕行应用服务器;必要时再让服务端浏览器 `setInputFiles` 指向挂载的临时对象。
3. **凭据服务**:平台 token 与硬广 token 分别加密存储(KMS + 信封),按「用户/项目/账号」授权;禁止再出现仓库内硬编码密钥。
4. **账号串行化**:以 `accountId` 为粒度排队;同一账号的下载/送审/审核查询并发需互斥(沿用现在 `runExclusive` 的思路,但改为分布式锁)。
5. **只读优先**:把「读」全部走接口(无浏览器),仅「写」用浏览器,显著降低风控暴露面与资源成本。
---
## 6. 关键机制与难点
### 6.1 Playwright 浏览器自动化
- **登录/会话**:`smartStart` 状态机——有会话且有效→复用;云端/本地 Cookie→无头启动注入→校验→写回;失败则有头手动登录(120 秒轮询)→切无头。所有会动会话的操作走 `runExclusive` 串行队列(`dw/electron/browser/manager.ts:223,617-812`)。
- **读路径**:先挂 `page.on('response')` 拦截页面自身列表响应(第 1 页),后续页/详情走接口;失败整体降级 DOM(点预览弹窗 + 解析字段)。DOM 兜底只保留一期,哈希类名集中在 `legacySelectors.ts` 并加门禁。
- **写路径**:开送审抽屉 → 定位 `#deliveryMediaIds`/`#xtTaskId`/`#videoId`/`#spuIds`/`#url input[type=file]` → `setInputFiles` → 等上传完成(最长 15 分钟)→ 点提交 → `waitForResponse(SUBMIT_API_PATH)`(60 秒)。选择器必须「限定抽屉内 + 按 `label[title]` 精确匹配」,否则会命中页面上的「KOL投放渠道」筛选框(真机踩坑记录 `dw/electron/submit/page.ts:209-222`、`dw/docs/星光平台API接口文档.md:800-830`)。
- **难点**:平台改版即失效(最高频维护工作);上传完成没有可靠事件,只能轮询百分比/表单回显;抽屉复用必须显式清空上一条残值;「提交超时」不能重试。
### 6.2 登录态托管
- 本地 `safeStorage` 加密 + 云端 v2 分区共享;`loginInteractive(force)` 换账号;`logout` 只清本机并置 `ignoreCloudCookies`;启动预检 `probeLoginState` 用无头浏览器只校验、不弹窗。
- **已知边界**:单账号单会话(网页版登录会顶掉);接口登录失败码未实测;应用侧凭据落地(落盘/注入/云同步)仍需真人点一次才能验证;`verify:login` 刻意不进 CI(会发真短信、会让跑的人生效掉线)。
### 6.3 批量上传:并发/重试/断点
- **串行**而非并发(同一个浏览器会话,且上传本身占满上行);整批圈进 `runExclusive`,下载/送审互斥。
- **可中断**:中断请求在当前条跑完才停(避免半截抽屉 + 账本未落盘)。
- **可续跑**:账本按 md5 拦住已成功项;失败项不记账;「结果未知」只回查(3 次退避)不重试。
- **重试**:只有网络错误/5xx 才重试 1 次;提交响应超时绝不重试。
- **失败显式**:抽屉打不开、字段找不到、上传未完成 → 带 `reasonCode` 的 failed,并 dump 页面实况(抽屉数量、入口是否被遮、`elementFromPoint`)。
### 6.4 命名字段组合与预检规则
- 命名只承载三要素(媒体任务ID/视频ID/SPUID),前缀不校验;末三段解析 + 文件夹名恰好三段双候选,冲突不自动挑、要求人工确认;`无spuid` 五种写法大小写不敏感;位数 ≠ 19 只警告不阻断。
- 预检两段式:① 文件名解析(毫秒级,不读盘)先出结果;② md5 查重 + SPUID 商品标题回显(秒~几十秒,异步可取消)。只有 `pending/warning` 可勾选;失败可「就地修好」。
- 送审前额外预检:原视频授权渠道、videoId↔xtTaskId 配对、SPUID 一致性、组数;「unknown 不阻断」是刻意保守策略(宁可白传一次,不可误拦)。
### 6.5 混剪任务
- 成品只解析最后一个 SPUID;每条成品带 N 组 `{xtTaskId, videoId}`;右栏从平台素材库或本机下载目录检索源,授权状态六档;已过期/未生效/明确未授权默认不勾;平台检索目前只取第一页(最多 30 条)、无「加载更多」;Phase 2 Excel/CSV 导入未实现。
- **风险点**:「混剪=是」的写链路(选「是」、增删行、无 id 的视频ID输入框、组数回读)**尚未真机验证**,只做了离线门禁。
### 6.6 错误码与重试
| 平台回答 | 应用行为 | 说明 |
|---|---|---|
| `code:200` | 成功,记 md5 | 成功必须看 `code`,不能只看 HTTP 状态 |
| HTTP 401 + `code:700` | 未登录:停止批次、侧边栏变红、指向重登 | 与平台故障区分 |
| HTTP 200 + `code:401` | 平台族故障(`hacking-kol`),不当作未登录 | 平台自身页面也报错 |
| HTTP 200 + `code:900` | 业务校验失败:**必须看 msg**;只有含 `md5/重复/已送审` 才当重复记账,否则按 failed 显式展示 | 曾因「只看 900」把业务拒绝误记成已送审 |
| 提交超时/无响应 | unknown → 回查审核列表(3 次);命中记 submitted,未命中提示稍后刷新 | 绝不重试 |
| 网络错误/5xx | 只读请求重试 1 次;下载按档位指数退避;发短信不重试 | — |
### 6.7 更新与代码签名(Web 化后大概率废弃,但资产有价值)
- 桌面更新链路:electron-updater + 自建 feed;更新元数据 `.sig` Ed25519 验签 → 强制/最低版本/跳过版本;mac 走 Squirrel/ShipIt 原生替换 `.app` 并支持改名迁移;Win NSIS 静默安装。
- 代码签名:mac Developer ID 签名 + 公证 + 装订 + 产物门禁;Win 目前无签名(历史状态)。
- **Web 化后这些全部消失**,但可复用其**设计模式**:信任根不可远端下发、原始字节签名、灰度放行策略表、revision 单调、双密钥隔离。这些模式可直接用于新平台的「配置中心/发布系统」安全设计。
---
## 7. 代码规模与质量
### 7.1 规模(口径见页首)
| 目录 | 文件数 | 行数 | 主要内容 |
|---|---|---|---|
| `src/` | 67(ts/tsx) | **19,120** | 6 页 + 19 组件 + 3 Context + 1 Hook + 15 个 `.d.ts` + 1 个 `types/index.ts` |
| `electron/` | 45(ts) | **17,345** | IPC/浏览器/平台接口/送审/商品查询/DPLink/配置/更新/日志 |
| `scripts/` | 73(mjs/cjs) | **27,828** | 32 个 verify 脚本 + 21 个共享纯逻辑 + 探针/截图/发版/签名 |
| `release-scripts/` | 5 | **875**(mjs/cjs;含 .sh/README 共 1,497) | 本机发版、mac 签名/公证、Windows 构建 |
| **合计** | **190** | **65,168** | 不含 CSS(3,675 行)与 docs(30 篇、14,633 行) |
**单文件 Top(复杂度集中点)**:`DownloadPage/index.tsx` 2,241 行;`browser/manager.ts` 1,962;`ipc/handlers.ts` 1,822;`submit/page.ts` 1,824;`ConfigPage/index.tsx` 1,241;`SubmitPrecheckModal.tsx` 1,035;`ReviewPage/index.tsx` 1,004;`UpdatePage/index.tsx` 896;`playwrightInstaller.ts` 864;`ShopPage/index.tsx` 841。
### 7.2 类型与规范
- `tsconfig.json`:`strict: true`、`noUnusedLocals`、`noUnusedParameters`、`noFallthroughCasesInSwitch`;`include: ["src","electron"]`;无 `allowJs`,因此 `scripts/lib/*.mjs` 靠**手写 `.d.ts` 副本**+ `verify:type-declarations` 门禁保证一致。
- **无 ESLint / Prettier 配置**(仓库根目录无相关文件);格式靠约定与 review。
- `any` 仍有:`preload.ts` 51 处、`handlers.ts` 15 处、`src` 16 处(跨进程类型未共享,历史评估标 P1)。
- 无全局状态管理库:Context + 页面内 state;下载/送审状态主进程持有,渲染层用事件订阅。
### 7.3 测试与质量门禁
| 层 | 现状 |
|---|---|
| 单元测试框架 | **无**(无 vitest/jest,`src/App.test.tsx` 名不副实,是组件不是测试) |
| 离线验证 | **32 个 `verify:*` 脚本**,合计 15,411 行;`verify:ui` 5,283 行 / 472 个 `ok()` 断言点(v2.7.0 记录 480 通过;真实 Chromium 加载真实 `dist/`);`verify:platform` 2,352 行 / 255 个 `ok()` 断言点(v2.7.0 记录 260 通过;真实抓包 fixture + 静态边界);`verify:channels` 101 条通道唯一来源;`verify:type-declarations` 33 个导出;`verify:mix` 24;`verify:filter` 43 |
| 实机对比脚本 | `compare:extraction-paths`、`compare:review-list`、`compare:submit-payload`(人工登录后跑,结果落盘对账) |
| CI | `ci.yml` 三 job(gates / ui-verify / package-size);发版走 `release.mjs` + `build-release.yml`;历史教训「验证资产 10 个只有 1 个进 CI」已修 |
| 依赖审计 | `audit:prod` 强制官方 registry(防 npmmirror 不支持 audit 导致「无漏洞」假象);critical/high 失败 |
| 已知质量债 | 大文件上帝模块;`preload` 大量 `any`;`CSP` 宽松(允许 http/https/ws/`unsafe-eval` 已移除但范围仍宽);`scanDirectoryMd5` 同步读内存;日志/控制台噪音;无前端虚拟列表;无端到端自动化(写链路靠人工真机) |
### 7.4 文档与已知风险
- `docs/` 现有 **121 个文件、30 篇 Markdown(14,633 行)**,包括:星光 API 接口文档(807 行,含签名盐与 106 条接口)、送审/审核可行性评估(928 行)、P1/P2/P4 实现说明、送审命名规范、混剪方案、DPLink 评估、远端配置运维手册、签名/公证清单、项目评估 2.1.0(1,063 行)等。
- `DEVELOPMENT.md` 2,380 行,是**以代码为准**的交接文档,但其 §1-§14 基线停在 v1.0.6,**已明显滞后于 v2.7.0**;§17-§19 是后续补的登录态/商品查询/接口登录章节。**新平台调研应优先信任代码与 2.x 的实现说明文档**。
- **已知问题与风险(代码/文档交叉确认)**:
1. 🔴 **仓库内仍硬编码运维凭据**(`cookieSyncKey`、FTP 密码,`dw/electron/config/developer.ts:130,135`),且在 git 历史中;Web 化前必须轮换、改服务端密钥管理,不可沿用。
2. 🟠 **单账号单会话**是平台行为(网页版登录顶掉应用 token);多用户 Web 化必须按账号串行/分账号。
3. 🟠 **明文 HTTP + FTP 无 TLS** 用于更新/远端配置/Cookie 同步;新平台应拒绝此模式。
4. 🟠 平台接口/页面结构随时可能变更:签名盐、字段、选择器、权限菜单;维护成本高(`docs/` 多篇文档都在记录平台变更)。
5. 🟡 Playwright 依赖声明 `^1.40.0` 与实际 1.60.0 漂移(已有 `verify:playwright-version` 守版本→revision 映射,但锁文件仍应 pin)。
6. 🟡 浏览器分发/更新/签名/旧包清理的复杂度对 Web 化无意义,但占主进程相当比例代码与文档。
- `DEVELOPMENT.md §12` 列出「文档与实现的偏差」,`docs/项目评估与优化建议-2.1.0.md` 给健康度加权 **3.0/5**(产品完成度 4.5、安全信任链 2.0、测试质量 2.0、性能 3.0);其中 P0 缺陷多数已在 2.1.x-2.7.0 修复(重试退避、可重入、浏览器版本、验签),**硬编码凭据与 MD5 扫描仍是历史遗留**。
---
## 8. 逐模块 Web 化复用评估(核心交付)
**复用级别**:A=前端页面/组件几乎直接复用;B=改造成本可控;C=需服务端重写;D=废弃。人日为粗估(含联调,不含需求/设计评审)。
| 模块 | 现有实现 | 目标端形态 | 复用级别 | 改造要点 | 粗估人日 | 风险 |
|---|---|---|---|---|---|---|
| 应用外壳/导航/主题/骨架屏/错误边界 | React + Semi + Tailwind;`App.tsx` 558 行 | 新平台 Shell | **A(结构)/B(视觉)** | 保留信息架构(侧边栏、页头、分区、骨架屏、未保存守卫);重写导航与主题 token;Semi 组件替换(见 §8.1) | 3–5 | 低 |
| 素材下载 · 任务工作台 UI | `DownloadPage` 2,241 行;阶段化布局、双重过滤、素材预览、结果分级、重试历史 | 新平台「原始物料获取」页 | **B** | 保留交互模型与状态机;Semi→新 UI;`localStorage` 偏好改用户配置;「打开目录/通知」改「导出到素材库/下载中心」 | 8–12 | 中(页面巨大、需重排信息层级) |
| 素材提取(只读接口路径) | `platform/materials.ts`、`browser/manager.ts` 拦截路径 | 服务端取数服务 | **B** | 直接用签名客户端;去掉浏览器与 DOM 兜底;保留「读取方式/降级」可观测语义 | 5–8 | 中(签名盐/接口变更) |
| 下载引擎(CDN/MD5/限速/重试) | `downloader/manager.ts` + `utils.ts` | 服务端下载 Worker + 对象存储 | **C** | 文件系统→对象存储;MD5 扫描→DB/Redis 索引+流式 MD5;并发/冷却策略保留;临时文件/原子改名改为分片上传完成标记 | 12–18 | 中高(带宽/存储成本、平台限速) |
| 商品查询 · 工作台 UI | `ShopPage` 841 行 + `ActionBar`/`MaterialGrid`/`useShopDownload` | 新平台「选品」页 | **B** | 保留「命令条+画布+面板+底栏」布局与 9 项素材格;去掉 webview 画布或改为可选;Semi→新 UI | 8–12 | 中 |
| 商品详情 SSR 直取/短链解析 | `shop/detail.ts` 359 行 | 服务端选品服务 | **B** | 服务端 HTTP + `__remixContext` 解析;短链解析+域名白名单;图片原图 URL 规则保留 | 4–6 | 低中(站点结构变更) |
| 内嵌商品 H5 + 净化模式 | `SpuCanvas.tsx`、`shop/session.ts` | — | **D** | Web 端无法等价复现;最多保留「查看原页」外链或服务端截图 | 0(可选 3–5) | 中(做了也价值低) |
| 素材审核页 | `ReviewPage` 1,004 行 + 6 子组件 + `review-csv.mjs` | 新平台「提审/审核」页 | **B** | 只读接口直接迁移;表格/筛选/驳回富文本/CSV 逻辑保留;Semi→新 UI;权限提示保留「建议不硬拦」 | 8–12 | 中(平台字段/权限变化) |
| 只读接口层(签名/客户端/缓存/授权预检) | `electron/platform/*` 2,526 行 | 服务端 SDK | **B** | 抽包;凭据来源改注入;缓存改 Redis/DB;错误分类保留;`authorization.ts` 的「unknown 不阻断」策略保留 | 5–8 | 中(签名/接口契约) |
| 命名解析/预检/CSV/过滤/进度模型等纯逻辑 | 21 个 `scripts/lib/*.mjs` + `.d.ts` | 服务端/前端共用包 | **A** | 直接抽成 npm 包(保持「应用与门禁同一份实现」);补真单测 | 3–5 | 低 |
| 批量送审判定与编排 | `submit/plan.ts`、`idempotency.ts`、`audit.ts`、`verify.ts`、`batch.ts` | 服务端送审服务 | **B–C** | 策略层可复用(串行/中断/回查/记账/审计);存储改 DB;队列改任务系统;进度改 SSE | 8–12 | 中高(写操作) |
| 送审页面自动化(Playwright) | `submit/page.ts` 1,824 行 | 服务端浏览器 Worker | **C** | 浏览器池 + 账号锁;选择器集中配置化;上传改「客户端→OSS→服务端挂载」;日志/证据链保留 | 18–25 | **高(平台风控/改版/大文件)** |
| 混剪配对 UI | `MixSubmitModal.tsx` 683 行 | 新平台混剪送审页 | **B** | 双栏交互与授权状态保留;Semi→新 UI;补「加载更多」与 Excel Phase 2 | 8–12 | 中 |
| 混剪服务端编排 | `submit/mix.ts`、`source-search.ts`、`mix-submit.mjs` | 服务端 | **C** | 源检索(平台+本地下载库)改「平台素材库+云端素材库」;N 组配对校验保留;「混剪=是」需真机验证 | 6–10 | 高(未真机) |
| DPLink 拼装/复制 | `dplink/link.ts` 420 行 + `account.ts` + `dplink-link.mjs` | 服务端投放配置服务 | **B** | 纯算法与错误分类直接迁移;硬广凭据改 KMS;落地页配置改项目级;复制按钮保留(前端) | 5–8 | 中(硬广账号权限/共用凭据) |
| 登录态与账号体系 | `auth.ts`、`cookieSync.ts`、`SidebarLoginStatus`、`LoginDialog` | 平台账号中心 | **C** | 接口登录可迁移;浏览器兜底需 Worker;云端 FTP 同步废弃→凭据库;多用户账号归属/授权/审计重设计 | 8–12 | **高(单会话/账号归属/合规)** |
| 配置管理/远端配置 | `config/*`(1,261 行)+ `remote-config-schema.mjs` | 平台配置中心 | **B** | 用户项→用户级 DB;档位预设与白名单保留;远端配置改服务端配置中心(不必 Ed25519,除非仍需离线下发);`enableSubmitReview` 保留为运维开关 | 4–6 | 低中 |
| 日志/审计/诊断 | `logger.ts`、`submit/audit.ts` | 服务端日志/审计 | **B** | 本地文件→结构化日志/审计表;保留「审计只追加、写失败不阻断」的语义 | 3–4 | 低 |
| IPC 通道层 | 101 条通道 / preload | HTTP API + SSE/WebSocket | **C** | 契约可参考(通道名/入参/进度事件),实现全部重写;长任务改异步任务 + 订阅 | 5–8 | 中 |
| 自动更新/关于页/旧版清理/窗口状态 | updater 438 行 + legacy + window state | — | **D** | Web 无客户端;关于页可保留为「帮助/版本信息」薄页 | 0–1 | 低 |
| 验证门禁资产 | 32 个 verify 脚本 + fixtures | 新平台测试套件 | **B** | `.mjs` 纯逻辑断言可直接用;真实抓包 fixture 可做契约测试;UI 断言(472 条)大部分随新 UI 失效,需重写 | 6–10 | 低中 |
**合计粗估:约 115–190 人日**(低值=理想路径、无返工;高值=含平台改版适配/联调/真机验证)。其中「送审页面自动化 + 账号体系 + 下载引擎」三项占 **50% 以上**,是 Web 化成败关键。
### 8.1 Semi Design 2 与新平台 UI 选型的冲突处理
**冲突事实**:当前 UI 是 2026-10 从 Ant Design 全量迁到 **Semi Design 2.103**(React 19),组件 API/主题/图标/表单校验/表格/弹窗都深度绑定 Semi;页面级 CSS 3,675 行大量使用 `--semi-color-*` token、`@layer tailwind-utils` 与 `.semi-*` 类名。如果新平台 UI 选型是 Ant Design/Arco/自研,**组件层不能直接复用**。
**建议策略(按投入产出排序)**:
1. **分层复用,不整体搬迁**:把「纯逻辑 + 类型 + 接口封装 + 页面状态机」与「视觉组件」切开。前者(约 30–40% 代码量)跨 UI 库可复用;后者重写。
2. **先定设计 token,再谈组件**:把 Semi 的语义色/圆角/间距收敛成新平台 token(现在代码里已有集中化迹象:`icons.tsx`、`PageHeader`、`ProgressRow`、`SettingsRow`),避免把 `--semi-*` 变量原样搬进新平台。
3. **只对高价值组件做适配层**:表格(审核列表)、弹窗/抽屉(送审判定)、进度行、Toast 提示可写薄适配层;不要为每个组件做 Semi↔新库 shim(维护成本会超过重写)。
4. **样式资产可复用的部分**:布局类(`.shop-*`/`.dl-*`/`.review-*`)本质是 CSS Grid/Flex,可提取为设计系统布局原语;**依赖 `.semi-*` 内部 DOM 的样式必须丢弃**(`verify:css-layers` 也说明层叠顺序是精心维护的)。
5. **如果新平台也选 Semi**:可复用度显著提高(页面组件可近乎直搬),但仍需处理 React 版本、主题、路由与 Electron API 替换。**建议在 UI 选型评审时把「是否 Semi」作为明确决策项**,因为它直接决定 8–12 人日的差异。
---
## 9. 可复用资产清单
### 9.1 纯逻辑 / 协议层(最高价值,A 级)
| 资产 | 路径 | 价值 |
|---|---|---|
| 星光请求签名 + 序列化规则 + 自校验 | `dw/scripts/lib/platform-sign.mjs`(含 SALT,敏感值已脱敏)、`dw/electron/platform/sign.ts` | 服务端直连星光的关键;自校验能第一时间发现盐变更 |
| 平台字段映射 / 授权时间解析 / 审核状态映射 / 权限判定 | `dw/scripts/lib/platform-material-mapping.mjs`(719 行) | 与真实抓包逐字段对齐;P1/P2 的地基 |
| 送审命名解析 + 预检/载荷/审计记录形状 | `dw/scripts/lib/submit-naming.mjs`(1,013 行) | 新平台「命名规则 + 提审预检」可直接复用;有大量真实样本 |
| 混剪成品/源组/授权状态/结构校验 | `dw/scripts/lib/mix-submit.mjs`(约 500 行) | 混剪业务规则唯一实现 |
| 审核 CSV 构建 + 公式注入防护 | `dw/scripts/lib/review-csv.mjs` | 导出与界面列同源;安全细节可复用 |
| 驳回原因 HTML 白名单 AST | `dw/scripts/lib/reason-html.mjs`(1,028 行) | 平台自由文本渲染的安全方案(非 `dangerouslySetInnerHTML`) |
| 素材过滤规则(30 天/渠道) | `dw/scripts/lib/material-filter.mjs` | 与 PRD「授权/到期」相关的业务规则 |
| DPLink 拼装(双层/encode/常量) | `dw/scripts/lib/dplink-link.mjs` | 4 条样本 4/4 逐字符复现,可直接迁移 |
| 进度模型 / 送审进度模型 | `dw/scripts/lib/progress-model.mjs`、`submit-progress.mjs` | 统一进度语义与界面断言 |
| 远端配置 schema/白名单/区间 | `dw/scripts/lib/remote-config-schema.mjs` | 若新平台需要「运维不发版改配置」,可直接复用设计 |
| Cookie 分区 bundle(v1/v2 兼容) | `dw/scripts/lib/cookie-bundle.mjs` | 多账号凭据分区与迁移思路;**传输方式需重写** |
| Ed25519 原始字节签名/验签 | `dw/scripts/lib/ed25519-signature.mjs`、`update-metadata.mjs`、`browser-manifest-trust.mjs` | 信任链设计可迁移到配置中心/发布系统 |
### 9.2 服务端候选代码(B 级,抽包后可用)
| 资产 | 路径 | 说明 |
|---|---|---|
| 只读 HTTP 客户端 + 信封解析 + 失败分类 | `dw/electron/platform/http.ts`、`client.ts`、`types.ts` | 超时/重试/响应上限/`code` 分类;与登录层共用一份 |
| 素材列表/详情取数(并发 4、降级可见) | `dw/electron/platform/materials.ts` | 直接改造成服务端模块 |
| 审核列表/记录/枚举/标签树 + TTL 缓存 | `dw/electron/platform/reviews.ts`、`cache.ts` | 缓存/TTL/stale 语义清晰 |
| 授权渠道预检(unknown 不阻断) | `dw/electron/platform/authorization.ts` | 送审前风控策略可直接用 |
| 商品详情 SSR 解析 + 短链解析 | `dw/electron/shop/detail.ts` | 无浏览器取数的高价值实现 |
| 商品素材下载/命名/URL 白名单 | `dw/electron/shop/materials.ts` | 原始物料落盘结构参考 |
| 送审批次策略机(串行/中断/回查/记账/审计) | `dw/electron/submit/{batch,plan,verify,idempotency,audit}.ts` | 写链路业务策略层,独立于 Playwright |
| DPLink 拼装编排/错误分类 | `dw/electron/dplink/link.ts`、`account.ts` | 依赖硬广账号;接口参数已在文档中核实到「两个只读接口」 |
### 9.3 前端页面/组件(B 级,需去 Semi 化)
- 页面:`DownloadPage`(任务工作台与三阶段布局)、`ShopPage`+`ActionBar`+`MaterialGrid`(工作台布局)、`ReviewPage`+`ReviewTable`+`ReviewFilters`+`ReviewDetailModal`+`PublishRecordsModal`(审核表格与筛选)、`SubmitPrecheckModal`(预检/就地编辑/批量补字段)、`MixSubmitModal`(双栏配对)、`SubmitProgressPopover`/`SubmitProgressBody`(进度浮层)、`ConfigPage`(分区清单设置页)、`UpdatePage`/`AboutPage`(可参考信息架构)。
- 通用组件:`PageHeader`、`ProgressRow`、`SettingsRow`、`SettingsSection`、`MaterialPreview`、`SpuCanvas`(仅参考承载思路)、`CopyButton`(页面内)、`ErrorBoundary`、4 个骨架屏、`LoginDialog`、`SidebarLoginStatus`、`DplinkAccountDialog`、`DplinkLandingDialog`。
- 状态/工具:`src/contexts/{Theme,LoginStatus,UnsavedChanges}.tsx`(未保存守卫可直接复用思路)、`src/utils/spu.ts`(SPU 解析/价格/最近查询)、`spuIdList.ts`(多 SPUID 归一化/校验)、`updateLabels.ts`、`remote-config-label.ts`。
- 类型契约:`src/types/index.ts`(1,000+ 行,含 ElectronAPI、IPC 载荷、审核/送审/混剪/下载/更新类型)、13 个 `.d.ts` 纯逻辑类型声明——可作为**新平台 API DTO 草案**,但需与后端共同评审。
### 9.4 文档(可直接迁移,价值排序)
| 文档 | 行数 | 迁移价值 |
|---|---|---|
| `docs/星光平台API接口文档.md` | 807 | ⭐⭐⭐⭐⭐ 接口/签名/枚举/选择器/错误码全量,是服务端复现的**唯一权威**(注意含盐值,不可外发) |
| `docs/送审命名规范与预检规则.md` | 235 | ⭐⭐⭐⭐⭐ 命名/解析/预检/批量语义,直接对应 PRD P-04 |
| `docs/星光平台送审与审核查询-可行性评估.md` | 928 | ⭐⭐⭐⭐ 读写分离、页面自动化理由、单会话约束 |
| `docs/DPLink组成与一键复制-可行性评估.md` | 545 | ⭐⭐⭐⭐ 双层链接算法/字段/实测样本,对应 PRD P-05 |
| `docs/P1读数据接口化-实现说明.md` / `P2素材审核查询-实现说明.md` / `P4素材送审-实现说明.md` | 540/516/1,068 | ⭐⭐⭐⭐ 实现取舍、真机踩坑、门禁清单(P4 含 19 轮真机记录) |
| `docs/混剪素材批量上传-开发方案.md` / `混剪素材批量送审-实现说明.md` | 496/151 | ⭐⭐⭐ 混剪业务规则与 Phase 2 方向 |
| `docs/接口登录通道-实现说明.md` | 273 | ⭐⭐⭐ 登录接口化实测与红线 |
| `docs/远端配置运维手册.md` | 190 | ⭐⭐⭐ 白名单/签名/revision/应急停用;可迁移为配置中心 SOP |
| `docs/项目评估与优化建议-2.1.0.md` | 1,063 | ⭐⭐⭐ 架构/性能/安全/测试的系统性评估(部分已修,需按现状核对) |
| `DEVELOPMENT.md` | 2,380 | ⭐⭐⭐ 目录/主进程/IPC/配置/踩坑全量,但基线 v1.0.6,**只读 §3-§8 与 §17-§19,其余核对代码** |
| `docs/签名与公证落地清单.md`、`构建发布与版本更新指南.md` | 227/859 | ⭐⭐ 桌面端专用,Web 化后仅作历史参考 |
---
## 10. 风险与待确认问题
### 10.1 风险
**R1 平台合规/封禁(高)**
- 本项目是**未授权的页面自动化 + 接口逆向**:签名盐从平台前端产物取得,账号是客户共享账号;平台条款是否允许此类工具、是否会把团队账号风控,**没有任何书面确认**。
- Web 化后从「个人电脑 IP」变为「机房 IP + 多用户高频」,平台侧流量特征更显眼,封号/限流风险上升。
- 建议:法务/客户成功提前确认;保留「只读优先、写操作人工确认、可一键停用」的设计(现有 `enableSubmitReview` 就是范式)。
**R2 登录态与账号归属(高)**
- 平台**单账号单会话**:网页版登录会顶掉应用 token;多用户 Web 化若共享一个 token 会互踢。现有云端 Cookie 同步(HTTP+FTP 明文)是「团队共享一个账号」的临时方案,不适合作为平台级账号体系。
- 硬广账号是客户给团队的**共用账号**,DPLink 依赖它;多人同时使用/退出语义(退出只清本机)需要产品化定义。
- 账号归属、权限(platform 的 `user/detail` 权限菜单表)、数据可见范围(谁看谁的送审/审核)都需要新平台的权限模型。
**R3 接口/页面稳定性(高)**
- 签名盐、字段、枚举、选择器、权限菜单都可能随平台发版变化;本项目用 32 个验证脚本 + 真机对比脚本 + 抓包 fixture 对抗,但**没有 SLA/版本协商**。
- 混剪「混剪=是」写链路、登录失败码、素材审核部分字段尚未真机覆盖(文档已标注)。
- 新平台应把「平台适配层」隔离为可独立发版的服务,并提供「平台变更检测/健康检查」机制(签名自校验已有雏形)。
**R4 Electron 能力缺口(中)**
- 内嵌 webview、本地文件路径/对话框、剪贴板、系统通知、打开目录、safeStorage、FTP、自动更新、代码签名、窗口状态——Web 端均无等价物,需要替代方案(对象存储、浏览器下载、通知中心、KMS、部署流水线)。
- 特别是**送审大文件**(140–190MB)从「本地路径 `setInputFiles`」变为「浏览器→服务端→平台」,带宽与上传成功率是新增风险;建议优先设计「客户端→OSS 直传 + 服务端只发凭证」。
**R5 数据迁移(中)**
- 本地 `config.json`/`cookies.json`/`dplink-*`/`submitted-md5.json`/`submit-audit.jsonl`/下载目录命名规则都是「单机事实」;迁移到多用户平台需要:
- 账号与凭据迁移(safeStorage 加密跨机器不可解,必须重新登录);
- 已送审账本(避免重复送审)与审计日志的历史价值;
- 下载文件/命名规则兼容(历史文件如何进新素材库);
- 本地路径偏好(下载目录)在新平台无意义。
- 建议迁移策略:**不做客户端数据自动迁移**,只提供「md5 账本 + 审计日志导入」与「重新登录」;历史素材通过新平台重新拉取/上传。
**R6 凭据泄露(高,且为既成事实)**
- 星光/FTP 凭据曾在源码与 git 历史中;即使轮换,旧仓库/文档/随包产物仍可能流出。新平台**不得复用**这些凭据;上线前必须轮换并审计。
### 10.2 待确认清单
| 编号 | 问题 | 为什么影响整合 | 建议向谁确认 |
|---|---|---|---|
| Q-01 | 星光平台是否允许内部工具/服务端自动化?客户/平台方对此的书面态度? | 决定「服务端重写送审」是否合规,可能直接否决整个写链路 | 业务方 + 客户成功 + 法务 |
| Q-02 | 新平台的账号体系:一人一星光账号,还是团队共享账号池? | 决定会话串行化、权限、审计与凭据设计;现有单账号假设不成立 | 产品负责人 + 客户 |
| Q-03 | 新平台是否也要服务唯品会?其后台是否有可复用的接口/登录方式? | 决定 P-06 是否另起一套适配层;本项目零参考 | 产品 + 另一项目调研(sucaigongfang) |
| Q-04 | DP / 承接页的准确业务口径是什么?DPLink 是否就是 PRD 所指的「自动配置」? | 决定 P-05 的范围;本项目只做了得物 DPLink(iOS/安卓唤端链接),不含 DP 字段 | 业务方(PRD 1.1.3) |
| Q-05 | 新平台的 UI 选型是否包含 Semi?还是 Ant Design/Arco/自研? | 直接决定前端 8–12 人日差异与组件复用边界(§8.1) | 设计负责人 + 前端负责人 |
| Q-06 | 平台原始素材(图片/视频)在新平台的存储形态:对象存储?CDN?保留多久? | 决定下载引擎改造与成本;现有「本地目录 + 文件名」语义要替换 | 架构 + 运维 |
| Q-07 | 「图片/视频 AI 产线」是否已有供应商/模型选型? | 与 dewu 项目无关,但影响整体排期与数据流(原始物料→产线→提审) | 产品 + AI 负责人 |
| Q-08 | 混剪送审是否仍由人工配对,还是需要 Excel/CSV 导入(Phase 2)? | 决定 8–12 人日 UI 的范围;当前 Phase 2 未实现 | 业务方 |
| Q-09 | 历史数据(已送审 md5、审计日志、下载文件)是否需要迁移? | 决定迁移成本与「重复送审」风险 | 业务方 + 产品 |
| Q-10 | 写链路是否保留「人工确认 + 可中断 + 远端一键停用」的安全设计? | 若要求无人值守批量提交,风险与实现复杂度都会显著上升 | 业务方 + 风控 |
| Q-11 | 平台接口的 SLA/变更通知机制是否存在?谁负责平台改版后的适配? | 决定长期维护成本归属(≈1 人持续投入) | 客户/平台方 + 研发负责人 |
| Q-12 | 账号权限菜单(`user/detail.list`)是否可作为新平台的功能门禁依据? | 现有实现只做「建议不硬拦」;若要做硬权限需确认菜单稳定性 | 平台方 + 产品 |
---
## 11. 证据索引
> 路径前缀 `dw/` = `参考项目/dewu-material-Downloader/`。以下为关键结论的最小证据集(≥25 条)。
| # | 结论 | 证据 |
|---|---|---|
| 1 | 版本 2.7.0、应用标识 | `dw/package.json:3`、`dw/electron-builder.yml:1-2` |
| 2 | 技术栈(Electron 44 / React 19 / Semi 2 / Playwright) | `dw/package.json:70-96` |
| 3 | 6 个一级入口与 BETA 标记 | `dw/src/App.tsx:240-306` |
| 4 | 6 条路由 | `dw/src/App.tsx:437-442` |
| 5 | 101 条 IPC 通道、87 invoke + 14 event | `dw/electron/ipc/channels.ts:1`、`dw/electron/preload.ts:15,87,124,190,230,300` |
| 6 | 写链路红线(不直接调写接口) | `dw/electron/submit/page.ts:2-30` |
| 7 | 提交接口路径与上传超时 15 分钟 | `dw/electron/submit/page.ts:47,68` |
| 8 | 送审抽屉选择器(按 label 精确匹配、限定抽屉) | `dw/electron/submit/page.ts:77-110,209-222` |
| 9 | 批量送审策略表(成功/重复/失败/超时/掉线) | `dw/electron/submit/batch.ts:1-30` |
| 10 | 只按 md5 幂等 + 双向同步 | `dw/electron/submit/idempotency.ts:1-36` |
| 11 | 审计/请求体 JSONL 路径 | `dw/electron/submit/audit.ts:29-40,151` |
| 12 | 命名规范(模式 A/B、末三段、无 spuid) | `dw/docs/送审命名规范与预检规则.md:20-93` |
| 13 | 下载命名规则 | `dw/electron/downloader/utils.ts:11-20` |
| 14 | 下载分批/并发/冷却/重试 | `dw/electron/downloader/manager.ts:181-227,374-430` |
| 15 | MD5 全量同步扫描(性能债) | `dw/electron/downloader/manager.ts:174`、`dw/electron/downloader/utils.ts:78-100` |
| 16 | 星光签名盐与算法 | `dw/scripts/lib/platform-sign.mjs:20-37,73-106` |
| 17 | 素材列表/详情接口路径 | `dw/electron/platform/materials.ts:91,177` |
| 18 | 审核/记录/枚举/账号接口路径 | `dw/electron/platform/reviews.ts:43-48` |
| 19 | 登录接口路径与 token 事实 | `dw/electron/platform/auth.ts:1-70` |
| 20 | 接口登录实测 9/9 | `dw/docs/接口登录通道-实现说明.md:146-169` |
| 21 | 平台 API 全量文档(含签名/枚举/送审) | `dw/docs/星光平台API接口文档.md:130,197,229,277,326,342,366,379,522,594` |
| 22 | 读写分离与单会话红线 | `dw/docs/星光平台送审与审核查询-可行性评估.md:1-120`、`dw/docs/新功能开发计划.md:455-475` |
| 23 | 送审四步链路与 SPU 误报教训 | `dw/docs/星光平台API接口文档.md:340-520` |
| 24 | 远端配置三道防线/白名单 | `dw/electron/config/remote.ts:1-40`、`dw/scripts/lib/remote-config-schema.mjs:32-53` |
| 25 | 送审总开关/线上配置 revision 2 | `dw/remote-config/app-config.json:26,72` |
| 26 | 硬编码凭据仍在源码 | `dw/electron/config/developer.ts:130,135` |
| 27 | 三档下载参数与远端区间 | `dw/electron/config/presets.ts:59-102`、`dw/scripts/lib/remote-config-schema.mjs:60-80` |
| 28 | 商品详情 SSR 直取(无浏览器) | `dw/electron/shop/detail.ts:11-27,182-280` |
| 29 | 商品素材落盘结构/URL 白名单 | `dw/electron/shop/materials.ts:24-26,82-95` |
| 30 | 内嵌页 UA/分区/净化(Electron 依赖) | `dw/electron/shop/session.ts:32-66,198-200` |
| 31 | DPLink 双层算法与接口 | `dw/docs/DPLink组成与一键复制-可行性评估.md:12-35`、`dw/electron/dplink/link.ts:39-41` |
| 32 | DPLink 硬广凭据隔离 | `dw/electron/dplink/account.ts:1-40` |
| 33 | Cookie 同步 v2 分区(soft/dplink) | `dw/electron/browser/cookieSync.ts:1-40` |
| 34 | 登录态状态机与串行队列 | `dw/electron/browser/manager.ts:46-55,223` |
| 35 | 浏览器组件三级检测/验签 | `dw/electron/browser/playwrightInstaller.ts:1-125,436` |
| 36 | 更新元数据验签/强制/最低版本 | `dw/electron/updater/manager.ts:185,231,274-301` |
| 37 | CI 三 job 与门禁清单 | `dw/.github/workflows/ci.yml:53-217,257` |
| 38 | 验证脚本规模(verify:ui 480 通过 / verify:platform 260 通过) | `dw/scripts/verify-ui-behavior.mjs`(5,283 行)、`dw/scripts/verify-platform.mjs`(2,352 行) |
| 39 | TypeScript strict | `dw/tsconfig.json` |
| 40 | 项目评估健康度 3.0/5 与 P0 清单 | `dw/docs/项目评估与优化建议-2.1.0.md:27-43,314,455,705` |
| 41 | 素材过滤规则(30 天/未授权快手) | `dw/scripts/lib/material-filter.mjs`、`dw/docs/素材下载过滤开关-实现说明.md:1-40` |
| 42 | 混剪 Phase 1 未真机 | `dw/docs/混剪素材批量送审-实现说明.md:1-65` |
| 43 | 审核状态映射与 CSV 列 | `dw/scripts/lib/platform-material-mapping.mjs:424-506`、`dw/scripts/lib/review-csv.mjs:34-45` |
| 44 | DEVELOPMENT.md 基线过时(v1.0.6 vs 2.7.0) | `dw/DEVELOPMENT.md:1-13,1715-1830` |
| 45 | 无唯品会代码 | 全仓检索 `唯品会\|vipshop\|vip.com` 零命中 |