Files
cma-management/gen_report.py
T
Hermes CI Fix 40d522cd34 fix(security): retire default-credential surface (yanxue-20260914-cma-defaultcred-code-fix)
- Login.vue: no prefilled default creds; no anonymous entity enumeration on mount

- deploy.sh: readiness polling (0.5s/30s cap) replaces fixed sleep 3; login smoke reads same-source 600 secret file, missing file = explicit abort

- 19 verify/check/gen_report scripts: shared _admin_creds() loader (CMA_ADMIN_ENV -> ops .secrets -> profile copy); no hardcoded creds

- tests: single TEST_USER_PASSWORD fixture constant; docs: pointer to 600 secret file; removed 4 legacy .bak + pycache residue
2026-09-14 17:39:53 +08:00

209 lines
9.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""汇总管理会计OS管理能力报告"""
import json, urllib.request
import os # noqa: E402 (凭据加载器需要)
# ---- 管理员凭据同源加载(2026-09-14 cma-defaultcred-code-fix)----
# 生产凭据只存 600 密钥文件;禁止在脚本/文档/日志里硬编码口令。
# 顺序:$CMA_ADMIN_ENV → wecom-ops 主副本(ops 轮换落点,权威) → 本 profile 副本。
def _admin_creds():
"""返回 (user, password, 来源文件);未找到 → 显式报错退出(不静默退回默认口令)"""
for p in (os.environ.get("CMA_ADMIN_ENV"),
"/root/.hermes/profiles/wecom-ops/.secrets/cma-admin.env",
"/root/.hermes/profiles/wecom-fullstack/.secrets/cma-admin.env"):
if p and os.path.isfile(p):
kv = {}
for line in open(p, encoding="utf-8"):
line = line.strip()
if "=" in line and not line.startswith("#"):
k, v = line.split("=", 1)
kv[k.strip()] = v.strip().strip('"').strip("'")
if kv.get("CMA_ADMIN_USER") and kv.get("CMA_ADMIN_PASS"):
return kv["CMA_ADMIN_USER"], kv["CMA_ADMIN_PASS"], p
raise SystemExit("[错误] 未找到 CMA 管理员凭据:请设 $CMA_ADMIN_ENV,或准备 600 权限密钥文件 "
".secrets/cma-admin.env(键名 CMA_ADMIN_USER / CMA_ADMIN_PASS)")
ADMIN_USER, ADMIN_PASS, CRED_SRC = _admin_creds()
print(f"[creds] 来源={CRED_SRC} 账号={ADMIN_USER}(口令不回显)")
BASE = "http://127.0.0.1:8010"
def login():
data = json.dumps({"username":ADMIN_USER,"password":ADMIN_PASS}).encode()
req = urllib.request.Request(BASE + "/api/cma/auth/login", data=data,
headers={"Content-Type":"application/json"}, method="POST")
resp = json.loads(urllib.request.urlopen(req).read())
return resp["token"]
def api(path, token):
auth = "Bearer " + token
req = urllib.request.Request(BASE + path, headers={"Authorization": auth})
resp = json.loads(urllib.request.urlopen(req).read())
return resp
token = login()
report = {
"战略管理": {
"战略地图": {
"状态": "✅ 已实现",
"数据": api("/api/cma/maps", token).get("data", [{}])[0].get("title", "未知"),
"说明": "1张已发布的战略地图,支持创建/编辑/发布",
"接口": ["GET/POST/PUT /api/cma/maps"],
},
"KPI字典": {
"状态": "✅ 已实现",
"数据数": len(api("/api/cma/kpis", token).get("data", [])),
"维度分布": str(api("/api/cma/dashboard/summary", token).get("dimension_stats", [])),
"说明": "10个KPI覆盖4维度(财务4/客户3/流程2/学习1),含阈值配置",
"接口": ["CRUD /api/cma/kpis", "阈值建议 GET /api/cma/thresholds/suggest/{id}"],
},
},
"驾驶舱": {
"多角色视图": {
"状态": "✅ 已实现",
"角色": ["CEO(仅读+审批)", "财务(读写导入导出)", "业务(读写)", "IT(读写删除管理)"],
"模块数": {"ceo":10, "finance":7, "business":4, "it":7},
},
"数据展示": {
"状态": "✅ 已实现",
"KPI总数": api("/api/cma/dashboard/summary", token).get("kpi_total"),
"预警数": api("/api/cma/dashboard/summary", token).get("alert_count"),
"布局": "双栏布局(2/3主数据+1/3预测面板),异常高亮+摘要卡片",
},
"AI分析": {
"状态": "✅ 已实现",
"功能": "驾驶舱AI分析(SSE流式)、单KPI深度分析、问答、计划审核",
"调用": "DeepSeek API",
},
"预测": {
"状态": "✅ 已实现",
"接口": "GET /api/cma/dashboard/predict — 基于历史数据预测下期",
},
},
"数据管理": {
"ERP接入": {
"状态": "✅ 已实现",
"数据源": "1个(测试ERP),通过erp-api-gateway(8300)连接SQL Server",
"引擎": "calc_engine.py 从科目余额表+销售报表计算KPI",
},
"Excel导入": {
"状态": "✅ 已实现",
"接口": "POST /api/cma/data/import-excel",
},
"数据源管理": {
"状态": "✅ 已实现",
"接口": "CRUD /api/cma/data/sources",
},
"定时同步": {
"状态": "✅ 已实现",
"cron": "每日凌晨1:00执行 daily_sync.py (ERP同步→预警检查→预警推送)",
},
},
"预警管理": {
"预警规则": {
"状态": "✅ 已实现",
"规则数": len(api("/api/cma/alert-rules", token).get("data", [])),
"说明": "每个KPI配绿/黄/红三级阈值,支持>=/<=/>/</=条件",
},
"预警记录": {
"状态": "✅ 已实现",
"记录数": len(api("/api/cma/alerts", token).get("data", [])),
"当前待处理": sum(1 for a in api("/api/cma/alerts", token).get("data", []) if a.get("status")=="pending"),
"处理流程": "预警→待处理→处理/解决闭环",
},
"行动计划": {
"状态": "✅ 已实现",
"计划数": len(api("/api/cma/action-plans", token).get("data", [])),
"接口": "CRUD /api/cma/action-plans",
},
},
"通知推送": {
"渠道配置": {
"状态": "✅ 已实现",
"渠道数": len(api("/api/cma/notifications/channels", token).get("data", [])),
"支持类型": ["企业微信群机器人(wecom)", "企微应用消息(wecom_app)", "邮件(mail)"],
"配置方式": "前端页面可视化配置,不重新部署",
},
"自动推送": {
"状态": "⚠️ 已实现有待优化",
"说明": "预警生成时自动推送到已配置渠道,但预警自动生成需等待daily_sync定时任务触发",
"紧急推送": "通过 POST /api/cma/admin/alerts/check 可手动触发预警检查+推送",
},
"AI经营简报": {
"状态": "✅ 已实现",
"cron": "每日凌晨1:05由cron触发,DeepSeek生成简报→企微推送",
},
},
"用户与权限": {
"认证": {
"状态": "✅ 已实现",
"用户数": 7,
"方式": "JWT Token认证,支持登录/注册",
},
"角色权限": {
"状态": "✅ 已实现",
"管理方式": "前端页面可视化配置(角色权限页),修改后实时生效",
"权限维度": "模块权限(每个角色能访问哪些页面)+ 操作权限(读/写/导入/导出/删除/审批/管理)",
"数据源": "数据库持久化,不依赖前端代码hardcode",
},
},
"基础设施": {
"部署": {
"前端": "Vue3 + Element Plus → Nginx → cma.sxbh.ltd (HTTPS/SSL)",
"后端": "FastAPI(Uvicorn 2workers) → systemd自动重启 → 端口8010",
"数据库": "MariaDB(Docker) + Redis(Docker, db1分配CMA)",
"ERP网关": "erp-api-gateway(端口8300, systemd服务)",
},
"自动化": {
"cron任务": "每日01:00 ERP同步+预警检查, 01:05 AI简报推送",
"健康检查": "5分钟一次 health_watchdog.sh",
},
"日志": {
"后端": "journald (systemd)",
"同步日志": "/var/log/cma-daily-sync.log",
"AI简报": "/var/log/cma-ai-brief.log",
},
},
"待完善项": {
"⑦ 预警通知推送": {
"现状": "通知模块已完整实现(notifier.py+前端配置页),但预警生成是定时触发的",
"改进建议": "增加KPI数据更新时实时预警检查+推送,降低通知延迟",
"优先级": "P2",
},
"⑧ 移动端适配": {
"现状": "MainLayout有基础响应式(768px断点折叠侧边栏),但主要内容的卡片/表格在手机端布局需完善",
"改进建议": "Dashboard/ECharts图表自适应、表格响应式转卡片列、表单小屏适配",
"优先级": "P2",
},
},
}
# 打印报告
print("=" * 66)
print(" 管理会计OS — 管理能力全景报告")
print(" 生成时间: 2026-05-27")
print("=" * 66)
for category, modules in report.items():
print("\n" + "─" * 66)
print(" 📋 %s" % category)
print("─" * 66)
for name, info in modules.items():
status = info.get("状态", "")
status_icon = "✅" if "已实现" in status else ("⚠️" if "有待优化" in status else "❌")
print("\n %s %s" % (status_icon, name))
for k, v in info.items():
if k == "状态":
continue
val = str(v)
if len(val) > 100:
val = val[:97] + "..."
print(" %-12s %s" % (k, val))
print("\n" + "=" * 66)
print(" 总结:共10大能力模块, 除2项P2待完善外, 其余全部完成")
print(" 网址: https://cma.sxbh.ltd")
print("=" * 66)