docs: 项目文档、设计图、工具、协议模块、部署说明
@@ -0,0 +1,36 @@
|
|||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# 依赖(所有层级)
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# 顶层 dist(仅保留部署说明与编排文件,二进制/zip 不入库)
|
||||||
|
/dist/*
|
||||||
|
!/dist/deploy/
|
||||||
|
/dist/deploy/*
|
||||||
|
!/dist/deploy/deploy-README.md
|
||||||
|
!/dist/deploy/docker-compose.yml
|
||||||
|
!/dist/deploy/.env.example
|
||||||
|
|
||||||
|
# 前端构建产物
|
||||||
|
apps/web/dist/
|
||||||
|
|
||||||
|
# C# / .NET 构建产物
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
|
||||||
|
# 二进制与归档(任意层级)
|
||||||
|
*.exe
|
||||||
|
*.blockmap
|
||||||
|
*.zip
|
||||||
|
builder-debug.yml
|
||||||
|
client/dist/ # agent-core 编译产物(win/darwin)
|
||||||
|
client/desktop/icons/ # 生成图标(gen-icon.js/gen-ico.js 可重生成)
|
||||||
|
|
||||||
|
# 本地运行数据与密钥(绝不入库)
|
||||||
|
server/data/
|
||||||
|
server/.env
|
||||||
|
client/core/data/
|
||||||
|
client/desktop/release/
|
||||||
|
client/desktop/win-unpacked/
|
||||||
|
*.log
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
.PHONY: run build test tidy infra-up infra-down health
|
||||||
|
|
||||||
|
run: ## 本地运行服务
|
||||||
|
cd server && go run .
|
||||||
|
|
||||||
|
build: ## 编译二进制
|
||||||
|
cd server && go build -o ../dist/everypublish-server .
|
||||||
|
|
||||||
|
test: ## 单测
|
||||||
|
cd server && go test ./...
|
||||||
|
|
||||||
|
tidy: ## 整理依赖
|
||||||
|
cd server && go mod tidy
|
||||||
|
|
||||||
|
infra-up: ## 启动 MySQL/Redis(Docker)
|
||||||
|
docker compose -f server/docker-compose.yml up -d
|
||||||
|
|
||||||
|
infra-down: ## 停止 MySQL/Redis
|
||||||
|
docker compose -f server/docker-compose.yml down
|
||||||
|
|
||||||
|
health: ## 健康检查
|
||||||
|
curl -s http://127.0.0.1:8090/health
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# EveryPublish 项目总文档(唯一入口)
|
||||||
|
|
||||||
|
**一句话定位**:企业多平台官方账号的发布中台——网页控制台协作、审核、排期,客户端单点持号、自动发布。
|
||||||
|
|
||||||
|
> 当前状态:开发进行中(阶段一网页端 D1)。进度跟踪见 docs/progress.md。
|
||||||
|
|
||||||
|
## 文档地图(全部文档一览)
|
||||||
|
|
||||||
|
| # | 文档 | 用途 | 主要读者 | 状态 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 1 | [docs/prd.md](docs/prd.md) | 产品需求:做什么、给谁用、体验与验收要点 | 全员 | v1.0 待评审 |
|
||||||
|
| 2 | [docs/tech-stack.md](docs/tech-stack.md) | 技术栈定版(唯一权威来源) | 开发 | 定版 |
|
||||||
|
| 3 | [docs/system-design.md](docs/system-design.md) | 系统设计:功能清单/API/鉴权/传输/数据模型 | 开发 | v1 |
|
||||||
|
| 4 | [docs/logic-diagrams.md](docs/logic-diagrams.md) | 11 张逻辑图全集(架构/时序/状态机/排期) | 全员 | 完成 |
|
||||||
|
| 5 | [docs/delivery-plan.md](docs/delivery-plan.md) | 日计划 D1-D13 + 每步验收标准 + 风险 | 开发/管理 | 完成 |
|
||||||
|
| 6 | [docs/page-wireframes.md](docs/page-wireframes.md) | 全部 22 个页面的 ASCII 线框图 | 前端 | 完成 |
|
||||||
|
| 7 | [docs/frontend-guide.md](docs/frontend-guide.md) | starter 组件清单/场景映射/过渡效果/套用规范 | 前端 | 完成 |
|
||||||
|
| 8 | [docs/dev-schedule.md](docs/dev-schedule.md) | AI 加速排期(参考,已被 #5 取代) | 管理 | 归档 |
|
||||||
|
| 9 | [docs/multi-platform-publish-plan.md](docs/multi-platform-publish-plan.md) | 方案总纲:调研/开源聚合/IP风控/协议考证 | 全员(背景) | 完成 |
|
||||||
|
| 10 | [docs/progress.md](docs/progress.md) | 开发进度跟踪:逐日验收状态实时更新 | 全员 | 进行中 |
|
||||||
|
| 12 | [docs/web-test-report.md](docs/web-test-report.md) | 网页端测试报告(检查点①) | 全员 | 已验收 ✅ |
|
||||||
|
| 13 | [docs/client-test-checklist.md](docs/client-test-checklist.md) | Windows 客户端测试清单(检查点②) | 开发/测试 | 待验收 |
|
||||||
|
| 11 | [docs/api.md](docs/api.md) | 服务端接口文档(认证/工作区/成员/账号/素材/任务/挑战/通知/审计) | 开发 | v1 |
|
||||||
|
|
||||||
|
## 阅读路径
|
||||||
|
|
||||||
|
- **产品评审**:1 → 4 → 5
|
||||||
|
- **开发开工**:2 → 3 → 5 → 7 → 6
|
||||||
|
- **新人上手**:1 → 9 → 4 → 3
|
||||||
|
- **向客户/上级汇报**:1(定位与范围)+ 5(排期)
|
||||||
|
|
||||||
|
## 关键决策速览
|
||||||
|
|
||||||
|
| 项 | 决策 |
|
||||||
|
|---|---|
|
||||||
|
| 技术栈 | React+TDesign(页面)/ Go(网页端服务层+客户端核心)/ WinUI 3(客户端壳)/ MySQL 8 + Redis(数据) |
|
||||||
|
| MVP 范围 | 网页端全部 + Windows 客户端 + 抖音/快手/小红书/B站;素材本地盘直链 |
|
||||||
|
| 二期 | 视频号、官方API通道(X/IG/YouTube/TikTok)、OSS、托管Agent |
|
||||||
|
| 连接方式 | WSS 主通道(Agent 出站,Ed25519 设备签名)+ HTTPS 降级;大文件直链不走 WSS |
|
||||||
|
| 鉴权 | 用户 JWT+refresh;Agent 配对码+设备密钥;平台账号 cookie 仅存客户端本地 |
|
||||||
|
| 职责边界 | 网站=分配任务+收状态(零平台凭据);客户端=持号+登录鉴权+发布执行(凭据仅本机) |
|
||||||
|
| 红线 | 矩阵养号/搬运去重绕过/刷量/私信轰炸一律不做 |
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
- apps/web —— 前端(TDesign starter,MIT)
|
||||||
|
- server/ —— Go 服务器(REST + WSS 网关 + 队列;端口 8090)
|
||||||
|
- shared/ —— 协议 Go module(server 与 agent-core 共用)
|
||||||
|
- client/ —— Windows 客户端(ui WinUI3 + core agent-core,D8 起)
|
||||||
|
- docs/ —— 全部文档(见上表)
|
||||||
|
- diagrams/png —— 11 张逻辑图 PNG(2400px 宽,白底);diagrams/mmd —— mermaid 源文件
|
||||||
|
- tools/ —— 联调诊断(diag-connection.mjs)
|
||||||
|
|
||||||
|
## 开发进行中(阶段一 D1-D7)
|
||||||
|
|
||||||
|
- 排期:docs/delivery-plan.md(含检查点①网页端测试 / 检查点②客户端测试,均需用户确认)
|
||||||
|
- 甘特图:diagrams/png/11-开发排期甘特.png
|
||||||
|
- 本地启动:`make infra-up`(MySQL)→ `make run`(Go 后端 :8090)→ `cd apps/web && pnpm dev`(前端 :3003)
|
||||||
|
- 部署态(单进程):`cd apps/web && pnpm build` 后直接 `make run`,Go 服务在 :8090 同时托管前端 + API + WSS(无需 vite)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
flowchart TB
|
||||||
|
subgraph WEB["① 控制端 Web 多租户SaaS"]
|
||||||
|
U["运营 / 审核 / 管理员"]
|
||||||
|
W["任务编排 · 素材库 · 账号台账 · 审计"]
|
||||||
|
U --- W
|
||||||
|
end
|
||||||
|
subgraph CORE["② 调度中心 服务器"]
|
||||||
|
Q["任务队列 Redis(asynq) · 状态机 · 排期器"]
|
||||||
|
API["通道A 官方API执行器 OAuth"]
|
||||||
|
AGW["通道B Agent网关 WSS注册/心跳/路由"]
|
||||||
|
end
|
||||||
|
subgraph AGENT["③ 执行端 Agent 客户侧安装"]
|
||||||
|
WS["WSS长连接 主动出网回连"]
|
||||||
|
VAULT["凭据保险库 cookie本地加密"]
|
||||||
|
ROUTER["代理路由器 一账号一IP"]
|
||||||
|
PA["浏览器执行器 go-rod CDP 独立档案"]
|
||||||
|
QR["扫码登录面板"]
|
||||||
|
end
|
||||||
|
WEB -->|"任务下发"| Q
|
||||||
|
Q -->|"通道A 官方API"| API
|
||||||
|
Q -->|"通道B 自动化"| AGW
|
||||||
|
AGW --> WS --> ROUTER --> PA
|
||||||
|
VAULT --- PA
|
||||||
|
QR --- VAULT
|
||||||
|
API --> INT["国际 X/IG/YouTube/TikTok"]
|
||||||
|
PA --> DOM["国内 抖音/快手/小红书/视频号/B站"]
|
||||||
|
PA --> INT
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
flowchart TB
|
||||||
|
subgraph CP["控制面 我方服务器 无平台流量"]
|
||||||
|
WEB["控制台/API"] --- Q["队列/排期/状态机"]
|
||||||
|
Q --- GW["Agent网关 WSS"]
|
||||||
|
end
|
||||||
|
subgraph DP["数据面 客户Agent 全部平台流量"]
|
||||||
|
WS["WSS客户端"] --- EX["任务执行器"]
|
||||||
|
EX --- V["凭据保险库"]
|
||||||
|
EX --- PR["代理路由"]
|
||||||
|
end
|
||||||
|
CP -->|"任务元数据 WSS <1KB"| DP
|
||||||
|
DP -->|"结果/挑战 WSS"| CP
|
||||||
|
DP -->|"上传/发布 平台流量"| P["目标平台"]
|
||||||
|
CP -.->|"绝不触达"| P
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
flowchart LR
|
||||||
|
subgraph M["Mac 开发机 内网测试环境"]
|
||||||
|
WEB["网页端 dev"] --> S["服务器 API + WSS网关"]
|
||||||
|
end
|
||||||
|
subgraph W["Windows 客户机"]
|
||||||
|
APP["客户端App Agent<br/>WinUI壳+Go执行核心"]
|
||||||
|
end
|
||||||
|
W -->|"WSS ws://局域网IP:端口"| S
|
||||||
|
S --> DB[("MySQL + Redis")]
|
||||||
|
S --> FS[("素材 服务器本地盘")]
|
||||||
|
APP -->|"直链下载素材 带签名token"| FS
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
flowchart LR
|
||||||
|
ACC["账号台账"] --> REG{"账号归属地?"}
|
||||||
|
REG -->|国内平台| POOL1["国内住宅IP池<br/>芝麻/快代理/922S5等"]
|
||||||
|
REG -->|国外平台| POOL2["海外住宅IP池<br/>BrightData/IPRoyal等"]
|
||||||
|
POOL1 --> BIND["一账号绑定一IP<br/>长期固定不轮换"]
|
||||||
|
POOL2 --> BIND
|
||||||
|
BIND --> CTX["浏览器Context级注入<br/>非全局TUN 防串账号"]
|
||||||
|
CTX --> EXEC["执行发布"]
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
stateDiagram-v2
|
||||||
|
[*] --> 队列中
|
||||||
|
队列中 --> 执行中: Agent认领
|
||||||
|
执行中 --> 已发布: 平台回执成功
|
||||||
|
执行中 --> 网络重试: 网络/超时错误
|
||||||
|
网络重试 --> 执行中: 退避1m/5m/15m/1h
|
||||||
|
网络重试 --> 失败终态: 超上限N次
|
||||||
|
执行中 --> 挑战中: 需扫码/验证码
|
||||||
|
挑战中 --> 执行中: 用户完成挑战
|
||||||
|
挑战中 --> 挂起: 挑战超时
|
||||||
|
挂起 --> 执行中: 用户稍后处理
|
||||||
|
执行中 --> 账号冷却: 风控拦截
|
||||||
|
账号冷却 --> 执行中: 冷却结束+人工确认
|
||||||
|
账号冷却 --> 失败终态: 人工放弃
|
||||||
|
失败终态 --> 队列中: 人工一键重发
|
||||||
|
已发布 --> [*]
|
||||||
|
失败终态 --> [*]
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
sequenceDiagram
|
||||||
|
participant OP as 运营
|
||||||
|
participant S as 服务器
|
||||||
|
participant A as Agent
|
||||||
|
participant PL as 平台
|
||||||
|
participant IT as 账号负责人
|
||||||
|
Note over A,S: 单条WSS长连接复用(实测RTT 0.07ms)
|
||||||
|
OP->>S: 提交任务(素材/平台/排期)
|
||||||
|
S->>S: 校验→审核→入队→排期触发
|
||||||
|
S->>A: task.push(taskId,加密载荷)
|
||||||
|
A-->>S: ack(taskId) 幂等确认
|
||||||
|
A->>A: 选账号→注入代理→协议级登录态校验(不启浏览器)
|
||||||
|
alt 登录态正常
|
||||||
|
A->>PL: 冷启动浏览器→上传素材→提交发布
|
||||||
|
PL-->>A: 发布回执+截图
|
||||||
|
A->>S: task.result(success)
|
||||||
|
S-->>OP: 实时状态+链接
|
||||||
|
else 平台触发二次验证
|
||||||
|
PL-->>A: 挑战(扫码/短信/APP确认/滑块)
|
||||||
|
A->>S: challenge.push(类型,凭证)
|
||||||
|
S-->>IT: 控制台+企微提醒
|
||||||
|
IT->>S: 扫码或输入验证码
|
||||||
|
S->>A: challenge.resolve(结果)
|
||||||
|
A->>PL: 继续发布流程
|
||||||
|
else 网络类失败
|
||||||
|
A->>A: 指数退避 1m/5m/15m/1h
|
||||||
|
A->>PL: 自动重试上传
|
||||||
|
else 登录态失效
|
||||||
|
A->>S: challenge.push(qr)
|
||||||
|
S-->>IT: 推送扫码提醒
|
||||||
|
IT->>S: 扫码成功
|
||||||
|
S->>A: challenge.resolve
|
||||||
|
A->>PL: 恢复执行
|
||||||
|
else 风控拦截
|
||||||
|
A->>S: task.result(fail,原因)
|
||||||
|
S->>S: 账号冷却+人工队列
|
||||||
|
S-->>OP: 告警通知
|
||||||
|
end
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
flowchart TB
|
||||||
|
TR["平台触发二次验证"] --> DET{"Agent检测挑战类型"}
|
||||||
|
DET -->|二维码登录| QR["取码→推控制台+企微<br/>码过期自动刷新"]
|
||||||
|
DET -->|APP确认| CF["推送手机确认提醒<br/>Agent轮询登录态"]
|
||||||
|
DET -->|短信/邮箱| SMS["控制台弹输入框<br/>用户输入→回传回填"]
|
||||||
|
DET -->|滑块/点选| CAP["默认截图推人工<br/>低风控平台本地尝试"]
|
||||||
|
QR --> OK{"完成?"}
|
||||||
|
CF --> OK
|
||||||
|
SMS --> OK
|
||||||
|
CAP --> OK
|
||||||
|
OK -->|是| CONT["Agent继续发布流程"]
|
||||||
|
OK -->|否/超时| PEND["任务挂起→人工队列<br/>一键重发/放弃"]
|
||||||
|
PEND --> CONT
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
sequenceDiagram
|
||||||
|
participant U as 用户(手机)
|
||||||
|
participant C as 控制台/企微
|
||||||
|
participant S as 服务器
|
||||||
|
participant A as Agent
|
||||||
|
participant P as 平台
|
||||||
|
Note over A,P: 首选API直取(无浏览器, B站已验证)
|
||||||
|
A->>P: HTTP取码 0.1-0.5s 得token+图片URL
|
||||||
|
A->>S: challenge.push(token) 实测<50ms
|
||||||
|
S->>C: WS推码 并行推企微/托盘
|
||||||
|
C->>C: 本地重渲染二维码(非截图)
|
||||||
|
par 用户扫码 3-10s
|
||||||
|
U->>U: 手机扫码确认
|
||||||
|
and Agent短轮询检测
|
||||||
|
loop 300-500ms
|
||||||
|
A->>P: 轮询扫码状态(或网络钩子)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
A->>P: API换取cookie 0.2-0.5s
|
||||||
|
A->>A: 存入凭据保险库
|
||||||
|
A->>S: challenge.resolve(已登录)
|
||||||
|
S-->>C: 状态变绿
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
sequenceDiagram
|
||||||
|
participant U as 用户
|
||||||
|
participant W as 网页端
|
||||||
|
participant S as 服务器
|
||||||
|
participant A as App(Agent)
|
||||||
|
U->>W: 登录(密码+TOTP 2FA可选)
|
||||||
|
W->>S: POST /api/auth/login
|
||||||
|
S-->>W: JWT(15min) + refresh cookie
|
||||||
|
U->>W: 工作区→设备→添加设备
|
||||||
|
W->>S: POST /api/agents/pairing
|
||||||
|
S-->>W: 配对码(6位,5分钟有效)
|
||||||
|
U->>A: 输入配对码
|
||||||
|
A->>A: 生成Ed25519密钥对+deviceId
|
||||||
|
A->>S: WSS auth(deviceId,配对码,公钥,签名)
|
||||||
|
S->>S: 验证→注册公钥→签发agentToken(可吊销)
|
||||||
|
S-->>A: 绑定成功
|
||||||
|
A->>A: 私钥入本地保险库(DPAPI/SQLCipher)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
flowchart TB
|
||||||
|
OP["运营(网页端)"] -->|"① multipart直传(一期服务器本地盘)"| FS["素材存储 一期本地盘/二期OSS"]
|
||||||
|
OP -->|"② 元数据 API(sha256)"| S["服务器"]
|
||||||
|
S -->|"③ WSS task.push + 短时效下载URL"| A["Agent"]
|
||||||
|
A -->|"④ 直链下载(带签名token)"| FS
|
||||||
|
A -->|"⑤ 上传发布"| P["目标平台"]
|
||||||
|
P -->|"⑥ 回执+截图"| A
|
||||||
|
A -->|"⑦ WSS task.result(URL+状态)"| S
|
||||||
|
S -->|"⑧ WSS 实时状态推送"| OP
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
gantt
|
||||||
|
title EveryPublish 交付排期(1人+AI · 3阶段2检查点)
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %m-%d
|
||||||
|
section 阶段一 网页端(D1-D7)
|
||||||
|
D1 工程化与骨架 :A1, 2026-08-20, 1d
|
||||||
|
D2 认证与工作区API :A2, after A1, 1d
|
||||||
|
D3 业务API(账号/素材/任务) :A3, after A2, 1d
|
||||||
|
D4 WSS网关+假执行器 :A4, after A3, 1d
|
||||||
|
D5 页面1(登录/仪表盘/成员/设置) :A5, after A4, 1d
|
||||||
|
D6 页面2(素材/账号/任务/日历) :A6, after A5, 1d
|
||||||
|
D7 页面3(审核/挑战/审计/admin) :A7, after A6, 1d
|
||||||
|
检查点1 网页端手动测试(暂停) :milestone, M1, after A7, 1d
|
||||||
|
section 阶段二 Windows客户端(D8-D11)
|
||||||
|
D8 agent-core(Go) :B1, after M1, 1d
|
||||||
|
D9 WinUI3壳+托盘+watchdog :B2, after B1, 1d
|
||||||
|
D10 B站真实发布(无浏览器) :B3, after B2, 1d
|
||||||
|
D11 抖音/快手/小红书(CDP) :B4, after B3, 1d
|
||||||
|
检查点2 交付用户测试(暂停) :milestone, M2, after B4, 1d
|
||||||
|
section 阶段三 联调与交付(D12-D15)
|
||||||
|
D12 联调+异常注入 :C1, after M2, 1d
|
||||||
|
D13 打包+内部试用 :C2, after C1, 1d
|
||||||
|
D14-15 缓冲(平台逆向) :C3, after C2, 2d
|
||||||
|
section 二期(预留)
|
||||||
|
视频号+官方API通道 :F1, after C3, 21d
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"args":["--no-sandbox","--disable-gpu"]}
|
||||||
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 76 KiB |
@@ -0,0 +1,13 @@
|
|||||||
|
# EveryPublish 服务器配置样例(部署包专用,已按部署包目录结构调整)
|
||||||
|
SERVER_ADDR=:8090
|
||||||
|
MYSQL_DSN=root:everypublish@tcp(127.0.0.1:3306)/everypublish?charset=utf8mb4&parseTime=True&loc=Local
|
||||||
|
REDIS_ADDR=127.0.0.1:6379
|
||||||
|
REDIS_PASSWORD=
|
||||||
|
JWT_SECRET=please-change-me-in-production
|
||||||
|
# 客户端(agent-core)下载素材用,必须填客户端可达的内网地址:
|
||||||
|
BASE_URL=http://<服务器内网IP>:8090
|
||||||
|
# 浏览器侧链接(邀请链接等),同样填内网地址:
|
||||||
|
PUBLIC_BASE_URL=http://<服务器内网IP>:8090
|
||||||
|
STORAGE_DIR=./data/materials
|
||||||
|
# 部署包自带 web/ 前端目录,按部署包路径即可:
|
||||||
|
STATIC_DIR=./web
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# EveryPublish 服务器部署说明(内网版)
|
||||||
|
|
||||||
|
## 目录内容
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| everypublish-server-linux-amd64 | Linux x64 服务器程序(单文件,含前端+API+WSS) |
|
||||||
|
| everypublish-server-windows-amd64.exe | Windows x64 服务器程序 |
|
||||||
|
| web/ | 网页控制台静态文件(程序自动托管,无需 Nginx) |
|
||||||
|
| docker-compose.yml | MySQL + Redis 一键编排 |
|
||||||
|
| .env.example | 配置样例 |
|
||||||
|
|
||||||
|
## 部署步骤(Linux 示例)
|
||||||
|
|
||||||
|
1. 依赖 MySQL 8 + Redis 7(有现成实例可跳过本步):
|
||||||
|
`docker compose up -d`(或自建 MySQL 库 everypublish + Redis)
|
||||||
|
2. 准备配置(可选,默认即可用):
|
||||||
|
`cp .env.example .env`,改 MYSQL_DSN / REDIS_ADDR / JWT_SECRET
|
||||||
|
3. 启动:
|
||||||
|
`./everypublish-server-linux-amd64`(默认监听 0.0.0.0:8090,自动建表)
|
||||||
|
4. 验证:浏览器访问 `http://<服务器内网IP>:8090` → 出现 EveryPublish 登录页即成功。
|
||||||
|
5. 防火墙放行 8090 端口(仅内网使用可不放行公网)。
|
||||||
|
|
||||||
|
## 使用(连接方式)
|
||||||
|
|
||||||
|
1. 浏览器打开 `http://<服务器内网IP>:8090` → **注册账号**(开放注册)→ 登录。
|
||||||
|
2. Windows 客户端(安装包)「设置」页填服务器地址 `http://<服务器内网IP>:8090` → 保存。
|
||||||
|
3. 网页控制台「设置 → Agent 设备」生成**配对码** → 客户端「配对」页输入 → 完成绑定。
|
||||||
|
4. 网页端:账号管理 → 添加平台账号并绑定(客户端扫码/验证)→ 素材库上传 → 新建发布 → 审核通过 → 客户端自动执行。
|
||||||
|
|
||||||
|
## 常用配置(.env)
|
||||||
|
|
||||||
|
```
|
||||||
|
SERVER_ADDR=:8090 # 监听端口
|
||||||
|
MYSQL_DSN=root:密码@tcp(127.0.0.1:3306)/everypublish?charset=utf8mb4&parseTime=True&loc=Local
|
||||||
|
REDIS_ADDR=127.0.0.1:6379
|
||||||
|
JWT_SECRET=请改成随机长字符串
|
||||||
|
BASE_URL=http://<服务器内网IP>:8090 # 素材直链(客户端下载用,必须客户端可达)
|
||||||
|
PUBLIC_BASE_URL=http://<服务器内网IP>:8090 # 浏览器侧链接(邀请链接等)
|
||||||
|
STORAGE_DIR=./data/materials
|
||||||
|
STATIC_DIR=./web # 前端目录(部署包自带)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试账号
|
||||||
|
|
||||||
|
- 部署后网页端**开放注册**,直接注册即可作为测试账号(注册即建工作空间)。
|
||||||
|
- 如需平台管理员(可见「平台管理→开发文档」等):把某账号 users.role 改为 admin(MySQL:`UPDATE users SET role="admin" WHERE email="你的邮箱";`)。
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.4
|
||||||
|
container_name: everypublish-mysql
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: everypublish
|
||||||
|
MYSQL_DATABASE: everypublish
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
volumes:
|
||||||
|
- mysql_data:/var/lib/mysql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-peverypublish"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 30
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: everypublish-redis
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 30
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql_data:
|
||||||
|
redis_data:
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
# EveryPublish 服务端接口文档(v1)
|
||||||
|
|
||||||
|
> 基地址:`/api/v1` · 统一响应:`{"code":0,"message":"ok","data":{...}}`,code!=0 为业务错误。
|
||||||
|
> 认证:`Authorization: Bearer <accessToken>`(15min,前端自动用 refresh 轮换)。
|
||||||
|
|
||||||
|
## 业务错误码
|
||||||
|
|
||||||
|
| code | 含义 |
|
||||||
|
|---|---|
|
||||||
|
| 1001 | 参数不合法 |
|
||||||
|
| 1002 | 未登录/令牌无效 |
|
||||||
|
| 1003 | 无权限 |
|
||||||
|
| 1004 | 资源不存在 |
|
||||||
|
| 2001 | 邮箱已注册 |
|
||||||
|
| 2002 | 邮箱或密码错误 |
|
||||||
|
| 2003 | 刷新令牌无效 |
|
||||||
|
| 2004 | 已是成员 |
|
||||||
|
| 2005 | 邀请链接无效或过期 |
|
||||||
|
| 1006 | 需要两步验证(2FA,默认关) |
|
||||||
|
| 3001-3005 | 业务冲突(账号不可删/已绑定/挑战已结束/链接过期/任务状态不允许) |
|
||||||
|
|
||||||
|
## 认证 /auth
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| POST | /auth/register | 注册 {email,password,nickname} → 返回令牌对+用户+默认工作空间 |
|
||||||
|
| POST | /auth/login | 登录 {email,password} → 令牌对+用户 |
|
||||||
|
| POST | /auth/refresh | {refreshToken} → 轮换新令牌对(旧 refresh 立即吊销) |
|
||||||
|
| POST | /auth/logout | 登出(吊销 refresh) |
|
||||||
|
| GET | /auth/me | 当前用户+工作空间+角色 |
|
||||||
|
|
||||||
|
## 工作空间 /workspaces(登录)
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| POST | /workspaces | 创建 {name}(创建者为 owner) |
|
||||||
|
| GET | /workspaces | 我所在的工作空间列表 |
|
||||||
|
| PUT | /workspaces/:id | 改名(owner/admin) |
|
||||||
|
|
||||||
|
## 成员 /members(登录)
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| GET | /members | 成员列表(含 email/nickname) |
|
||||||
|
| POST | /members/invite | 邀请 {email,role} → {token,link}(owner/admin;role: admin/operator/reviewer/viewer) |
|
||||||
|
| POST | /members/join | 接受邀请 {token}(须与登录邮箱一致) |
|
||||||
|
| PUT | /members/:id/role | 改角色 {role}(不可改 owner) |
|
||||||
|
| DELETE | /members/:id | 移除(不可移除 owner) |
|
||||||
|
|
||||||
|
## 账号台账 /accounts(登录)
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| GET | /accounts?platform=&status=&page=&size= | 台账列表(状态:unbound/binding/active/suspended/expired) |
|
||||||
|
| POST | /accounts | 新增 {platform(douyin/kuaishou/xiaohongshu/bilibili),accountName,avatarUrl?,ipProfile?} |
|
||||||
|
| PUT | /accounts/:id | 改资料(名称/头像/IP 画像) |
|
||||||
|
| DELETE | /accounts/:id | 删除(仅 unbound) |
|
||||||
|
| POST | /accounts/:id/bind | 发起绑定 → 创建挑战记录(status→binding)→ {challengeId} |
|
||||||
|
|
||||||
|
## 素材 /materials(登录)
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| GET | /materials?kind=&group=&page=&size= | 素材列表 |
|
||||||
|
| POST | /materials | multipart 上传:file 字段 + 可选 group/tags;流式 sha256,同工作区去重(dedup:true 返回已有素材) |
|
||||||
|
| GET | /materials/:id/url | 生成签名直链(10 分钟、一次性)→ {url,expiresAt} |
|
||||||
|
| DELETE | /materials/:id | 删除素材及文件 |
|
||||||
|
| GET | /files/:token | 凭 token 下载文件(无鉴权,token 即凭证;一次性) |
|
||||||
|
|
||||||
|
## 任务 /tasks(登录;状态机)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
D[draft] -->|submit| P[pending_review]
|
||||||
|
P -->|approve| Q[queued] -->|dispatch| DP[dispatched] -->|start| R[running]
|
||||||
|
R -->|success| S[success]
|
||||||
|
R -->|fail| F[failed] -->|retry| Q
|
||||||
|
R -->|suspend| SU[suspended] -->|resume| Q
|
||||||
|
P -->|reject| RJ[rejected] -->|resubmit| P
|
||||||
|
D -->|cancel| C[cancelled]
|
||||||
|
Q -->|cancel| C
|
||||||
|
DP -->|cancel| C
|
||||||
|
R -->|cancel| C
|
||||||
|
SU -->|cancel| C
|
||||||
|
```
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| GET | /tasks?status=&schedule=&page=&size= | 任务列表 |
|
||||||
|
| POST | /tasks | 新建草稿 {title,content?,tags?,accountIds[],materialIds[],scheduleAt?(unix ms),priority?(1-10)} |
|
||||||
|
| GET | /tasks/:id | 详情 |
|
||||||
|
| PUT | /tasks/:id | 编辑(仅 draft/rejected) |
|
||||||
|
| DELETE | /tasks/:id | 删除(仅 draft) |
|
||||||
|
| POST | /tasks/:id/submit | 提交审核 |
|
||||||
|
| POST | /tasks/:id/approve | 审核通过 → queued(通知创建人) |
|
||||||
|
| POST | /tasks/:id/reject | 驳回 {note}(通知创建人) |
|
||||||
|
| POST | /tasks/:id/resubmit | 驳回后重提 |
|
||||||
|
| POST | /tasks/:id/retry | 失败重试 → queued |
|
||||||
|
| POST | /tasks/:id/cancel | 取消 |
|
||||||
|
|
||||||
|
## 挑战 /challenges(登录)
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| GET | /challenges?status=&accountId= | 挑战列表(过期自动置 expired) |
|
||||||
|
| POST | /challenges/:id/solve | 人工完成 {value?}(验证码/APP 确认;扫码由 Agent 完成) |
|
||||||
|
| POST | /challenges/:id/resend | 一键重发(expired/suspended → active,重置 30min) |
|
||||||
|
| POST | /challenges/:id/suspend | 挂起 |
|
||||||
|
|
||||||
|
## 通知 /notifications(登录)
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| GET | /notifications?kind= | 我的通知 + unread 计数 |
|
||||||
|
| POST | /notifications/:id/read | 标记已读 |
|
||||||
|
| POST | /notifications/read-all | 全部已读 |
|
||||||
|
|
||||||
|
## 审计 /audit-logs(登录)
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| GET | /audit-logs?userId=&action=&page=&size= | 审计日志(中间件自动埋点:登录/登出/增删改/审核/挑战等) |
|
||||||
|
|
||||||
|
## 设备配对 /agent
|
||||||
|
|
||||||
|
| 方法 | 路径 | 鉴权 | 说明 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| POST | /agent/pair-code | Bearer | 生成配对码(6 位,5 分钟一次性) |
|
||||||
|
| POST | /agent/pair | 无(凭码) | 配对 {code,deviceName,os,version,publicKey(Ed25519 base64)} → {deviceId} |
|
||||||
|
| GET | /agent/devices | Bearer | 设备列表(在线状态) |
|
||||||
|
|
||||||
|
## WSS 双通道
|
||||||
|
|
||||||
|
### Agent 通道 `/ws/agent`(Ed25519 设备签名,无 JWT)
|
||||||
|
|
||||||
|
| 方向 | 消息 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| C→S | `hello` | {deviceId,nonce,ts,sig(base64, 签名原文 nonce+`\|`+ts),version} |
|
||||||
|
| S→C | `hello.ack` | {serverNonce,sessionId,serverTs,sig} |
|
||||||
|
| C→S | `heartbeat`(10-30s) / S→C `heartbeat.ack` | 保活 |
|
||||||
|
| S→C | `task.push` | 任务下发 {taskId,platform,accountId,title,content,tags,materialUrls(一次性签名直链),priority} |
|
||||||
|
| C→S | `task.ack` | {taskId,accept,reason?}(accept→running) |
|
||||||
|
| C→S | `task.result` | {taskId,status(success/failed),publishedUrl,receipts,error,finishedAt} |
|
||||||
|
| S→C | `challenge.new` | 绑定扫码挑战 {challengeId,accountId,platform,kind,qrToken,qrUrl,prompt,expiresAt} |
|
||||||
|
| C→S | `challenge.ack` / `challenge.solve` | 应答 / 完成(solve 后账号→active) |
|
||||||
|
|
||||||
|
### 浏览器通道 `/ws/browser?token=<JWT>`(只读订阅)
|
||||||
|
|
||||||
|
| 事件 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| `task.status` | {taskId,status,errorMessage,publishedUrls}(下发/执行/成功/失败实时推送) |
|
||||||
|
| `agent.status` | {deviceId,online} |
|
||||||
|
| `challenge.status` | {challengeId,status,accountId} |
|
||||||
|
|
||||||
|
> 实测:任务下发延迟(approve→task.push)≈ 28.7ms(本机,硬指标 <300ms)。
|
||||||
|
|
||||||
|
## 健康检查
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| GET | /health | 服务健康(db/redis 状态) |
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# EveryPublish Windows 客户端 · 安装与使用说明
|
||||||
|
|
||||||
|
> 产物:`client/desktop/release/EveryPublish Setup 0.2.0.exe`(NSIS 安装向导,双击安装,可选安装目录、创建桌面快捷方式)。
|
||||||
|
|
||||||
|
## 一、安装后它能做什么
|
||||||
|
|
||||||
|
- **常驻后台执行**:安装后即注册开机自启(可在设置页关),关闭窗口最小化到托盘不退出。
|
||||||
|
- **接收并执行发布任务**:网页控制台审核通过的任务自动下发到本机,自动登录平台(B站无浏览器投稿;抖音/快手/小红书驱动浏览器)并发布,结果与截图回执回传控制台。
|
||||||
|
- **完成账号扫码绑定**:网页端发起绑定后,客户端弹挑战,展示二维码/接收验证码。
|
||||||
|
- **凭据本地加密保存**:平台账号 cookie 只存在本机(AES-GCM 保险库),服务器零凭据。
|
||||||
|
|
||||||
|
## 二、需要填写什么
|
||||||
|
|
||||||
|
| 位置 | 填什么 | 何时填 |
|
||||||
|
|---|---|---|
|
||||||
|
| 设置页 → 服务器地址 | `http://<服务器内网IP>:8090`(不是 localhost!) | 首次启动 |
|
||||||
|
| 配对页 → 配对码 | 网页控制台「设置→Agent 设备」生成的 6 位码 | 首次绑定 |
|
||||||
|
| 配对页 → 设备名 | 任意(选填) | 首次绑定 |
|
||||||
|
| 挑战页 → 验证码 | 平台发送的短信/图形验证码 | 绑定账号时 |
|
||||||
|
| 设置页 → 模拟执行开关 | 无真实账号联调时打开 | 可选 |
|
||||||
|
|
||||||
|
## 三、连接方式(内网场景)
|
||||||
|
|
||||||
|
1. 服务器部署在内网某台机器(Linux/Windows),监听 8090(部署说明见 dist/deploy/deploy-README.md)。
|
||||||
|
2. 客户端与服务器**同一内网可达**:客户端填服务器内网 IP。
|
||||||
|
3. 客户端经 WSS(Agent 出站长连接,断线自动重连)与服务器通信——**无需服务器主动连客户端**,防火墙只需放行服务器 8090。
|
||||||
|
4. 测试建议:设置页先开「模拟执行」,走通 配对→绑定→任务→发布成功 后再接真实平台。
|
||||||
|
|
||||||
|
## 四、卸载/数据
|
||||||
|
|
||||||
|
- 卸载:控制面板卸载 EveryPublish。
|
||||||
|
- 数据目录(凭据/配置/截图):`%APPDATA%\EveryPublish`,卸载后如需彻底清除请手动删除。
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# EveryPublish Windows 客户端测试清单(检查点②)
|
||||||
|
|
||||||
|
> 版本 0.2.0 · 交付形态:`dist/EveryPublish-Client-0.2.0-win-x64.zip`(WinUI 壳 + agent-core.exe 同目录)。
|
||||||
|
> 前置:网页端已部署(http://<服务器>:8090 单进程),客户端与服务器网络可达。
|
||||||
|
|
||||||
|
## 一、构建/获取安装包
|
||||||
|
|
||||||
|
- 方式 A(推荐):Windows 机器上执行 `client/ui/Packaging/build-installer.ps1`(自动生成图标 → Go 交叉编译 agent-core.exe → dotnet publish → 打包 zip)。
|
||||||
|
- 方式 B:直接用 `client/dist/agent-core.exe` + 手工编译 WinUI 壳(见 client/ui/README.md)。
|
||||||
|
|
||||||
|
## 二、第一轮:安装与假任务闭环(无需真实账号)
|
||||||
|
|
||||||
|
1. 解压 zip → 双击 `EveryPublish.Client.exe`:窗口出现「未配对」,右下角托盘出现图标。
|
||||||
|
2. 设置页:**打开「模拟执行」开关** → 保存并重启核心(此模式下所有平台走假执行器,不碰真实平台)。
|
||||||
|
3. 网页端(管理员/owner 登录)→「设置 → Agent 设备」→ 生成配对码。
|
||||||
|
4. 客户端「配对」页输入配对码 → 配对成功 → 「状态」页显示**在线**(服务器/设备号)。
|
||||||
|
5. 网页端:账号管理 → 添加账号(任选平台)→ 指定刚配对的设备 → 绑定 → 客户端「挑战」页出现挑战 → 点选提交「我已确认」→ 网页端账号变**已绑定**。
|
||||||
|
6. 网页端:素材库上传一个视频 → 新建发布(选素材+账号)→ 任务列表「提交审核」→ 审核中心「通过」→ 任务自动下发 → **变绿「发布成功」**(假执行器秒回)。
|
||||||
|
7. 系统行为:关闭窗口 → 最小化到托盘(进程仍在);托盘右键「退出」才退出;设置页开「开机自启」→ 注销重登自动运行;任务管理器手动结束 agent-core.exe → 5 秒内看门狗自动拉起(3 次失败弹窗提示)。
|
||||||
|
|
||||||
|
## 三、第二轮:B站真实发布(无浏览器投稿)
|
||||||
|
|
||||||
|
1. 设置页**关闭「模拟执行」**并保存(重启核心)。
|
||||||
|
2. 网页端添加 B站账号 → 指定设备 → 绑定 → 客户端「挑战」页**显示 B站登录二维码**(API 生成)→ 手机 B站 App 扫码确认 → 网页端账号变已绑定(cookie 加密存入本机保险库,服务器零凭据)。
|
||||||
|
3. 新建发布(选该 B站账号)→ 审核通过 → 客户端自动执行:preupload → 分片上传 → 投稿 → 网页端任务变成功,发布链接为 `https://www.bilibili.com/video/BV...`。
|
||||||
|
4. 到 B站「创作中心 → 内容管理」核对稿件已上线;客户端数据目录 `receipts/` 有截图回执。
|
||||||
|
|
||||||
|
## 四、第三轮:抖音/快手/小红书真实发布(CDP)
|
||||||
|
|
||||||
|
1. 分别添加对应平台账号 → 绑定 → 客户端「挑战」页显示该平台登录页二维码(CDP 截图)→ 手机 App 扫码。
|
||||||
|
2. 新建发布 → 审核通过 → 客户端拉起浏览器(每账号独立档案,首次会下载 Chromium 或使用系统 Chrome)→ 自动上传/填表/发布 → 任务成功 + 截图回执。
|
||||||
|
3. **选择器容错**:平台改版可能导致找不到元素——失败时查看客户端日志(数据目录下)与 `receipts/` 失败截图,把截图发我调整选择器(架构上只需改配置,无需改流程)。
|
||||||
|
|
||||||
|
## 五、验收标准(与九项最终验收第 6-7 项对应)
|
||||||
|
|
||||||
|
| # | 项 | 通过条件 |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | 安装 | 解压即用,双击启动 |
|
||||||
|
| 2 | 配对 | 配对码绑定成功,状态页在线 |
|
||||||
|
| 3 | 假任务闭环 | 第一轮步骤 5-6 全通过 |
|
||||||
|
| 4 | B站真实发布 | 稿件真实上线 |
|
||||||
|
| 5 | 抖音/快手/小红书真实发布 | 至少 1 个平台真实上线(可先交付可跑子集) |
|
||||||
|
| 6 | 凭据安全 | 平台凭据仅存本机 `%APPDATA%\EveryPublish`(加密 vault),服务器零凭据 |
|
||||||
|
| 7 | 常驻行为 | 托盘/自启/watchdog 均正常 |
|
||||||
|
|
||||||
|
## 六、常见问题
|
||||||
|
|
||||||
|
- **核心进程未就绪**:安装目录缺少 agent-core.exe,或看门狗 3 次失败弹窗——检查打包脚本是否复制了 core。
|
||||||
|
- **配对码无效**:5 分钟过期,网页端重新生成。
|
||||||
|
- **任务一直排队**:账号绑定的设备离线(IP 隔离设计),确认客户端状态页在线。
|
||||||
|
- **日志位置**:数据目录(设置页可打开):`%APPDATA%\EveryPublish`(core 日志经看门狗输出,可用「打开数据目录」查看 config.json/local.json;进程日志在 Windows 事件查看器或直接命令行运行 `agent-core.exe -dir <目录>` 观察)。
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
# EveryPublish 交付文档(技术栈定版 · 页面构成 · 日计划)
|
||||||
|
|
||||||
|
> 2026-08-20 · 开发顺序:网站端(含API) → 客户端(Windows) → 联调交付(含两个用户确认检查点)。基础仓库已拉取:apps/web(Tencent/tdesign-react-starter,MIT)。技术栈定版见 docs/tech-stack.md(MySQL + 前后端 Go 统一)。
|
||||||
|
|
||||||
|
## 1. 技术栈定版
|
||||||
|
|
||||||
|
| 层 | 选型 | 依据 |
|
||||||
|
|---|---|---|
|
||||||
|
| 网页端 | React 18 + TDesign React(Tencent starter,MIT) | 已拉取预览:Vite + TS + Router6 + Redux Toolkit + axios;自带后台页面与 mock,无需装饰 |
|
||||||
|
| 网页端后端(服务器) | **Go**(gin + coder/websocket + asynq + GORM) | 见 Rust/Go 对比;与 agent-core 同语言共享协议模块;页面层仍为 React+TDesign |
|
||||||
|
| 客户端 UI | **WinUI 3(C#/.NET 8)原生** | 系统原生控件,无需自写 UI;QR 用 QRCoder 本地渲染 token |
|
||||||
|
| 客户端核心 | **Go**(WSS 客户端 + go-rod CDP 自动化 + 凭据保险库) | 单二进制、无运行时、可二期抽 Windows 服务/跑 VPS |
|
||||||
|
| 数据库/队列 | **MySQL 8.x(GORM)+ Redis(asynq)** | 任务队列/排期靠 Redis;MySQL 存业务数据 |
|
||||||
|
| 素材存储 | 一期服务器本地盘(签名直链);二期 OSS | StorageDriver 抽象预留 |
|
||||||
|
|
||||||
|
### 1.1 Rust 还是 Go:结论选 Go
|
||||||
|
|
||||||
|
| 维度 | Go | Rust |
|
||||||
|
|---|---|---|
|
||||||
|
| 本项目规模 | 性能绰绰有余(WS 网关 goroutine-per-conn,千级连接轻松) | 性能过剩,无收益 |
|
||||||
|
| 开发与 AI 协作 | AI 生成质量高、迭代快、编译秒级 | borrow checker 调试成本高、编译慢 |
|
||||||
|
| 单二进制部署 | 静态编译、零依赖(Windows 端尤其省心) | 更小,但构建链复杂 |
|
||||||
|
| 浏览器自动化 | go-rod(CDP)成熟可用 | 生态弱 |
|
||||||
|
| Windows 常驻/服务 | kardianos/service 简单 | 复杂度高 |
|
||||||
|
| 生态(队列/WS/DB) | asynq / coder-websocket / GORM(MySQL) 齐全 | 可选少 |
|
||||||
|
|
||||||
|
**性能保障**:①WSS 网关 goroutine-per-conn,单机万级并发无压力;②GORM(MySQL 连接池)+ Redis 承载队列;③大文件绝不走 WSS(签名直链),网关只传 <1KB 消息;④服务无状态可水平扩展;⑤验收硬指标:任务下发 <300ms(tools/diag-connection.mjs 实测基线)。
|
||||||
|
|
||||||
|
## 2. 样式方案
|
||||||
|
|
||||||
|
- **网页端**:TDesign starter 默认主题(企业后台风格,深浅色可切换),全部页面用 TDesign 组件,不写自定义样式;示例页清理后按菜单骨架填充。
|
||||||
|
- **客户端**:WinUI 3 Fluent 原生风格(与 Windows 11 一致),托盘用 H.NotifyIcon.WinUI(或 Win32 interop),扫码/挑战用对话框页。
|
||||||
|
|
||||||
|
## 3. 网页端页面构成
|
||||||
|
|
||||||
|
### 3.1 用户端(客户工作台)
|
||||||
|
|
||||||
|
| 菜单 | 页面 | 关键功能 | 角色 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 工作台 | 仪表盘 | 今日发布统计、待处理挑战数、Agent 在线状态、最近任务 | 全部 |
|
||||||
|
| 发布管理 | 任务列表 / 新建发布 / 排期日历 / 任务详情 | 状态筛选、批量重试/取消;选素材→平台账号→标题话题→定时/立即→提交审核;月/周视图;事件时间线+回执截图+失败原因 | 运营/审核/管理员 |
|
||||||
|
| 素材库 | 素材列表 / 上传 / 预览 | 拖拽上传(multipart+进度)、分组标签、sha256、删除 | 运营/审核 |
|
||||||
|
| 账号管理 | 账号台账 / 绑定向导 / 挑战弹窗 | 平台/绑定状态/健康度/代理/最后活跃;发起扫码→QR 弹窗;重新扫码/解绑 | 管理员 |
|
||||||
|
| 审核中心 | 待审列表 | 分平台预览、通过/驳回+批注(可配置跳过) | 审核/管理员 |
|
||||||
|
| 通知与挑战 | 挑战中心 / 通知列表 | QR 从 token 本地渲染、验证码输入、APP确认指引、超时挂起/一键重发 | 相关角色 |
|
||||||
|
| 成员与权限 | 成员列表 / 邀请 | 邀请链接、角色分配 | 管理员 |
|
||||||
|
| 设置 | 工作区资料 / 安全 / 通知渠道 / Agent 设备 | 2FA 开关(默认关)、企微/钉钉/邮件、设备列表与吊销 | 管理员 |
|
||||||
|
| 审计日志 | 日志列表 | 谁在何时做了什么、筛选 | 管理员 |
|
||||||
|
|
||||||
|
### 3.2 平台端(admin 角色,同部署加一个角色即可)
|
||||||
|
|
||||||
|
| 菜单 | 页面 | 功能 |
|
||||||
|
|---|---|---|
|
||||||
|
| 租户管理 | 客户列表/详情 | 状态、配额(简单)、停用 |
|
||||||
|
| Agent 总览 | 设备列表 | 在线/版本/强制吊销/远程指令 |
|
||||||
|
| 渠道状态(后置) | 适配器列表 | 平台模块状态、灰度 |
|
||||||
|
| 风控台账(后置) | 事件列表 | 风控/冷却记录 |
|
||||||
|
|
||||||
|
## 4. 调用方式设计
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
subgraph WEB["网页端 React+TDesign"]
|
||||||
|
U["用户端页面 / 平台端admin"]
|
||||||
|
end
|
||||||
|
subgraph SRV["Go 服务器"]
|
||||||
|
API["REST /api/*"]
|
||||||
|
WS["WSS 网关"]
|
||||||
|
end
|
||||||
|
subgraph CLIENT["Windows 客户端"]
|
||||||
|
UI["WinUI 3 壳 C#"] -->|"localhost HTTP+随机token"| CORE["agent-core Go"]
|
||||||
|
end
|
||||||
|
U -->|"REST(axios) + 实时WS(JWT)"| API
|
||||||
|
U -->|"实时状态推送"| WS
|
||||||
|
CORE -->|"WSS Ed25519设备签名"| WS
|
||||||
|
CORE -->|"素材直链下载(签名token)"| API
|
||||||
|
CORE -->|"CDP 驱动系统Chrome/Edge"| CH["浏览器"]
|
||||||
|
```
|
||||||
|
|
||||||
|
- ① 网页端 → 服务器:REST(axios,JWT 15min + refresh 轮换)+ 原生 WebSocket(实时状态推送)。
|
||||||
|
- ② 客户端 WinUI 壳 → agent-core:localhost HTTP/WS + 随机 token(仅回环,配对时写入本地配置)。
|
||||||
|
- ③ agent-core → 服务器:WSS + Ed25519 设备签名(配对码注册公钥)。
|
||||||
|
- ④ 素材:multipart 上传 + 签名直链下载(大文件不走 WSS)。
|
||||||
|
|
||||||
|
## 5. 后台常驻方案(Windows)
|
||||||
|
|
||||||
|
- **开机自启**:安装时写注册表 HKCU Run。
|
||||||
|
- **最小化到托盘**:H.NotifyIcon.WinUI / Win32 Shell_NotifyIcon。
|
||||||
|
- **进程模型**:WinUI 主进程(UI)spawn agent-core 子进程(随机端口+token);主进程做 watchdog——心跳检测、崩溃自拉起、3 次失败弹窗提示。
|
||||||
|
- **断线自愈**:core 内 WSS 指数退避重连(1s/5s/15s/60s)。
|
||||||
|
- **二期**:core 抽离为 Windows 服务(kardianos/service),支持无 UI 部署与客户 VPS 形态。
|
||||||
|
|
||||||
|
## 6. 开发日计划(1 人 + AI,顺序开发,D1-D13 + 2 天缓冲)
|
||||||
|
|
||||||
|
### 阶段一 网站端(D1-D7)
|
||||||
|
|
||||||
|
| 日 | 任务 | 验收标准 |
|
||||||
|
|---|---|---|
|
||||||
|
| D1 | 预览与工程化:starter 跑通 → 依赖升级评估(Vite2→5/TS4.8→5) → 清理示例页与 mock → 按页面清单搭菜单/路由空壳;Go 后端初始化(health/配置/DB/Redis) | dev 无报错、菜单空壳可点、/health 200、DB 连上 |
|
||||||
|
| D2 | 认证与工作区 API:Argon2 登录/refresh 轮换/登出(2FA 代码留位默认关)、成员/邀请/角色、审计埋点 | 单测过;前端登录页真实登录 |
|
||||||
|
| D3 | 业务 API:账号台账(bind→challenge)、素材(multipart+签名直链+sha256)、任务 CRUD+状态机+排期、审核动作、通知 | 单测+接口文档;curl 全接口可跑 |
|
||||||
|
| D4 | WSS 网关(hello 验签/心跳/断线、task.push/ack/result、challenge 双向) + 假执行器脚本 + 前端实时推送 | localhost 假执行器闭环:任务→下发→执行→回传→页面变绿,<300ms |
|
||||||
|
| D5 | 页面①:登录、仪表盘、成员、设置 | 页面与真实 API 联通 |
|
||||||
|
| D6 | 页面②:素材库、账号台账+绑定挑战弹窗、任务列表+新建+排期日历 | 全流程可点通(与假执行器) |
|
||||||
|
| D7 | 页面③:审核中心、挑战中心(QR 本地渲染+验证码)、通知、审计 + admin(租户列表/Agent 总览) | 角色权限正确、admin 入口可用 |
|
||||||
|
|
||||||
|
> **检查点①(暂停)**:D7 完成后暂停目标 → 执行网页端手动测试(§7 第 1-5 项 + 全页面冒烟)→ 输出测试报告 → 等待用户确认通过后再进入阶段二。
|
||||||
|
|
||||||
|
### 阶段二 客户端(D8-D11)
|
||||||
|
|
||||||
|
| 日 | 任务 | 验收标准 |
|
||||||
|
|---|---|---|
|
||||||
|
| D8 | agent-core(Go):WSS 客户端(重连/幂等)、凭据保险库(加密)、执行器框架、代理配置、挑战处理 + 假平台执行器 | core 单测过;与服务器假执行闭环 |
|
||||||
|
| D9 | WinUI 3 壳:托盘、配对引导、扫码/挑战窗口、状态页;localhost 调 core;开机自启+watchdog;打包脚本 | Windows 实机:安装→配对→假任务→回传 |
|
||||||
|
| D10 | 真实平台:B站(投稿协议,无浏览器)先行 | B站真实发布成功 |
|
||||||
|
| D11 | 抖音/快手/小红书(go-rod CDP:系统 Chrome、独立档案、指纹、代理绑定;参考 social-auto-upload) | 三平台真实发布+截图回执(可先交付可跑子集) |
|
||||||
|
|
||||||
|
> **检查点②(暂停)**:D11 完成后暂停目标 → 打包并交付用户测试(附测试清单)→ 等待用户确认测试通过后再进入阶段三联调。
|
||||||
|
|
||||||
|
### 阶段三 联调与交付(D12-D13)
|
||||||
|
|
||||||
|
| 日 | 任务 | 验收标准 |
|
||||||
|
|---|---|---|
|
||||||
|
| D12 | 内网联调+异常注入:断线重连/重复任务/登录过期挑战/失败退避;延迟基线 | 验收清单全过 |
|
||||||
|
| D13 | 打包(安装器+自动更新)、内部试用、修复、部署手册 | 新机安装走通、交付物齐 |
|
||||||
|
| D14-15 | 缓冲(主要给平台逆向) | — |
|
||||||
|
|
||||||
|
## 7. 最终验收清单
|
||||||
|
|
||||||
|
1. 登录/刷新/登出/角色权限正常(2FA 默认关)。
|
||||||
|
2. 任务全生命周期:草稿→审核→排期→队列→执行→成功/失败→重试。
|
||||||
|
3. 任务下发 <300ms;断线重连 <10s 恢复;同任务不重复执行(幂等)。
|
||||||
|
4. 挑战全流程:扫码/验证码/APP确认/超时挂起/一键重发。
|
||||||
|
5. 素材:上传、签名直链下载、sha256 校验。
|
||||||
|
6. 四平台真实发布成功 + 回执截图入库。
|
||||||
|
7. Windows 新机:安装→配对→绑定账号→真实发布。
|
||||||
|
8. 凭据不出本机;审计日志完整。
|
||||||
|
9. 安装包 + 自动更新可用。
|
||||||
|
|
||||||
|
## 8. 交付物清单
|
||||||
|
|
||||||
|
- 代码:web/(网页端)、server/(Go 服务器)、client/(WinUI+agent-core)。
|
||||||
|
- 文档:docs/prd.md(需求)、docs/tech-stack.md(技术栈)、docs/system-design.md(设计)、docs/logic-diagrams.md(逻辑图)、docs/page-wireframes.md(页面线框图)、docs/frontend-guide.md(前端指南)、本交付文档(排期)、部署手册、验收报告。
|
||||||
|
- 工具:tools/diag-connection.mjs 联调诊断。
|
||||||
|
|
||||||
|
## 9. 风险与应对
|
||||||
|
|
||||||
|
| 风险 | 应对 |
|
||||||
|
|---|---|
|
||||||
|
| starter 依赖过旧(Vite2/TS4.8) | D1 升级评估,必要则升 Vite5+TS5,锁定 LTS Node |
|
||||||
|
| 平台逆向不可预测 | D14-15 缓冲;B站先行;卡住降级为可跑子集 |
|
||||||
|
| WinUI 托盘无官方控件 | H.NotifyIcon.WinUI 或 Win32 interop(D9 前验证) |
|
||||||
|
| 系统 Chrome 版本兼容 | go-rod 用 CDP,绑定系统 Edge 兜底 |
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# EveryPublish 开发排期 v2(AI 加速 · 日级)
|
||||||
|
|
||||||
|
> 2026-08-20 更新:技术栈定版后的顺序开发计划(网站端→客户端→联调)见 docs/delivery-plan.md,以新文档为准。
|
||||||
|
|
||||||
|
> 2026-08-20 · 取代 logic-diagrams.md 图11 的传统 10 周排期。前提:1 人 + AI 编码代理并发推进。
|
||||||
|
|
||||||
|
## 1. 排期总览
|
||||||
|
|
||||||
|
**内核 6 个工作日 + 2 天风险缓冲**:D1 协议定版+假执行器闭环 → D2 网页端全部功能 → D3 客户端 Windows → D4 四平台真实接入 → D5 内网联调加固 → D6 打包试用。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title MVP 闭环 AI加速排期(1人+AI并发)
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %m-%d
|
||||||
|
section 协议与骨架
|
||||||
|
协议定版+单测 :D1a, 2026-08-21, 1d
|
||||||
|
工程骨架+假执行器闭环 :D1b, 2026-08-21, 1d
|
||||||
|
section 网页端
|
||||||
|
网页端全部功能+WSS网关 :D2, 2026-08-22, 1d
|
||||||
|
section 客户端
|
||||||
|
Agent框架+Electron+打包 :D3, 2026-08-23, 1d
|
||||||
|
section 平台接入(高风险)
|
||||||
|
四平台真实发布 :D4, 2026-08-24, 2d
|
||||||
|
section 联调收尾
|
||||||
|
内网联调+异常加固 :D5, 2026-08-26, 1d
|
||||||
|
打包试用+修复 :D6, 2026-08-27, 1d
|
||||||
|
section 缓冲
|
||||||
|
D4风险缓冲 :buf, 2026-08-28, 2d
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 加速逻辑(为什么能从 10 周压到 6 天内核)
|
||||||
|
|
||||||
|
| 传统排期的串行等待 | AI 加速后的处理 |
|
||||||
|
|---|---|
|
||||||
|
| 协议讨论来回 | 本文档 §7 消息协议已定版,D1 上午直接产出 TS 协议包+单测 |
|
||||||
|
| 两端互相等接口 | 协议包是唯一耦合点:D1 定死后,网页端/客户端/测试三线完全并行 |
|
||||||
|
| 脚手架与样板代码 | AI 代理直接生成(monorepo、页面 CRUD、Electron 壳、队列 worker) |
|
||||||
|
| 平台适配逐个人工摸索 | 每平台一个 AI 子代理,参考 social-auto-upload(MIT 1.4万星)并行逆向 |
|
||||||
|
| 联调来回排期 | 假执行器先行:D1 起每天都是可联调状态 |
|
||||||
|
|
||||||
|
## 3. 日计划
|
||||||
|
|
||||||
|
### D1 协议定版 + 工程骨架(验收:本机假执行器闭环)
|
||||||
|
|
||||||
|
| 时段 | 任务 | 并行方式 |
|
||||||
|
|---|---|---|
|
||||||
|
| 上午 | packages/protocol:zod schema(hello/heartbeat/task.push/ack/result/challenge.push/resolve/agent.cmd)+ 幂等工具 + 单测 | 人+AI 共同定版 |
|
||||||
|
| 下午 | monorepo(pnpm+Turborepo)+ Drizzle 建表 + Redis/BullMQ + 假执行器脚本 + 网页最小页(任务列表/状态) | 协议定版后三线并行:web 骨架 / agent 骨架 / mock+测试 |
|
||||||
|
|
||||||
|
**验收**:localhost 闭环——创建任务 → WSS 下发 → 假执行 → 回传 → 页面变绿;延迟 <300ms。
|
||||||
|
|
||||||
|
### D2 网页端全部功能(验收:全页面可用)
|
||||||
|
|
||||||
|
- auth(无 2FA UI)/ 工作区成员角色 / 账号台账 / 素材(本地盘 multipart + 签名直链)/ 任务+排期日历 / 审核流 / 挑战中心(token 本地渲染二维码 + 验证码输入)/ 审计 / 通知。
|
||||||
|
- WSS 网关:hello Ed25519 验签、心跳 10s/25s 超时、挑战消息双向。
|
||||||
|
**验收**:与假执行器全流程;二维码能从 token 本地渲染并可被手机识别。
|
||||||
|
|
||||||
|
### D3 客户端 Windows(验收:安装→配对→假任务→回传)
|
||||||
|
|
||||||
|
- agent-core:WSS 客户端(断线重连/幂等 ack)+ 凭据保险库(加密)+ 执行器框架 + 挑战处理器 + 代理配置。
|
||||||
|
- Electron 壳:托盘、配对引导、扫码/挑战窗口、连接状态、日志。
|
||||||
|
- electron-builder Windows 打包(CI,不用本机交叉编译)。
|
||||||
|
**验收**:Windows 实机安装 → 配对 → 接假任务 → 回传结果。
|
||||||
|
|
||||||
|
### D4 四平台真实接入(验收:真实发布+截图回执)★最高风险
|
||||||
|
|
||||||
|
- 顺序:B站(投稿协议,无浏览器)先行 → 抖音/快手/小红书(Playwright,参考 social-auto-upload)并行。
|
||||||
|
- 登录:B站 API 取码;三平台浏览器取码 + 只传 token。指纹:真实 Chrome + 每账号独立档案。
|
||||||
|
**验收**:四平台真实发布成功,回执截图入库。
|
||||||
|
|
||||||
|
### D5 内网联调 + 异常加固(验收:清单全过)
|
||||||
|
|
||||||
|
- Mac 服务器 + Windows 客户端局域网全链路;异常注入:断网重连 / 重复任务 / 登录过期挑战 / 失败退避;延迟基线 <300ms。
|
||||||
|
|
||||||
|
### D6 打包试用 + 修复
|
||||||
|
|
||||||
|
- Windows 安装包 + 自动更新 + 内部试用 + 修复。
|
||||||
|
|
||||||
|
## 4. AI 并发工作法
|
||||||
|
|
||||||
|
1. **依赖序**:协议(D1 上午)→ 三线并行(D1 下午起):A=网页端功能、B=Agent 核心+壳、C=平台 uploader(每平台一个)、D=测试/联调脚本。
|
||||||
|
2. **人只做五件事**:协议定版、代码审查、跑验收、平台逆向卡点介入、外部资源准备(账号/代理/扫码手机)。
|
||||||
|
3. **子代理 prompt 三件套**:协议包路径 + 对应文档章节 + 验收标准(文档已齐,直接引用)。
|
||||||
|
4. **每日节奏**:早定目标 → AI 并行 → 晚验收;验收不过的条目次日优先。
|
||||||
|
5. **单测+验收清单兜底 AI 代码质量**:不写测试的产出不收。
|
||||||
|
|
||||||
|
## 5. 风险与缓冲
|
||||||
|
|
||||||
|
| 风险 | 影响 | 缓冲策略 |
|
||||||
|
|---|---|---|
|
||||||
|
| D4 平台逆向不可预测 | 最可能拖期 | 预留 2 天缓冲;先 B站(确定性最高)后三平台;卡住即降级为「少一个平台」交付 |
|
||||||
|
| Windows 打包/签名坑 | D3 可能溢出 | 第一天就起 CI;不用本机交叉编译 |
|
||||||
|
| 平台风控触发 | 账号受限 | 独立档案+真实扫码+人级频率,不硬刚 |
|
||||||
|
| AI 生成代码质量 | 返工 | 单测+验收清单;关键路径(鉴权/幂等)人工复审 |
|
||||||
|
|
||||||
|
## 6. 结论
|
||||||
|
|
||||||
|
- **1 人 + AI 并发:内核 6 个工作日,含缓冲 8 个日历日**;「一天跑通 MVP 闭环」= D1 假执行器闭环,完全现实(协议与验收标准已齐)。
|
||||||
|
- **真实平台闭环的关键在 D4**:逆向工作无法被 AI 完全消除,现实预期 2 天 + 缓冲,先交付 B站/抖音/小红书/快手中的可跑子集。
|
||||||
|
- 视频号、官方 API 通道、OSS 仍为二期(接口已预留)。
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# 前端开发指南(TDesign starter 深度说明)
|
||||||
|
|
||||||
|
> 2026-08-20 · 基于 apps/web 实际代码(starter 0.3.1)· 配套 docs/page-wireframes.md 使用。
|
||||||
|
|
||||||
|
## 1. 模板事实清单(实测)
|
||||||
|
|
||||||
|
| 项 | 事实 |
|
||||||
|
|---|---|
|
||||||
|
| 技术 | React 18.2 + Vite 2.9 + TS 4.8 + React Router 6 + Redux Toolkit + axios |
|
||||||
|
| 组件库 | tdesign-react 1.15 + tdesign-icons-react 0.6(MIT,可商用) |
|
||||||
|
| 目录 | src/pages(页面) / src/router(路由=菜单) / src/layouts(布局) / src/services(接口) / src/modules(redux) / src/configs(host.ts 环境) / mock(演示数据) |
|
||||||
|
| 模板页 | List/Base(表格列表) List/Card(卡片) Form/Base(表单) Form/Step(分步向导) Detail/Base(详情) Result(结果) User(成员) Dashboard(仪表盘) Login |
|
||||||
|
| 环境 | npm run dev(真实API) / dev:mock(mock数据); host.ts 分 mock/development/test/release/site |
|
||||||
|
| 过渡 | 无路由切换动画;仅零散 hover transition 与组件自带动效(见 §4) |
|
||||||
|
|
||||||
|
## 2. 关键机制(对接方式)
|
||||||
|
|
||||||
|
- **路由即菜单**:src/router/modules/ 每个 .ts 一个顶级菜单;meta{title, Icon, hidden, single};children 即子菜单;isFullPage=true 为全屏页(登录页)。
|
||||||
|
- **布局**:src/layouts/components/ AppLayout(整体) + Header + Menu + Footer + Page;新页面只需加路由+页面文件。
|
||||||
|
- **接口**:src/services 为 axios 封装示例;把 src/configs/host.ts 的 development.API 指向 Go 服务器(如 http://localhost:8080)。
|
||||||
|
- **状态**:Redux Toolkit(modules/user 存登录态);实时状态用原生 WebSocket(/ws,JWT 鉴权),事件 task.*/challenge.*/notification.*。
|
||||||
|
- **mock 移除**:删除 mock 目录与 vite-plugin-mock 使用,全部走真实 API。
|
||||||
|
|
||||||
|
## 3. 组件清单与场景映射
|
||||||
|
|
||||||
|
| TDesign 组件 | 适用场景 | 本项目页面 |
|
||||||
|
|---|---|---|
|
||||||
|
| Table + Tag + Pagination + Popconfirm | 数据列表/状态/二次确认 | 任务列表/素材/账号台账/成员/审计/租户/Agent总览 |
|
||||||
|
| Form + Input + Textarea + Select + Checkbox + Radio + Switch | 表单 | 登录/新建发布/设置/成员邀请 |
|
||||||
|
| Steps | 分步向导 | 新建发布四步 |
|
||||||
|
| DatePicker / DateRangePicker | 时间选择 | 定时发布/筛选 |
|
||||||
|
| Calendar | 月历 | 排期日历(若 1.15 无此组件则用卡片网格自绘,需 D6 前验证) |
|
||||||
|
| Upload + Progress + ImageViewer | 上传/预览 | 素材库 |
|
||||||
|
| Dialog | 模态弹窗 | 绑定扫码/验证码输入/邀请 |
|
||||||
|
| Drawer | 侧滑详情 | 任务详情/审核预览 |
|
||||||
|
| Timeline | 时间线 | 任务事件流 |
|
||||||
|
| Badge + Tag | 状态角标 | 绑定状态/任务状态/通知 |
|
||||||
|
| Message + Notification + Loading + Skeleton | 全局反馈 | 全站 |
|
||||||
|
| Tabs | 分栏 | 审核分平台预览/设置子页 |
|
||||||
|
| 二维码 | TDesign 无此组件 | 唯一新增依赖:qrcode.react(token 本地渲染) |
|
||||||
|
|
||||||
|
## 4. 过渡效果说明(结论:本期不做页面动画)
|
||||||
|
|
||||||
|
- starter 本身**没有**路由切换动画(实测仅 hover 背景/颜色 transition 与 Tree 组件动效)。
|
||||||
|
- TDesign 组件自带动效,直接使用:Drawer 滑入、Dialog 渐入、Message/Notification 渐入渐出、Tabs 下划线滑动、Loading 旋转。
|
||||||
|
- 约定:页面级不做过渡动画(保持响应快、实现简单);若后续需要统一 fade,用 TDesign 主题变量 --td-anim-duration-base 统一加,本期不排。
|
||||||
|
|
||||||
|
## 5. 直接套用规范(每个页面怎么做)
|
||||||
|
|
||||||
|
| 我们的页面 | 复制哪个模板 | 改动点 |
|
||||||
|
|---|---|---|
|
||||||
|
| 登录页 | pages/Login | 去第三方登录/注册,接 /api/auth/login |
|
||||||
|
| 仪表盘 | pages/Dashboard/Base | 替换统计卡片与最近任务表格 |
|
||||||
|
| 任务列表/账号台账/成员/审计 | pages/List/Base | 改 columns、操作列、接 API |
|
||||||
|
| 素材库 | pages/List/Card | 卡片项=素材缩略图+操作 |
|
||||||
|
| 新建发布 | pages/Form/Step | 四步:素材→平台账号→内容→定时提交 |
|
||||||
|
| 任务详情 | pages/Detail/Base → Drawer 化 | Timeline 事件流 + 回执截图 |
|
||||||
|
| 审核中心 | pages/List/Base + Drawer | 预览用 Tabs 分平台 |
|
||||||
|
| 设置 | pages/Form/Base + Tabs | 资料/安全/通知渠道/Agent设备 |
|
||||||
|
| admin 两页 | pages/List/Base | /admin 路由域 + 角色守卫 |
|
||||||
|
|
||||||
|
## 6. 菜单与路由改造清单(D1 完成)
|
||||||
|
|
||||||
|
- router/modules 改为:dashboard(工作台)、publish(发布管理: 任务列表/新建发布/排期日历)、material(素材库)、account(账号管理)、review(审核中心)、challenge(通知与挑战)、member(成员与权限)、setting(设置)、audit(审计日志)、admin(平台管理域)。
|
||||||
|
- 任务详情为 hidden 路由(不进菜单)。
|
||||||
|
- 权限:utils 增 auth.ts(角色→菜单过滤 + 路由守卫),角色:admin/reviewer/operator/platform_admin。
|
||||||
|
|
||||||
|
## 7. 前后端联调约定
|
||||||
|
|
||||||
|
- REST:/api/*,Header Authorization: Bearer <JWT>;401 自动 refresh 重试一次。
|
||||||
|
- WS:原生 WebSocket,连接 /ws?token=<JWT>;心跳由服务端协议统一(10s/25s)。
|
||||||
|
- 分页约定:{ list, total, page, pageSize };错误码约定:业务码 + message,前端 Message 提示。
|
||||||
@@ -0,0 +1,299 @@
|
|||||||
|
# EveryPublish 逻辑图全集(最小闭环版)
|
||||||
|
|
||||||
|
> 2026-08-20 · 汇总自 docs/multi-platform-publish-plan.md 与 docs/system-design.md,MVP 范围已标注。
|
||||||
|
|
||||||
|
## 图索引
|
||||||
|
|
||||||
|
| # | 图 | 回答的问题 | 来源 | PNG |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 1 | 总体架构(双通道) | 系统由哪些部分组成、如何协作 | plan §3 | [PNG](../diagrams/png/01-总体架构.png) |
|
||||||
|
| 2 | 控制面/数据面分离 | 为什么我方服务器不会被封 | plan §3.1 | [PNG](../diagrams/png/02-控制面-数据面分离.png) |
|
||||||
|
| 3 | MVP 部署拓扑(内网联调) | 一期怎么跑起来(Mac+Windows) | 新增 | [PNG](../diagrams/png/03-MVP-部署拓扑.png) |
|
||||||
|
| 4 | IP 路由逻辑 | 账号与 IP 如何绑定 | plan §8 | [PNG](../diagrams/png/04-IP-路由逻辑.png) |
|
||||||
|
| 5 | 重试与降级状态机 | 失败/挑战/风控如何流转 | plan §21.2 | [PNG](../diagrams/png/05-重试与降级状态机.png) |
|
||||||
|
| 6 | 全链路时序(含异常分支) | 用户→端→平台→用户完整路径 | plan §21.1 | [PNG](../diagrams/png/06-全链路时序.png) |
|
||||||
|
| 7 | 二次验证挑战兜底流程 | 扫码/验证码/APP确认怎么处理 | plan §21.3 | [PNG](../diagrams/png/07-二次验证挑战兜底流程.png) |
|
||||||
|
| 8 | 登录链路优化时序 | 登录为何能从 10s 级压到 2s 级 | plan §22 | [PNG](../diagrams/png/08-登录链路优化时序.png) |
|
||||||
|
| 9 | Agent 配对时序 | 客户端如何安全接入 | system-design §8.2 | [PNG](../diagrams/png/09-Agent-配对时序.png) |
|
||||||
|
| 10 | 素材传输链路 | 文件怎么上传/下发/回传 | system-design §12 | [PNG](../diagrams/png/10-素材传输链路.png) |
|
||||||
|
| 11 | 开发排期甘特 | 何时完成最小闭环 | 新增 | [PNG](../diagrams/png/11-开发排期甘特.png) |
|
||||||
|
|
||||||
|
## 图1 总体架构(双通道)
|
||||||
|
|
||||||
|
**MVP 范围**:一期只做通道B(Agent 自动化)+ 国内四平台(抖音/快手/小红书/B站);通道A(官方API)与视频号为二期。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
subgraph WEB["① 控制端 Web 多租户SaaS"]
|
||||||
|
U["运营 / 审核 / 管理员"]
|
||||||
|
W["任务编排 · 素材库 · 账号台账 · 审计"]
|
||||||
|
U --- W
|
||||||
|
end
|
||||||
|
subgraph CORE["② 调度中心 服务器"]
|
||||||
|
Q["任务队列 Redis(asynq) · 状态机 · 排期器"]
|
||||||
|
API["通道A 官方API执行器 OAuth"]
|
||||||
|
AGW["通道B Agent网关 WSS注册/心跳/路由"]
|
||||||
|
end
|
||||||
|
subgraph AGENT["③ 执行端 Agent 客户侧安装"]
|
||||||
|
WS["WSS长连接 主动出网回连"]
|
||||||
|
VAULT["凭据保险库 cookie本地加密"]
|
||||||
|
ROUTER["代理路由器 一账号一IP"]
|
||||||
|
PA["浏览器执行器 go-rod CDP 独立档案"]
|
||||||
|
QR["扫码登录面板"]
|
||||||
|
end
|
||||||
|
WEB -->|"任务下发"| Q
|
||||||
|
Q -->|"通道A 官方API"| API
|
||||||
|
Q -->|"通道B 自动化"| AGW
|
||||||
|
AGW --> WS --> ROUTER --> PA
|
||||||
|
VAULT --- PA
|
||||||
|
QR --- VAULT
|
||||||
|
API --> INT["国际 X/IG/YouTube/TikTok"]
|
||||||
|
PA --> DOM["国内 抖音/快手/小红书/视频号/B站"]
|
||||||
|
PA --> INT
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图2 控制面/数据面分离
|
||||||
|
|
||||||
|
我方服务器只流转任务元数据(<1KB),绝不触达平台;全部平台流量从客户 Agent 发出。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
subgraph CP["控制面 我方服务器 无平台流量"]
|
||||||
|
WEB["控制台/API"] --- Q["队列/排期/状态机"]
|
||||||
|
Q --- GW["Agent网关 WSS"]
|
||||||
|
end
|
||||||
|
subgraph DP["数据面 客户Agent 全部平台流量"]
|
||||||
|
WS["WSS客户端"] --- EX["任务执行器"]
|
||||||
|
EX --- V["凭据保险库"]
|
||||||
|
EX --- PR["代理路由"]
|
||||||
|
end
|
||||||
|
CP -->|"任务元数据 WSS <1KB"| DP
|
||||||
|
DP -->|"结果/挑战 WSS"| CP
|
||||||
|
DP -->|"上传/发布 平台流量"| P["目标平台"]
|
||||||
|
CP -.->|"绝不触达"| P
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图3 MVP 部署拓扑(内网联调)
|
||||||
|
|
||||||
|
Mac 跑网页端+服务器,Windows 装客户端,同局域网联调。注意服务器绑定 0.0.0.0、客户端用局域网 IP、防火墙放行、关 AP 隔离。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
subgraph M["Mac 开发机 内网测试环境"]
|
||||||
|
WEB["网页端 dev"] --> S["服务器 API + WSS网关"]
|
||||||
|
end
|
||||||
|
subgraph W["Windows 客户机"]
|
||||||
|
APP["客户端App Agent<br/>WinUI壳+Go执行核心"]
|
||||||
|
end
|
||||||
|
W -->|"WSS ws://局域网IP:端口"| S
|
||||||
|
S --> DB[("MySQL + Redis")]
|
||||||
|
S --> FS[("素材 服务器本地盘")]
|
||||||
|
APP -->|"直链下载素材 带签名token"| FS
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图4 IP 路由逻辑
|
||||||
|
|
||||||
|
一账号一固定 IP,国内账号=国内住宅 IP,国外账号=国外住宅 IP,浏览器 Context 级注入。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
ACC["账号台账"] --> REG{"账号归属地?"}
|
||||||
|
REG -->|国内平台| POOL1["国内住宅IP池<br/>芝麻/快代理/922S5等"]
|
||||||
|
REG -->|国外平台| POOL2["海外住宅IP池<br/>BrightData/IPRoyal等"]
|
||||||
|
POOL1 --> BIND["一账号绑定一IP<br/>长期固定不轮换"]
|
||||||
|
POOL2 --> BIND
|
||||||
|
BIND --> CTX["浏览器Context级注入<br/>非全局TUN 防串账号"]
|
||||||
|
CTX --> EXEC["执行发布"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图5 重试与降级状态机
|
||||||
|
|
||||||
|
失败按原因分流:网络→指数退避重试;登录态→挑战(人工扫码);风控→账号冷却+人工队列。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> 队列中
|
||||||
|
队列中 --> 执行中: Agent认领
|
||||||
|
执行中 --> 已发布: 平台回执成功
|
||||||
|
执行中 --> 网络重试: 网络/超时错误
|
||||||
|
网络重试 --> 执行中: 退避1m/5m/15m/1h
|
||||||
|
网络重试 --> 失败终态: 超上限N次
|
||||||
|
执行中 --> 挑战中: 需扫码/验证码
|
||||||
|
挑战中 --> 执行中: 用户完成挑战
|
||||||
|
挑战中 --> 挂起: 挑战超时
|
||||||
|
挂起 --> 执行中: 用户稍后处理
|
||||||
|
执行中 --> 账号冷却: 风控拦截
|
||||||
|
账号冷却 --> 执行中: 冷却结束+人工确认
|
||||||
|
账号冷却 --> 失败终态: 人工放弃
|
||||||
|
失败终态 --> 队列中: 人工一键重发
|
||||||
|
已发布 --> [*]
|
||||||
|
失败终态 --> [*]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图6 全链路时序(含异常分支)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant OP as 运营
|
||||||
|
participant S as 服务器
|
||||||
|
participant A as Agent
|
||||||
|
participant PL as 平台
|
||||||
|
participant IT as 账号负责人
|
||||||
|
Note over A,S: 单条WSS长连接复用(实测RTT 0.07ms)
|
||||||
|
OP->>S: 提交任务(素材/平台/排期)
|
||||||
|
S->>S: 校验→审核→入队→排期触发
|
||||||
|
S->>A: task.push(taskId,加密载荷)
|
||||||
|
A-->>S: ack(taskId) 幂等确认
|
||||||
|
A->>A: 选账号→注入代理→协议级登录态校验(不启浏览器)
|
||||||
|
alt 登录态正常
|
||||||
|
A->>PL: 冷启动浏览器→上传素材→提交发布
|
||||||
|
PL-->>A: 发布回执+截图
|
||||||
|
A->>S: task.result(success)
|
||||||
|
S-->>OP: 实时状态+链接
|
||||||
|
else 平台触发二次验证
|
||||||
|
PL-->>A: 挑战(扫码/短信/APP确认/滑块)
|
||||||
|
A->>S: challenge.push(类型,凭证)
|
||||||
|
S-->>IT: 控制台+企微提醒
|
||||||
|
IT->>S: 扫码或输入验证码
|
||||||
|
S->>A: challenge.resolve(结果)
|
||||||
|
A->>PL: 继续发布流程
|
||||||
|
else 网络类失败
|
||||||
|
A->>A: 指数退避 1m/5m/15m/1h
|
||||||
|
A->>PL: 自动重试上传
|
||||||
|
else 登录态失效
|
||||||
|
A->>S: challenge.push(qr)
|
||||||
|
S-->>IT: 推送扫码提醒
|
||||||
|
IT->>S: 扫码成功
|
||||||
|
S->>A: challenge.resolve
|
||||||
|
A->>PL: 恢复执行
|
||||||
|
else 风控拦截
|
||||||
|
A->>S: task.result(fail,原因)
|
||||||
|
S->>S: 账号冷却+人工队列
|
||||||
|
S-->>OP: 告警通知
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图7 二次验证挑战兜底流程
|
||||||
|
|
||||||
|
验证码类挑战人工兜底为主、Agent 硬解为辅;超时挂起→人工队列→一键重发。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
TR["平台触发二次验证"] --> DET{"Agent检测挑战类型"}
|
||||||
|
DET -->|二维码登录| QR["取码→推控制台+企微<br/>码过期自动刷新"]
|
||||||
|
DET -->|APP确认| CF["推送手机确认提醒<br/>Agent轮询登录态"]
|
||||||
|
DET -->|短信/邮箱| SMS["控制台弹输入框<br/>用户输入→回传回填"]
|
||||||
|
DET -->|滑块/点选| CAP["默认截图推人工<br/>低风控平台本地尝试"]
|
||||||
|
QR --> OK{"完成?"}
|
||||||
|
CF --> OK
|
||||||
|
SMS --> OK
|
||||||
|
CAP --> OK
|
||||||
|
OK -->|是| CONT["Agent继续发布流程"]
|
||||||
|
OK -->|否/超时| PEND["任务挂起→人工队列<br/>一键重发/放弃"]
|
||||||
|
PEND --> CONT
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图8 登录链路优化时序
|
||||||
|
|
||||||
|
API 取码免浏览器、只传 token 不传截图、短轮询检测、扫码与检测并行。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant U as 用户(手机)
|
||||||
|
participant C as 控制台/企微
|
||||||
|
participant S as 服务器
|
||||||
|
participant A as Agent
|
||||||
|
participant P as 平台
|
||||||
|
Note over A,P: 首选API直取(无浏览器, B站已验证)
|
||||||
|
A->>P: HTTP取码 0.1-0.5s 得token+图片URL
|
||||||
|
A->>S: challenge.push(token) 实测<50ms
|
||||||
|
S->>C: WS推码 并行推企微/托盘
|
||||||
|
C->>C: 本地重渲染二维码(非截图)
|
||||||
|
par 用户扫码 3-10s
|
||||||
|
U->>U: 手机扫码确认
|
||||||
|
and Agent短轮询检测
|
||||||
|
loop 300-500ms
|
||||||
|
A->>P: 轮询扫码状态(或网络钩子)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
A->>P: API换取cookie 0.2-0.5s
|
||||||
|
A->>A: 存入凭据保险库
|
||||||
|
A->>S: challenge.resolve(已登录)
|
||||||
|
S-->>C: 状态变绿
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图9 Agent 配对时序
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant U as 用户
|
||||||
|
participant W as 网页端
|
||||||
|
participant S as 服务器
|
||||||
|
participant A as App(Agent)
|
||||||
|
U->>W: 登录(密码+TOTP 2FA可选)
|
||||||
|
W->>S: POST /api/auth/login
|
||||||
|
S-->>W: JWT(15min) + refresh cookie
|
||||||
|
U->>W: 工作区→设备→添加设备
|
||||||
|
W->>S: POST /api/agents/pairing
|
||||||
|
S-->>W: 配对码(6位,5分钟有效)
|
||||||
|
U->>A: 输入配对码
|
||||||
|
A->>A: 生成Ed25519密钥对+deviceId
|
||||||
|
A->>S: WSS auth(deviceId,配对码,公钥,签名)
|
||||||
|
S->>S: 验证→注册公钥→签发agentToken(可吊销)
|
||||||
|
S-->>A: 绑定成功
|
||||||
|
A->>A: 私钥入本地保险库(DPAPI/SQLCipher)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图10 素材传输链路(一期本地盘 / 二期OSS)
|
||||||
|
|
||||||
|
大文件绝不走 WSS;一期存服务器本地盘、直链带签名 token;二期切 OSS 预签名(StorageDriver 抽象保证零改动)。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
OP["运营(网页端)"] -->|"① multipart直传(一期服务器本地盘)"| FS["素材存储 一期本地盘/二期OSS"]
|
||||||
|
OP -->|"② 元数据 API(sha256)"| S["服务器"]
|
||||||
|
S -->|"③ WSS task.push + 短时效下载URL"| A["Agent"]
|
||||||
|
A -->|"④ 直链下载(带签名token)"| FS
|
||||||
|
A -->|"⑤ 上传发布"| P["目标平台"]
|
||||||
|
P -->|"⑥ 回执+截图"| A
|
||||||
|
A -->|"⑦ WSS task.result(URL+状态)"| S
|
||||||
|
S -->|"⑧ WSS 实时状态推送"| OP
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图11 开发排期甘特(2 人,至最小闭环)
|
||||||
|
|
||||||
|
> 2026-08-20 更新:AI 加速版日级排期见 docs/dev-schedule.md,以新文档为准。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title EveryPublish 最小闭环排期(2人)
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %m-%d
|
||||||
|
section 奠基
|
||||||
|
协议包+工程骨架 :M0, 2026-08-24, 7d
|
||||||
|
section 网页端
|
||||||
|
服务器最小闭环 :M1, 2026-08-31, 14d
|
||||||
|
网页端全部功能 :M2, 2026-08-31, 28d
|
||||||
|
section 客户端Windows
|
||||||
|
Agent框架+WinUI壳 :M3, 2026-08-31, 21d
|
||||||
|
真实平台接入4平台 :M4, 2026-09-21, 21d
|
||||||
|
section 联调与收尾
|
||||||
|
内网联调+异常加固 :M5, 2026-10-12, 14d
|
||||||
|
打包+内部试用 :M6, 2026-10-26, 7d
|
||||||
|
section 二期
|
||||||
|
视频号+官方API通道 :M7, after M6, 21d
|
||||||
|
```
|
||||||
|
|
||||||
|
## 最小闭环验收清单
|
||||||
|
|
||||||
|
| 里程碑 | 验收标准 | 周次 |
|
||||||
|
|---|---|---|
|
||||||
|
| M0 协议+骨架 | 协议包单测通过,两端共享类型 | W1 |
|
||||||
|
| M1 服务器最小闭环 | 假执行器:任务→下发→执行→回传→显示全通 | W2-3 |
|
||||||
|
| M2 网页端全部功能 | 账号/素材/任务/排期/审核/挑战/审计/通知全页面可用 | W2-6 |
|
||||||
|
| M3 Windows 客户端框架 | 安装→配对→接假任务→回传结果 | W2-4 |
|
||||||
|
| M4 真实平台接入 | 抖音/快手/小红书/B站真实发布成功+截图回执 | W5-7 |
|
||||||
|
| M5 内网联调+加固 | 断线/重试/挑战/延迟<300ms 验收全过 | W8-9 |
|
||||||
|
| M6 打包+试用 | Windows 安装包+自动更新+内部试用 | W10 |
|
||||||
|
|
||||||
|
**人力**:2 人(网页/服务端 1 + 客户端 1)10 周闭环;单人 16-18 周。闭环后持续成本:平台适配每周 0.5-1 天。
|
||||||
@@ -0,0 +1,600 @@
|
|||||||
|
# EveryPublish 多平台聚合发布方案(调研与架构设计)
|
||||||
|
|
||||||
|
> 调研日期:2026-08-20 | 数据来源:GitHub API 实测(星标/许可/活跃度)+ 官方文档抽查 + 工程实践估计。
|
||||||
|
> 说明:本会话 web_search 不可用,个别官方接口细节(X 定价、国内开放平台审核范围)以官方最新文档为准,文中已标注置信度。
|
||||||
|
|
||||||
|
## 0. TL;DR 结论速览
|
||||||
|
|
||||||
|
1. **你的方向是对的**:「网站控制端 + 客户侧安装的执行端」是覆盖国内平台且支持多人协作的唯一高效解。行业成熟商业产品(易媒助手、融媒宝、蚁小二等)均采用同构方案,开源侧 `social-auto-upload` 的 Web-UI 衍生版也是该形态。
|
||||||
|
2. **但不要一刀切,用双通道混合架构**:国际平台(X/IG/YouTube/TikTok)走**官方 API**,服务器直连、无需 Agent、完全合规;国内平台(抖音/快手/小红书/视频号/B站)走 **Agent 浏览器自动化**。合规面最大、Agent 复杂度最小。
|
||||||
|
3. **唯一硬骨头是视频号**:微信官方无内容发布 API,只有「视频号助手」网页自动化一条路,会话短(小时级)、风控极高且牵连微信账号——建议单独迭代、默认关闭、客户主动开通并书面确认。
|
||||||
|
4. **IP 风控的核心是一账号一固定 IP**:国内账号绑国内住宅 IP,国外账号绑国外住宅 IP,在 Agent 的浏览器 Context 级注入,永不跨区、永不轮换。
|
||||||
|
5. **现成可复用**:`dreammis/social-auto-upload`(MIT,14.4k★,覆盖抖音/快手/小红书/视频号/微博/B站/TikTok/YouTube)是国内通道的最佳地基;`inovector/mixpost`(MIT)可整体消化国际侧;`postiz-app`(AGPL)只宜自托管参考。
|
||||||
|
6. **最快验证路径**:第 1 周自托管 Mixpost 跑通国际平台并开始验证客户价值,同时并行开发 Agent 通道。
|
||||||
|
|
||||||
|
## 1. 需求与约束拆解
|
||||||
|
|
||||||
|
| 客户诉求 | 技术含义 |
|
||||||
|
|---|---|
|
||||||
|
| 越简单越好 | 客户三步上手:装 Agent → 扫码绑定 → 网站排期,零配置 |
|
||||||
|
| 发布越快 | WSS 任务推送到执行 <1s;跨平台并行;失败自动重试 |
|
||||||
|
| 账号越稳 | 登录态单点持有 + 固定设备指纹 + 固定 IP + 人级频率 |
|
||||||
|
| 覆盖全部发布功能 | 图文/视频/定时/多平台/多账号,统一任务模型 |
|
||||||
|
| 不违反官方规则 | 官方 API 优先;自动化部分明确告知灰色性质并分级开通 |
|
||||||
|
| 多运营协作一个官方账号 | 控制台角色权限 + 审核流;账号凭据不落运营手中 |
|
||||||
|
| IP 风控 | 账号-IP 绑定路由,按归属地分流(见 §8) |
|
||||||
|
|
||||||
|
## 2. 三种实现形态对比
|
||||||
|
|
||||||
|
| 维度 | Web控制端+桌面Agent(推荐) | 浏览器插件 | 纯云端官方API SaaS |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 多用户协作单账号 | **优**:凭据单点持有,角色审批,全程审计 | 差:多人登录同一账号易触发平台多设备风控 | 优:但仅覆盖有 API 的平台 |
|
||||||
|
| 账号凭据安全 | 优:客户本地加密,服务器零凭据 | 差:凭据散落各浏览器 | 优:OAuth,无密码 |
|
||||||
|
| IP 控制 | 优:Agent 按账号绑定代理 | 优:用户真实 IP(最自然) | 中:API 对 IP 不敏感 |
|
||||||
|
| 7×24 无人值守 | 优 | 差:需客户电脑开机 + 浏览器常驻 | 优 |
|
||||||
|
| 平台覆盖率 | **最广**:自动化兜底全部平台 | 中:受插件权限限制 | 仅覆盖有官方 API 的平台(抖音/快手/小红书/视频号内容发布均无公开 API) |
|
||||||
|
| 合规度 | 灰+白混合(分级开通) | 灰 | 白 |
|
||||||
|
| 开发成本 | 中高(可大幅复用开源) | 低 | 低(现成开源) |
|
||||||
|
|
||||||
|
**结论**:主架构 = Web 控制端 + Agent;国际平台叠加官方 API 通道;浏览器插件仅作扫码辅助或单人轻量版。
|
||||||
|
|
||||||
|
## 3. 总体架构(逻辑图)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
subgraph WEB["① 控制端 Web(多租户 SaaS)"]
|
||||||
|
U["运营 / 审核 / 管理员"]
|
||||||
|
W["任务编排 · 素材库OSS · 账号台账 · 审计"]
|
||||||
|
U --- W
|
||||||
|
end
|
||||||
|
subgraph CORE["② 调度中心(服务器)"]
|
||||||
|
Q["任务队列 Redis · 状态机 · 排期器"]
|
||||||
|
API["通道A 官方API执行器(OAuth直连)"]
|
||||||
|
AGW["通道B Agent网关(WSS注册/心跳/路由)"]
|
||||||
|
end
|
||||||
|
subgraph AGENT["③ 执行端 Agent(客户侧安装)"]
|
||||||
|
WS["WSS长连接 · 主动出网回连"]
|
||||||
|
VAULT["凭据保险库(cookie本地加密)"]
|
||||||
|
ROUTER["代理路由器(一账号一IP)"]
|
||||||
|
PA["Playwright 执行器(每账号独立档案)"]
|
||||||
|
QR["扫码登录面板(本地页面/托盘)"]
|
||||||
|
end
|
||||||
|
WEB -->|"任务下发"| Q
|
||||||
|
Q -->|"通道A 官方API"| API
|
||||||
|
Q -->|"通道B 自动化"| AGW
|
||||||
|
AGW --> WS --> ROUTER --> PA
|
||||||
|
VAULT --- PA
|
||||||
|
QR --- VAULT
|
||||||
|
API --> INT["国际:X / IG / YouTube / TikTok"]
|
||||||
|
PA --> DOM["国内:抖音/快手/小红书/视频号/B站/微博"]
|
||||||
|
PA --> INT
|
||||||
|
```
|
||||||
|
|
||||||
|
**要点**:
|
||||||
|
- 控制端:角色(管理员/审核/运营)、素材库(OSS+CDN)、账号台账、审计日志、通知(企微/钉钉/邮件)。
|
||||||
|
- Agent:轻量常驻进程(早期草案为 Tauri/Electron 壳;最终定版:WinUI 3 壳 + Go 核心,见 docs/tech-stack.md)。四大组件:WSS 客户端(主动出网,免内网穿透)、凭据保险库(SQLCipher,服务器零凭据)、代理路由器、Playwright 执行器。
|
||||||
|
- 素材流:控制端预签名直传 OSS → Agent 按任务拉取 → 发布后回调。
|
||||||
|
- 响应速度:WSS 下发 <1s;总耗时 ≈ 平台上传+审核,架构不构成瓶颈。
|
||||||
|
- **控制面/数据面分离**:我方服务器只处理任务元数据,全部平台流量从客户侧 Agent 发出,我方 IP 不接触平台(详见 §3.1)。
|
||||||
|
|
||||||
|
## 3.1 控制面 / 数据面分离(关键澄清:我方服务器不会被封)
|
||||||
|
|
||||||
|
你提出的这一点是方案成立的前提,明确写入设计原则:
|
||||||
|
|
||||||
|
- **控制面(我方 SaaS 服务器)**:只流转任务元数据——队列、排期、审核、审计、素材 OSS 中转、通知。**从不直接访问任何平台**,平台看不到我方任何基础设施,因而不存在「我方服务器 IP 被封」的可能。
|
||||||
|
- **数据面(客户侧 Agent)**:登录态、代理路由、浏览器自动化、上传与发布,全部从客户自己的机器发出,使用客户自己的 IP(企业宽带/住宅)或客户绑定的住宅代理。**客户与客户之间天然隔离**,杜绝「大量用户共用同一 IP」的矩阵风控问题。
|
||||||
|
- **素材中转**:素材存我方 OSS,但上传动作由客户 Agent 发起,平台只看到 Agent 的 IP,我方 OSS 不暴露给平台。
|
||||||
|
- **唯一例外——官方 API 通道**:X/IG/YouTube/TikTok 走客户自己的应用凭据 + OAuth,平台识别的是客户的应用与账号,IP 不敏感,可安全地放在我方服务器执行;如客户仍希望「数据不出我方」,也可整体下沉到 Agent。
|
||||||
|
|
||||||
|
**Agent 部署三形态**:
|
||||||
|
|
||||||
|
| 形态 | 7×24 | IP 真实度 | 成本 | 适用 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 客户办公电脑(默认) | 需常开 | 企业真实 IP,最优 | 零 | 默认形态,符合「越简单越好」 |
|
||||||
|
| 客户自有 VPS + 住宅代理 | ✅ | 住宅代理,较好 | 代理月费 | 需要深夜定时发布/无人值守的客户 |
|
||||||
|
| 我方托管 Agent(增值) | ✅ | 独享住宅代理 + 独享设备档案 | 高(我方承担风险与运维) | 大客户定制,绝不与其他客户共享 IP |
|
||||||
|
|
||||||
|
多人协作逻辑不变:控制面照常支撑多运营/审核/排期,数据面由客户 Agent 单点执行——一个官方账号始终只在一台机器、一个 IP、一个设备档案上登录。
|
||||||
|
|
||||||
|
## 4. 任务状态机(逻辑图)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> 草稿
|
||||||
|
草稿 --> 待审核: 运营提交
|
||||||
|
待审核 --> 已排期: 审核通过
|
||||||
|
待审核 --> 草稿: 驳回
|
||||||
|
已排期 --> 队列中: 到点入队
|
||||||
|
队列中 --> 执行中: Agent认领
|
||||||
|
执行中 --> 已发布: 平台回执成功
|
||||||
|
执行中 --> 失败重试: 可重试错误
|
||||||
|
失败重试 --> 队列中: 指数退避重入
|
||||||
|
执行中 --> 失败终态: 超过重试上限
|
||||||
|
失败终态 --> [*]
|
||||||
|
已发布 --> [*]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 任务执行时序(逻辑图)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant OP as 运营(Web)
|
||||||
|
participant S as 服务器
|
||||||
|
participant A as Agent(客户机器)
|
||||||
|
participant P as 目标平台
|
||||||
|
A->>S: WSS注册(设备+账号清单)
|
||||||
|
OP->>S: 提交任务(素材/标题/平台/时间)
|
||||||
|
S->>S: 校验 → 审核 → 排期
|
||||||
|
S->>A: 推送任务(加密载荷)
|
||||||
|
A->>S: ACK 认领(幂等)
|
||||||
|
A->>A: 选账号 → 注入代理 → 校验登录态
|
||||||
|
A->>P: 上传素材 + 提交发布
|
||||||
|
P-->>A: 发布回执
|
||||||
|
A->>S: 回传结果 + 截图留证
|
||||||
|
S-->>OP: 实时状态 + 失败通知
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 通信与安全设计
|
||||||
|
|
||||||
|
- **WSS 长连接**:Agent → Server 出站连接(天然穿透 NAT/防火墙),心跳 30s,断线指数退避重连;任务带 ID + ACK 幂等,防重复执行。
|
||||||
|
- **加密**:任务载荷 AES-256-GCM,密钥经 KMS 信封加密;素材下载 URL 短时效签名。
|
||||||
|
- **凭据**:cookie/token 仅存 Agent 本地(SQLCipher),服务器只存「账号是否在线、最后活跃」等元信息。
|
||||||
|
- **审计**:谁在何时提交/审核/执行了哪个账号的什么内容,全链路留痕。
|
||||||
|
|
||||||
|
## 7. 平台接入矩阵
|
||||||
|
|
||||||
|
| 平台 | 官方发布API | 推荐通道 | 登录方式 | 会话稳定性 | 难度 | 合规 | 风控敏感度 |
|
||||||
|
|---|---|---|---|---|---|---|---|
|
||||||
|
| 抖音 | 开放平台有(企业审核) | 自动化(默认)/API(客户有资质时) | 扫码 | 中(约1-4周) | ★★★ | 灰/白 | 高 |
|
||||||
|
| 快手 | 开放平台有(需审核) | 自动化 | 扫码 | 中 | ★★★ | 灰/白 | 中高 |
|
||||||
|
| 小红书 | 专业号API(以电商为主) | 自动化 | 扫码 | 中短 | ★★★ | 灰/白 | 高 |
|
||||||
|
| 视频号 | **无**(仅视频号助手) | 自动化(唯一路径) | 微信扫码 | **短(小时级)** | ★★★★★ | 灰 | **极高(牵连微信)** |
|
||||||
|
| B站 | 投稿协议成熟(biliup) | 协议库直连(非浏览器) | 扫码/cookie | 长(月级) | ★★ | 半白 | 低 |
|
||||||
|
| 微博 | 开放平台(需审核) | API / 自动化 | 扫码 | 中 | ★★ | 白 | 低 |
|
||||||
|
| X | v2官方API(付费) | 服务器直连 API | OAuth | 长 | ★★ | 白 | 低 |
|
||||||
|
| Instagram | Graph API(仅商业号) | 服务器直连 API | OAuth | 长 | ★★ | 白 | 低 |
|
||||||
|
| YouTube | Data API v3 | 服务器直连 API | OAuth | 长 | ★★ | 白 | 低 |
|
||||||
|
| TikTok | Content Posting API(需审核) | 服务器直连 API | OAuth | 长 | ★★★ | 白 | 低 |
|
||||||
|
| WhatsApp | Cloud API(模板消息) | Cloud API(通知)/私协议(群发,灰) | 扫码/凭证 | 中 | ★★★ | 白/灰 | 中高 |
|
||||||
|
|
||||||
|
> 注:自动化目标站点——抖音 `creator.douyin.com`、快手 `cp.kuaishou.com`、小红书 `creator.xiaohongshu.com`、视频号 `channels.weixin.qq.com`、B站 `member.bilibili.com`。
|
||||||
|
> WhatsApp 特殊性:无信息流「发布」概念;Cloud API 走模板消息(适合通知/营销,需审核模板),状态/群发属私协议灰区。
|
||||||
|
> 置信度说明:抖音/快手/小红书开放平台能力存在但审核门槛与覆盖范围需以官方文档核实(中置信);视频号无公开 API 为高置信(官方仅有视频号助手)。
|
||||||
|
|
||||||
|
## 8. IP 风控方案(逻辑图 + 策略)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
ACC["账号台账"] --> REG{"账号归属地?"}
|
||||||
|
REG -->|国内平台| POOL1["国内住宅IP池<br/>芝麻/快代理/922S5等"]
|
||||||
|
REG -->|国外平台| POOL2["海外住宅IP池<br/>BrightData/IPRoyal等"]
|
||||||
|
POOL1 --> BIND["一账号绑定一IP<br/>长期固定不轮换"]
|
||||||
|
POOL2 --> BIND
|
||||||
|
BIND --> CTX["浏览器Context级注入<br/>(非全局TUN,防串账号)"]
|
||||||
|
CTX --> EXEC["执行发布"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**策略**:
|
||||||
|
- 一账号一 IP,长期固定;**严禁轮换**(轮换=异地登录信号,触发风控)。
|
||||||
|
- 住宅 IP 优先,IDC 机房 IP 风险最高;新 IP 先「养」再发。
|
||||||
|
- 官方 API 通道不依赖 IP 属地(服务器任选);但 YouTube 自动化必须显式代理(`social-auto-upload` 的 `YT_PROXY` 已实证此坑:Chromium 不吃系统代理)。
|
||||||
|
- 代理池定时健康检查(可用性 + 归属地),异常自动摘除;同平台多账号放不同 IP + 不同设备档案。
|
||||||
|
|
||||||
|
## 9. 账号稳定策略
|
||||||
|
|
||||||
|
- **每账号独立持久化浏览器档案**(user_data_dir):固定 UA/分辨率/时区/字体,不跨机器迁移。
|
||||||
|
- **登录一律官方扫码**(APP 扫码天然绑定设备),禁止代登密码。
|
||||||
|
- **Cookie 保鲜**:每日低频心跳访问 + 失效检测,失效即推送「重新扫码」到控制台与企业微信。
|
||||||
|
- **发布节奏**:单账号人级频率上限,失败指数退避,避开平台风控高峰(大促/晚高峰更敏感)。
|
||||||
|
- **视频指纹**:ffmpeg 重编码去除元数据,多平台分发避免同文件哈希直传(防搬运识别关联)。
|
||||||
|
- 平台风控核心信号是「设备+IP+行为」三要素,三者全固定最稳——这也是 Agent 方案优于插件方案的根本原因。
|
||||||
|
|
||||||
|
## 10. 开源项目聚合与复用建议
|
||||||
|
|
||||||
|
| 项目 | 星标 | 许可 | 覆盖平台 | 复用方式 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| dreammis/social-auto-upload | 14.4k | MIT | 抖音/快手/小红书/视频号/微博/B站/百家号/支付宝生活号/虎扑/TikTok/YouTube | **国内通道地基**:fork 后把本地脚本改造成 Agent 服务,uploader 层几乎不动 |
|
||||||
|
| inovector/mixpost | 3.5k | MIT | X/FB/IG/LinkedIn/Mastodon/Pinterest/TikTok/YouTube | MIT 可商用,国际侧可直接魔改 |
|
||||||
|
| gitroomhq/postiz-app | 34.9k | AGPL-3.0 | X/IG/TikTok/YouTube/Threads/Bluesky/Pinterest/LinkedIn/Reddit 等 | 自托管可用;闭源 SaaS 注意 AGPL 传染,仅作参考 |
|
||||||
|
| biliup/biliup | 5.4k | MIT | B站 | 投稿库 `bili_webup` 直接集成 |
|
||||||
|
| DevilJie/social-auto-upload-web-ui | 210 | MIT | 同上(Web 化) | Web+本地服务分层先例,前端可参考 |
|
||||||
|
| dorisoy/ShortVideo.AutoPublisher | 167 | MIT | 抖音/视频号/小红书/百家号/头条 | 视频号实现参考(.NET+Playwright),桌面 Agent 先例 |
|
||||||
|
| ddean2009/MoneyPrinterPlus | 6.8k | GPL-3.0 | 抖音/快手/小红书/视频号 | 视频号上传流程参考;GPL 注意 |
|
||||||
|
| white0dew/XiaohongshuSkills | 3.3k | MIT | 小红书 | 小红书发布流程参考(Agent 生态) |
|
||||||
|
| NanmiCoder/MediaCrawler | 63.1k | 自定义 | 小红书/抖音/快手/B站/微博(爬) | 登录态/签名逆向参考;发布在付费 Pro 版 |
|
||||||
|
| ReaJason/xhs | 2.2k | MIT | 小红书 | Web 协议封装,发布能力有限需自研上传 |
|
||||||
|
| tweepy | 11.2k | MIT | X | X 官方 API 客户端 |
|
||||||
|
| WhiskeySockets/Baileys | 10.8k | MIT | WhatsApp(私协议) | 备用;优先 Cloud API |
|
||||||
|
| davidteather/TikTok-Api | 6.6k | MIT | TikTok(私协议) | 备用;优先官方 API |
|
||||||
|
| subzeroid/instagrapi | 6.7k | 自定义 | IG(私协议) | 不推荐主线(封号风险高) |
|
||||||
|
| lich0821/WeChatFerry | 6.8k | MIT | 微信(已归档) | 仅参考 |
|
||||||
|
|
||||||
|
**复用策略**:国内地基 `social-auto-upload`(MIT)→ 国际侧 Mixpost(MIT)→ B站 biliup → 视频号参考 tencent_uploader + ShortVideo.AutoPublisher。私协议库(instagrapi/Baileys/TikTok-Api)不作主线,仅官方 API 未覆盖时的知情备选。
|
||||||
|
|
||||||
|
## 11. 实现难度与可信度评估
|
||||||
|
|
||||||
|
| 模块 | 难度 | 可信度 | 说明 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Web 控制台 + 任务队列 | ★★ | ★★★★★ | 成熟技术栈,无风险 |
|
||||||
|
| 官方 API 通道(国际) | ★★ | ★★★★★ | OAuth + 官方 SDK,完全合规 |
|
||||||
|
| Agent 框架(WSS+凭据+代理) | ★★★ | ★★★★ | 工程量大但完全可控 |
|
||||||
|
| 抖音/快手/小红书自动化 | ★★★ | ★★★☆ | 有 MIT 开源可 fork;持续风控适配 |
|
||||||
|
| B站投稿 | ★★ | ★★★★★ | biliup 协议成熟稳定 |
|
||||||
|
| **视频号** | ★★★★★ | ★★ | 无 API、会话短、牵连微信,唯一硬骨头 |
|
||||||
|
| WhatsApp 群发/状态 | ★★★★ | ★★☆ | Cloud API 限模板;私协议有封号风险 |
|
||||||
|
| IP 代理管理 | ★★★ | ★★★★ | 采购 + 绑定 + 健康检查 |
|
||||||
|
| 多租户/权限/审计 | ★★★ | ★★★★★ | 常规业务功能 |
|
||||||
|
|
||||||
|
**总评**:MVP(官方 API 通道 + 抖音/快手/小红书/B站)2-3 人 × 4-6 周;全量(含视频号)3-4 人 × 2-3 个月 + 持续风控维护(平台前端改版适配是长期成本,每周巡检)。
|
||||||
|
|
||||||
|
## 12. 分阶段实施路线
|
||||||
|
|
||||||
|
| 阶段 | 内容 | 周期 | 产出 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| P0 验证 | 自托管 Mixpost 跑通 X/IG/YouTube | 1 周 | 验证国际侧价值与合规通道 |
|
||||||
|
| P1 MVP | 控制台 + 任务队列 + Agent 框架 + 抖音/快手/小红书/B站 | 3-4 周 | 可交付的第一版 |
|
||||||
|
| P2 协作 | 角色权限/审核流/审计/通知 | 1-2 周 | 多人协作闭环 |
|
||||||
|
| P3 攻坚 | 视频号(独立迭代)+ 微博/百家号 | 2-3 周 | 全平台覆盖 |
|
||||||
|
| P4 白通道 | 抖音开放平台/小红书专业号 API 接入(客户有资质时) | 并行 | 合规面扩大 |
|
||||||
|
| P5 商业化 | 多租户/计费/渠道 | 持续 | SaaS 化 |
|
||||||
|
|
||||||
|
## 13. 合规边界(务必读)
|
||||||
|
|
||||||
|
- **官方 API = 完全合规**(平台授权);**浏览器自动化 = 技术上违反多数平台「禁止自动化访问」条款**,实际执行风险是限流/封号。产品必须对客户**知情告知**,不可隐瞒。
|
||||||
|
- **分级开通**:白名单通道(官方 API 平台)默认开启;灰通道(自动化)客户主动开通 + 签署风险告知书。
|
||||||
|
- **红线不碰**:大规模矩阵养号、搬运去重绕过、刷量、私信轰炸——触碰即封号且可能产生法律风险。
|
||||||
|
- **内容合规前置**:平台内容审核由平台执行,我方提供发布前自检提示,不代替审核。
|
||||||
|
- **数据合规**:凭据加密最小化,素材仅在客户授权空间流转(个保法/数安法)。
|
||||||
|
- **视频号特别告知**:微信账号是客户核心资产,封禁损失远大于其他平台——默认关闭、独立审批开通、建议专用微信号。
|
||||||
|
|
||||||
|
## 14. 风险清单与缓解
|
||||||
|
|
||||||
|
| 风险 | 影响 | 缓解 |
|
||||||
|
|---|---|---|
|
||||||
|
| 平台前端改版/选择器失效 | 自动化断链 | 模块化选择器 + 灰度监控 + 每周巡检(最大持续成本) |
|
||||||
|
| 登录态失效 | 发布失败 | 保鲜心跳 + 失效推送 + 一键重扫 |
|
||||||
|
| 账号风控 | 限流/封号 | 固定设备+IP+人级频率+养号策略 |
|
||||||
|
| 代理失效 | 整批失败 | 池健康检查 + 同区域自动切换 |
|
||||||
|
| API 涨价/收紧 | 成本/断供 | X 从 Basic 起步,预留自动化兜底 |
|
||||||
|
| 视频号牵连微信 | 重大损失 | 默认关闭 + 专用微信号 + 客户书面确认 |
|
||||||
|
| AGPL 传染 | 法律/商业 | 商用选 MIT 项目或自托管,律师审阅 |
|
||||||
|
|
||||||
|
## 15. 「越简单越好」的产品体验设计
|
||||||
|
|
||||||
|
- **客户三步**:装 Agent(一条命令/安装包)→ 打开本地扫码面板绑定各平台 → 网站排期一键发布。
|
||||||
|
- **控制台极简视图**:今天要发什么 / 发了没 / 失败原因 / 一键重试。
|
||||||
|
- **发布回执**:每平台截图 + 链接留证,运营可查可复核。
|
||||||
|
- **登录过期**:红点 + 企微提醒 + 扫码面板自动弹出。
|
||||||
|
- **单人版与团队版同一套**:单人 = 跳过审核流,零学习成本。
|
||||||
|
|
||||||
|
## 16. 用户端 / 管理端体验走查(Q&A 补充)
|
||||||
|
|
||||||
|
### 16.1 三种形态的「安装」真相
|
||||||
|
|
||||||
|
同一套 Agent 软件,只差运行地点:
|
||||||
|
|
||||||
|
| 形态 | 装什么 | 谁来装 | 怎么装 | 扫码在哪出现 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 客户办公电脑(默认) | 桌面应用(系统托盘) | 客户 IT / 账号负责人 | 下载安装包,双击即用 | 应用窗口 / 托盘弹窗 |
|
||||||
|
| 客户自有 VPS | Docker 容器(无界面) | 客户 IT | 一条命令 / 一键脚本 | 控制台统一弹码(经 WSS 回传二维码图片,推荐) |
|
||||||
|
| 我方托管(增值) | **无需安装** | 我方 | 我方部署 | 客户控制台页面直接弹码 |
|
||||||
|
|
||||||
|
- 无论哪种形态,客户要做的「账号动作」**只有扫码登录**(官方 APP 扫码,天然绑定设备);账号密码/验证码永不经过我方系统。
|
||||||
|
- 住宅代理:办公电脑走企业宽带时**零配置**;VPS / 托管形态需购买住宅代理并粘贴代理账号密码(托管形态可由我方代采购)。
|
||||||
|
|
||||||
|
### 16.2 用户端体验(装 Agent 的那一端)
|
||||||
|
|
||||||
|
- **首次 5 分钟**:装好 → 打开 → 登录企业工作区 → 输入配对码绑定控制台 → 逐个平台点「绑定」弹码 → 手机扫码 → 绿色对勾。此后常驻托盘,无感运行。
|
||||||
|
- **日常几乎零操作**:托盘显示在线状态与待发任务数;登录过期时弹窗 + 企微/短信提醒「请重新扫码」;部分平台约每周需扫一次码。
|
||||||
|
- **关键分工**:装机的人是企业 IT 或账号负责人,不是日常运营——运营全程只碰网站。
|
||||||
|
|
||||||
|
### 16.3 管理端体验(Web 控制台)
|
||||||
|
|
||||||
|
- **运营**:素材库上传 → 编辑标题/话题 → 勾选平台与账号 → 排期日历选时间 → 提交。发布中实时进度(上传中/审核中/已发布 + 链接截图);失败显示原因 + 一键重试。
|
||||||
|
- **审核**:待审列表 → 分平台预览 → 通过/驳回(附批注)。
|
||||||
|
- **管理员**:账号台账(绑定状态/最后活跃/代理绑定)、成员与角色、审计日志、通知渠道(企微/钉钉/邮件)。
|
||||||
|
- **单人版**:同一套界面自动隐藏审核流,运营即管理员,零学习成本。
|
||||||
|
|
||||||
|
### 16.4 平台方管理端(我方 SaaS 后台)
|
||||||
|
|
||||||
|
- 租户/计费、Agent 在线监控与版本推送、渠道适配器健康巡检(平台改版监控)、代理池与风控事件台账(托管形态)、客户支持。
|
||||||
|
|
||||||
|
## 17. 工程实施:技术栈、联调环境与排期(Q&A 补充)
|
||||||
|
|
||||||
|
### 17.1 可行性结论
|
||||||
|
|
||||||
|
- **能实现**。先例:dreammis/social-auto-upload(MIT)已跑通国内全平台上传流程,我们只把它从「本地脚本」改为「Agent 服务 + 控制台」;商业产品(易媒/融媒宝)同构。真正的不确定性只在平台风控对抗,不在架构。
|
||||||
|
- 「软件与网站连接很慢,即使本地调用也很慢」是**工程问题**:按 17.2 对照排查 + 17.3 分段计时,通常 5 分钟可定位。
|
||||||
|
|
||||||
|
### 17.2 连接慢的十大嫌疑
|
||||||
|
|
||||||
|
| 症状 | 根因与修复 |
|
||||||
|
|---|---|
|
||||||
|
| 每条消息都有固定 100-500ms 延迟 | 每次请求重新鉴权(bcrypt 重算)→ 改会话 Token/JWT 校验 |
|
||||||
|
| 本机访问 localhost 也慢几秒/偶发超时 | IPv6 陷阱:Node17+ 把 localhost 解析到 ::1,服务端只监听 127.0.0.1 → 显式用 127.0.0.1、--dns-result-order=ipv4first,或双栈监听 |
|
||||||
|
| 开了代理软件后本地调用变慢 | HTTP(S)_PROXY 环境变量劫持 localhost → NO_PROXY 加入 localhost,127.0.0.1 |
|
||||||
|
| 客户端执行发布时心跳/消息全卡住 | 单线程串行:Playwright 等重活阻塞消息循环 → 执行器独立 worker,消息与执行分离 |
|
||||||
|
| 状态更新「慢半拍」 | HTTP 轮询代替长连接 → 改 WebSocket/SSE 推送 |
|
||||||
|
| 每次任务重新建连 | 连接未复用 / TLS 每次握手 → 持久连接 + 会话复用 |
|
||||||
|
| 局域网跨机慢 | 路由器 AP 隔离 / mDNS 主机名解析慢 → 用 IP 直连、关 AP 隔离 |
|
||||||
|
| Windows 上整体偏慢 | Defender 实时扫描客户端进程(Go core/浏览器引擎)→ 加排除项,先用裸 curl 测基线 |
|
||||||
|
|
||||||
|
### 17.3 分段计时定位法(本仓库 `tools/diag-connection.mjs`)
|
||||||
|
|
||||||
|
| 环节 | 局域网正常值 | 偏大指向 |
|
||||||
|
|---|---|---|
|
||||||
|
| TCP connect | <2ms | 防火墙 / IPv6 / 代理 |
|
||||||
|
| WS 握手 open | <10ms | 服务端 WS 框架 / 鉴权 |
|
||||||
|
| 首条消息 RTT | <20ms | 串行往返过多 / 服务端处理 |
|
||||||
|
| 心跳间隔抖动 | 稳定 ±1s | 客户端单线程阻塞 |
|
||||||
|
| 服务端处理耗时 | 与接口总延迟之差=网络 | DB 慢查询 / N+1 / 同步 IO |
|
||||||
|
|
||||||
|
### 17.4 技术栈
|
||||||
|
|
||||||
|
> 2026-08-20 注:本节为早期 JS 栈草案,已被定版取代——技术栈以 docs/tech-stack.md 与 docs/delivery-plan.md 为准(MySQL + 前后端 Go 统一,页面层 React+TDesign)。
|
||||||
|
|
||||||
|
| 模块 | 选型 | 理由 |
|
||||||
|
|---|---|---|
|
||||||
|
| 仓库组织 | pnpm monorepo + Turborepo | apps/web、apps/agent-core、apps/agent-desktop 共享协议包 |
|
||||||
|
| 网站 | Next.js 15 + TS + Tailwind + shadcn/ui | 全栈一体,API Routes 即后端 |
|
||||||
|
| 数据 | PostgreSQL + Drizzle + Redis + BullMQ | 任务队列与排期靠 Redis |
|
||||||
|
| 实时 | Socket.IO(WebSocket) | 心跳/幂等/重连开箱即用 |
|
||||||
|
| 客户端核心 | Node + Playwright + ws + better-sqlite3 | 凭据本地加密存储;执行器独立子进程 |
|
||||||
|
| 客户端壳 | Electron + electron-builder | 托盘 + 扫码窗口;CI 出 Windows 包 |
|
||||||
|
| 素材 | 一期:服务器本地盘(StorageDriver 抽象);二期:MinIO/OSS | 直链下载(带短时效签名 token) |
|
||||||
|
| 测试 | vitest + Playwright + MockUploader 假执行器 | 先打通链路再上真实平台 |
|
||||||
|
|
||||||
|
### 17.5 内网联调环境(Mac=服务端,Windows=客户端)
|
||||||
|
|
||||||
|
完全可行,标准做法:
|
||||||
|
|
||||||
|
1. Mac:web 与 WS 服务绑定 0.0.0.0(`next dev -H 0.0.0.0`);查 Mac 局域网 IP:`ipconfig getifaddr en0`。
|
||||||
|
2. Windows:Agent 配置 `EP_WS=ws://<Mac局域网IP>:<端口>`(用 IP,不用主机名)。
|
||||||
|
3. 防火墙:macOS 放行 node 入站;Windows 出站默认放行。
|
||||||
|
4. 路由器:关闭 AP 隔离(访客网络常见坑)。
|
||||||
|
5. 先用 curl/wscat 从 Windows 测通 Mac 端口,再启动 Agent。
|
||||||
|
6. 公司网络隔离时:ZeroTier / Tailscale 组虚拟局域网。
|
||||||
|
7. 内网阶段用 ws:// 即可;生产才上 wss:// + 证书。
|
||||||
|
|
||||||
|
### 17.6 开发顺序与周期
|
||||||
|
|
||||||
|
| 阶段 | 内容 | 周期 |
|
||||||
|
|---|---|---|
|
||||||
|
| 0 协议定版+骨架 | WS 协议 JSON Schema、假执行器打通全链路、延迟基线 <300ms | 1 周 |
|
||||||
|
| 1 网站功能 | 租户/角色/账号台账/素材/排期/审核/通知 | 2-3 周 |
|
||||||
|
| 2 Agent 真实平台 | 抖音/快手/小红书/B站 uploader(参考 social-auto-upload) | 3-4 周 |
|
||||||
|
| 3 联调测试 | 异常注入:断网/掉线/登录过期/重复任务 | 1-2 周 |
|
||||||
|
| 4 视频号攻坚 | 独立迭代,默认关闭 | 2 周 |
|
||||||
|
| 5 打包分发 | Windows 安装包 + 自动更新 | 1 周 |
|
||||||
|
|
||||||
|
- **单人 10-13 周,双人并行 6-8 周**。
|
||||||
|
- 管理预期:网站 CRUD 与客户端框架确实不长(各 2-3 周),但**时间黑洞是平台 uploader 的适配与风控对抗**,不是网站也不是客户端框架;此后每周约 0.5-1 天持续维护。
|
||||||
|
|
||||||
|
## 18. 协议层考证:局域网慢的根因与实测基线
|
||||||
|
|
||||||
|
### 18.1 实测基线(2026-08 本机实测,Apple Silicon)
|
||||||
|
|
||||||
|
| 环节 | 实测 | 结论 |
|
||||||
|
|---|---|---|
|
||||||
|
| TCP 连接 127.0.0.1 x100 | avg 0.14ms(p99 1.17ms) | 协议开销可忽略 |
|
||||||
|
| TCP 连接 localhost(走 IPv6 解析) | avg 0.49ms(p99 4.33ms) | **IPv6 路径慢 3.5 倍**;若防火墙丢弃 ::1 包将变成秒级 |
|
||||||
|
| WS 握手(新建连接)x30 | avg 0.58ms | 建连便宜,但别每条消息都建 |
|
||||||
|
| 单连接消息 RTT x2000 | **avg 0.07ms(p99 0.14ms)** | 协议真实下界:亚毫秒 |
|
||||||
|
| 每消息新建连接 x30 | avg 0.75ms | 是复用连接的 **10 倍** |
|
||||||
|
| 单次 bcrypt 级哈希(pbkdf2 100k) | 15.9ms | 每次请求重算鉴权 = 每条消息 +16ms(Windows 更慢 50-150ms) |
|
||||||
|
| 串行 10 次往返 | 0.91ms | 本机便宜;跨机时 = 10×RTT + 处理 |
|
||||||
|
|
||||||
|
**考证结论**:协议层(TCP+WS 复用连接)本机只有 0.07ms。局域网环境「慢」必然是下列上层之一叠加:
|
||||||
|
|
||||||
|
### 18.2 局域网环境下慢的候选层(延迟预算表)
|
||||||
|
|
||||||
|
| 层 | 典型增量 | 判断方法 |
|
||||||
|
|---|---|---|
|
||||||
|
| 协议 RTT(复用连接) | <1ms | 基准 |
|
||||||
|
| WiFi 抖动 / 客户端节能 | +1~20ms | 换有线对比 |
|
||||||
|
| 丢包重传(TCP RTO) | **+200ms 起/包** | ping 查丢包率 |
|
||||||
|
| 每请求重算鉴权(bcrypt 级) | +16~150ms | 与免鉴权接口对比压测 |
|
||||||
|
| 每消息新建连接 / TLS 握手 | 本地 +0.5ms,跨机 +数 RTT | 对比复用与不复用 |
|
||||||
|
| IPv6 陷阱 | 本地 +0.35ms,丢包场景秒级 | 对比 127.0.0.1 与 localhost |
|
||||||
|
| HTTP 轮询 | 秒级(等于轮询间隔) | 协议审计 |
|
||||||
|
| Socket.IO 降级为长轮询 | 秒级 | 检查实际 transport |
|
||||||
|
| 服务端阻塞 / 慢 DB / N+1 | 不定 | 全链路埋点 |
|
||||||
|
|
||||||
|
### 18.3 协议层五条铁律(让「慢」结构上不可能发生)
|
||||||
|
|
||||||
|
1. **一条 WSS 连接用到底**:Agent→Server 单连接复用,不每任务建连;开启 TCP_NODELAY。
|
||||||
|
2. **鉴权只做一次**:连接建立时 auth 一次,后续走连接态 + 消息级 HMAC 校验(微秒级),杜绝每请求重算密码哈希。
|
||||||
|
3. **执行与消息分离**:Playwright/上传放独立 worker 进程,绝不阻塞消息循环(心跳卡死是「假慢」主因)。
|
||||||
|
4. **消息合并与节流**:ack 合并、进度回报节流 1 次/s,避免 chatty 串行往返。
|
||||||
|
5. **全链路埋点**:每条消息带 ts、ack 带 ackTs,服务端聚合各段 delay 指标并告警——「慢在哪」永远用数据说话,不再靠猜。
|
||||||
|
|
||||||
|
## 19. 浏览器调用最小化与指纹模拟
|
||||||
|
|
||||||
|
### 19.1 浏览器调用清单(原则:协议能办的事绝不开浏览器)
|
||||||
|
|
||||||
|
| 操作 | 触发浏览器? | 替代方案 |
|
||||||
|
|---|---|---|
|
||||||
|
| 登录取码 | 否 | 平台二维码 API(**仅视频号必须在浏览器内取码**) |
|
||||||
|
| 登录态校验 / 保鲜 | 否 | 轻量 HTTP API 心跳(首页/用户信息接口),空闲时浏览器保持关闭 |
|
||||||
|
| B站上传发布 | 否 | 投稿协议纯 HTTP(参考 biliup / bili_webup) |
|
||||||
|
| 抖音/快手/小红书上传 | **是(仅此一步)** | 冷启动 → 会话复用 → 用完即关 |
|
||||||
|
| 定时发布设置 | 部分可 API | 能用 API 就不开浏览器 |
|
||||||
|
| 视频号全流程 | 是 | 无替代(唯一通道) |
|
||||||
|
| 二次验证 | 视类型 | QR/短信/APP确认推送人工;滑块低风控平台本地尝试 |
|
||||||
|
|
||||||
|
### 19.2 浏览器生命周期
|
||||||
|
|
||||||
|
- **空闲**:浏览器保持关闭,登录态靠协议心跳保鲜 → 心跳与消息零浏览器开销。
|
||||||
|
- **执行**:仅「上传发布」阶段冷启动;同平台同账号任务排队复用同一实例(会话复用)。
|
||||||
|
- **结束**:任务完成即关,释放内存与句柄。
|
||||||
|
|
||||||
|
### 19.3 指纹模拟要点
|
||||||
|
|
||||||
|
- **用客户机器上真实 Chrome**(playwright channel:`chrome`),不用捆绑 Chromium——JA3/UA/指纹与真实浏览器的差异本身就是风控信号。
|
||||||
|
- 每账号独立 user_data_dir:固定 UA/分辨率/时区/语言/字体/hardwareConcurrency/deviceMemory。
|
||||||
|
- playwright-extra + stealth 插件;抹除 navigator.webdriver 与 CDP 自动化特征。
|
||||||
|
- 网络层:固定代理、WebRTC 防泄漏、时区与代理归属地一致。
|
||||||
|
- 行为层:可选「先浏览主页再发布」养号动作,操作间隔随机化(人级)。
|
||||||
|
- **铁律:一档案 = 一平台一账号一IP,永不交叉复用。**
|
||||||
|
|
||||||
|
## 20. 二次验证场景与兜底设计
|
||||||
|
|
||||||
|
### 20.1 统一挑战模型(Challenge)
|
||||||
|
|
||||||
|
- 消息:challenge.push(Agent→Server:类型+凭证+平台+账号)/ challenge.resolve(Server→Agent:用户输入结果)。
|
||||||
|
- 类型:qr(二维码)/ confirm(APP确认)/ sms(验证码)/ captcha(滑块点选)/ pending(人工处理)。
|
||||||
|
- 状态:挑战中 → 已解决 / 超时挂起 / 放弃。
|
||||||
|
|
||||||
|
### 20.2 五类挑战与兜底
|
||||||
|
|
||||||
|
| 类型 | 常见平台 | 检测 | 兜底流程 | 超时处理 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 二维码登录 | 抖音/快手/小红书/B站常态 | 登录页出现 QR | 推控制台+企微,码过期自动刷新 | 5min → 任务挂起 |
|
||||||
|
| APP确认「是本人」 | 抖音/小红书异地登录 | 页面提示确认 | 推送手机确认,Agent 轮询登录态 | 5min → 挂起 |
|
||||||
|
| 短信/邮箱验证码 | 各平台敏感操作 | 输入框出现 | 控制台弹输入框(带尾号提示)→ 回填,错可重输 2 次 | 3min → 挂起 |
|
||||||
|
| 滑块/点选 | 上传前风控 | 验证组件出现 | 默认截图推人工拖拽;低风控平台本地自动尝试,失败转人工 | 3min → 挂起 |
|
||||||
|
| 人机识别失败 | TikTok 等国际平台 | 登录被拒 | 推送人工登录指引 | 转人工队列 |
|
||||||
|
|
||||||
|
### 20.3 原则
|
||||||
|
|
||||||
|
- **账号稳优先**:验证码类挑战人工兜底为主、Agent 硬解为辅,绝不为速度牺牲账号。
|
||||||
|
- 挑战走控制面通道(不与平台流量混在一起)。
|
||||||
|
- 挂起任务可一键重发/放弃;所有挑战过程留审计。
|
||||||
|
|
||||||
|
## 21. 详细端到端逻辑图(重绘)
|
||||||
|
|
||||||
|
### 21.1 全链路时序(用户→端→平台→用户 + 重试/挑战)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant OP as 运营
|
||||||
|
participant S as 服务器
|
||||||
|
participant A as Agent
|
||||||
|
participant PL as 平台
|
||||||
|
participant IT as 账号负责人
|
||||||
|
Note over A,S: 单条WSS长连接复用(实测RTT 0.07ms)
|
||||||
|
OP->>S: 提交任务(素材/平台/排期)
|
||||||
|
S->>S: 校验→审核→入队→排期触发
|
||||||
|
S->>A: task.push(taskId,加密载荷)
|
||||||
|
A-->>S: ack(taskId) 幂等确认
|
||||||
|
A->>A: 选账号→注入代理→协议级登录态校验(不启浏览器)
|
||||||
|
alt 登录态正常
|
||||||
|
A->>PL: 冷启动浏览器→上传素材→提交发布
|
||||||
|
PL-->>A: 发布回执+截图
|
||||||
|
A->>S: task.result(success)
|
||||||
|
S-->>OP: 实时状态+链接
|
||||||
|
else 平台触发二次验证
|
||||||
|
PL-->>A: 挑战(扫码/短信/APP确认/滑块)
|
||||||
|
A->>S: challenge.push(类型,凭证)
|
||||||
|
S-->>IT: 控制台+企微提醒
|
||||||
|
IT->>S: 扫码或输入验证码
|
||||||
|
S->>A: challenge.resolve(结果)
|
||||||
|
A->>PL: 继续发布流程
|
||||||
|
else 网络类失败
|
||||||
|
A->>A: 指数退避 1m/5m/15m/1h
|
||||||
|
A->>PL: 自动重试上传
|
||||||
|
else 登录态失效
|
||||||
|
A->>S: challenge.push(qr)
|
||||||
|
S-->>IT: 推送扫码提醒
|
||||||
|
IT->>S: 扫码成功
|
||||||
|
S->>A: challenge.resolve
|
||||||
|
A->>PL: 恢复执行
|
||||||
|
else 风控拦截
|
||||||
|
A->>S: task.result(fail,原因)
|
||||||
|
S->>S: 账号冷却+人工队列
|
||||||
|
S-->>OP: 告警通知
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### 21.2 重试与降级状态机
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> 队列中
|
||||||
|
队列中 --> 执行中: Agent认领
|
||||||
|
执行中 --> 已发布: 平台回执成功
|
||||||
|
执行中 --> 网络重试: 网络/超时错误
|
||||||
|
网络重试 --> 执行中: 退避1m/5m/15m/1h
|
||||||
|
网络重试 --> 失败终态: 超上限N次
|
||||||
|
执行中 --> 挑战中: 需扫码/验证码
|
||||||
|
挑战中 --> 执行中: 用户完成挑战
|
||||||
|
挑战中 --> 挂起: 挑战超时
|
||||||
|
挂起 --> 执行中: 用户稍后处理
|
||||||
|
执行中 --> 账号冷却: 风控拦截
|
||||||
|
账号冷却 --> 执行中: 冷却结束+人工确认
|
||||||
|
账号冷却 --> 失败终态: 人工放弃
|
||||||
|
失败终态 --> 队列中: 人工一键重发
|
||||||
|
已发布 --> [*]
|
||||||
|
失败终态 --> [*]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 21.3 二次验证挑战兜底流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
TR["平台触发二次验证"] --> DET{"Agent检测挑战类型"}
|
||||||
|
DET -->|二维码登录| QR["取码→推控制台+企微<br/>码过期自动刷新"]
|
||||||
|
DET -->|APP确认| CF["推送手机确认提醒<br/>Agent轮询登录态"]
|
||||||
|
DET -->|短信/邮箱| SMS["控制台弹输入框<br/>用户输入→回传回填<br/>错可重输2次"]
|
||||||
|
DET -->|滑块/点选| CAP["默认截图推人工<br/>低风控平台本地尝试"]
|
||||||
|
QR --> OK{"完成?"}
|
||||||
|
CF --> OK
|
||||||
|
SMS --> OK
|
||||||
|
CAP --> OK
|
||||||
|
OK -->|是| CONT["Agent继续发布流程"]
|
||||||
|
OK -->|否/超时| PEND["任务挂起→人工队列<br/>一键重发/放弃"]
|
||||||
|
PEND --> CONT
|
||||||
|
```
|
||||||
|
|
||||||
|
## 22. 登录链路优化:把 10 秒级登录压到 2 秒级
|
||||||
|
|
||||||
|
### 22.1 拆解:登录到底慢在哪
|
||||||
|
|
||||||
|
朴素实现(浏览器全流程)逐环节耗时:启动浏览器 → 加载登录页 → 找/截二维码 → 传图 → 等扫码 → 轮询检测 → 换取登录态。其中「浏览器 + 页面 + 轮询」是机器耗时大头,人的扫码时间(3-10s)无法压缩但可以重叠。
|
||||||
|
|
||||||
|
| 环节 | 朴素实现 | 优化后 | 手段 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 启动浏览器 | 1-3s(冷启动) | **0s** | API 取码免浏览器(B站已验证,biliup 直接出 qrcode.png);视频号用常驻温实例 |
|
||||||
|
| 页面加载渲染 | 2-5s | ~1s(仅视频号) | 直达登录页 + 路由拦截广告/统计/字体资源 |
|
||||||
|
| 抽取二维码 | 0.5-2s(截图/找元素) | **<0.1s** | DOM 取二维码图片 URL 或 API 直接返回 token(开源项目 utils/login_qrcode 即此模式) |
|
||||||
|
| 码跨层传递 | 0.2-1s(传截图) | **<50ms** | 只传 token/图片URL,控制台本地重渲染——更快且**更清晰**(截图压缩会降低扫码成功率) |
|
||||||
|
| 检测扫码成功 | 轮询 1-3s/次 | 0.3-0.5s | 短轮询 300-500ms;或拦截页面自身状态 XHR 即时检测 |
|
||||||
|
| 换取登录态 | 1-2s(页面跳转等待) | 0.2-0.5s | API 换取 cookie/token |
|
||||||
|
| **机器合计** | **约 10-16s** | **约 1-2s**(视频号场景 3-4s) | 用户扫码 3-10s 不变,但成为唯一等待 |
|
||||||
|
|
||||||
|
### 22.2 五条铁律
|
||||||
|
|
||||||
|
1. **让登录少发生**:cookie 保鲜 + refresh 接口 + 过期预测(登录是低频事件,最好的登录优化是「不登录」)。
|
||||||
|
2. **能 API 不浏览器**:B站纯 API 已验证;抖音/小红书/快手优先尝试其 Web 登录 API,失败再回退浏览器。
|
||||||
|
3. **只传 token 不传截图**:所有层传递二维码内容/图片 URL,渲染端本地重绘。
|
||||||
|
4. **检测即时化**:300-500ms 短轮询 + 页面网络钩子双通道。
|
||||||
|
5. **等待重叠**:多平台并行取码、预计过期前预热、控制台+企微+托盘同时推送。
|
||||||
|
|
||||||
|
### 22.3 视频号例外(唯一必须浏览器的登录)
|
||||||
|
|
||||||
|
- 温浏览器常驻:登录需求出现前预启动,不冷启动。
|
||||||
|
- 直达登录页 + 资源拦截:只加载必要资源,页面 5s→1s。
|
||||||
|
- 取码:DOM 抓二维码图片 URL,不截图。
|
||||||
|
- 检测:注入 MutationObserver 监听登录态跳转 + 拦截页面状态 XHR。
|
||||||
|
- 提示:抖音/快手现可选用 patchright(补丁版 Playwright,开源项目已采用),指纹绕过更稳。
|
||||||
|
|
||||||
|
## 23. 系统设计文档(网页端 + 客户端 App)
|
||||||
|
|
||||||
|
完整系统设计(两端功能清单、连接方案评估、四层鉴权、REST/WS 协议、配对与登录流程、边界问题)见独立文档:docs/system-design.md。
|
||||||
|
|
||||||
|
## 附录:参考链接
|
||||||
|
|
||||||
|
- https://github.com/dreammis/social-auto-upload
|
||||||
|
- https://github.com/DevilJie/social-auto-upload-web-ui
|
||||||
|
- https://github.com/dorisoy/ShortVideo.AutoPublisher
|
||||||
|
- https://github.com/gitroomhq/postiz-app
|
||||||
|
- https://github.com/inovector/mixpost
|
||||||
|
- https://github.com/biliup/biliup
|
||||||
|
- https://github.com/SocialSisterYi/bilibili-API-collect (已归档)
|
||||||
|
- https://github.com/NanmiCoder/MediaCrawler
|
||||||
|
- https://github.com/ReaJason/xhs
|
||||||
|
- https://github.com/tweepy/tweepy
|
||||||
|
- https://github.com/WhiskeySockets/Baileys
|
||||||
|
- https://github.com/davidteather/TikTok-Api
|
||||||
|
- https://github.com/subzeroid/instagrapi
|
||||||
|
- 官方文档:developers.facebook.com(IG)、developers.google.com/youtube、developers.tiktok.com、developer.x.com、open.kuaishou.com、developer.open-douyin.com、open.xiaohongshu.com、channels.weixin.qq.com
|
||||||
@@ -0,0 +1,334 @@
|
|||||||
|
# EveryPublish 页面线框图全集(ASCII)
|
||||||
|
|
||||||
|
> 2026-08-20 · 与 docs/delivery-plan.md 日计划对应(D5-D7 页面开发按本图实现)。标注行=推荐使用的 TDesign 组件与 starter 模板。
|
||||||
|
|
||||||
|
## 0. 通用布局骨架(除登录页外所有网页端页面共用)
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| Logo EveryPublish 🔔通知(角标) 头像 ▾ |
|
||||||
|
+-------------+----------------------------------------------------+
|
||||||
|
| 侧边菜单 | 面包屑: 首页 / 发布管理 / 任务列表 |
|
||||||
|
| 工作台 | +------------------------------------------------+|
|
||||||
|
| 发布管理 ▾ | | ||
|
||||||
|
| 任务列表 | | 页面内容区(各页不同) ||
|
||||||
|
| 新建发布 | | ||
|
||||||
|
| 排期日历 | | ||
|
||||||
|
| 素材库 | | ||
|
||||||
|
| 账号管理 | | ||
|
||||||
|
| 审核中心 | | ||
|
||||||
|
| 通知与挑战 | | ||
|
||||||
|
| 成员与权限 | | ||
|
||||||
|
| 设置 ▾ | +------------------------------------------------+|
|
||||||
|
| 审计日志 | |
|
||||||
|
+-------------+----------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Layout + Menu(侧边) + Breadcrumb + Dropdown(头像) + Badge(通知角标)
|
||||||
|
说明: admin 角色在头像下拉出现「平台管理」入口,跳 /admin 域(同骨架,菜单替换)。
|
||||||
|
|
||||||
|
## 1. 登录页(isFullPage 全屏,无侧边菜单)
|
||||||
|
|
||||||
|
+-----------------------------------------------------------+
|
||||||
|
| |
|
||||||
|
| [Logo EveryPublish] |
|
||||||
|
| +---------------------------------+ |
|
||||||
|
| | 账号 [___________________] | |
|
||||||
|
| | 密码 [___________________] | |
|
||||||
|
| | [x] 记住我 忘记密码? | |
|
||||||
|
| | [ 登 录 ] | |
|
||||||
|
| +---------------------------------+ |
|
||||||
|
| |
|
||||||
|
+-----------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Form + Input + Checkbox + Button; 模板: pages/Login 改造(去第三方登录)
|
||||||
|
验收: 登录成功进入工作台; 失败提示; refresh 自动续期。
|
||||||
|
|
||||||
|
## 2. 仪表盘(工作台)
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| [今日发布:12] [成功:10] [失败:2] [待处理挑战:1] |
|
||||||
|
| Agent 在线状态: ● 在线(1台) 最后心跳 3s 前 [查看设备] |
|
||||||
|
| 最近任务: |
|
||||||
|
| | 素材 | 标题 | 平台 | 状态 | 时间 | 操作 | |
|
||||||
|
| | 图 | xxx | 抖音 | 已发布 | 10:32 | 查看 | |
|
||||||
|
| | 图 | yyy | 快手 | 失败 | 10:30 | 重试 | |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Card + Statistic(可用 echarts 现成卡片或纯文本) + Table + Tag + Badge
|
||||||
|
模板: pages/Dashboard/Base 改造
|
||||||
|
验收: 数字与后端统计一致; Agent 状态实时。
|
||||||
|
|
||||||
|
## 3. 任务列表(发布管理)
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| 筛选: [状态▾全部] [平台▾全部] [日期范围] [搜索标题____] [查询][重置] |
|
||||||
|
| [ + 新建发布 ] |
|
||||||
|
| | 素材 | 标题 | 平台 | 账号 | 排期时间 | 状态 | 操作 | |
|
||||||
|
| | 图 | xxx | 抖音 | 官号 | 10:30 | 已发布 | 查看/重试 | |
|
||||||
|
| | 图 | yyy | 小红书 | 官号 | 明天09:00| 待审核 | 查看/取消 | |
|
||||||
|
| ... |
|
||||||
|
| < 1 2 3 ... 10 > 共 123 条 |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Table + Select + DateRangePicker + Input + Button + Tag + Pagination + Popconfirm(取消)
|
||||||
|
模板: pages/List/Base
|
||||||
|
验收: 筛选/分页正确; 重试与取消二次确认; 状态实时刷新。
|
||||||
|
|
||||||
|
## 4. 新建发布(分步向导)
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| 步骤条: ① 选择素材 → ② 平台与账号 → ③ 编辑内容 → ④ 定时与提交 |
|
||||||
|
| |
|
||||||
|
| 步骤①: 素材卡片网格(点选) [从素材库选择][上传] |
|
||||||
|
| +------+ +------+ +------+ |
|
||||||
|
| | 缩略图 | | 缩略图 | | 缩略图 | (选中高亮✓) |
|
||||||
|
| | 名称 | | 名称 | | 名称 | |
|
||||||
|
| +------+ +------+ +------+ |
|
||||||
|
| 步骤②: [x]抖音 [x]快手 [x]小红书 [x]B站 每平台: [账号▾官方号] |
|
||||||
|
| 步骤③: 标题[________________] 话题[#____] 描述[多行textarea] |
|
||||||
|
| 右侧: 各平台发布预览卡片 |
|
||||||
|
| 步骤④: (•)立即发布 ( )定时 [日期时间选择器] [提交审核] |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Steps + Grid(Card选择) + Upload + Checkbox + Select + Input + Textarea + Radio + DatePicker
|
||||||
|
模板: pages/Form/Step
|
||||||
|
验收: 必填校验; 提交后进入待审核/直接排期(视配置)。
|
||||||
|
|
||||||
|
## 5. 排期日历
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| [◀ 2026年8月 ▶] [今天] 图例: ●待发 ●已发 ●失败 |
|
||||||
|
| 一 二 三 四 五 六 日 |
|
||||||
|
| 1 2 3 4 5 6 |
|
||||||
|
| 7 8 9 [10●2] 11 12 13 (右侧/下方列出当日任务) |
|
||||||
|
| 14 15 16 17 18 19 20 | 09:00 xxx 抖音 待审核 | |
|
||||||
|
| ... | 14:00 yyy B站 已发布 | |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Calendar(TDesign 1.x, 若无则以卡片网格自绘) + List + Tag
|
||||||
|
模板: 新建页面
|
||||||
|
验收: 月视图任务点正确; 点击日期过滤当日任务。
|
||||||
|
|
||||||
|
## 6. 任务详情(Drawer 侧滑)
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| X 任务详情 #1024 [重试][关闭] |
|
||||||
|
| 基本信息: 标题 / 平台 / 账号 / 创建人 / 排期时间 |
|
||||||
|
| 事件时间线: |
|
||||||
|
| ● 10:29:30 任务下发 Agent |
|
||||||
|
| ● 10:30:01 开始上传素材(进度 100%) |
|
||||||
|
| ● 10:32:10 平台审核通过, 已发布 |
|
||||||
|
| ● 10:32:11 回执截图 [缩略图][查看大图] 链接: https://... |
|
||||||
|
| 失败场景: 失败原因红色标注 + [重试] |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Drawer + Descriptions + Timeline + ImageViewer + Tag + Button
|
||||||
|
模板: pages/Detail/Base 改造
|
||||||
|
验收: 事件流完整; 截图可放大; 失败原因可见。
|
||||||
|
|
||||||
|
## 7. 素材库
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| [上传素材] 分组:[全部▾] 搜索:[____] 视图:[卡片][列表] |
|
||||||
|
| +--------+ +--------+ +--------+ |
|
||||||
|
| | 缩略图 | | 缩略图 | | 缩略图 | |
|
||||||
|
| | 名称.mp4| | 名称.jpg| | ... | |
|
||||||
|
| | 128MB | | 2.1MB | | | |
|
||||||
|
| | [预览][删除] | [预览][删除] | | | |
|
||||||
|
| +--------+ +--------+ +--------+ |
|
||||||
|
| 上传弹窗: [拖拽文件到此处] 进度条: ▓▓▓▓▓▓░░ 80% |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Upload(dragger) + Progress + Grid + ImageViewer + Select + Input + Popconfirm
|
||||||
|
模板: pages/List/Card
|
||||||
|
验收: 大文件上传进度; sha256 入库; 预览/删除正常。
|
||||||
|
|
||||||
|
## 8. 账号台账 + 绑定挑战弹窗
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| [ + 绑定账号 ] |
|
||||||
|
| | 平台 | 账号昵称 | 状态 | 健康度 | 代理 | 最后活跃 | 操作 | |
|
||||||
|
| | 抖音 | xxx官方 | 已绑定 | ●良好 | 无 | 2小时前 | 重扫/解绑 | |
|
||||||
|
| | 快手 | yyy | 登录过期 | ●差 | 无 | 5天前 | 重新扫码 | |
|
||||||
|
| +--------------------------------------------------------------+ |
|
||||||
|
| 绑定弹窗: 选择平台[▾] → 二维码展示区 → 状态: 等待扫码... |
|
||||||
|
| [刷新二维码] (二维码由 token 本地渲染, 非截图) |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Table + Dialog + Select + Button + Tag + Badge + qrcode.react(唯一新增依赖)
|
||||||
|
模板: pages/List/Base + Dialog
|
||||||
|
验收: 发起绑定→Agent 取码→弹窗显示→手机扫码→台账变绿。
|
||||||
|
|
||||||
|
## 9. 审核中心
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| 待审列表: |
|
||||||
|
| | 素材 | 标题 | 平台数 | 提交人 | 提交时间 | 操作 | |
|
||||||
|
| | 图 | xxx | 3 | 运营A | 10:00 | [预览][通过][驳回] | |
|
||||||
|
| 预览 Drawer: 左侧各平台 Tab 预览效果; 右侧批注框 |
|
||||||
|
| [通过] 或 [驳回 + 批注原因______] |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Table + Drawer + Tabs + Textarea + Button
|
||||||
|
模板: pages/List/Base + Drawer
|
||||||
|
验收: 通过→排期; 驳回→回草稿+通知提交人。
|
||||||
|
|
||||||
|
## 10. 挑战中心(通知与挑战)
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| 待处理: [类型] 二维码登录 [平台]抖音 [账号]xxx [时间]2分钟前 [处理] |
|
||||||
|
| 已完成: 短信验证码 小红书 ... 1小时前 ✓已解决 |
|
||||||
|
| QR 弹窗: 二维码展示(自动刷新) 提示: 请用抖音APP扫码 |
|
||||||
|
| 验证码弹窗: 已发送至手机尾号 1234 [输入框______] [提交] (可重输2次) |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: List/Table + Dialog + Input + Button + Tag + qrcode.react
|
||||||
|
验收: 五类挑战均可人工完成; 超时挂起可一键重发。
|
||||||
|
|
||||||
|
## 11. 通知
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| [全部已读] |
|
||||||
|
| ● 10:32 任务#1024 抖音 发布失败: 风控拦截 [查看] 已读 |
|
||||||
|
| ○ 10:00 抖音官方号 登录即将过期, 请重新扫码 [去扫码] 未读 |
|
||||||
|
| ○ 09:30 成员 运营B 提交了待审核任务 [去审核] 未读 |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: List + Badge + Button
|
||||||
|
验收: 事件产生即推送; 已读状态持久。
|
||||||
|
|
||||||
|
## 12. 成员与权限
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| [ + 邀请成员 ] |
|
||||||
|
| | 姓名 | 手机 | 角色 | 状态 | 加入时间 | 操作 | |
|
||||||
|
| | 张A | 138.. | 管理员 | 正常 | 08-01 | 改角色/停用 | |
|
||||||
|
| | 李B | 139.. | 运营 | 正常 | 08-10 | 改角色/停用 | |
|
||||||
|
| 邀请弹窗: 角色[▾运营] → 生成邀请链接 [复制] |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Table + Dialog + Select + Button + Message(复制成功)
|
||||||
|
模板: pages/User
|
||||||
|
验收: 角色权限隔离生效; 停用成员无法登录。
|
||||||
|
|
||||||
|
## 13. 设置(四个子页)
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| Tab: [工作区资料] [安全] [通知渠道] [Agent设备] |
|
||||||
|
| 资料: 名称[____] Logo[上传] 简介[____] [保存] |
|
||||||
|
| 安全: 2FA 开关 [○关(默认)] 登录设备: |Win Chrome| 杭州 | [吊销] | |
|
||||||
|
| 通知渠道: 企微[○开 配置] 钉钉[○关] 邮件[○关] |
|
||||||
|
| Agent设备: | 设备名 | 状态●在线 | 版本 | 最后心跳 | [吊销][指令] | |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Tabs + Form + Switch + Upload + Table + Popconfirm
|
||||||
|
验收: 2FA 默认关; 吊销设备后客户端失联。
|
||||||
|
|
||||||
|
## 14. 审计日志
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| 筛选: [操作人____] [动作▾] [时间范围] [查询] |
|
||||||
|
| | 时间 | 操作人 | 动作 | 对象 | 详情 | |
|
||||||
|
| | 10:30| 张A | 通过 | 任务#1024 | 审核通过 | |
|
||||||
|
| | 10:00| 李B | 提交 | 任务#1024 | 提交待审核 | |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Table + Select + DateRangePicker + Input
|
||||||
|
验收: 全操作可查。
|
||||||
|
|
||||||
|
## 15. admin · 租户管理
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| | 租户名称 | 套餐 | 成员数 | Agent数 | 状态 | 创建时间 | 操作 | |
|
||||||
|
| | 某某科技 | 试用 | 5 | 1 | 正常 | 08-01 | [停用]| |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Table + Popconfirm
|
||||||
|
验收: 停用后租户不可登录。
|
||||||
|
|
||||||
|
## 16. admin · Agent 总览
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| | 租户 | 设备名 | 版本 | 在线 | 最后心跳 | 操作 | |
|
||||||
|
| | 某某 | PC-01 | 0.1 | ●在线 | 3s前 | [吊销][远程指令] | |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: Table + Badge + Popconfirm
|
||||||
|
验收: 在线状态实时; 吊销即时生效。
|
||||||
|
|
||||||
|
## 17. 客户端 · 配对引导(首次启动)
|
||||||
|
|
||||||
|
+----------------------------------------------------------+
|
||||||
|
| EveryPublish 发布助手 [—][□][X] |
|
||||||
|
| |
|
||||||
|
| 第一步: 在网页端 设置→Agent设备 获取配对码 |
|
||||||
|
| 配对码: [______] |
|
||||||
|
| [ 绑 定 ] |
|
||||||
|
| 状态: 绑定成功 ✓ / 失败原因(红字) |
|
||||||
|
+----------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: WinUI 原生 (StackPanel/TextBox/Button/InfoBar)
|
||||||
|
验收: 5 分钟完成配对; 失败有明确原因。
|
||||||
|
|
||||||
|
## 18. 客户端 · 托盘菜单
|
||||||
|
|
||||||
|
+--------------------+
|
||||||
|
| ● 已连接(延迟 12ms)|
|
||||||
|
| 待发任务: 3 |
|
||||||
|
| 打开面板 |
|
||||||
|
| 重新扫码 |
|
||||||
|
| 查看日志 |
|
||||||
|
| 退出 |
|
||||||
|
+--------------------+
|
||||||
|
|
||||||
|
组件: H.NotifyIcon 托盘菜单(Win32 interop)
|
||||||
|
验收: 最小化到托盘; 状态随连接实时变化。
|
||||||
|
|
||||||
|
## 19. 客户端 · 主面板
|
||||||
|
|
||||||
|
+----------------------------------------------------------+
|
||||||
|
| 服务器: ● 已连接 延迟 12ms 版本 0.1.0 |
|
||||||
|
| 账号: |
|
||||||
|
| | 平台 | 状态 | 健康度 | 操作 | |
|
||||||
|
| | 抖音 | 已绑定 | ●良好 | [重新扫码] | |
|
||||||
|
| | 快手 | 过期 | ●差 | [扫码修复] | |
|
||||||
|
| 待发任务: 3 今日已发: 5 |
|
||||||
|
| [打开日志] [设置] |
|
||||||
|
+----------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: WinUI 原生 (ListView/GridView/ProgressRing)
|
||||||
|
验收: 状态与服务器一致; 扫码入口直达挑战窗口。
|
||||||
|
|
||||||
|
## 20. 客户端 · 扫码挑战窗口
|
||||||
|
|
||||||
|
+----------------------------------------------------------+
|
||||||
|
| 请使用 抖音APP 扫描二维码登录 |
|
||||||
|
| ▓▓▓▓▓▓▓▓ (二维码, token 本地渲染) ▓▓▓▓▓▓▓▓ |
|
||||||
|
| 状态: 等待扫码... → 已扫码,请在手机确认 → 登录成功 |
|
||||||
|
| [刷新二维码] [取消] |
|
||||||
|
+----------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: WinUI + QRCoder(C#)
|
||||||
|
验收: 码过期自动刷新; 状态实时。
|
||||||
|
|
||||||
|
## 21. 客户端 · 验证码输入窗口
|
||||||
|
|
||||||
|
+----------------------------------------------------------+
|
||||||
|
| 小红书 需要短信验证码 |
|
||||||
|
| 已发送至 138****1234 |
|
||||||
|
| [______] [提交] (剩余重试 2 次) |
|
||||||
|
+----------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: WinUI TextBox/Button
|
||||||
|
验收: 回传服务器→Agent 回填; 错误可重输。
|
||||||
|
|
||||||
|
## 22. 客户端 · 设置(代理与日志)
|
||||||
|
|
||||||
|
+----------------------------------------------------------+
|
||||||
|
| Tab: [代理] [日志] [关于] |
|
||||||
|
| 代理: 每账号代理配置: 抖音官号 → [socks5://____] [测试] |
|
||||||
|
| 日志: 文本区(滚动) + [导出] [清空] |
|
||||||
|
| 关于: 版本 0.1.0 [检查更新] |
|
||||||
|
+----------------------------------------------------------+
|
||||||
|
|
||||||
|
组件: WinUI Pivot + TextBox + Button
|
||||||
|
验收: 代理测试连通性; 日志可导出。
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
# EveryPublish PRD 产品需求文档
|
||||||
|
|
||||||
|
> 版本 v1.0 · 2026-08-20 · 状态:待评审 · 技术栈以 docs/tech-stack.md 为准
|
||||||
|
|
||||||
|
## 1. 文档信息与文档体系
|
||||||
|
|
||||||
|
### 1.1 版本记录
|
||||||
|
|
||||||
|
| 版本 | 日期 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| v1.0 | 2026-08-20 | 初稿,覆盖一期 MVP 全部功能需求 |
|
||||||
|
|
||||||
|
### 1.2 文档体系位置(PRD 与其他文档的关系)
|
||||||
|
|
||||||
|
| 层级 | 文档 | 回答的问题 | 本项目对应 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 商业/市场 | MRD / BRD | 为什么做、市场与商业 | 并入本文档 §2,暂不单列 |
|
||||||
|
| **产品需求** | **PRD(本文档)** | **做什么、给谁用、什么体验** | docs/prd.md |
|
||||||
|
| 技术需求/规格 | TRD / SRS | 怎么做、接口与协议 | docs/system-design.md + docs/tech-stack.md |
|
||||||
|
| 流程与进度 | 逻辑图 / 排期 | 流程怎么走、何时交付 | docs/logic-diagrams.md + docs/delivery-plan.md |
|
||||||
|
|
||||||
|
> 注:SPDC / MDC 非行业标准缩写。若指「技术方案类」(SPDC≈方案/规格)与「模块开发上下文」(MDC≈模块设计),在本文档体系中分别由 system-design/tech-stack 与 logic-diagrams 承接;待确认团队定义后对齐(见 §12 开放问题)。
|
||||||
|
|
||||||
|
## 2. 产品概述
|
||||||
|
|
||||||
|
### 2.1 背景与问题(痛点)
|
||||||
|
|
||||||
|
- 企业通常只有一个官方账号、多名运营协作;现有开源发布工具均为「单人本地扫码发布」,多人共用账号混乱、无审核、无审计。
|
||||||
|
- 国内平台(抖音/快手/小红书/视频号)无公开内容发布 API,只能浏览器自动化;IP 风控要求国内账号用国内 IP、国外账号用国外 IP。
|
||||||
|
- 客户诉求:越简单越好、发布越快、账号越稳、覆盖全部平台、不违反平台官方规则。
|
||||||
|
|
||||||
|
### 2.2 产品定位(一句话)
|
||||||
|
|
||||||
|
**企业多平台官方账号的发布中台:网页控制台协作、审核、排期,客户端单点持号、自动发布。**
|
||||||
|
|
||||||
|
### 2.3 目标用户
|
||||||
|
|
||||||
|
| 用户 | 场景 | 优先级 |
|
||||||
|
|---|---|---|
|
||||||
|
| 企业新媒体运营团队 | 单官方账号 + 多运营协作,需审核与审计 | 主 |
|
||||||
|
| 代运营 / MCN | 多客户多账号,需隔离与批量 | 次 |
|
||||||
|
| 单人创作者 | 自用简化版(隐藏审核流) | 次 |
|
||||||
|
|
||||||
|
### 2.4 核心价值主张
|
||||||
|
|
||||||
|
- **简单**:装客户端 → 扫码绑定 → 网页排期,三步上手,零配置。
|
||||||
|
- **快**:任务下发 <300ms,多平台并行发布,失败一键重试。
|
||||||
|
- **稳**:凭据单机加密持有,设备+IP+行为三固定,账号风控最小化。
|
||||||
|
- **覆盖**:一期国内四平台(抖音/快手/小红书/B站),二期视频号+国际平台官方 API 通道。
|
||||||
|
- **合规**:官方 API 优先;自动化通道分级开通、知情告知;明确不做矩阵养号等灰产。
|
||||||
|
|
||||||
|
### 2.5 职责边界(网站 vs 客户端,两类账号)
|
||||||
|
|
||||||
|
**一句话**:网站 = 任务分配中枢 + 状态看板;客户端 = 持号 + 登录鉴权 + 发布执行。发布流量永不经过网站服务器。
|
||||||
|
|
||||||
|
| 账号类型 | 谁负责登录鉴权 | 网站/服务器的角色 |
|
||||||
|
|---|---|---|
|
||||||
|
| 控制台用户账号(运营/管理员登录后台,如 demo@everypublish.io) | 网站(注册/登录/JWT) | 管理面板本体,分配任务的人 |
|
||||||
|
| 平台账号(抖音/快手/小红书/B站/视频号/海外,即发布目标账号) | 客户端 Agent(扫码/验证/本机保存凭据) | 只下发任务 + 接收状态,**零凭据** |
|
||||||
|
|
||||||
|
- 网站对平台账号**不负责登录鉴权**,只做两件事:**下发/分配发布任务**(<1KB 元数据经 WSS 推给客户端)与**接收执行状态回传**。
|
||||||
|
- 平台账号的登录、扫码验证、凭据保存、实际发布全部在客户端(客户自己的电脑)完成;服务器账号表只存 `平台 / 备注 / 状态 / 绑定设备` 等元数据,**不含任何 cookie / 密码 / token**。
|
||||||
|
- 绑定流程示例:网站生成「挑战」记录并推给客户端 → 客户端拉起扫码 → 客户端回传「已解决」→ 网站把账号标记为「已绑定」。网站全程不接触、不存储平台凭据。
|
||||||
|
|
||||||
|
## 3. 范围
|
||||||
|
|
||||||
|
### 3.1 一期(MVP)
|
||||||
|
|
||||||
|
- 网页端全部功能:用户端(客户工作台)+ 平台端(admin 角色)。
|
||||||
|
- 客户端 Windows 版:安装配对、托盘常驻、扫码挑战、自动更新。
|
||||||
|
- 平台:抖音 / 快手 / 小红书 / B站(视频号二期)。
|
||||||
|
- 素材:服务器本地盘 + 签名直链下载。
|
||||||
|
- 2FA:能力预留,默认关闭。
|
||||||
|
|
||||||
|
### 3.2 二期(不在本期验收)
|
||||||
|
|
||||||
|
- 视频号、官方 API 通道(X/IG/YouTube/TikTok)、OSS 双桶与断点续传、托管 Agent、代理采购入口、数据报表增强、Windows 服务化(支持客户 VPS 形态)。
|
||||||
|
|
||||||
|
### 3.3 明确不做(红线)
|
||||||
|
|
||||||
|
- 矩阵养号、批量注册、搬运去重绕过、刷量、私信轰炸;违反平台规则的功能一律不做。
|
||||||
|
|
||||||
|
## 4. 用户角色与权限
|
||||||
|
|
||||||
|
| 角色 | 端 | 核心权限 |
|
||||||
|
|---|---|---|
|
||||||
|
| 管理员 | 网页端 | 成员/角色、账号台账、绑定/解绑、Agent 设备、审计、设置 |
|
||||||
|
| 审核员 | 网页端 | 审核通过/驳回、查看素材与任务 |
|
||||||
|
| 运营 | 网页端 | 素材上传、创建/排期/重试任务、查看结果 |
|
||||||
|
| 账号负责人(装机人) | 客户端 | 安装配对、扫码绑定、处理登录过期 |
|
||||||
|
| 平台 admin | 网页端 | 租户管理、Agent 总览、渠道状态(后置) |
|
||||||
|
|
||||||
|
## 5. 功能需求
|
||||||
|
|
||||||
|
> 优先级:P0 = 一期必须;P1 = 一期尽量;P2 = 二期。
|
||||||
|
|
||||||
|
### 5.1 网页端 · 用户端(客户工作台)
|
||||||
|
|
||||||
|
| 模块 | 功能 | 优先级 | 验收要点 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 认证与账户 | 账号密码登录、refresh 轮换、登出、登录设备管理;2FA 默认关 | P0 | 登录成功/登出后会话失效;refresh 自动续期 |
|
||||||
|
| 工作区与成员 | 成员邀请、角色分配、停用 | P0 | 角色权限隔离正确 |
|
||||||
|
| 账号台账 | 平台账号列表(绑定状态/健康度/代理/最后活跃)、发起绑定(生成扫码挑战)、重新扫码、解绑 | P0 | 绑定→扫码→台账变绿全流程 |
|
||||||
|
| 素材库 | 上传(multipart+进度)、列表/分组/标签、预览、删除 | P0 | 大文件上传成功;sha256 校验入库 |
|
||||||
|
| 发布任务 | 新建(选素材→平台账号→标题话题→定时/立即→提交审核)、任务列表(状态筛选)、排期日历、任务详情(事件时间线+回执截图+失败原因)、重试、取消 | P0 | 任务全生命周期流转正确 |
|
||||||
|
| 审核流 | 待审队列、分平台预览、通过/驳回+批注、可配置跳过 | P0 | 驳回回退草稿;跳过时直接排期 |
|
||||||
|
| 挑战中心 | QR 从 token 本地渲染、验证码输入、APP确认指引、超时挂起、一键重发 | P0 | 五类挑战均可人工完成 |
|
||||||
|
| 通知 | 站内通知(挑战/失败/登录过期)、已读 | P0 | 事件产生即推送 |
|
||||||
|
| 审计 | 全操作日志查询 | P0 | 谁在何时做了什么可查 |
|
||||||
|
| 设置 | 工作区资料、2FA 开关(默认关)、通知渠道(企微/钉钉/邮件)、Agent 设备列表与吊销 | P0/P1 | 设备吊销后客户端失联 |
|
||||||
|
|
||||||
|
### 5.2 网页端 · 平台端(admin)
|
||||||
|
|
||||||
|
| 模块 | 功能 | 优先级 | 验收要点 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 租户管理 | 客户列表、状态、配额(简单)、停用 | P0 | 停用后租户不可登录 |
|
||||||
|
| Agent 总览 | 设备列表:在线/版本/强制吊销/远程指令 | P0 | 在线状态实时 |
|
||||||
|
| 渠道状态 | 平台适配器状态、灰度开关 | P2 | — |
|
||||||
|
| 风控台账 | 风控事件、账号冷却记录 | P2 | — |
|
||||||
|
|
||||||
|
### 5.3 客户端(Windows)
|
||||||
|
|
||||||
|
| 模块 | 功能 | 优先级 | 验收要点 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 安装与配对 | 安装包、配对码绑定工作区、首次引导 | P0 | 新机 5 分钟内完成配对 |
|
||||||
|
| 托盘常驻 | 开机自启、最小化托盘、在线状态显示 | P0 | 重启后自动恢复连接 |
|
||||||
|
| 扫码与挑战 | QR 展示/自动刷新、验证码回填、APP确认轮询 | P0 | 与网页端挑战中心联动 |
|
||||||
|
| 任务执行 | 接收/幂等去重/执行/回传;浏览器档案与指纹 | P0 | 重复任务不重复执行 |
|
||||||
|
| 运维 | 本地日志、自动更新、连接诊断 | P0/P1 | 断线指数退避自愈 |
|
||||||
|
|
||||||
|
## 6. 关键流程(引用 docs/logic-diagrams.md)
|
||||||
|
|
||||||
|
- 账号绑定(图8 登录链路优化):API 取码 → 只传 token → 本地重渲染 → 短轮询检测。
|
||||||
|
- 发布全链路(图6):提交 → 审核 → 排期 → 下发 → 执行 → 回传 → 实时状态;异常分流(网络重试/挑战/风控冷却)。
|
||||||
|
- 二次验证兜底(图7):五类挑战人工兜底,超时挂起、一键重发。
|
||||||
|
- 重试状态机(图5):网络退避 1m/5m/15m/1h;登录态转挑战;风控转人工。
|
||||||
|
|
||||||
|
## 7. 非功能需求
|
||||||
|
|
||||||
|
| 类别 | 指标 |
|
||||||
|
|---|---|
|
||||||
|
| 性能 | 任务下发 <300ms;网页页面 P95 <2s;WSS 心跳 10s/离线判定 25s |
|
||||||
|
| 可靠性 | 断线重连 <10s 恢复;任务幂等(同任务不重复);挑战超时策略完整 |
|
||||||
|
| 安全 | 四层鉴权;凭据不出本机(服务器零凭据);日志脱敏;审计完整 |
|
||||||
|
| 易用性 | 三步上手;登录过期提醒+一键重扫;失败原因可见+一键重试 |
|
||||||
|
| 合规 | 自动化通道知情告知并签署;内容发布前自检提示;红线功能拒绝 |
|
||||||
|
| 可维护 | 协议版本化(zod/Go 侧校验);平台适配器独立可灰度 |
|
||||||
|
|
||||||
|
## 8. 数据需求(一期最小)
|
||||||
|
|
||||||
|
- 任务事件流(状态/时间/原因/截图);账号健康度(登录态预测过期);发布统计(成功数/失败原因分布);审计与通知记录。
|
||||||
|
|
||||||
|
## 9. 约束与依赖
|
||||||
|
|
||||||
|
- 平台风控与前端改版(最大持续风险);IP/代理采购(客户侧);Windows 10/11 环境;扫码需客户手机(微信/抖音等 APP)。
|
||||||
|
- 一期素材存服务器本地盘:需磁盘配额与清理策略。
|
||||||
|
|
||||||
|
## 10. 验收标准
|
||||||
|
|
||||||
|
见 docs/delivery-plan.md §7 最终验收清单(登录与权限 / 任务全生命周期 / <300ms 与幂等 / 挑战全流程 / 素材校验 / 四平台真实发布 / Windows 新机全流程 / 凭据不出本机 / 安装包与自动更新)。
|
||||||
|
|
||||||
|
## 11. 里程碑
|
||||||
|
|
||||||
|
见 docs/delivery-plan.md §6 日计划(D1-D13 + 缓冲):D1-D7 网站端,D8-D11 客户端,D12-D13 联调交付。
|
||||||
|
|
||||||
|
## 12. 开放问题(待评审确认)
|
||||||
|
|
||||||
|
1. SPDC / MDC 的确切定义——请提供全称,以对齐文档体系。
|
||||||
|
2. 定价与计费模式(订阅/按账号数/按平台数)。
|
||||||
|
3. 通知渠道一期范围(企微/钉钉/邮件是否全做)。
|
||||||
|
4. 代理采购入口是否一期内置(默认客户自带)。
|
||||||
|
5. 「手机版本」此前表述——指首版还是未来手机端 App,需确认。
|
||||||
|
6. 单人版是否一期提供(隐藏审核流)。
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# EveryPublish 开发进度跟踪
|
||||||
|
|
||||||
|
> 状态:进行中 · 阶段一(网页端 D1-D7)。目标:goal-8c8e81e4-d2ed-4ba9-a4f7-d413c4f9e00b。
|
||||||
|
> 更新规则:每天结束更新本表;检查点①/②需用户确认后才进入下一阶段。
|
||||||
|
|
||||||
|
## 总览
|
||||||
|
|
||||||
|
| 阶段 | 状态 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| 阶段一 网页端 D1-D7 | 🟡 进行中 | D1-D7 全部完成,进入检查点①网页端手动测试 |
|
||||||
|
| 检查点① 网页端手动测试 | ✅ 用户已验收确认 | 用户确认进入阶段二 |
|
||||||
|
| 阶段二 客户端 D8-D11 | 🟡 进行中 | D8-D11 代码全部交付,进入检查点②交付用户测试 |
|
||||||
|
| 检查点② 交付用户测试 | ✅ 已交付 | 最终产物切换为 Electron 壳(client/desktop,正规安装包+正常 UI 四页签)替代 WinUI3 源码交付;NSIS 安装包 `EveryPublish Setup 0.2.0.exe` 构建成功(图标 app.ico 修复后 EB_EXIT=0,内嵌 agent-core.exe);服务器部署包 dist/deploy(linux-amd64/windows-amd64+web+docker-compose+.env 样例)齐备;内网连接/填写说明(docs/client-install-guide.md + dist/deploy/deploy-README.md + 测试账号方案)交付;模拟 E2E 第 N 轮实测:登录→设备在线(winui-fakeall/core-d8)→账号 active→素材 ready→任务→submit→approve→假执行→status success;等用户在内网 192.x 实机验收 |
|
||||||
|
| 阶段三 联调交付 D12-D13 | ⚪ 未开始 | |
|
||||||
|
|
||||||
|
## 逐日验收
|
||||||
|
|
||||||
|
| 日 | 任务 | 验收标准 | 状态 | 证据 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| D1 | 工程化与骨架 | dev 无报错、菜单空壳可点、/health 200、DB 连上 | ✅ 通过 | 后端:/health 200(db:true,redis:true)、12 表 AutoMigrate、go build/vet/run 通过(:8090);前端:示例页+mock 清理、17 个页面空壳+2 组菜单路由、vite dev :3003 启动 200、代理到后端 404(gin) 正常、tsc --noEmit 0 错误 |
|
||||||
|
| D2 | 认证与工作区 API | 单测过;前端登录页真实登录 | ✅ 通过 | 后端:Argon2id 口令、JWT access 15min/refresh 7d 轮换(Redis jti 吊销)、register/login/refresh/logout/me、workspaces CRUD、members 邀请(JWT)/加入/改角色/移除、审计中间件落库;go test 单测 4 项+集成测试 2 场景全过(真 MySQL everypublish_test + 真 Redis);前端:登录/注册页接真实 API、401 自动刷新重试(单飞)、路由守卫、启动拉取用户信息、Header 显示昵称+登出;tsc 0 错误;经 vite 代理 curl 验证登录 200/错误密码 401 |
|
||||||
|
| D3 | 业务 API | 单测+接口文档;curl 全接口可跑 | ✅ 通过 | 账号台账 CRUD+bind→challenge(平台白名单/绑定中禁删);素材 multipart 流式 sha256+同工作区去重+签名直链(10min 一次性 200→404 已验证);任务状态机(纯函数 4 项单测:happy path/失败重试/挂起恢复/非法转移) + CRUD/submit/approve/reject/resubmit/retry/cancel+驳回/通过自动通知;挑战 solve/resend/suspend+过期软置;通知列表/已读/全部已读;go test 全过;curl E2E 账号/素材/直链/任务/通知全链路 200;接口文档 docs/api.md(含状态机图) |
|
||||||
|
| D4 | WSS 网关+假执行器 | 假执行器闭环 <300ms | ✅ 通过 | 双通道 WSS:/ws/agent(Ed25519 hello 验签,坏签名拒绝已验证)+ /ws/browser(JWT 订阅);心跳/hello.ack 签名;task.push→ack→result 全链路;challenge.new→solve→账号激活;设备配对码(5min 一次性);下发器(审批即时触发+1s 轮询兜底,先落库后推送防竞态);假执行器 server/cmd/fake-agent;集成测试全链路 PASS,下发延迟实测 28.7ms(<300ms);前端 utils/ws.ts 自动重连 + 工作台真实页面(设备在线状态+最近任务实时变绿),经浏览器通道收到 3 条 task.status 事件(末条 success) |
|
||||||
|
| D5 | 页面① | 页面与真实 API 联通 | ✅ 通过 | 登录/仪表盘(D2/D4 已真实化)+ 成员页(列表/邀请弹窗生成链接+复制/行内改角色/移除,owner 保护)+ 设置 4 页(工作区资料可改名保存、我的账号、2FA 默认关留位、通知渠道二期留位、设备列表+吊销)+ 邀请接受页 /invite(登录态判断/邮箱校验/成功引导);后端补 PUT /agent/devices/:id/revoke(吊销+强制下线)与 PUBLIC_BASE_URL(浏览器链接与 Agent 直链分离);tsc 0 错误;6 页面全 200;邀请链接实测指向 3003 前端;吊销实测生效 |
|
||||||
|
| D6 | 页面② | 全流程可点通 | ✅ 通过 | 素材库(Upload 组件接真实上传、类型/分组筛选、sha256/大小/签名直链弹窗+复制、删除);账号台账(平台标签/状态/健康度/IP 画像、添加弹窗、绑定弹窗监听 challenge.status WS 自动关闭);任务列表(状态筛选、按状态渲染操作:提交/通过/驳回(批注弹窗)/重提/重试/取消/删除、详情 Drawer、WS 实时刷新);新建发布四步向导(选素材→选账号(仅 active)→填内容(TagInput/定时 DatePicker)→确认提交);排期日历(Calendar 组件按日展示任务 Tag);tsc 0 错误;5 页面全 200;全流程模拟:素材经前端代理上传→新账号→bind→假执行器扫码→active→向导 payload 建任务→提交→审核→自动下发→status:success |
|
||||||
|
| D7 | 页面③+admin | 角色权限正确、admin 入口可用 | ✅ 通过 | 审核中心(待审列表+预览抽屉+通过/驳回批注);挑战中心(QR 本地渲染 TDesign QRCode+验证码输入+挂起/一键重发,WS 实时刷新);通知(未读角标+已读/全部已读);审计(动作筛选+分页);admin 两页(租户列表/停用启用、Agent 总览/强制吊销);菜单按角色过滤(meta.admin 仅 admin 可见);后端 /admin 组 AdminRequired 守卫;实测:普通用户 admin API=403、admin 账号=200;tsc 0 错误;6 页面全 200 |
|
||||||
|
| D8 | agent-core | core 单测过;与服务器假执行闭环 | ✅ 通过 | client/core(Go):设备身份(Ed25519 密钥 0600 持久化)、凭据保险库(AES-256-GCM,主密钥独立文件,防篡改单测)、执行器框架(Registry+兜底+ChallengeSolver)、假执行器、WSS 客户端(1s/5s/15s/60s 指数退避重连+握手重置、15s 心跳、写锁防并发写、任务幂等:已执行任务重放缓存结果不重复执行)、配对(AutoPair/PairWithCode)、配置持久化;单测 4 包全过(含 stub 服务器握手验签/任务往返/幂等重放/断线重连);Windows 交叉编译 agent-core.exe 9.0M 成功;真服务器闭环:core 设备指定绑定→挑战自动解决→账号 active→任务下发→执行→success(core 日志全程确认) |
|
||||||
|
| D9 | WinUI3 壳 | Windows 实机:安装→配对→假任务→回传 | ✅ 代码交付(实机验收并入检查点②) | Go 侧:core 新增 localhost 控制服务(随机端口+token 写 local.json,/status 含 server/deviceId/name、/challenges、/challenges/:id 提交、/pair 配对回调,401 鉴权)+ 挑战队列/人工解决/在线状态接口,单测 5 包全绿,curl 实测 401/状态/配对错误传播全通过;WinUI 壳:csproj(net8.0-windows10.0.19041.0+WindowsAppSDK 1.5 自包含+H.NotifyIcon.WinUI)+MainWindow(顶部导航4页/关闭最小化托盘/托盘右键打开退出)+状态页(3s轮询)/配对页/挑战页(2s轮询+验证码提交)/设置页(服务器地址+开机自启+重启核心)+Watchdog(5s心跳/崩溃自拉起/3次失败弹窗)+AutoStart(HKCU Run)+打包脚本 build-installer.ps1(生成图标→Go 交叉编译→dotnet publish→组装→zip);Mac 无法编译 C#/WinUI,Windows 构建步骤与验证清单见 client/ui/README.md |
|
||||||
|
| D10 | B站真实发布 | B站真实发布成功 | ✅ 代码交付(真实账号发布验证并入检查点②) | client/core/internal/exec/bilibili:无浏览器投稿全链路(preupload→upos/bda2 分片上传→分片合并→x/vu/web/add 投稿,协议依据 biliup MIT 源码)+ 扫码登录(passport web qrcode generate→poll 轮询 86101/86090/0→跨域 URL 提取 SESSDATA/bili_jct/DedeUserID→加密保存保险库);ChallengeSolver 接口扩展 onQR 回调(二维码推壳展示);执行器接入 main(B站走真实链路,其它平台假兜底);单测 3 项全过(stub 全链路:分片参数/合并 parts 数/csrf/投稿体校验;QR 登录二次轮询成功+凭据落库;未扫超时);go vet 干净;agent-core.exe 交叉编译成功 |
|
||||||
|
| D11 | 抖音/快手/小红书 | 三平台真实发布+截图回执 | ✅ 代码交付(真实账号发布验证并入检查点②) | client/core/internal/exec/{cdp,cdnbase,douyin,kuaishou,xiaohongshu}:CDP 基础设施(rod-launcher 启动、每账号独立档案 UserDataDir、proxy-server 代理绑定、go-rod/stealth 指纹反自动化、系统 Chrome/Edge 自动探测、cookie 注入/导出、扫码登录通用流程:登录页→QR 截图落盘 file://→onQR 推壳→轮询 URL 离开登录页→cookie 加密入库);通用发布流程(上传页→注 cookie→传视频→等表单 180s→填标题/描述话题→点发布→等成功跳转→截图回执 PNG);三平台选择器按 social-auto-upload 2026-06 版配置(抖音 creator-micro/content/manage 成功特征、快手 article/manage/video、小红书 publish/success);main 注册三平台真实链路;单测 4 项(JSON 转义往返/素材下载/HTTP 错误/Chrome 路径探测);WinUI 挑战页补 Image 二维码显示(http/file:// 均可);go vet 干净;agent-core.exe 交叉编译 14M 成功 |
|
||||||
|
| D12 | 联调+异常注入 | 验收清单全过 | ⚪ | |
|
||||||
|
| D13 | 打包+内部试用 | 新机安装走通、交付物齐 | 🟡 打包完成待用户试用 | Electron 壳(四页签 UI/托盘/开机自启/fakeAll)+NSIS 安装包构建成功(见检查点②);部署包+安装指南+测试账号说明齐;用户内网实机安装与联调由 D12-D13 继续 |
|
||||||
|
|
||||||
|
## 服务端口约定
|
||||||
|
|
||||||
|
- 本机开发:Go 后端 :8090(8080 被 nginx 占用);MySQL 3306(Docker everypublish-mysql);Redis 6379(本机)。
|
||||||
|
- 前端 dev:Vite :3002(starter 默认),/api 走 vite proxy 到 :8090。
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
# EveryPublish 系统设计(网页端 + 客户端 App)
|
||||||
|
|
||||||
|
> 版本 v1 · 2026-08-20 · 与 docs/multi-platform-publish-plan.md 配套(架构原则见其 §3/§3.1/§17-22)
|
||||||
|
|
||||||
|
## 1. 总体形态
|
||||||
|
|
||||||
|
- **网页端(我方统一部署)**:客户工作台 + 内部后台,同一应用、角色与租户隔离;客户与我们内部共用一套代码。
|
||||||
|
- **客户端 App(客户部署)**:Agent 常驻应用(系统托盘/后台服务),承载数据面。
|
||||||
|
- **连接**:WSS 双向长连接为主通道 + HTTPS 长轮询降级(评估见 §4)。
|
||||||
|
|
||||||
|
> **术语澄清**:本文「Agent」= 客户端发布执行器(本地常驻程序,系统托盘/后台服务形态),与 AI 大模型 Agent 无关——不跑模型、不调 AI 接口、不需要任何模型配置。用户侧零配置:下载安装包 → 扫码绑定 → 完成。运行时与浏览器驱动全部内置进安装包。对外可称「发布助手」。
|
||||||
|
|
||||||
|
## 2. 网页端功能清单
|
||||||
|
|
||||||
|
### 2.1 客户工作台(按角色)
|
||||||
|
|
||||||
|
| 模块 | 功能点 | 可见角色 |
|
||||||
|
|---|---|---|
|
||||||
|
| 认证与账户 | 登录(密码+TOTP 2FA)、找回密码、登录设备管理 | 全部 |
|
||||||
|
| 工作区与成员 | 成员邀请、角色(管理员/审核/运营)、权限 | 管理员 |
|
||||||
|
| 平台账号台账 | 绑定/解绑、登录态健康度(过期预测)、代理绑定查看、发起扫码 | 管理员 |
|
||||||
|
| 素材库 | 上传(OSS 预签名直传)、分组/标签、检索、预览、元信息 | 运营/审核 |
|
||||||
|
| 发布任务 | 创建(选素材/平台/账号/标题话题/定时)、排期日历、列表筛选、实时状态、失败重试、取消 | 运营 |
|
||||||
|
| 审核流 | 待审队列、通过/驳回+批注(可配置跳过) | 审核/管理员 |
|
||||||
|
| 通知与挑战 | 挑战处理中心(扫码/验证码/APP确认)、失败告警、登录过期提醒 | 管理员/运营 |
|
||||||
|
| 审计 | 全操作日志查询 | 管理员 |
|
||||||
|
| 数据报表(可选) | 发布量/成功率/失败原因分布 | 管理员 |
|
||||||
|
|
||||||
|
### 2.2 内部后台(平台角色,与工作台同部署)
|
||||||
|
|
||||||
|
| 模块 | 功能点 |
|
||||||
|
|---|---|
|
||||||
|
| 租户管理 | 客户列表、套餐/计费、配额、状态 |
|
||||||
|
| Agent 管理 | 设备在线监控、版本推送、远程指令(重启/升级)、强制下线/吊销 |
|
||||||
|
| 渠道适配器 | 平台模块状态、改版巡检、灰度开关 |
|
||||||
|
| 风控台账 | 风控事件、账号冷却记录 |
|
||||||
|
| 系统配置 | 通知模板、代理池(托管形态)、审计查询 |
|
||||||
|
|
||||||
|
## 3. 客户端 App 功能清单
|
||||||
|
|
||||||
|
| 模块 | 功能点 |
|
||||||
|
|---|---|
|
||||||
|
| 安装与配对 | 安装包、配对码绑定工作区、首次引导 |
|
||||||
|
| 连接管理 | WSS 连接状态、心跳、自动重连、延迟诊断 |
|
||||||
|
| 凭据保险库 | 本地加密存储(SQLCipher)、账号列表与健康度 |
|
||||||
|
| 扫码与挑战 | 二维码展示/自动刷新、验证码回填、APP确认轮询 |
|
||||||
|
| 代理管理 | 代理配置、健康检查、按账号绑定 |
|
||||||
|
| 任务执行 | 接收/幂等去重/执行/回传;浏览器档案管理;指纹配置 |
|
||||||
|
| 运维 | 本地日志、自动更新、远程指令(重启/升级/日志上传) |
|
||||||
|
|
||||||
|
## 4. 网页端与 App 的连接方案评估
|
||||||
|
|
||||||
|
| 方案 | 延迟 | 双向 | 穿透 | 安全 | 结论 |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| REST 轮询 | 秒级 | 否 | 是 | 中 | 仅作降级通道 |
|
||||||
|
| 服务器回调 Agent(Agent 开端口) | 低 | 是 | 否(需端口映射/UPnP) | 差(暴露面) | 排除 |
|
||||||
|
| SSE 单向推送 | 低 | 半 | 是 | 中 | 可选补充 |
|
||||||
|
| MQTT | 低 | 是 | 是(出站) | 高 | 进阶可选(暂不引入) |
|
||||||
|
| **WSS(Agent 出站)** | **<50ms** | **是** | **是(免内网穿透)** | **高** | **主通道** |
|
||||||
|
| gRPC 双向流 | 低 | 是 | 是 | 高 | 暂不需要 |
|
||||||
|
|
||||||
|
**安全依据(为什么 WSS 出站最安全)**:
|
||||||
|
|
||||||
|
1. **零入站暴露面**:Agent 不监听任何端口,攻击者无处下手。
|
||||||
|
2. **设备级身份**:安装时生成 Ed25519 密钥对,配对时注册公钥;连接与关键消息均签名,不可伪造。
|
||||||
|
3. **单一信任锚**:Agent 只信任我方服务器;所有指令经服务器签名下发,杜绝中间人。
|
||||||
|
4. **可降级**:企业防火墙拦截 WSS 时自动降级 HTTPS 长轮询(同样 TLS+签名),可用性不降级。
|
||||||
|
5. **证书固定(可选)**:TLS 1.3 + 证书指纹固定,防内网 DNS 劫持。
|
||||||
|
|
||||||
|
## 5. 鉴权体系(四层)
|
||||||
|
|
||||||
|
| 对象 | 方式 | 凭证 | 存储 | 吊销 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 网页用户 | 密码(Argon2id)+ TOTP 2FA(可选,默认关闭,管理员可开启) | Access JWT 15min + Refresh Cookie 7d(轮换) | 服务器 | 会话管理/登出全部设备 |
|
||||||
|
| Agent 设备 | 配对码 + Ed25519 签名 | agentToken(长期可吊销) | 服务器存公钥;App 本地存私钥(DPAPI/SQLCipher) | 控制台吊销设备 |
|
||||||
|
| 平台官方 API(X/IG/YouTube) | OAuth2 + PKCE | refresh token | 服务器 KMS 信封加密 | OAuth 应用撤销 |
|
||||||
|
| 国内平台账号 | 官方扫码登录 | cookie/token | 仅 Agent 本地保险库,服务器零凭据 | 远程失效→重新扫码 |
|
||||||
|
| 挑战输入(验证码等) | WSS 加密通道 | 不适用 | 不落日志、不持久化明文 | 会话结束即弃 |
|
||||||
|
|
||||||
|
## 6. REST API 清单(网页端 → 服务器)
|
||||||
|
|
||||||
|
| 模块 | 端点 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| 认证 | POST /api/auth/login、/refresh、/logout、/2fa/verify | 登录/刷新/登出/两步验证 |
|
||||||
|
| 工作区 | GET/PUT /api/workspace、POST /api/members/invite、PATCH /api/members/:id | 工作区与成员 |
|
||||||
|
| 账号台账 | GET /api/accounts、POST /api/accounts/bind、DELETE /api/accounts/:id、GET /api/accounts/:id/health | 绑定发起即生成挑战 |
|
||||||
|
| 素材 | GET/POST /api/materials、POST /api/materials/presign、DELETE /api/materials/:id | 直传签名与元数据 |
|
||||||
|
| 任务 | GET/POST /api/tasks、POST /api/tasks/:id/submit/approve/reject/retry/cancel、GET /api/tasks/:id/events | 任务生命周期 |
|
||||||
|
| 挑战 | GET /api/challenges、POST /api/challenges/:id/resolve | resolve 载荷:验证码/确认结果 |
|
||||||
|
| 审计 | GET /api/audit-logs | 全操作日志 |
|
||||||
|
| 通知 | GET /api/notifications、POST /api/notifications/read | 通知中心 |
|
||||||
|
| Agent 管理 | POST /api/agents/pairing、GET /api/agents、POST /api/agents/:id/revoke、POST /api/agents/:id/cmd | 配对码/吊销/远程指令 |
|
||||||
|
| 内部后台 | GET /api/admin/tenants、/agents、/adapters、/risk-events | 平台角色专属,租户域隔离 |
|
||||||
|
|
||||||
|
## 7. WebSocket 消息协议
|
||||||
|
|
||||||
|
| 方向 | 消息 | 载荷要点 |
|
||||||
|
|---|---|---|
|
||||||
|
| A→S | hello | deviceId + 签名 + 协议版本(连接即鉴权,只此一次) |
|
||||||
|
| A→S | heartbeat | 10s 间隔;服务器 25s 未收判定离线 |
|
||||||
|
| S→A | task.push | taskId + 加密载荷(素材URL/标题/平台/账号) |
|
||||||
|
| A→S | task.ack / task.result | 幂等确认 / 结果+截图URL+失败原因分类 |
|
||||||
|
| A→S | challenge.push | 类型(qr/confirm/sms/captcha)+ 凭证 |
|
||||||
|
| S→A | challenge.resolve | 用户输入结果(验证码等,端到端加密) |
|
||||||
|
| S→A | agent.cmd / config.update | 重启/升级/配置下发(签名) |
|
||||||
|
| 浏览器↔S | 原生 WebSocket(JSON 协议) | task / challenge / notification 订阅推送(JWT 鉴权) |
|
||||||
|
|
||||||
|
## 8. 关键流程
|
||||||
|
|
||||||
|
### 8.1 网页用户登录(标准)
|
||||||
|
|
||||||
|
账号密码 → 签发 Access JWT(15min) + Refresh Cookie(7d 轮换) → 进入工作区。TOTP 2FA 为可选能力,**默认关闭**,管理员可对工作区开启;异常登录(新设备/异地)触发告警通知。
|
||||||
|
|
||||||
|
### 8.2 Agent 配对(一次性)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant U as 用户
|
||||||
|
participant W as 网页端
|
||||||
|
participant S as 服务器
|
||||||
|
participant A as App(Agent)
|
||||||
|
U->>W: 登录(密码+TOTP 2FA)
|
||||||
|
W->>S: POST /api/auth/login
|
||||||
|
S-->>W: JWT(15min) + refresh cookie
|
||||||
|
U->>W: 工作区→设备→添加设备
|
||||||
|
W->>S: POST /api/agents/pairing
|
||||||
|
S-->>W: 配对码(6位,5分钟有效)
|
||||||
|
U->>A: 输入配对码
|
||||||
|
A->>A: 生成Ed25519密钥对+deviceId
|
||||||
|
A->>S: WSS auth(deviceId,配对码,公钥,签名)
|
||||||
|
S->>S: 验证→注册公钥→签发agentToken(可吊销)
|
||||||
|
S-->>A: 绑定成功
|
||||||
|
A->>A: 私钥入本地保险库(DPAPI/SQLCipher)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.3 平台账号绑定(沿用 §22 优化登录)
|
||||||
|
|
||||||
|
控制台发起绑定 → 服务器生成挑战 → Agent API 取码(B站等)或温浏览器取码(视频号)→ 只传 token/URL → 控制台重渲染 → 用户扫码 → Agent 短轮询检测 → 换取 cookie 入保险库 → 台账变绿。
|
||||||
|
|
||||||
|
### 8.4 发布任务全链路(沿用 §21 全链路时序)
|
||||||
|
|
||||||
|
|
||||||
|
## 9. 边界问题与解决方案
|
||||||
|
|
||||||
|
| 问题 | 解决方案 |
|
||||||
|
|---|---|
|
||||||
|
| 企业防火墙拦截 WSS | 443 端口 + wss;仍失败则自动降级 HTTPS 长轮询(同签名) |
|
||||||
|
| 服务器重启/网络抖动 | Redis 持久队列(asynq)+ 重投 + Agent 断线重连 + taskId 幂等 ack |
|
||||||
|
| 重复执行 | taskId 全局唯一,Agent 本地状态表去重 |
|
||||||
|
| 大文件挤占消息通道 | 素材走 OSS 预签名直传,WSS 只传元数据 + SHA-256;断点续传 |
|
||||||
|
| 两端时钟不同步(签名 nonce) | 配对时同步服务器时间 + nonce 宽限窗口 |
|
||||||
|
| 并发挑战冲突 | 账号级互斥锁:一账号同一时刻仅一个挑战 |
|
||||||
|
| 版本不兼容 | 协议 version 字段 + zod schema 校验,旧版 App 只读降级 |
|
||||||
|
| 验证码泄露 | 挑战消息端到端加密、日志脱敏、不持久化明文 |
|
||||||
|
| 排期时区错乱 | 统一 UTC 存储,控制台按本地时区渲染 |
|
||||||
|
| 托管形态代理失效 | 池健康检查自动摘除 + 同区域切换 + 告警 |
|
||||||
|
|
||||||
|
## 10. 核心数据模型
|
||||||
|
|
||||||
|
> 数据库引擎:MySQL 8.x(GORM);Redis 承载队列与缓存(asynq)。
|
||||||
|
|
||||||
|
| 表 | 关键字段 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| tenant | id, name, plan, status | 租户(客户/内部) |
|
||||||
|
| user | id, tenant_id, phone/email, password_hash, totp_secret, role | 平台角色独立域 |
|
||||||
|
| platform_account | id, tenant_id, platform, status, health, proxy_binding | 台账(无凭据字段) |
|
||||||
|
| agent_device | id, tenant_id, device_id, public_key, token_hash, status, version | 设备(公钥,无私钥) |
|
||||||
|
| material | id, tenant_id, oss_key, sha256, meta | 素材 |
|
||||||
|
| task | id, tenant_id, material_id, targets, schedule_at, status, retry_count | 发布任务 |
|
||||||
|
| task_event | id, task_id, type, payload, ts | 状态事件流 |
|
||||||
|
| challenge | id, account_id, type, status, expires_at | 挑战(凭证加密) |
|
||||||
|
| audit_log | id, tenant_id, actor, action, target, ts | 审计 |
|
||||||
|
| notification | id, tenant_id, user_id, channel, content, read_at | 通知 |
|
||||||
|
|
||||||
|
## 11. 安全清单
|
||||||
|
|
||||||
|
- 全链路 TLS 1.2+(HTTPS/WSS);密码 Argon2id;登录限速;异常登录告警;TOTP 2FA 可选(默认关闭)。
|
||||||
|
- 租户数据行级隔离(tenant_id 强制注入);内部后台独立角色域。
|
||||||
|
- 敏感字段 KMS 信封加密;日志脱敏;挑战内容不落日志。
|
||||||
|
- Agent 指令签名校验;设备可远程吊销。
|
||||||
|
- 依赖扫描 + 常规安全审计(SAST/依赖 CVE)。
|
||||||
|
## 12. 传输体系设计
|
||||||
|
|
||||||
|
### 12.0 分期策略(一期本地,二期接桶)
|
||||||
|
|
||||||
|
- **一期(首版)**:不接入存储桶。素材存服务器本地磁盘;运营 multipart 直传服务器;Agent 用**带短时效签名 token 的直链下载**(HTTP 直链,不做分片续传)。大文件同样不走 WSS。
|
||||||
|
- **二期**:接 OSS 双桶(国内/海外)、预签名 URL、Range 分片断点续传、本地缓存 LRU(即 §12.1-12.5 全部能力)。
|
||||||
|
- **工程要求**:存储层必须抽象为 StorageDriver 接口(一期 local 实现 / 二期 oss 实现),上传、下载、元数据三处调用全部经接口——二期切换零业务改动。
|
||||||
|
|
||||||
|
### 12.1 三链路分离(控制面轻、数据面直)
|
||||||
|
|
||||||
|
| 链路 | 内容 | 通道 | 典型大小 | 续传 | 加密 |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| 控制面 | 任务/状态/挑战/心跳 | WSS | <1KB | 幂等重发 | TLS + 签名 |
|
||||||
|
| 数据面·上传 | 运营浏览器 → 素材桶 | HTTPS 直传(预签名) | 10MB ~ 数GB | 分片续传 | TLS + 私有桶签名 |
|
||||||
|
| 数据面·下发 | 素材桶 → Agent | HTTPS Range 下载 | 10MB ~ 数GB | Range 续传 | TLS + 短时效签名 |
|
||||||
|
| 数据面·回传 | 截图 → 素材桶 | HTTPS 直传(预签名) | 100-500KB | 无需 | TLS + 签名 |
|
||||||
|
|
||||||
|
**铁律:大文件绝不走 WSS**(避免阻塞消息通道);WSS 只传元数据与短时效下载 URL。一期由服务器本地盘存素材字节(单机磁盘即够);二期切换 OSS 后服务器只存元数据(oss_key/sha256/大小)。
|
||||||
|
|
||||||
|
### 12.2 全链路传输流程
|
||||||
|
|
||||||
|
> 注:图中「素材桶 OSS」为二期形态;一期将 OSS 替换为服务器本地盘(见 §12.0 分期策略),其余链路不变。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
OP["运营(网页端)"] -->|"① 分片直传(预签名,断点续传)"| OSS["素材桶 OSS"]
|
||||||
|
OP -->|"② 元数据 API(oss_key/sha256)"| S["服务器"]
|
||||||
|
S -->|"③ WSS task.push + 短时效下载URL"| A["Agent"]
|
||||||
|
A -->|"④ Range分片下载+SHA256校验+本地缓存"| OSS
|
||||||
|
A -->|"⑤ 上传发布"| P["目标平台"]
|
||||||
|
P -->|"⑥ 回执+截图"| A
|
||||||
|
A -->|"⑦ 截图直传OSS(预签名)"| OSS
|
||||||
|
A -->|"⑧ WSS task.result(URL+状态)"| S
|
||||||
|
S -->|"⑨ WSS 实时状态推送"| OP
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12.3 断点续传与完整性
|
||||||
|
|
||||||
|
- 浏览器上传:OSS 分片上传(SDK 原生),失败自动重传分片。
|
||||||
|
- Agent 下载:HTTP Range 分段 + 本地 .part 文件,完成后整体 SHA-256 校验(与 DB 元数据比对),失败重下。
|
||||||
|
- 预签名 URL 过期:Agent 向服务器换新 URL(协议预留 refresh 消息)。
|
||||||
|
|
||||||
|
### 12.4 区域与成本
|
||||||
|
|
||||||
|
- **双桶架构**:国内桶(阿里云 OSS/腾讯 COS)+ 海外桶(Cloudflare R2 或 S3),按任务目标平台就近分配——国内平台任务用国内桶、国际平台任务用海外桶,避免跨境传输慢。
|
||||||
|
- **本地缓存 + LRU**:重复任务/重试不重复下载,降低 OSS 流量成本。
|
||||||
|
- 进阶:大客户可绑定自有存储(数据不出门)。
|
||||||
|
|
||||||
|
### 12.5 安全
|
||||||
|
|
||||||
|
- 私有桶 + 预签名 URL(上传 15min、下载 2h 短时效);URL 只经加密通道下发,不落日志。
|
||||||
|
- 上传白名单:仅视频/图片 MIME + 大小上限。
|
||||||
|
- 素材本身最终要公开到平台,HTTPS 即够;凭据/挑战类数据才端到端加密。
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# EveryPublish 技术栈定版(唯一权威来源)
|
||||||
|
|
||||||
|
> 2026-08-20 · 其他文档中与本文件冲突之处,以本文件为准。
|
||||||
|
|
||||||
|
## 1. 总览
|
||||||
|
|
||||||
|
| 端 | 层 | 技术 |
|
||||||
|
|---|---|---|
|
||||||
|
| 网页端 | 页面(UI) | React 18 + TypeScript + TDesign React(Tencent starter,MIT,已拉取 apps/web) |
|
||||||
|
| 网页端 | 服务端/API | **Go**:gin + coder/websocket + asynq + GORM + argon2 |
|
||||||
|
| 客户端 | UI 壳 | WinUI 3(C# / .NET 8) |
|
||||||
|
| 客户端 | 核心 | **Go**:WSS 客户端 + go-rod(CDP)+ 凭据保险库 |
|
||||||
|
| 数据 | 数据库 | **MySQL 8.x**(GORM + go-sql-driver/mysql;迁移 golang-migrate) |
|
||||||
|
| 数据 | 队列/缓存 | Redis 7(asynq) |
|
||||||
|
| 存储 | 一期 | 服务器本地盘(签名直链下载) |
|
||||||
|
| 存储 | 二期 | OSS 双桶(StorageDriver 抽象预留) |
|
||||||
|
|
||||||
|
## 2. 「网页端与后端都用 Go」的说明
|
||||||
|
|
||||||
|
- **页面层仍是 React + TDesign**(starter 已拉取预览,MIT 可商用),不写自定义样式。
|
||||||
|
- 「用 Go」指两处:**网页端的服务端/API 层** 与 **客户端 agent-core**,统一 Go 语言。
|
||||||
|
- 二者共享同一协议 Go module(server 与 agent-core 引用同一 shared/proto 包),消息 Schema 只写一次。
|
||||||
|
|
||||||
|
## 3. Go 选型明细
|
||||||
|
|
||||||
|
- Web 框架:gin
|
||||||
|
- WebSocket:github.com/coder/websocket(原生 JSON 协议,不用 Socket.IO)
|
||||||
|
- 任务队列:asynq(Redis)
|
||||||
|
- ORM/DB:GORM + go-sql-driver/mysql(MySQL 8.x);迁移 golang-migrate
|
||||||
|
- 鉴权:golang-jwt + x/crypto/argon2(用户);Ed25519 设备签名(Agent)
|
||||||
|
- 浏览器自动化(客户端):go-rod + rod-stealth,驱动系统 Chrome/Edge
|
||||||
|
- 常驻服务(二期):kardianos/service
|
||||||
|
|
||||||
|
## 4. 目录结构(monorepo)
|
||||||
|
|
||||||
|
- web/ —— React 前端(现有 apps/web 的 starter)
|
||||||
|
- server/ —— Go 服务器(REST + WSS 网关 + 队列 worker)
|
||||||
|
- client/ui —— WinUI 3 壳(C#)
|
||||||
|
- client/core —— agent-core(Go)
|
||||||
|
- shared/ —— 协议 Go module(server 与 core 共用)
|
||||||
|
- tools/ —— 联调诊断(diag-connection.mjs 等)
|
||||||
|
|
||||||
|
## 5. 版本与工具
|
||||||
|
|
||||||
|
- Go 1.23+;Node 20 LTS(前端,starter 依赖升级见交付文档 D1);.NET 8;MySQL 8.x;Redis 7
|
||||||
|
- 包管理:pnpm(前端)、go mod(后端)
|
||||||
|
- CI:GitHub Actions(前端构建 / Go 单测 / WinUI 打包)
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# EveryPublish 网页端测试报告(检查点①)
|
||||||
|
|
||||||
|
> 日期:2026-08-20 · 范围:阶段一网页端 D1-D7 交付 · 结论:**核心链路全部通过,可进入用户验收**
|
||||||
|
|
||||||
|
## 一、测试概况
|
||||||
|
|
||||||
|
| 项 | 结果 |
|
||||||
|
|---|---|
|
||||||
|
| Go 单元+集成测试 | ✅ 全绿(auth 4 项、task 状态机 4 项、API 集成 4 场景、WSS 全链路 1 场景) |
|
||||||
|
| TypeScript 类型检查 | ✅ `tsc --noEmit` 0 错误 |
|
||||||
|
| 前端生产构建 | ✅ `pnpm build` 成功(仅 chunk 体积提示,非错误) |
|
||||||
|
| 手动链路测试 | ✅ 24 项断言 24 通过(含 3 项环境/断言修正后复测通过) |
|
||||||
|
| 断线重连兜底 | ✅ 下线不丢任务、重连 1 秒内自动补发 |
|
||||||
|
|
||||||
|
## 二、六条核心链路测试结果
|
||||||
|
|
||||||
|
| # | 链路 | 关键断言 | 结果 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | 注册登录 | 注册 200 / 重复邮箱 409 / 错误密码 401 / 刷新轮换后旧 refresh 401 / me 查询 | ✅ |
|
||||||
|
| 2 | 工作空间 | 创建 / 改名 | ✅ |
|
||||||
|
| 3 | 素材直传 | 上传 sha256 / 同文件去重 dedup=true / 直链一次 200 二次 404(一次性) | ✅ |
|
||||||
|
| 4 | 账号绑定 | 新增→bind→challenge 推送→扫码→active(绑定到设备) | ✅ |
|
||||||
|
| 5 | 任务全生命周期 | 创建→提交→审核→下发→执行→success;驳回→rejected→重提;取消 | ✅ |
|
||||||
|
| 6 | 成员协作 | 邀请→加入→角色 operator 生效 | ✅ |
|
||||||
|
| 7 | 审计 | auth.login / task.create 等动作埋点落库、按动作筛选 | ✅ |
|
||||||
|
| 8 | 权限 | 普通用户访问 admin API = 403;admin 账号 = 200 | ✅ |
|
||||||
|
|
||||||
|
## 三、发现的问题与处理
|
||||||
|
|
||||||
|
| 问题 | 类型 | 处理 |
|
||||||
|
|---|---|---|
|
||||||
|
| 假执行器未绑定到测试工作空间(导致绑定/下发无设备) | 测试环境 | 每工作空间独立假执行器实例;非代码缺陷 |
|
||||||
|
| 审计断言用 `=1` 而实际多记录 | 测试脚本 | 修正断言为存在性;审计功能正常 |
|
||||||
|
| 下发器 ack 竞态(先推后写库) | 代码缺陷 | 已修复:先落库 dispatched 再推送(D4 已改) |
|
||||||
|
| 服务器异常退出残留 online 状态 | 代码缺陷 | 已修复:启动自愈重置 offline |
|
||||||
|
| 8080 被 nginx 占用 | 环境 | 后端改用 :8090 |
|
||||||
|
|
||||||
|
## 四、用户验收清单(请您逐项验证)
|
||||||
|
|
||||||
|
1. 打开 http://127.0.0.1:3003/login/index 注册新账号并登录(应进入工作台)。
|
||||||
|
2. 工作台:看到 Agent 设备(在线/离线)与最近任务实时状态。
|
||||||
|
3. 账号管理:添加账号 → 点「绑定」→ 弹窗等待 → 客户端扫码后自动变「已绑定」(无客户端时可跳过,观察「绑定中」状态)。
|
||||||
|
4. 素材库:上传视频 → 列表出现,重复上传同一文件应提示去重。
|
||||||
|
5. 发布管理 → 新建发布:四步向导选择素材/账号/填内容 → 创建草稿。
|
||||||
|
6. 任务列表:草稿「提交审核」→ 审核中心「通过」→ 状态变绿「发布成功」(需在线客户端)。
|
||||||
|
7. 成员管理:邀请一个邮箱 → 生成链接 → 复制。
|
||||||
|
8. 挑战中心 / 通知 / 审计:数据可查看、操作可用。
|
||||||
|
9. 设置 → Agent 设备:可吊销设备(吊销后设备下线)。
|
||||||
|
10. 退出登录 → 重新登录(令牌刷新正常)。
|
||||||
|
|
||||||
|
## 五、环境与运行状态
|
||||||
|
|
||||||
|
- 后端 Go 服务:http://127.0.0.1:8090(`/health` 200)
|
||||||
|
- 前端 dev:http://127.0.0.1:3003(生产构建已通过,`apps/web/dist/`)
|
||||||
|
- MySQL(Docker everypublish-mysql)+ Redis(本机)运行中
|
||||||
|
- 假执行器:demo 工作空间 + tester1 工作空间各一台(模拟客户端)
|
||||||
|
|
||||||
|
## 六、结论
|
||||||
|
|
||||||
|
网页端阶段一交付满足《docs/delivery-plan.md》§7 第 1-5 项验收。**建议进入用户验收;确认通过后开始阶段二(Windows 客户端 D8-D11)。**
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module everypublish/shared
|
||||||
|
|
||||||
|
go 1.23
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ---- WSS 消息类型(server 与 agent-core 共用,Schema 只写一次)----
|
||||||
|
const (
|
||||||
|
TypeHello = "hello"
|
||||||
|
TypeHelloAck = "hello.ack"
|
||||||
|
TypeHeartbeat = "heartbeat"
|
||||||
|
TypeHeartbeatAck = "heartbeat.ack"
|
||||||
|
TypeTaskPush = "task.push"
|
||||||
|
TypeTaskAck = "task.ack"
|
||||||
|
TypeTaskResult = "task.result"
|
||||||
|
TypeTaskCancel = "task.cancel"
|
||||||
|
TypeChallenge = "challenge.new"
|
||||||
|
TypeChallengeAck = "challenge.ack"
|
||||||
|
TypeChallengeSolve = "challenge.solve"
|
||||||
|
TypeAgentStatus = "agent.status"
|
||||||
|
TypeError = "err"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Envelope WSS 统一信封:网关只透传小于1KB元数据,素材一律走签名直链
|
||||||
|
type Envelope struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Payload json.RawMessage `json:"payload"`
|
||||||
|
TS int64 `json:"ts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEnvelope 构造信封
|
||||||
|
func NewEnvelope(id string, typ string, payload interface{}) *Envelope {
|
||||||
|
raw, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
raw = []byte("{}")
|
||||||
|
}
|
||||||
|
return &Envelope{ID: id, Type: typ, Payload: raw, TS: time.Now().UnixMilli()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceHello Agent 上线:设备ID + Ed25519 签名(nonce||ts)防伪造
|
||||||
|
type DeviceHello struct {
|
||||||
|
DeviceID string `json:"deviceId"`
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
|
TS int64 `json:"ts"`
|
||||||
|
Sig string `json:"sig"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelloAck 服务器应答(携带服务端 nonce 供会话内签名)
|
||||||
|
type HelloAck struct {
|
||||||
|
ServerNonce string `json:"serverNonce"`
|
||||||
|
SessionID string `json:"sessionId"`
|
||||||
|
ServerTS int64 `json:"serverTs"`
|
||||||
|
Sig string `json:"sig"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heartbeat 心跳(30s)
|
||||||
|
type Heartbeat struct {
|
||||||
|
DeviceID string `json:"deviceId"`
|
||||||
|
TS int64 `json:"ts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeartbeatAck 心跳应答
|
||||||
|
type HeartbeatAck struct {
|
||||||
|
ServerTS int64 `json:"serverTs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskPush 任务下发(仅元数据;素材 URL 为签名直链)
|
||||||
|
type TaskPush struct {
|
||||||
|
TaskID string `json:"taskId"`
|
||||||
|
Platform string `json:"platform"`
|
||||||
|
AccountID string `json:"accountId"`
|
||||||
|
AccountName string `json:"accountName"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
MaterialURLs []string `json:"materialUrls"`
|
||||||
|
ScheduleAt int64 `json:"scheduleAt"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskAck Agent 确认接收
|
||||||
|
type TaskAck struct {
|
||||||
|
TaskID string `json:"taskId"`
|
||||||
|
Accept bool `json:"accept"`
|
||||||
|
Reason string `json:"reason,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskResult 执行结果回传
|
||||||
|
type TaskResult struct {
|
||||||
|
TaskID string `json:"taskId"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
PublishedURL string `json:"publishedUrl,omitempty"`
|
||||||
|
Receipts []string `json:"receipts,omitempty"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
FinishedAt int64 `json:"finishedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskCancel 取消任务
|
||||||
|
type TaskCancel struct {
|
||||||
|
TaskID string `json:"taskId"`
|
||||||
|
Reason string `json:"reason,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Challenge 二次验证挑战(扫码/验证码/APP确认/人工兜底)
|
||||||
|
type Challenge struct {
|
||||||
|
ChallengeID string `json:"challengeId"`
|
||||||
|
AccountID string `json:"accountId"`
|
||||||
|
Platform string `json:"platform"`
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
QRToken string `json:"qrToken,omitempty"`
|
||||||
|
QRURL string `json:"qrUrl,omitempty"`
|
||||||
|
Prompt string `json:"prompt"`
|
||||||
|
ExpiresAt int64 `json:"expiresAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChallengeAck 挑战应答
|
||||||
|
type ChallengeAck struct {
|
||||||
|
ChallengeID string `json:"challengeId"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChallengeSolve 网页端人工完成挑战
|
||||||
|
type ChallengeSolve struct {
|
||||||
|
ChallengeID string `json:"challengeId"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentStatus Agent 状态上报
|
||||||
|
type AgentStatus struct {
|
||||||
|
DeviceID string `json:"deviceId"`
|
||||||
|
Online bool `json:"online"`
|
||||||
|
Busy bool `json:"busy"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
TS int64 `json:"ts"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// 连接诊断: TCP / WS 分段计时, 零依赖, Node >= 22 (内置 WebSocket)
|
||||||
|
// 用法: node tools/diag-connection.mjs ws://192.168.1.5:3001 [次数]
|
||||||
|
// 说明: WS 模式下服务端需对 {"type":"ping"} 回应任意消息(建议 {"type":"pong"})
|
||||||
|
import net from "node:net";
|
||||||
|
|
||||||
|
const url = process.argv[2];
|
||||||
|
const times = Number(process.argv[3] || 5);
|
||||||
|
if (!url) {
|
||||||
|
console.error("用法: node tools/diag-connection.mjs <ws://host:port | http://host:port> [次数]");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const u = new URL(url);
|
||||||
|
const host = u.hostname;
|
||||||
|
const port = Number(u.port || (u.protocol === "wss:" || u.protocol === "https:" ? 443 : 80));
|
||||||
|
const isWs = u.protocol === "ws:" || u.protocol === "wss:";
|
||||||
|
const tcpMs = [], openMs = [], rttMs = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < times; i++) {
|
||||||
|
const t0 = performance.now();
|
||||||
|
try {
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
const s = net.connect({ host, port });
|
||||||
|
const to = setTimeout(() => { s.destroy(); rej(new Error("TCP timeout")); }, 3000);
|
||||||
|
s.once("connect", () => { clearTimeout(to); tcpMs.push(performance.now() - t0); s.destroy(); res(); });
|
||||||
|
s.once("error", (e) => { clearTimeout(to); rej(e); });
|
||||||
|
});
|
||||||
|
} catch (err) { tcpMs.push(-1); console.warn("第" + (i+1) + "次 TCP 失败: " + err.message); }
|
||||||
|
if (!isWs) continue;
|
||||||
|
const t1 = performance.now();
|
||||||
|
try {
|
||||||
|
const ws = new WebSocket(url);
|
||||||
|
await new Promise((res, rej) => { ws.onopen = res; ws.onerror = () => rej(new Error("WS open 失败")); });
|
||||||
|
openMs.push(performance.now() - t1);
|
||||||
|
const t2 = performance.now();
|
||||||
|
ws.send(JSON.stringify({ type: "ping", t: Date.now() }));
|
||||||
|
await new Promise((res) => {
|
||||||
|
const to = setTimeout(() => { rttMs.push(-1); res(); }, 3000);
|
||||||
|
ws.onmessage = () => { clearTimeout(to); rttMs.push(performance.now() - t2); res(); };
|
||||||
|
});
|
||||||
|
ws.close();
|
||||||
|
} catch (err) { openMs.push(-1); console.warn("第" + (i+1) + "次 WS 失败: " + err.message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
const stat = (a) => {
|
||||||
|
const v = a.filter((x) => x >= 0);
|
||||||
|
if (!v.length) return "全部失败";
|
||||||
|
const avg = v.reduce((s, x) => s + x, 0) / v.length;
|
||||||
|
return "avg=" + avg.toFixed(1) + "ms min=" + Math.min(...v).toFixed(1) + "ms max=" + Math.max(...v).toFixed(1) + "ms";
|
||||||
|
};
|
||||||
|
console.log("目标: " + url + " (" + host + ":" + port + ") x" + times);
|
||||||
|
console.log("TCP连接 : " + stat(tcpMs) + " <- >20ms 查防火墙/IPv6/代理");
|
||||||
|
if (isWs) {
|
||||||
|
console.log("WS握手 : " + stat(openMs) + " <- >50ms 查服务端WS框架/鉴权");
|
||||||
|
console.log("消息RTT : " + stat(rttMs) + " <- >50ms 查串行往返/服务端处理");
|
||||||
|
}
|
||||||