Files
gyxx-flow/docs/runbook.md
T

274 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GYXX Flow 运维手册
## 日常检查
```bash
uv run gyxx doctor --json
uv run gyxx schedule status
uv run gyxx list
lark-cli --profile hermes-analyzer auth status --json --verify
lark-cli --profile hermes-analyzer auth check --scope "im:message.send_as_user im:message" --json
```
两条 lark-cli 检查必须在实际运行调度服务的系统账户下执行。业务消息统一由该 profile 的
user 身份投递;只有日报卡片的图片预上传因接口限制使用同 profile 的 bot 身份。不要用
交互登录账户的授权结果代替 NSSM/systemd 服务账户验证。
Linux 服务同时检查:
```bash
systemctl status gyxx-flow.service
journalctl -u gyxx-flow.service --since today
```
工作流失败时,使用 `workflow_id`、业务日期和 `run_id` 对照 `<GYXX_DATA_ROOT>/logs`、运行记录、节点产物和副作用账本。
## 手工执行
```bash
uv run gyxx run <workflow_id> --date 2026-08-01
uv run gyxx run <workflow_id> --date 2026-08-01 --execute
uv run gyxx scripts list --json
uv run gyxx scripts run <command_id> --date 2026-08-01
uv run gyxx scripts run <command_id> --date 2026-08-01 --execute
```
`gyxx run` 接受 23 个调度工作流和已注册的手动工作流;补采和维护操作使用 `gyxx scripts run`。两类入口都默认 dry-run,只有业务日期、凭据、浏览器登录态、PostgreSQL、lark-cli user 身份,以及工作流确实需要大模型时对应的 Hermes 角色全部确认后才使用 `--execute`
| 手动场景 | 命令 ID | 日期行为 |
|---|---|---|
| 内容映射重建 | `content.mapping.rebuild` | 业务日期只用于运行审计 |
| 内容失败任务重试 | `content.failed.retry` | 业务日期只用于运行审计 |
| 内容日报重跑 | `gyxx backfill content.metrics.daily` | 使用 `--from/--to` 指定日期范围 |
| 京东自营品牌单日回采 | `shop.jd_self_operated.collect_brand` | `--date` 自动成为起止日期 |
| 商品历史补采 | `product.backfill.run` | `--date` 自动成为 `--from/--to` |
| 商品评价补采 | `product.review.orchestrate` | `--date` 自动成为目标日期 |
| 天猫旗舰店和箱包店视频上传 | `product.video_upload.run` | 业务日期用于运行审计;默认顺序扫描两店,由 PostgreSQL 附件级账本防重 |
| 京东旗舰店视频上传 | `product.jd_video_upload.run` | 业务日期只用于运行审计;从固定凌云air记录锚点按飞书视图顺序扫描,日期可为空 |
| 采购单更新 | `supply.workflow.run` | 自动选择 `mcp-run purchase-order-update` |
命令的额外脚本参数使用可重复的 `--arg=<值>` 传入。`supply.workflow.run --execute` 会进入真实 ERP 修改链路,必须在任务文件、目标范围和回滚条件全部复核后执行。
### 天猫视频上传工作流
天猫视频上传注册为手动幂等工作流 `product.video_upload`,不会被常驻调度器自动触发。前端“立即运行”窗口会显示“光影行星旗舰店 / 光影行星鑫华达专卖店:龙虾仔”单选下拉框,并允许填写天猫话题完整名称或关键词;正式执行只处理所选店铺,话题搜索结果按包含关系选中。默认 dry-run 只生成运行记录并跳过浏览器节点;确认飞书待传记录、对应淘宝光合店铺的登录态和 PostgreSQL 后,再显式正式执行:
```bash
uv run gyxx run product.video_upload --date 2026-08-07
uv run gyxx run product.video_upload --date 2026-08-07 --execute
```
前端把店铺选择作为受限运行参数传给脚本,只允许 `flagship``luggage`。脚本先读取飞书并应用所选店铺路由,再写入/读取附件级账本;只有存在可领取的未上传附件时才启动有头 Chrome、登录对应账号并上传。旗舰店只选择“天猫上传”未勾选且有附件的光影行星男/女记录;箱包店只选择女性、全部款式命中箱包店白名单且有附件的记录。两店分别使用 `GUANGHE_USERNAME` / `GUANGHE_PASSWORD``GUANGHE_LUGGAGE_USERNAME` / `GUANGHE_LUGGAGE_PASSWORD`,箱包店当前账号为 `光影行星鑫华达专卖店:龙虾仔`,密码配置保持不变,并保留独立浏览器 Profile。发生登录或页面错误时保留窗口供人工定位,排查完成后手动关闭。
每次正式扫描都会把视频附件写入 PostgreSQL 附件级发布账本,防重身份由视频 `file_token + 目标账号 + 飞书记录` 构成。账本状态为 `published` 的附件在下次执行时自动跳过,因此同一业务日期可以安全重跑并发现新增附件。旗舰店仍只在一条飞书记录的全部附件发布成功后回写“天猫上传”;箱包店不借用该勾选状态,是否已上传完全以自己的目标账号账本为准。
首次正式执行会读取 `config/tmall-video-publication-backfill.json`,将 2026-07-26 箱包店旧日志中可按 `record_id + file_token + 文件名` 再次核验的 25 个明确成功附件回填为 `published`。配置与当前飞书任一身份不一致都会停止,不会静默猜测。该回填不依赖部署机保留旧 `var/` 日志文件;证据路径与成功行号已固化在配置中供审计。
只有淘宝光合页面出现明确成功回执后才把附件记为 `published`。点击发布后超时、浏览器中断或回执无法确认时,必须记为 `ambiguous` 并停止自动重发;先在对应店铺的作品管理中按账号、附件和飞书记录人工核对,再做审计式对账,不能通过更换业务日期或重复运行绕过。
单条续跑或指定附件使用公开命令,并通过可重复的 `--arg` 传参。下面的命令会停在正式发布前,核对无误后才能去掉 `--stop-before-publish`
```bash
uv run gyxx scripts run product.video_upload.run --date 2026-08-07 --arg=--record-id --arg=RECORD_ID --arg=--stop-before-publish --execute
```
### 京东视频上传工作流
京东视频上传注册为手动幂等工作流 `product.jd_video_upload`。它不会被常驻调度器自动触发;同一业务日期可以重复执行。每次都从飞书视图中的固定凌云air记录 `recvrXTRWWFXeJ`(含)扫描到视图末尾,不依赖“日期”字段。真正的防重键是 PostgreSQL 中的 `视频 file_token + 目标账号 + 飞书记录`
```bash
uv run gyxx run product.jd_video_upload --date 2026-08-07
uv run gyxx run product.jd_video_upload --date 2026-08-07 --execute
```
框架 dry-run 不启动子进程,不下载附件、不写数据库也不打开京东。前端控制台点击“京东视频上传”会重新扫描锚点之后的视图后缀:新建的记录即使日期为空也能被发现;既有后缀记录后来新增第二个视频附件时,新 `file_token` 也会形成独立任务。已入账的同一 `record_id + file_token` 不会重复发布。锚点丢失或重复时脚本拒绝退化为全表扫描,避免误发历史内容。组合款在下载素材前直接跳过。正式执行按视图顺序逐条处理:有图片时设置飞书封面;没有图片时保留京东从视频生成的默认封面;生成 5-27 字标题、关联同款同色商品、选择相关话题和标签。只有页面出现明确成功回执后才把账本写为 `published`;点击发布后超时或浏览器中断会写为 `ambiguous` 并停止,必须先在京东内容管理中人工核对,不能自动重发。
商品关联只按页面商品标题中的款式名做受控模糊匹配,再由直连多模态大模型判断包体颜色;价格和 SPU 不参与查询或勾选。同款同色商品可多选,整个表单最多 10 个。SPU 仅作为可选审计信息;组合款视频当前主动跳过。
首次登录或遇到验证码时,使用公开命令限定一条记录并停在发布按钮前:
```bash
uv run gyxx scripts run product.jd_video_upload.run --date 2026-08-07 --arg=--record-id --arg=RECORD_ID --arg=--manual-login --arg=--stop-before-publish --execute
```
首次部署或多京东账号部署必须显式配置稳定且不可变的 `JD_VIDEO_ACCOUNT_KEY`;已有单账号账本时,前端运行会从 JD 渠道唯一账号 cohort 自动解析,数据库不保存登录账号明文。浏览器由 Scrapling 隐身会话启动真实 Chrome,默认使用该命令独立的 Profile、Cookie 和 Storage State,不能改成其他京东采集任务的共享目录。需要复用一个已启动且已验证的京麦 Chrome 会话时,使用运行时绑定注入的 `GYXX_BROWSER_CDP_URL`;脚本只借用唯一的现有 context,并在退出时保留原浏览器及其页面。
### 主图周工作流
主图调度的唯一工作流 ID 是 `product.main_image.weekly`,每周日 08:30 启动。图同时运行 JD、TM 两个无依赖分支;一个平台失败不会取消、跳过或回滚另一个平台的采集、PG upsert 和飞书插入。两个分支全部结束后再汇总工作流状态:任一平台失败时整体状态为失败,并在错误摘要中标明具体平台和错误,另一平台已经成功的结果继续保留。
两个平台默认都在采集后执行飞书主图表插入和本地 PostgreSQL `main_image_creatives` upsert。执行前必须同时确认两套浏览器登录态、飞书身份和本地数据库;排障时分别检查 `jd``tmall` 节点以及对应插入脚本日志,不能只以采集文件存在判断双 sink 已完成。
### 三平台电商费用日报
唯一工作流 ID 是 `product.ecommerce_costs.daily`,每天 10:00 由项目内 Python 调度器运行,
业务日期为前一天;天猫、京东、抖音三个下载分支并行启动,各自完成下载后再导入。天猫流程先进入万相台商品报表选择昨日,提交下载任务;任务在下载列表显示“生成成功”
后下载 ZIP、做路径安全校验并解压 CSV,随后将商品级广告指标幂等写入
`product_daily_metrics.raw_data.tm_ad_metrics`。商品款式先按 `dim_style.tm_spus` 的商品 ID 匹配,
匹配不到再按商品名称做精确包含匹配,并在节点内保留 `style_name`
`style_match_method``product_id`/`product_name`/`ambiguous`/`unmatched`)。它不会覆盖同一商品日期
已有的访客、成交等经营日报字段;data-hub 产品生命进程从该节点读取广告成交金额和花费并计算投产比、电商费比。
正式服务必须注入 `WANXIANG_ACCOUNT``WANXIANG_PASSWORD``GYXX_DATA_ROOT`
`GYXX_POSTGRES_DSN`。默认浏览器 Profile 是
`<GYXX_DATA_ROOT>\state\browser-profiles\wanxiang-ads`,登录失效时不能在另一个 Profile 中登录后
期待服务自动获得状态。
手工验证或补采某一天时,先 dry-run,再显式执行完整工作流:
```powershell
uv run gyxx run product.ecommerce_costs.daily --date 2026-08-17
uv run gyxx run product.ecommerce_costs.daily --date 2026-08-17 --execute
```
如果登录态失效,使用有头的采集命令(不要给它加 `--headless`)完成验证码/滑块,再单独导入:
```powershell
uv run gyxx scripts run product.tmall_wanxiang_ads.collect --date 2026-08-17 --execute
uv run gyxx scripts run product.import.tmall_ads --date 2026-08-17 --execute
```
原始文件位于 `<GYXX_DATA_ROOT>\data\raw\product_commerce\tmall_wanxiang_ads\<date>\`,其中
`manifest.json` 记录 ZIP 和解压出的 CSV。定时工作流每次都会进入商品报表点击“下载报表”,在日期范围
选择“昨日”并点击“确定”,再到下载列表等待“生成成功”。若只需重新解析已下载文件,可以跳过采集步骤
重跑导入;默认导入以 manifest 列出的本次 CSV 为准,避免同一日期目录中历史 CSV 被重复合并;重复导入同一日期和商品不会产生重复行。采集命令手工重试时若不加 `--force-request`,仍可
复用下载列表中已有的“生成成功”任务。
排障顺序:
1. 检查 `gyxx schedule status`、服务日志和对应 `run_id`,确认失败发生在申请、下载、解压还是入库。
2. 检查同一 Profile 是否仍能打开万相台商品报表;登录失效时在有头命令中人工完成验证。
3. 检查日期目录中的 `manifest.json`、CSV 表头和 `日期/主体ID/主体名称`ZIP 存在不代表数据库已导入。
4. 检查 PostgreSQL 的 `product_daily_metrics` 是否存在 `platform='tm'`、目标日期和商品 ID,且
`raw_data->'tm_ad_metrics'` 已更新;确认经营日报其他 JSON 节点仍保留。
5. 只有在外部状态核对清楚后才按同一业务日期重跑,不能用换日期或重复申请掩盖下载结果不明。
## 调度操作
```bash
uv run gyxx schedule run --dry-run --once
uv run gyxx schedule status
sudo systemctl restart gyxx-flow.service
```
调度服务也只加载显式指定的受控环境文件:
```bash
uv run gyxx schedule run --env-file /etc/gyxx-flow/gyxx-flow.env
```
业务时间只在 `config/schedules.json` 中维护。systemd 负责进程守护,Python scheduler 负责全部业务定时;不要重复注册 cron、systemd timer 或 Windows 任务计划。
需要停用单个工作流时,优先修改该工作流的调度启用状态,先 dry-run,再重启服务。其他模块继续运行。
## 数据和浏览器状态
生产 `GYXX_DATA_ROOT` 必须位于源码目录之外,Linux 推荐 `/var/lib/gyxx-flow`
- `data/raw/<module>`:原始采集结果,不按临时文件清理。
- `data/normalized``data/curated`:清洗、标准化和聚合数据。
- `data/exports`Markdown、Excel 等交付文件。
- `state/browser/<module>/<script>`:独立 Cookie、Profile 和 storage state。
- `state/accounts/<account>`:账号级登录态主库(同一账号多个脚本共用,`runtime-bindings.json` 中声明 `account` 的脚本每次运行前自动合并同步)。
- `state/scheduler``state/locks`、运行账本:调度和幂等状态。
- `tmp`:仅存放可重建临时文件。
账号级登录态管理:
- `gyxx accounts list`:查看账号、登录态有效性、成员脚本。
- `gyxx accounts login <account_id>`:打开该账号的登录浏览器(独立 Profile),人工扫码/验证码登录后自动保存 `cookies.json`/`storage_state.json` 到账号主库,并同步到全部成员脚本;`--timeout` 控制等待时长,`--force` 强制重登。
- `gyxx accounts sync [<account_id>]`:把账号主库 cookie 推送到成员脚本(不打开浏览器)。
- `gyxx accounts seed <account_id> --source-binding <binding_id>`:一次性把已有脚本的登录态按平台域名过滤后导入账号主库;仅用于迁移,不替代后续的账号级登录。
同一账号只需登录一次:之后任何成员脚本运行前都会从账号主库合并最新登录态到自己的独立 Cookie 文件,无需逐脚本登录。
当前配置按登录身份拆分为以下账号,避免同一平台不同店铺互相覆盖 Cookie:
- `douyin-shop`:抖店,600 秒;
- `jd-shop``jd-self-operated``jd-market-rank`:京东三个独立登录身份,各 900 秒;
- `tmall-shop`:天猫/淘宝/万相台光影行星账号,900 秒;
- `tmall-ozko`:商品经营日报 ozko 账号,900 秒;
- `xiaohongshu-content`:小红书,900 秒;
- `douyin-content`:抖音内容侧,900 秒;
- `xingtu`:星图,900 秒;
- `chanmama`:蝉妈妈,900 秒;
- `bilibili-content`B 站,900 秒。
商品经营日报 `product_commerce:taobao_sycm_collect.py` 会在同一次运行中按品牌路由账号:光影行星使用 `tmall-shop`ozko 使用 `tmall-ozko`,两边各自使用独立 Profile 和账号 vault。`tmall-ozko` 没有静态成员脚本是有意设计,因为它由这个按品牌拆分的包装工作流动态选择;登录态成功后仍会回写 `state/accounts/tmall-ozko/`,由同一套 keepalive 负责续期。
账号需要在 `config/runtime-bindings.json``accounts.<account_id>.keepalive` 中显式开启续期,并配置一个不会产生业务副作用的已登录页面 URL。所有账号由唯一的 `gyxx schedule run` 常驻进程在后台维护;每个账号使用独立 CDP 端口、独立 Profile 和账号级 Cookie vault,启动后按 `initial_delay_seconds` 错峰访问,避免同时打开多个登录页面。访问成功且没有跳转到登录页时,Scrapling 才会把最新 Cookie/storage state 原子写回 `state/accounts/<account_id>/`,并同步给成员脚本;不需要另建 Windows 任务计划、cron 或 timer。
Cookie 续期只能延长站点支持滑动续期的会话,不能突破服务端硬过期、主动退出、风控或验证码。keepalive 检测到登录页/无效 Cookie 时会停止发布,不会用失效状态覆盖最后一份有效 vault;此时按 `gyxx accounts list` 检查 `keepalive.status`,必要时重新执行 `gyxx accounts login <account_id>`
项目不会自动搬动或删除已有 `var/`。迁移数据根时应停止调度器,完整备份和复制原目录,修改环境变量后依次运行 doctor、dry-run 和一次指定日期的对账执行。
## 清理和保留
可以在确认没有任务运行后清理缓存、`tmp`、dry-run 目录和可重建构建产物。以下内容不得作为普通缓存删除:
- `data/raw` 原始数据;
- `state/scheduler` 和资源锁;
- EffectLedger、运行 journal 和浏览器状态;
- 尚未核对或尚未导入数据库的 Excel、JSON、Markdown 导出。
旧 Windows 任务计划 XML 只属于历史部署证据,确认 Python scheduler 已接管且完成备份后再归档。
## 故障处理
1.`workflow_id`、业务日期和 `run_id` 定位失败节点。
2. 检查本机 PostgreSQL、对应 Hermes 角色、飞书身份和脚本专属 CDP 端口。
3. 检查 Cookie/storage state 是否存在且未失效。
4. 修复后按同一业务日期重跑,依靠唯一键和 EffectLedger 防止重复写入。
5. 仍失败时停用该工作流,不停止其他模块。
### 副作用账本对账
正式节点失败后,EffectLedger 会保留 `ambiguous` 回执,避免不确定状态下重复写库或重复
发送飞书。不要删除或手工修改回执文件。只有先从 PostgreSQL、飞书消息回执和目标文档
确认该节点是否已产生外部效果,才可执行审计式对账:
```bash
uv run gyxx effects reconcile <workflow_id> \
--date 2026-08-04 \
--step <step_id> \
--expected-run-id <原失败run_id> \
--action retry \
--operator <操作人> \
--reason <重试原因> \
--evidence <已核对的证据> \
--execute
```
`retry` 仅在证明确实没有完成外部写入时允许同日重试;若证据表明外部写入已经完成,使用
`--action applied`。命令只接受当前仍为 `ambiguous``run_id` 精确匹配的回执,并保留
操作人、原因、证据和时间,不能用于绕过正在执行的节点。
## 回滚
代码回滚时先停止 `gyxx-flow.service`,切换到上一个已验收版本,重新安装锁定依赖并执行 doctor 和调度 dry-run;确认后再启动服务。不得用代码回滚覆盖 `GYXX_DATA_ROOT`
数据撤销必须针对明确表、业务键和 `run_id`,先备份 PostgreSQL,再执行经过复核的 SQL。不得删除整个数据库、数据卷或数据根。
浏览器状态异常时,只备份并处理目标脚本的 `state/browser/<module>/<script>`,不要清理其他脚本状态。调度状态异常时先停止唯一调度进程并备份 `state/scheduler`,不要同时启动第二实例。
## 业务源码同步
外部业务源码目录只用于发现更新,GYXX Flow 运行时不导入或挂载这些目录。只读检查:
```powershell
uv run gyxx sources status `
--source-root content_marketing=<内容源码目录> `
--source-root product_commerce=<商品源码目录> `
--source-root shop_intelligence=<店铺源码目录> `
--source-root supply_chain=<供应链源码目录> `
--json
```
只有未转换、无冲突的 `source_changed` 可以显式执行:
```powershell
uv run gyxx sources apply ... --execute --json
```
经过统一路径、数据库、Hermes、飞书或浏览器适配的文件必须人工合并并运行回归测试。新增可执行脚本还需登记工作流、调度和独立 CDP 绑定。同步完成后执行测试、doctor、acceptance 和 source status;真实采集不在源码同步过程中自动触发。