- 新增双项目整合可行性评估 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 文档索引与仓库结构
83 KiB
dewu-material-Downloader 调研报告
调研对象:
参考项目/dewu-material-Downloader/(应用名「得物素材助手」,v2.7.0,git HEADfe7646b)。 密级:内部;文中平台接口/签名等敏感信息已脱敏,原文见受控知识库。 调研方式:只读通读源码与文档,交叉核对 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:uiv2.7.0 记录 480 条通过、verify:platform260 条通过;脚本内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,也没有批量下载能力。运营与剪辑的日常是:
- 在软广视频库里逐条点「预览」,复制视频地址,手工记录
媒体任务ID / 视频ID / SPUID / 投放类目 / 授权时间; - 手工改名落盘,反复登录网页版导致桌面工具掉线;
- 剪辑后回到素材池「KOL原生二创素材上传」抽屉,逐条填投放渠道、媒体任务ID、视频ID、SPUID、挂 140–190MB 成片、点提交;
- 被拒后去审核列表逐条看驳回原因;
- 投放前再去星光「商品生成链接」页手工选落地页、商品,复制 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):
- 业务写只走页面自动化,绝不直接调用写接口;唯一豁免是登录(
electron/platform/auth.ts只含两条硬编码路径且不重试); - 不另开浏览器 context(平台单账号单会话,另开会被判新会话并踢掉自己);
- 提交超时只回查、不重试;
- 筛选/取数失败显式失败,不静默降级为「下载全部」;
- 选择器不绑哈希类名(
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 |
登录方式(三条通道)
- 接口登录(主力,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)。 - 浏览器登录(兜底):有头 Chromium 打开登录页,轮询 120 秒 + 登录后
detectLoginState()再确认;成功后切无头;换账号时先清本地 Cookie。 - 云端 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 目标形态(供后续架构评审):
- BFF + Worker 两层:BFF 走只读接口与元数据;Worker 承担 Playwright 浏览器任务(送审、登录兜底),按账号加分布式锁,任务状态落 Redis/DB,前端用 SSE/WebSocket 订阅进度(替代 IPC 事件)。
- 上传走「浏览器/客户端 → OSS 直传」:服务端只发 STS 临时凭证并登记 mediaId,避免 140MB 文件绕行应用服务器;必要时再让服务端浏览器
setInputFiles指向挂载的临时对象。 - 凭据服务:平台 token 与硬广 token 分别加密存储(KMS + 信封),按「用户/项目/账号」授权;禁止再出现仓库内硬编码密钥。
- 账号串行化:以
accountId为粒度排队;同一账号的下载/送审/审核查询并发需互斥(沿用现在runExclusive的思路,但改为分布式锁)。 - 只读优先:把「读」全部走接口(无浏览器),仅「写」用浏览器,显著降低风控暴露面与资源成本。
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;更新元数据
.sigEd25519 验签 → 强制/最低版本/跳过版本;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.ts51 处、handlers.ts15 处、src16 处(跨进程类型未共享,历史评估标 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.md2,380 行,是以代码为准的交接文档,但其 §1-§14 基线停在 v1.0.6,已明显滞后于 v2.7.0;§17-§19 是后续补的登录态/商品查询/接口登录章节。新平台调研应优先信任代码与 2.x 的实现说明文档。- 已知问题与风险(代码/文档交叉确认):
- 🔴 仓库内仍硬编码运维凭据(
cookieSyncKey、FTP 密码,dw/electron/config/developer.ts:130,135),且在 git 历史中;Web 化前必须轮换、改服务端密钥管理,不可沿用。 - 🟠 单账号单会话是平台行为(网页版登录顶掉应用 token);多用户 Web 化必须按账号串行/分账号。
- 🟠 明文 HTTP + FTP 无 TLS 用于更新/远端配置/Cookie 同步;新平台应拒绝此模式。
- 🟠 平台接口/页面结构随时可能变更:签名盐、字段、选择器、权限菜单;维护成本高(
docs/多篇文档都在记录平台变更)。 - 🟡 Playwright 依赖声明
^1.40.0与实际 1.60.0 漂移(已有verify:playwright-version守版本→revision 映射,但锁文件仍应 pin)。 - 🟡 浏览器分发/更新/签名/旧包清理的复杂度对 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/自研,组件层不能直接复用。
建议策略(按投入产出排序):
- 分层复用,不整体搬迁:把「纯逻辑 + 类型 + 接口封装 + 页面状态机」与「视觉组件」切开。前者(约 30–40% 代码量)跨 UI 库可复用;后者重写。
- 先定设计 token,再谈组件:把 Semi 的语义色/圆角/间距收敛成新平台 token(现在代码里已有集中化迹象:
icons.tsx、PageHeader、ProgressRow、SettingsRow),避免把--semi-*变量原样搬进新平台。 - 只对高价值组件做适配层:表格(审核列表)、弹窗/抽屉(送审判定)、进度行、Toast 提示可写薄适配层;不要为每个组件做 Semi↔新库 shim(维护成本会超过重写)。
- 样式资产可复用的部分:布局类(
.shop-*/.dl-*/.review-*)本质是 CSS Grid/Flex,可提取为设计系统布局原语;依赖.semi-*内部 DOM 的样式必须丢弃(verify:css-layers也说明层叠顺序是精心维护的)。 - 如果新平台也选 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 零命中 |