# AppsFlyer Click Gap 运行目标是 **mac2**。流水线复用 `appsflyer/af_click_gap.py` 的 Superset 查询、AppsFlyer payload、特殊包名映射和差值公式,但新入口增加 timeout/重试、严格错误边界、AppsFlyer 重复键按 `dt+pkg_name` 求和,并将日志写到仓库外 `~/Library/Logs/af-click-gap/pipeline.log`。固定数据库:`/Users/ads-center/Documents/database/af_click_gap.db`;不复制或提交数据库、cookie、token、`.env`、日志和缓存。 ## 文件和字段 - `scripts/af_click_gap_pipeline.py`:提取、校验、发布和 Git 操作。 - `data/YYYYMMDD.json`:**直接 JSON 数组**;无数据日期也写 `[]`。每行字段为 `dt`(`YYYYMMDD` 字符串)、`attr_platform`、`pkg_name`(非空字符串)、`shareit_clicks`、`af_clicks`、`installs_ua`、`clicks_diff`(有限 number)、`clicks_diff_rate`(有限 number 或 `null`)。业务键为 `pid + dt + pkg_name`。 - `data/index.json`:版本、生成时间及 `data/YYYYMMDD.json` 清单。 - `index.html`:无构建依赖的静态看板。 默认窗口是 mac2 本地运行日及前 6 天,首尾包含,共 7 天;这是对旧脚本实际可能覆盖 8 个日期的修正。空 Superset 结果、无达标包或 AppsFlyer 合法空数组都是成功空结果(退出码 0),仍生成日期数组、同步清空 SQLite 窗口并可提交;HTTP、认证、响应格式、SQLite schema 或文件校验错误退出码 1,不覆盖正式文件。 SQLite 必须预先存在 `click_gap` 表,列类型为 `pid TEXT NOT NULL, dt TEXT NOT NULL, attr_platform TEXT NOT NULL, pkg_name TEXT NOT NULL, shareit_clicks REAL NOT NULL, af_clicks REAL NOT NULL, installs_ua REAL NOT NULL, clicks_diff REAL NOT NULL, clicks_diff_rate REAL, updated_at TEXT NOT NULL`,主键严格为 `(pid, dt, pkg_name)`。脚本不建表、不迁移;每次成功运行先删除窗口内 `shareit` 旧行再插入完整新结果,因此消失日期/业务键也会清零。 ## 手工运行 在 mac2 先确认依赖和登录环境(`pandas`、`requests`、`requests_toolbelt`、`af_browser`、`superset_login`): ```sh cd /Users/shareit./Library/CloudStorage/OneDrive-个人/ubuntu/Gitea/af-click-gap python3 -m py_compile scripts/af_click_gap_pipeline.py python3 scripts/af_click_gap_pipeline.py --help python3 scripts/af_click_gap_pipeline.py --dry-run # 正式写入并提交、push(默认行为) python3 scripts/af_click_gap_pipeline.py --push # 只写 data 和 SQLite,不操作 Git python3 scripts/af_click_gap_pipeline.py --no-push # 手工补数,日期首尾包含且最多 7 天 python3 scripts/af_click_gap_pipeline.py --start-date 2026-10-10 --end-date 2026-10-16 --no-push ``` `--dry-run` 只生成仓库外临时 JSON 并校验,不写 data、数据库或执行 Git。正式文件先原子替换,再提交 SQLite,最后才 `git add/commit/push`;数据库提交失败时不执行 Git。commit/push 失败会保留本地文件和数据库,下一次可重跑或手工 `git add data index.html ... && git commit && git push`;不会伪造认证或 force push。工作区在 push 模式有非本任务改动时拒绝继续。 ## 看板 直接打开 `index.html` 或运行 `python3 -m http.server` 后访问。页面读取并校验 `data/index.json` 及清单路径,只执行安全 JSON 解析;缺失/损坏文件显示“数据不完整”并跳过该文件,清单本身失败则不展示数据。默认最近 30 天,可选有效开始/结束日期和 `pkg_name`;默认 `dt` 降序、同日期 `shareit_clicks` 降序,点击表头切换排序。 ## mac2 launchd `launchd/com.roasbloom.af-click-gap.plist.template` 明确为 `TEMPLATE_ONLY`。当前开发机不能证明自己是 mac2,因此没有伪造最终 plist、解释器或安装结果。必须在 mac2 执行并记录: ```sh hostname command -v python3 python3 --version python3 -c 'import pandas,requests,requests_toolbelt,af_browser,superset_login; print("dependencies ok")' cd /Users/shareit./Library/CloudStorage/OneDrive-个人/ubuntu/Gitea/af-click-gap mkdir -p "$HOME/Library/Logs/af-click-gap" PY=$(command -v python3); sed -e "s#__MAC2_PYTHON3__#$PY#" -e "s#__PROJECT_DIR__#$PWD#g" -e "s#__LOG_DIR__#$HOME/Library/Logs/af-click-gap#g" launchd/com.roasbloom.af-click-gap.plist.template > /tmp/com.roasbloom.af-click-gap.plist plutil -lint /tmp/com.roasbloom.af-click-gap.plist cp /tmp/com.roasbloom.af-click-gap.plist "$HOME/Library/LaunchAgents/" launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.roasbloom.af-click-gap.plist" launchctl print "gui/$(id -u)/com.roasbloom.af-click-gap" # 卸载:launchctl bootout "gui/$(id -u)/com.roasbloom.af-click-gap"; rm "$HOME/Library/LaunchAgents/com.roasbloom.af-click-gap.plist" ``` 定时任务失败不会触发成功 push;stdout/stderr 位于 mac2 用户日志目录。Git remote、SSH/HTTPS credential helper 或 Gitea token 必须由用户在 mac2 预先配置。若 `git remote -v` 为空,请按组织 Git 文档创建仓库并绑定 remote 后再运行;本项目不会猜测仓库名称或修改 git config。 ## 安全和验证 允许提交的路径仅为脚本、README、看板、`data/*.json`、`data/index.json`、模板和 `.gitignore`。提交前禁止 `.env*`、SQLite、日志、浏览器状态,脚本还扫描 `cookie`、`authorization`、`password`、`api-key`、`token` 文本。建议验证:`python3 -m py_compile ...`、`--help`、mac2 上 `--dry-run`、`python3 -m json.tool data/index.json`,以及浏览器/静态 HTTP 打开页面。当前环境未验证 mac2 依赖、固定数据库、外部登录、launchd 安装和远程 push;这些不能以本机结果冒充。不要新增测试套件。