fix: 备份覆盖补全(动作库/系统配置入清单)+ 覆盖自检与未登记表告警;发布流程/红线文档更新

问题(2026-09-10 用户反馈):动作库"没有备份"。实测导出 zip 里 agent_action 数据其实在
(导出是 users.db 全库快照),但清单/预览没列它 → 看起来像没备份。同类还有 app_meta。

修复(core/system_backup.py):
- SUMMARY_TABLES 补 agent_action(动作库) 与 app_meta(系统配置) + 中文标签;
- 导出侧**覆盖自检**:登记表若在快照缺失 → manifest.coverage_missing + 日志告警;
- 导入侧**反向自检**:备份含未登记表 → 预览告警提示登记(extra_tables);
- 顶部注释写明新增持久化表必须登记(红线)。

其余:
- monitor.html:数据备份面板文案改为明列全部业务表 + 指引(预览见表行数 / 新增表须登记);
- doc/DEPLOY.md §3.5:新增「备份覆盖清单(红线)」小节(含清单与两侧自检说明);
- doc/DEVELOPMENT.md:§5.6 增「备份覆盖红线」;§6 发布流程重写为分支流程
  (本机建分支 → 用户确认 → 合 dev → dev 整体就绪 → 合 main → 220 部署,附部署命令);
- doc/ARCHITECTURE.md §3.6:补备份覆盖登记提示。

实测:导出清单 12 表(含动作库 3 行、系统配置 8 行),coverage_missing 空;上传预览同样显示、
extra_tables 空。
This commit is contained in:
2026-09-10 21:03:41 +08:00
parent 4b5b836d31
commit d40c867d6f
5 changed files with 65 additions and 12 deletions
+21 -2
View File
@@ -38,16 +38,21 @@ DB_FILE = os.path.join(DATA_DIR, "users.db")
# 判定「本平台备份库」的必需表(缺任何一张即拒绝导入)
REQUIRED_TABLES = ("app_meta", "user", "task_job", "device_group")
# 预览/清单里展示行数的业务表(存在才统计)
# ⚠️ 红线:**新增任何持久化表,必须同步登记到这里**——备份覆盖清单以此为准,
# 漏登记的表在导出清单/预览里不可见(2026-09-10 动作库 agent_action 即因此
# 被误判为"没有备份":数据其实在 users.db 快照里,只是清单没列)。
SUMMARY_TABLES = (
"user", "device_group", "task_job", "custom_action",
"app_meta", "user", "device_group", "task_job", "custom_action",
"apk_file", "device", "pending_device",
"agent_conversation", "agent_experience", "experience_audit",
"agent_conversation", "agent_experience", "experience_audit", "agent_action",
)
TABLE_LABELS = {
"app_meta": "系统配置(app_meta)",
"user": "用户", "device_group": "设备分组", "task_job": "任务计划",
"custom_action": "自定义动作", "apk_file": "APK 记录", "device": "设备池",
"pending_device": "待连接设备", "agent_conversation": "AI 会话",
"agent_experience": "经验库", "experience_audit": "经验巡检",
"agent_action": "动作库",
}
_STAGE_TTL = 1800 # 导入暂存有效期(秒)
@@ -193,6 +198,13 @@ def create_export(include_apk=True):
"tables": _summary_info(con),
"apks": apk_meta,
}
# 覆盖自检:登记在册的业务表若在快照里缺失(新增功能忘了登记 / 建表失败),
# 显式告警并写进 manifest——避免"以为备份了其实没有"(备份覆盖红线)。
_present = {t["table"] for t in manifest["tables"]}
_missing = [t for t in SUMMARY_TABLES if t not in _present]
if _missing:
manifest["coverage_missing"] = _missing
_log.warning(f"备份覆盖检查:以下登记表未纳入快照,请确认是否应备份: {_missing}")
finally:
con.close()
@@ -241,6 +253,12 @@ def validate_backup(db_file):
warnings.append("备份缺少部分可选表(" + "、".join(
TABLE_LABELS.get(t, t) for t in missing_optional)
+ "),应用后启动会自动补建空表")
# 反向自检:备份里有"未登记"的表 → 提醒把它纳入覆盖清单(红线)
extra_tables = sorted(t for t in tables
if t not in SUMMARY_TABLES and not t.startswith("sqlite_"))
if extra_tables:
warnings.append("备份含未登记的其它表(" + "、".join(extra_tables)
+ ")——如属业务数据,请登记进 core/system_backup.py 的覆盖清单")
warnings.append("备份为全量快照:含用户口令哈希、AI 配置里的 API Key 等敏感信息,请妥善保管")
return True, {
"integrity": integrity,
@@ -248,6 +266,7 @@ def validate_backup(db_file):
"current_schema_version": CURRENT_SCHEMA_VERSION,
"tables": _summary_info(con),
"missing_optional": missing_optional,
"extra_tables": extra_tables,
"warnings": warnings,
}
except sqlite3.Error as e: