本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交 (5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。 一、docs 结构整改(整改 #14) 根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入, 随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录 树并存于 docs/,共 791 文件、双分类体系冲突。 修复动作: - b2 同名异主题文件改名迁移保全 9 个 - C 类 39 个孤立文件按主题正确归类 - A/B1 类 222 个重复文件删除(新结构已有内容副本) - 9 个旧独有空目录删除 - 270 处内部引用按 verified 映射改写 - 整改记录 #14 登记于 04-运维文档/部署运维 结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。 残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。 二、compose 双目录对齐(消除踩坑 A) - docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist 改为 src/frontend-*/dist(h5 / agent / admin / terminal) - docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/ - 效果:本地 docker compose up 不再把根目录 stale dist 挂回, 与线上一致,分叉隐患消除(已 docker compose config 校验通过) 防复发铁律: - 重构须提交;仓库修复须 git stash -u 或先 commit - 新结构须 git add 并提交,避免再次 untracked 复活 - H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
26 KiB
管理后台日志体系 — 系统设计 + 任务分解
阶段:standard SOP 第二阶段(架构设计,输入给工程师 寇豆码) 架构师:高见远(software-architect)|日期:2026-07-09 上游输入(已 Read 并核对):
- 产品+设计文档:
docs/04-功能设计/系统日志与审计日志-产品与设计.md(许清楚)- PRD 增补:
01-产品文档/08-集成生态/PRD-REQ-集成-002-管理后台-v1.2.md§「管理后台日志体系」- 架构增补:
docs/02-技术文档/技术架构/IT智能服务台-系统架构设计文档v2.md§7.1.1 日志体系- 运行期日志设计:
docs/06-安全审计/审计报告-安全审计/健康检查+错误码+日志结构化.md§3- 真实代码:
frontend-admin/src/{components/Sidebar.vue,router/index.ts,views/SystemLogs.vue,api/admin.ts}、backend/app/{api/admin_api.py,api/audit_logs.py,api/router.py,services/admin_service.py,services/audit_log_service.py,models/config_change_log.py,models/audit_log.py,models/automation.py,utils/logging_config.py}、docker-compose.yml
设计原则:沿用现有技术栈,不新建表(A/B/D 均复用既有存储),不引入新 npm/PyPI 依赖;D 后端方案必须可部署、可执行。
一、设计要点摘要
| 项 | 结论 |
|---|---|
| 技术栈 | 前端 Vue3 + TS + Element Plus + Pinia(沿用);后端 FastAPI + SQLAlchemy(async) + PostgreSQL(沿用) |
| A 配置变更历史 | 复用 config_change_logs;后端 GET /api/admin/system-logs 增 config_key/changed_by/from/to 过滤;前端 SystemLogs.vue 加筛选控件;侧边栏改名「配置变更历史」 |
| B 安全审计日志 | 后端已存在 GET /api/admin/audit-logs(权限 audit_log:read:all,参数 employee_id/action/resource/from/to/page/page_size);本次仅前端新建页面 + API 函数,后端零改动 |
| C 自动化动作日志 | 本次不做管理后台可视化(P2,标注后续) |
| D 运行期结构化日志 | 新建前端页 + 后端 GET /api/admin/runtime-logs(级别/时间/关键字筛选 + 下载);落地方案:应用层 JSON 文件日志 + docker-compose 宿主机目录 bind mount(不挂 docker.sock) |
| 双写边界(决策2) | 不改代码,T5 做回归验证:PUT /api/admin/configs/{key} 同时写 config_change_logs 与 audit_logs(config_change 事件) |
| 权限 | A 沿用 require_admin(建议后续命名为 config_log:read,本迭代不纳入 RBAC 重构);B 沿用 audit_log:read:all;D 用 require_admin |
| 新增依赖 | 无(前端无新 npm 包;后端用标准库 pathlib/os/glob + logging.RotatingFileHandler + fastapi.responses.StreamingResponse) |
二、实现方案与框架选型
难点与对策
- A 增加筛选而不改表/不加索引:
config_change_logs已有idx_ccl_config_key、idx_ccl_changed_at索引;changed_by无索引,但配置变更频率低、数据量小,用WHERE changed_by = :v(或 JOIN agents 按姓名匹配)可接受,短期不新增索引(见「待明确」)。 - B 零后端改动:后端
audit_logs.py完整可用,前端补齐页面与getAuditLogs即可。 - D 容器内读日志(核心难点):见第六节,采用「应用写文件 + bind mount」而非 docker.sock。
- 命名歧义消除:侧边栏/路由将「系统日志」拆为「配置变更历史(A)」与新增「安全审计日志(B)」「运行期日志(D)」。
架构模式:前端 视图组件 + API 函数(axios);后端 路由层(FastAPI Router)→ 服务层(service function)→ 数据层(SQLAlchemy / 文件系统)。A/B 走 DB,D 走文件系统。
三、文件列表(标注 新增 / 修改)
前端(frontend-admin/)
| 文件 | 类型 | 说明 |
|---|---|---|
src/components/Sidebar.vue |
修改 | 「监控与数据」分组:将原 系统日志 改名为 配置变更历史;新增 安全审计日志(/audit-logs)、运行期日志(/runtime-logs) 两个入口 |
src/router/index.ts |
修改 | A 路由 meta.title → 配置变更历史;新增 audit-logs→AuditLogs.vue、runtime-logs→RuntimeLogs.vue |
src/views/SystemLogs.vue |
修改 | A 页:新增「配置键 / 操作人 / 时间」筛选控件,调用 getSystemLogs 新参数 |
src/views/AuditLogs.vue |
新增 | B 页:调用 getAuditLogs(级别/操作人/动作/资源/时间筛选 + 分页) |
src/views/RuntimeLogs.vue |
新增 | D 页:级别/时间/关键字筛选 + 分页 + 下载按钮 |
src/api/admin.ts |
修改 | getSystemLogs 增参;新增 getAuditLogs / getRuntimeLogs / getRuntimeLogDownload |
后端(backend/app/)
| 文件 | 类型 | 说明 |
|---|---|---|
api/admin_api.py |
修改 | GET /system-logs 增加 config_key/changed_by/from/to Query 参数并下传 |
services/admin_service.py |
修改 | get_system_logs 增加过滤参数与 WHERE 构造(函数位于文件末尾 ~L1689) |
api/runtime_logs.py |
新增 | 路由前缀 /admin/runtime-logs:GET /runtime-logs(列表 + download=true 下载) |
services/runtime_log_service.py |
新增 | 读 RUNTIME_LOG_DIR 下 *.log,逐行 JSON 解析、按 level/time/keyword 过滤、分页、生成下载流 |
utils/logging_config.py |
修改 | setup_logging 支持 log_dir,新增 RotatingFileHandler(JSON 格式)写入 RUNTIME_LOG_DIR,保留 stdout handler |
core/config.py |
修改 | 新增配置项 RUNTIME_LOG_DIR(默认 /app/logs) |
api/router.py |
修改 | 挂载 runtime_logs_router(与 audit_logs_router 一致,~L243-244 后追加) |
docker-compose.yml |
修改 | backend 服务 volumes 增加 ${RUNTIME_LOG_HOST_DIR:-/var/log/wecom-it-desk}:/app/logs;environment 增加 RUNTIME_LOG_DIR=/app/logs、LOG_FORMAT=json |
注:
api/audit_logs.py、models/audit_log.py、services/audit_log_service.py、models/config_change_log.py、schemas/admin.py(ConfigHistoryItem) 本次均不修改,直接复用。
四、接口设计(含请求/响应 JSON 示例)
统一响应体:
{ "code": 0, "message": "success", "data": {...} };错误code != 0。 时间参数统一 ISO8601(如2026-07-09T00:00:00+08:00或2026-07-09T16:00:00Z)。
① A — 配置变更历史(修改已有)
请求
GET /api/admin/system-logs
Query:
page int = 1
page_size int = 50 (<=200)
config_key string? 配置键精确/模糊匹配
changed_by string? 操作人 agent_id(前端用坐席下拉取 id 传入)
from string? ISO8601 起始时间(alias from_time)
to string? ISO8601 结束时间(alias to_time)
响应
{
"code": 0,
"message": "success",
"data": {
"items": [
{
"id": "3f1a...",
"log_type": "config_change",
"config_key": "auto_reply_enabled",
"old_value": "false",
"new_value": "true",
"changed_by": "a-1001",
"changed_by_name": "张明",
"changed_at": "2026-07-09T14:32:10+08:00"
}
],
"total": 128,
"page": 1,
"page_size": 50
}
}
② B — 安全审计日志(复用后端,仅前端对接)
请求(后端已支持,无需改动)
GET /api/admin/audit-logs
Query:
employee_id string?
action string? 如 login / config_change / role_change
resource string? 如 agent / system_config / conversation
from string? ISO8601(alias from_time)
to string? ISO8601(alias to_time)
page int = 1
page_size int = 50 (<=500)
Header/Permission: audit_log:read:all
响应(节选)
{
"code": 0,
"message": "success",
"data": {
"items": [
{
"id": "9b2c...",
"employee_id": "a-1001",
"action": "config_change",
"resource": "system_config",
"resource_id": "auto_reply_enabled",
"details": { "old": "false", "new": "true", "ip": "10.80.0.5" },
"result": "success",
"ip_address": "10.80.0.5",
"user_agent": "Mozilla/5.0 ...",
"created_at": "2026-07-09T14:32:10+08:00"
}
],
"total": 5321,
"page": 1,
"page_size": 50
}
}
③ D — 运行期结构化日志(新增)
列表请求
GET /api/admin/runtime-logs
Query:
level string? 枚举 DEBUG/INFO/WWARNING/ERROR/CRITICAL(按 >= 阈值语义过滤)
from string? ISO8601 起始时间(按日志 timestamp 字段)
to string? ISO8601 结束时间
keyword string? 子串匹配(message 或整行,支持 trace_id / employee_id 检索)
page int = 1
page_size int = 100 (可 20/50/100)
download bool = false 为 true 时返回文件流(忽略分页)
Permission: require_admin
列表响应
{
"code": 0,
"message": "success",
"data": {
"items": [
{
"timestamp": "2026-07-09T06:32:10Z",
"level": "ERROR",
"logger": "app.api.admin_api",
"message": "request_start: POST /api/admin/configs/...",
"module": "admin_api",
"function": "get_system_logs",
"line": 902,
"request_id": "req-abc-123",
"user_id": "a-1001"
}
],
"total": 342,
"page": 1,
"page_size": 100
}
}
下载响应(download=true)
HTTP 200
Content-Type: text/plain; charset=utf-8
Content-Disposition: attachment; filename="runtime-logs-20260709T143200Z.log"
Body: 每行一条原始 JSON 日志(已按筛选条件命中)
实现用
StreamingResponse(iter([...]), media_type="text/plain", headers={...}),逐块吐出,避免大文件占内存。
五、数据模型 / 字段确认
| 类 | 落点 | 字段(关键) | 本次是否改 |
|---|---|---|---|
A ConfigChangeLog |
config_change_logs |
config_key, old_value, new_value, changed_by, changed_at |
不改;复用 ConfigHistoryItem 响应 schema |
B AuditLog |
audit_logs |
employee_id, action, resource, resource_id, details, result, ip_address, user_agent, created_at |
不改 |
C ActionLog |
auto_action_logs |
session_id, event, direction, system, request, response, status, latency_ms, error, created_at |
不改(本次不暴露) |
| D 运行期日志 | 文件系统(不建表) | 每行 JSON:timestamp, level, logger, message, module, function, line, [request_id], [user_id], [extra] |
由 logging_config.py 写出 |
D 不新建数据库表,符合 PM 决策(D 是应用运行 stdout 日志的「查看」,非新业务数据)。
六、D 后端落地方案(重点 · 可部署)
6.1 结论
采用「应用层 JSON 文件日志 + docker-compose 宿主机目录 bind mount」方案。
- 后端容器内部:扩展
logging_config.setup_logging,在现有 stdout handler 之外,新增一个RotatingFileHandler,用已有的JSONFormatter把结构化日志写入RUNTIME_LOG_DIR(容器内默认/app/logs/wecom-it-desk.log,轮转备份-1/-2...)。 - docker-compose:把宿主机目录(默认
/var/log/wecom-it-desk)bind mount 为容器内的/app/logs。 GET /api/admin/runtime-logs:直接读/app/logs/*.log文本文件,逐行json.loads,按level/from~to(比较timestamp)/keyword过滤,时间倒序,分页返回;download=true时以StreamingResponse返回命中行的文本流。
6.2 为什么不用 docker.sock / 不读 /var/lib/docker
| 备选方案 | 否决原因 |
|---|---|
挂载 /var/run/docker.sock 调 Docker API 读日志 |
❌ 安全风险(容器逃逸)、可移植性差(NAS/单机不应依赖 Docker API);本次明确禁止 |
读 /var/lib/docker/containers/<id>/*-json.log |
❌ 路径含随机容器 ID,每次 compose up 变化,无法稳定 bind mount;且 json-file 行格式需二次解析 {"log":...,"stream":...,"time":...} |
| 应用层写文件 + bind mount(选定) | ✅ 路径确定、格式可控(复用现有 JSONFormatter)、与 stdout 并存(stdout 仍由 docker json-file 捕获供 docker logs)、零新依赖、跨环境可移植 |
6.3 部署依赖(docker-compose.yml · backend 服务改动)
backend:
environment:
# 已有环境变量保留 ...
- RUNTIME_LOG_DIR=/app/logs # 新增:运行期日志写入目录(容器内)
- LOG_FORMAT=${LOG_FORMAT:-json} # 新增:确保 JSON 输出(D 端点按 JSON 解析)
- LOG_LEVEL=${LOG_LEVEL:-INFO}
volumes:
- backend-uploads:/app/uploads
- ${RUNTIME_LOG_HOST_DIR:-/var/log/wecom-it-desk}:/app/logs # 新增:宿主机日志目录 bind mount
- 宿主机目录不存在时 compose 自动创建(bind mount 语义)。
- 轮转:
RotatingFileHandler(maxBytes=20*1024*1024, backupCount=5),与现有 dockerlogging.max-size: "20m"对齐。 - 本地开发(非 compose,直接
uvicorn):RUNTIME_LOG_DIR默认/app/logs;若该目录不存在,端点需容错——返回{code:非0, message:"日志目录不可读"}或空列表,不抛 500(见共享知识)。
6.4 读取逻辑要点(给工程师)
- 遍历
RUNTIME_LOG_DIR下所有*.log(含轮转备份),按文件 mtime 倒序合并。 - 逐行
json.loads;解析失败行跳过(容错)。 level过滤:大小写不敏感,按「>= 阈值」语义(选ERROR→ 显示 ERROR/CRITICAL;选INFO→ 显示 INFO+WARNING+ERROR+CRITICAL;选DEBUG→ 全部)。from/to:取每条日志timestamp字段(现有JSONFormatter输出 UTCZ格式),解析为datetime后比较(兼容+08:00与Z)。keyword:对整行 JSON 文本(或message)做子串匹配,支持trace_id/employee_id检索。- 分页:收集命中行 → 时间倒序 → 切片(
page/page_size)。短期限制:全量读入内存再切片;若单文件超大,后续接 Loki(已在审计报告中规划)。 download=true:将命中行原样(每行一条 JSON)写入StreamingResponse,Content-Disposition文件名带时间戳。
七、权限设计
| 页面/接口 | 权限 | 现状 | 本次 |
|---|---|---|---|
A GET /api/admin/system-logs |
require_admin(admin 角色) |
已有 | 沿用;建议后续明确命名为 config_log:read(与 B 一并纳入 RBAC 梳理,本迭代不做) |
B GET /api/admin/audit-logs |
audit_log:read:all(admin/auditor) |
已有(require_permission) |
复用,零改动 |
D GET /api/admin/runtime-logs |
require_admin(admin 角色) |
新增 | 新增端点用 require_admin |
双写 PUT /api/admin/configs/{key} |
已有 | 维持 A+B 双写 | 不改(T5 验证) |
八、依赖包
- 前端:无新增 npm 包(沿用 Element Plus / axios / Pinia)。
- 后端:无新增 PyPI 包。仅用标准库
pathlib / os / glob+logging.RotatingFileHandler+fastapi.responses.StreamingResponse。
九、共享知识(跨文件约定)
- 日志级别枚举:
DEBUG < INFO < WARNING < ERROR < CRITICAL(D 的level参数按此序做 >= 阈值过滤)。 - 时间参数:统一 ISO8601(前端用
toISOString()或本地带时区;后端用datetime解析,兼容Z与+08:00)。 - 统一响应体:
{ code: int, message: string, data: any };成功code=0。 - D 日志目录:统一读
settings.RUNTIME_LOG_DIR(默认/app/logs),不在代码里硬编码绝对路径。 - D 容错:日志目录不存在 / 无可读文件时,列表返回空
items或友好错误码,禁止 500。 - 命名一致性:A 时间参数用
from/to(alias注入),与 B 的from/to命名保持一致(见「待明确」)。
十、待明确事项(遗留决策点)
- A 时间参数命名:PM 文档用
changed_from/changed_to,本设计对齐 B 采用from/to(alias 注入,对前端透明)。若坚持 PM 原文命名需回退——建议采用from/to。 - A
changed_by筛选粒度:按agent_id精确匹配(前端坐席下拉取 id);若要支持「按姓名搜」需 JOIN agents 表做ILIKE。本设计采用 agent_id 精确匹配,实现最简。 - A
changed_by缺索引:高数据量下建议后续加idx_ccl_changed_by;本次不强制。 - A 页面权限命名
config_log:read:本迭代不纳入 RBAC 重构,沿用require_admin;列入后续迭代。 - B 待补事件(敏感数据查询/角色变更/系统配置变更/异常登录 P1,API 调用统计 P2):本次不实现,仅保证接口与页面可扩展(后续迭代直接写
audit_logs即可被 B 页展示)。 - C 管理后台可视化:本次不做(P2),标注后续。
- E 与 B 衔接:阶段四「会话日志格式规范」落地后,再在 B 补对应审计事件;本次不实现。
- D
level语义:采用「>= 阈值」;若产品要求「精确匹配」需调整(影响前端下拉说明)。
十一、任务分解清单(有序 · 含依赖 · 验收要点)
颗粒度按团队主理人指定:T1 侧边栏+路由;T2 A 筛选;T3 B 页;#104(T4 旧名)D 页(前后端);T5 双写回归;T6 联调权限。
T1 — 侧边栏重命名 + 路由入口(A 改名、B/D 新增)
- 涉及文件:
frontend-admin/src/components/Sidebar.vue、frontend-admin/src/router/index.ts - 依赖:无
- 优先级:P1
- 验收要点:① 侧边栏「监控与数据」出现「配置变更历史(A)」「安全审计日志(B)」「运行期日志(D)」三个独立入口,无「系统日志」歧义统称;② 路由
audit-logs、runtime-logs已注册且meta.title正确;③ A 路由 title 改为「配置变更历史」。
T2 — A 页筛选(前端控件 + 后端参数)
- ���及文件:
frontend-admin/src/views/SystemLogs.vue、frontend-admin/src/api/admin.ts(改getSystemLogs)、backend/app/api/admin_api.py(改GET /system-logs)、backend/app/services/admin_service.py(改get_system_logs) - 依赖:T1
- 优先级:P1
- 验收要点:① 前端 A 页有「配置键/操作人/时间」三类筛选控件且与分页联动;② 后端
GET /api/admin/system-logs支持config_key/changed_by/from/to且过滤正确;③ 空筛选时等价原行为。
T3 — 新建 B 安全审计日志页(前端,复用后端)
- 涉及文件:
frontend-admin/src/views/AuditLogs.vue(新增)、frontend-admin/src/api/admin.ts(新增getAuditLogs) - 依赖:T1
- 优先级:P1
- 验收要点:① 页面可筛选
employee_id/action/resource/from/to并分页展示;② 调通既有GET /api/admin/audit-logs;③ 非 admin/auditor 角色被拦截(后端已保证)。
#104(T4 旧名) — D 运行期日志页(前端 + 后端,含部署改动,已落地)
- 涉及文件(均已落地,保留原路径便于回溯当时设计):
- 前端:
frontend-admin/src/views/RuntimeLogs.vue(已落地)、frontend-admin/src/api/admin.ts(已落地,新增getRuntimeLogs/getRuntimeLogDownload) - 后端:
backend/app/api/runtime_logs.py(已落地)、backend/app/services/runtime_log_service.py(已落地)、backend/app/utils/logging_config.py(已改)、backend/app/core/config.py(已改,新增RUNTIME_LOG_DIR)、backend/app/api/router.py(已改,挂载 router) - 部署:
docker-compose.yml(已改,backendvolumes增加${RUNTIME_LOG_HOST_DIR:-/var/log/wecom-it-desk}:/app/logs、environment增加RUNTIME_LOG_DIR=/app/logs+LOG_FORMAT=json)
- 前端:
- 依赖:T1
- 优先级:P0
- 验收要点:① admin 可按级别+时间+关键字筛选查看日志;② 可下载
.log/.txt文件流;③logging_config写入RUNTIME_LOG_DIR且 docker-compose 挂载生效;④ 非 admin 调接口返回 403;⑤ 目录不可读时不 500。
T5 — 双写回归验证(决策2,无代码改动)
- 涉及文件:无(验证
PUT /api/admin/configs/{key}既有行为) - 依赖:T2
- 优先级:P0
- 验收要点:① 修改任一配置项后,
config_change_logs与audit_logs(config_change事件) 均新增对应记录;② 两表记录数一致、内容可对应;③ 不在任一侧删除/合并。
T6 — 联调与权限核对
- 涉及文件:全链路(A/B/D 页面 + 接口)
- 依赖:T2、T3、#104(T5 可并行)
- 优先级:P0
- 验收要点:① A(admin)/B(audit_log:read:all)/D(admin) 权限分别核对通过;② 三个页面在管理后台真实跑通;③ 与 PM 文档 §5 五项决策验收标准逐条对齐。
十二、对 PM 文档遗留 5 问的回应
- D 后端聚合方案定稿 → 已定稿:应用层 JSON 文件日志 + docker-compose 宿主机目录 bind mount(见第六节)。不使用 docker.sock,不读
/var/lib/docker。这是短期可执行方案,中期可平滑切 Loki(D 端点抽象出RuntimeLogService,换实现不影响前端)。 - B 待补事件是否纳入本次 → 不建议纳入。敏感数据查询/角色变更/系统配置变更/异常登录(P1)、API 调用统计(P2) 列入后续迭代;本次仅保证 B 接口与页面可扩展(新事件直接写
audit_logs即被展示)。 - C 是否需管理后台可视化查询接口 → 本次不做(标注 P2)。
auto_action_logs已落表,后续如需可补GET /admin/auto-action-logs(权限待定)。 - A 页面权限命名 → 沿用
require_admin;建议后续明确为config_log:read并与 B 一并纳入 RBAC 梳理,本迭代不实现。 - E 与 B 衔接 → 阶段四「会话日志格式规范」落地后,再在 B 补对应审计事件;本次不实现。
附录 A — 类图(Mermaid)
classDiagram
direction LR
class ConfigChangeLog {
+String id
+String config_key
+Text old_value
+Text new_value
+String changed_by
+DateTime changed_at
}
class AuditLog {
+String id
+String employee_id
+String action
+String resource
+String resource_id
+JSON details
+String result
+String ip_address
+Text user_agent
+DateTime created_at
}
class RuntimeLogEntry {
+String timestamp
+String level
+String logger
+String message
+String module
+String function
+int line
+String request_id
+String user_id
}
class ConfigHistoryItem {
+String id
+String config_key
+String old_value
+String new_value
+String changed_by
+String changed_by_name
+DateTime changed_at
}
class AdminApi {
+GET /system-logs
}
class AuditApi {
+GET /audit-logs
}
class RuntimeApi {
+GET /runtime-logs
}
class AdminService {
+get_system_logs(db, config_key, changed_by, from, to, page, page_size)
}
class AuditLogService {
+list_audit_logs(db, employee_id, action, resource, from, to, page, page_size)
}
class RuntimeLogService {
+read_runtime_logs(dir, level, from, to, keyword, page, page_size)
+build_runtime_log_stream(dir, level, from, to, keyword)
}
class RuntimeLogStore {
<<filesystem>>
+RUNTIME_LOG_DIR/*.log
}
AdminApi ..> AdminService : 调用
AuditApi ..> AuditLogService : 调用
RuntimeApi ..> RuntimeLogService : 调用
AdminService ..> ConfigChangeLog : SELECT
AuditLogService ..> AuditLog : SELECT
RuntimeLogService ..> RuntimeLogStore : 读文件
ConfigHistoryItem <|.. ConfigChangeLog : 映射
RuntimeLogEntry <|.. RuntimeLogStore : 逐行解析
附录 B — 时序图(Mermaid)
sequenceDiagram
autonumber
participant FE as 前端页面
participant API as admin.ts
participant RT as FastAPI Router
participant SVC as Service
participant DB as PostgreSQL
participant FS as 日志文件(RUNTIME_LOG_DIR)
Note over FE,FS: A 配置变更历史(带筛选)
FE->>API: getSystemLogs({config_key, changed_by, from, to, page, page_size})
API->>RT: GET /api/admin/system-logs?...
RT->>RT: require_admin()
RT->>SVC: get_system_logs(db, filters)
SVC->>DB: SELECT ConfigChangeLog WHERE ... ORDER changed_at DESC
SVC->>DB: JOIN agents 取 changed_by_name
DB-->>SVC: rows
SVC-->>RT: {items, total, page, page_size}
RT-->>FE: {code:0, data}
Note over FE,FS: B 安全审计日志(复用后端)
FE->>API: getAuditLogs({employee_id, action, resource, from, to, page, page_size})
API->>RT: GET /api/admin/audit-logs?...
RT->>RT: require_permission(audit_log:read:all)
RT->>SVC: list_audit_logs(db, filters)
SVC->>DB: SELECT AuditLog WHERE ...
DB-->>SVC: rows
SVC-->>RT: {items, total, page, page_size}
RT-->>FE: {code:0, data}
Note over FE,FS: D 运行期日志(列表)
FE->>API: getRuntimeLogs({level, from, to, keyword, page, page_size})
API->>RT: GET /api/admin/runtime-logs?...
RT->>RT: require_admin()
RT->>SVC: read_runtime_logs(RUNTIME_LOG_DIR, filters)
SVC->>FS: 读 *.log 逐行 json.loads
FS-->>SVC: 命中行
SVC-->>RT: {items, total, page, page_size}
RT-->>FE: {code:0, data}
Note over FE,FS: D 运行期日志(下载)
FE->>API: getRuntimeLogDownload({level, from, to, keyword})
API->>RT: GET /api/admin/runtime-logs?download=true
RT->>RT: require_admin()
RT->>SVC: build_runtime_log_stream(RUNTIME_LOG_DIR, filters)
SVC->>FS: 读 *.log 过滤行
RT-->>FE: StreamingResponse(text/plain, attachment)
附录 C — 变更记录
| 版本 | 日期 | 变更说明 |
|---|---|---|
| v1.3 | 2026-07-09 | 初稿(高见远) |
| v1.3.1 | 2026-08-04 | 任务编号对齐:T4 → #104(T4 旧名),与项目状态看板 / 任务说明书保持一致;T4 段「涉及文件」由「(新增)」批量调整为「(已落地)」,反映代码已存在的现状;保留原文件路径与依赖信息便于回溯设计 |