# 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 即够;凭据/挑战类数据才端到端加密。