feat(账号): 账号台账(web 页 + 任务取号 + 设备端身份页);fix(剪贴板): 注入通道改走设备端 Agent
一、账号台账(新表 device_account,schema v8) - core/ledger.py:CRUD、Excel 粘贴解析(Tab 分隔 / 表头乱序缺列 / 是·否布尔 / 逐行留痕)、 按范围取号(本机台账 / 全部 / 按设备分组)、设备端账号块、dry_run 预览 - 新「账号」顶级 Tab(权限 devices):列表 + 搜索 + 设备筛选 + 增删改 + 粘贴导入(默认先预览再写,逐行显示 新增/覆盖/跳过/失败 + 留痕) - 设备池加「账号 N」列(0 个显示"未登记"),点开看该设备账号明细 - 任务「条件判断」新增 cmp_source / cmp_group:候选值可直接来自台账, 与手填值**合并(OR)**;取不到号时回落手填值,并把原因写进步骤明细与日志 (否则"永远走 else 分支"而任务照样显示成功,最难查) - 设备端契约:身份页多推 accounts_b64(base64 JSON;默认不含手机号; 超预算按整条丢且绝不算字节切),见 doc/DEVICE_AGENT.md §5.2.1 - 备份/文档红线:TABLE_LABELS 加「账号台账」;DATA_MODEL/API/ARCHITECTURE/DEPLOY/ TASK_DEV/DEVICE_AGENT/DEVELOPMENT/README 同步;顺手补上 DATA_MODEL 漏列的 done_mark ⚠ 台账里的抖音号是**纯号**,只当"比对用的候选值":绝不能拿去填「去重」的身份元素 (身份是元素原文逐字算 key,格式不同会让去重静默失效)。代码与文档都写明了。 二、剪贴板注入通道(修:平台还在调早期的独立 APK) - 改为按顺序尝试:设备端 Agent(com.example.deviceagent/.ClipActivity)→ 旧版独立 ClipInject 兜底;两个都没有时报"需要设备端 Agent"(不再只说 ClipInject) - 读回验证改成**轮询到 3 秒**:透明 Activity 要等窗口拿到焦点才写, 原来只睡 0.5s 会读到上一次的内容 → 误报"写入可能被拒"(实测内容已写入却回失败)
This commit is contained in:
+60
-2
@@ -17,7 +17,9 @@
|
||||
|
||||
管理端接口在文件后半段(`/api/agent-store/*`,走正常登录 + 权限)。
|
||||
"""
|
||||
import base64
|
||||
import hmac
|
||||
import json
|
||||
import os
|
||||
import secrets
|
||||
import time
|
||||
@@ -25,7 +27,7 @@ import time
|
||||
from flask import Blueprint, jsonify, request, send_file
|
||||
|
||||
from config import APK_DIR
|
||||
from core import device_pool
|
||||
from core import device_pool, ledger
|
||||
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
|
||||
@@ -317,6 +319,32 @@ 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 回给操作者。
|
||||
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 会把大字页开在锁屏**后面**,
|
||||
# 人看到的还是一片黑(实测踩过)——这正是原来"定位"按钮里的"点亮屏幕"那一步
|
||||
@@ -339,7 +367,37 @@ def api_agent_show_info():
|
||||
"error": "这台设备没装设备 Agent(com.example.deviceagent)——"
|
||||
"可以从「应用管理 → 设备端应用商店」装,或用扫码配置"}), 400
|
||||
_log.info("设备端 Agent %s: %s", "关闭身份页" if close else "打开身份页", serial)
|
||||
return jsonify({"ok": True, "msg": "已关闭" if close else "已让设备显示身份信息"})
|
||||
resp = {"ok": True, "msg": "已关闭" if close else "已让设备显示身份信息"}
|
||||
if not close and warn:
|
||||
resp["warn"] = warn # 前端用 toast 提示(同二维码接口的 warn 惯例)
|
||||
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"])
|
||||
|
||||
Reference in New Issue
Block a user