feat(发布计划): 改按名字发(文件名 名字_日期_编号)+ 账号管理整页下线 + 自动切号一轮发完
发布计划不再绑设备、不再依赖账号台账:上传文件名 `名字_日期_编号`(标题行 `标题 || 正文_名字_日期_编号`),运行时读本机当前抖音昵称取计划;靠循环块 「一轮把本机待发的号跑完」自动切号,一直发到发完。 · 解析/配对换键:右锚定认名字(不许含下划线、不许纯数字 —— 不合法直接拒收并说清原因), 删掉三处"手机号必须命中台账"的校验,加索引 ix_video_plan_name_date(启动时补建) · 新步骤三个:读本机账号名字 / 切换账号(**按名字点控件**、切完回读校验、遇登录页停本设备)/ 循环本机待发的号(试过不回头、跑一轮没进展就换号、停止·超时·上限三个干净出口) · 发布骨架改 18 步:读名字 → 校验账号(cmp_source=account_target)→ 推送(**推送前重读昵称核对**) → 抖音流程 → 标记结果;push_release 的 account 默认 name · 账号管理整页下线:删 core/ledger.py(serial_of 迁到 core/device_pool.py)、6 个 /api/ledger*、 「账号」页与 ledger.js、设备池「账号」列、设备端账号块;发布计划升为一级页签; device_account 表与 42 行历史数据保留、仍在备份清单内(**停用不等于删数据**) · 人工入口「按名字找设备」:新增 core/account_probe.py(只读探测、跳过在跑任务的设备) + POST /api/video_plan/push_by_account,没设备登着的号逐条报出来; 推送时把"实际是谁发的"写进 posted_serial / posted_device_name · 通知:新增 task.account.switch_failed / task.account.need_login · schema v12(posted_*)/ v13(device.cur_account_name / cur_name_at) · 文档同步:TASK_DEV / API / DATA_MODEL / ARCHITECTURE / NOTIFY / DEVICE_AGENT / DEPLOY / DEVELOPMENT / README / .env.example(两个抖音控件选择器抓一次填 .env) · 新增 scripts/tests/:6 套用例 + run_all.py(临时库 + 假设备,不碰真实数据、不连真机)
This commit is contained in:
+3
-55
@@ -17,9 +17,7 @@
|
||||
|
||||
管理端接口在文件后半段(`/api/agent-store/*`,走正常登录 + 权限)。
|
||||
"""
|
||||
import base64
|
||||
import hmac
|
||||
import json
|
||||
import os
|
||||
import secrets
|
||||
import time
|
||||
@@ -27,7 +25,7 @@ import time
|
||||
from flask import Blueprint, jsonify, request, send_file
|
||||
|
||||
from config import APK_DIR
|
||||
from core import device_pool, ledger
|
||||
from core import device_pool
|
||||
from core.db_config import meta_get, meta_set
|
||||
from core.logger import get_logger
|
||||
from web.auth import PERM_APKS, PERM_DEVICES, perm_required
|
||||
@@ -319,32 +317,9 @@ def api_agent_show_info():
|
||||
"--es", "server", server]
|
||||
if ip:
|
||||
args += ["--es", "ip", ip]
|
||||
# 本机账号台账(可选块):身份页顺带显示"这台登的是哪几个号"。
|
||||
# **中文必须 base64**(§5.1:adb shell 直传中文/特殊字符会变形);
|
||||
# `_adb` 是 argv 形式、不过 shell,base64 里的 +/= 安全 —— 将来谁把
|
||||
# 这里改成 shell=True 就会静默坏掉,别改。
|
||||
# 台账里没有这台设备时**整个 extra 不发**(设备端"没有这个 extra"=
|
||||
# 本来就没登记,跟"发了但解不开"能区分开),原因随响应体的 warn 回给操作者。
|
||||
# 账号台账块(`accounts_b64`)随账号管理一起下线了:身份页只显示
|
||||
# 名称/地址/平台地址这些设备信息。老版本 Agent 收到"没有这个 extra"照常显示。
|
||||
warn = ""
|
||||
try:
|
||||
acc = ledger.agent_accounts(serial, device_name=name,
|
||||
include_phone=bool(data.get("include_phone")))
|
||||
if acc.get("accounts"):
|
||||
b64, acc = _encode_accounts(acc)
|
||||
if len(b64) + sum(len(a) for a in args) < _ARGS_CMD_MAX:
|
||||
args += ["--es", "accounts_b64", b64]
|
||||
_log.info("身份页附账号:%s 共 %s 个号(accounts_b64 %d 字节)",
|
||||
serial, acc.get("shown"), len(b64))
|
||||
else:
|
||||
warn = "账号台账过大,未随身份页下发"
|
||||
_log.warning("身份页未附账号:%s %s(%d 字节)", serial, warn, len(b64))
|
||||
else:
|
||||
warn = f"这台设备在账号台账里没有账号({acc.get('note') or '未匹配到设备名'})"
|
||||
_log.warning("身份页未附账号:%s %s", serial, warn)
|
||||
except Exception as e:
|
||||
# 台账坏了不能把"显示身份"这个动作弄坏:跳过该 extra,只回告警
|
||||
warn = f"账号台账读取失败:{e}"
|
||||
_log.warning("身份页附账号失败 %s: %s", serial, e)
|
||||
|
||||
# 先点亮屏幕、解除锁屏:设备息屏时 am start 会把大字页开在锁屏**后面**,
|
||||
# 人看到的还是一片黑(实测踩过)——这正是原来"定位"按钮里的"点亮屏幕"那一步
|
||||
@@ -373,33 +348,6 @@ def api_agent_show_info():
|
||||
return jsonify(resp)
|
||||
|
||||
|
||||
# accounts_b64 的预算:44 条 × ~90 字节 ≈ 4KB,base64 后 ≈ 5.3KB,余量充足。
|
||||
# 再加一道**命令行总长**闸(Windows CreateProcess 上限 ~32767)。
|
||||
ACCOUNTS_PAYLOAD_MAX = 20000
|
||||
_ARGS_CMD_MAX = 28000
|
||||
|
||||
|
||||
def _encode_accounts(acc):
|
||||
"""把账号块压成 base64(超预算**按整条丢**,永不按字节切)。
|
||||
|
||||
按字节切会产出破损 JSON —— 设备端解析失败只能整块丢掉,
|
||||
还不如少几条但能解开(`truncated`/`shown` 如实反映)。
|
||||
返回 (base64 文本, 修正后的块)。
|
||||
"""
|
||||
acc = dict(acc or {})
|
||||
rows = list(acc.get("accounts") or [])
|
||||
total = acc.get("total", len(rows))
|
||||
while True:
|
||||
block = dict(acc, accounts=rows, shown=len(rows),
|
||||
truncated=len(rows) < total)
|
||||
b64 = base64.b64encode(
|
||||
json.dumps(block, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
||||
).decode("ascii")
|
||||
if len(b64) <= ACCOUNTS_PAYLOAD_MAX or not rows:
|
||||
return b64, block
|
||||
rows = rows[:-1]
|
||||
|
||||
|
||||
@bp.route("/api/agent-store/config", methods=["GET", "POST"])
|
||||
@perm_required(PERM_APKS)
|
||||
def api_agent_store_config():
|
||||
|
||||
Reference in New Issue
Block a user