- “产品毛利率”参数行不再标记可编辑:说明页写明它是导出时的只读快照, 各行毛利率已写入计算明细 MarginRate 列,调整需在计算明细页按行修改 - 参数页新增完整维度键-显示名对照行(此前说明页提到“本页分组维度”但无字典) - 使用说明新增输入字典:Revenue/Cost/GrossInput/MarginRate/ListAmount 逐项列出 单位、来源字段、修改位置与缺失含义;新增固定结构边界行(哪些列/页不可改、 改结构需重新导出) - 测试:参数页全部“否”、每个可修改输入出现在计算明细表头并被汇总公式引用、 维度对照完整;LibreOffice 用例验证修改 MarginRate 后 lookup 毛利重算(本机跳过)
TraceIQ 财务分析工作台
响应式 Web 前端 + Django API + PostgreSQL。基于分享会话、医院财务 Excel 原始附件及 AG Grid 开发计划实现,当前交付是可运行、经过本地回归的核心业务版本,不等于全部需求完成或已获生产上线认证。缺口与上线门禁见 验收状态。
已实现
- XLSX / UTF-8 CSV 导入、双行表头、原始解释保留、字段语义确认、产品毛利率及自营产品范围配置。
- 原始文件私有存储、组织内文件 SHA-256 去重、导入事务、持久队列、PostgreSQL worker 崩溃恢复。
- 实际收入、成本、毛利、两种明确 ROI 公式、去重人数、客单价、毛利率、产品扣率、自营收入、升单率、原价客单价。
- 同比、环比、自定义等长月份范围;缺基期禁止比较,缺成本显示不可用,缺月份趋势断开。
- 总览、异常追踪、贡献分解、自定义下钻层级、面包屑、原始行证据分页、CSV / XLSX 证据导出。
- 个人设置保存/取消/预览与乐观锁;调查备注与状态持久化;账号启停、角色及科室范围、操作审计。
- 本地规则助手;可选 OpenAI Responses 结构化意图接口,用户同意后仅发送问题与维度/当前筛选,查询仍受后端权限约束。
当前原附件没有医生、成本和连续月度数据,不会生成虚构的医生排名、ROI 或环比结论。毛利是根据产品分类毛利率计算的贡献值,不等同于已审计净利润。
技术结构
| 目录 | 职责 |
|---|---|
frontend/src |
React 19 / TypeScript、AG Grid、ECharts、响应式 UI |
finance/importing.py |
解析、映射、Decimal 校验、批量导入 |
finance/analysis.py |
权限内聚合、比较、异常与贡献 |
finance/views.py |
身份认证、受控 API、导出、设置、账号、审计 |
finance/assistant.py |
规则助手及外部模型意图校验 |
server |
Django 配置、路由、WSGI |
tests |
PostgreSQL 集成测试、Playwright 浏览器回归 |
scripts |
本地账号初始化、原始附件独立对账 |
后端金额使用 NUMERIC(44,24),Python Decimal 精度 60;JSON 金额为字符串。浏览器 Number 仅用于绘图与显示,不作为账务计算或持久化来源。Excel 精确金额以文本单元格导出,避免 Excel 的 15 位有效数字上限。客户端不会一次加载 33 万条明细,只加载聚合结果,证据查询每页最多 200 行。
本地运行
需要 Node.js 22、uv、Python 3.12、PostgreSQL 17。所有示例从仓库根目录执行。
uv sync --frozen --python 3.12
npm ci --registry=https://registry.npmjs.org
export DATABASE_URL='postgresql://USER:PASSWORD@127.0.0.1:5432/traceiq'
uv run python manage.py migrate
PYTHONPATH=. uv run python scripts/init_local.py
npm run build
uv run python manage.py collectstatic --noinput
uv run python manage.py runserver 127.0.0.1:8000 --noreload
另一个终端启动导入 worker:
export DATABASE_URL='postgresql://USER:PASSWORD@127.0.0.1:5432/traceiq'
uv run python manage.py import_worker
访问 http://127.0.0.1:8000,本地随机账号保存在 var/local-access.txt,文件权限 0600。该脚本拒绝生产模式。开发热更新可在第三个终端运行 npm run dev,Vite 将 /api 代理到 8000。
本次本机验证使用专属集群 var/postgres、端口 55432、库 traceiq:
/opt/homebrew/opt/postgresql@17/bin/pg_ctl -D var/postgres -l var/postgres.log -o '-h 127.0.0.1 -p 55432' start
export DATABASE_URL='postgresql://jianwel@127.0.0.1:55432/traceiq'
该集群只监听本机,使用开发认证策略,不可公开端口或复制到生产。没有设置开机自启。SQLite 仅作轻量开发后备,不保证高精度金额聚合,禁止财务验收和生产使用。
生产部署
- 在独立服务器配置域名 DNS、防火墙及 TLS,创建
.env,参考.env.example。生成至少 50 字符随机 SECRET_KEY、数据库强密码,两处数据库密码需一致;密码有特殊字符时 DATABASE_URL 中必须 URL 编码。 - 设置准确的
DOMAIN、ALLOWED_HOSTS、CSRF_TRUSTED_ORIGINS。仅开放 Caddy 80/443;数据库与 Gunicorn 不发布主机端口。 - 执行
docker compose --env-file .env config --quiet,再执行docker compose up --build -d。migrate 服务成功后才启动 web/worker,Caddy 自动申请 HTTPS 证书。 - 执行
docker compose exec web python manage.py bootstrap --username admin --organization '医院财务中心',交互输入管理员密码,无默认生产密码。 - 执行
docker compose exec web python manage.py check --deploy,在隔离预生产环境运行验收,完成下面运维门禁再上线。
默认单个导入 worker 用 PostgreSQL advisory lock 防止重复处理;其他 worker 获取不到锁会退出。worker 异常退出,数据库回滚未完成的 Fact 事务并释放锁,下次启动会重新处理 processing。需要受控停机时先等任务 ready 再停,强制停止会导致整个文件重新导入。通过 import_file PATH --username admin --confirm-mapping 的 CLI 导入也入队,不绕过 worker。
TRUST_PROXY=1 仅用于 web 无公网直连、反向代理覆盖 X-Forwarded-Proto 和 X-Real-IP 的部署。登录 IP 限流在此模式使用经过校验的 X-Real-IP,未启用时忽略该头。.env.example 的 DB_SSLMODE=disable 仅适用同宿主私有容器网络;跨主机数据库用 verify-full 与受信证书。代理上传上限 66 MB、应用文件上限 64 MiB。生产使用环境变量/密钥服务注入密码,不提交 .env。
备份与运维
- 同时备份 PostgreSQL 与 uploads 卷,不能只备份其中一个。uploads 包含原始财务/客户标识,需加密磁盘、受控访问及留存期限。
- 低频窗口暂停新上传,等待当前导入完成并停止 worker;用
pg_dump -Fc创建数据库备份,再快照 uploads,记录同一备份批次。将备份放在加密、异地主机或受控对象存储。 - 恢复到隔离新库和新卷,使用
pg_restore --exit-on-error后检查迁移、账号范围、导入状态,执行金额/人数对账,抽查原始行与导出,再切换流量。未完成恢复演练不能上线。 - 定时执行
python manage.py clearsessions;监控/api/health、HTTP 5xx、查询 P95、导入队列等待时间、processing 持续时间、磁盘/数据库容量、失败审计与备份新鲜度。当前未配置外部告警渠道。 - 管理员忘记密码由运维执行
python manage.py changepassword USERNAME,现阶段没有邮件找回、自助密码修改、SSO 或 MFA。 - 审计记录对网页用户只读,但数据库管理员仍可修改;需要不可抵赖审计时,应外送到独立追加写日志平台。
- 发布前备份并固定镜像 digest;失败时回滚应用镜像。涉及不可逆数据迁移不得直接反向迁移,按批准的数据库恢复方案操作。
测试
测试数据库需要 CREATE DATABASE 权限。测试账号和 fixture 均为虚构数据。
uv run ruff check server finance tests scripts
uv run ruff format --check server finance tests scripts
uv run python manage.py makemigrations --check --dry-run
uv run pytest --cov=finance --cov-report=term-missing --cov-fail-under=85
npm run build
npm run test:e2e
npm audit --omit=dev --registry=https://registry.npmjs.org
uv run pip-audit
Playwright 需要正在运行的本地 API 和 worker,可用 E2E_PASSWORD 指定测试 admin 密码,默认读取本地凭据文件。首次执行 npx playwright install chromium。浏览器 fixture 测试会新建 e2e-finance.csv 数据集,不可指向正式生产环境。.github/workflows/ci.yml 在独立 PostgreSQL 上执行同样流程,尚未推送到远端触发。
原附件只在本机使用,不在仓库中:
uv run python scripts/inspect_source.py '/Users/jianwel/Downloads/分析数据_原稿(1).xlsx'
PYTHONPATH=. uv run python scripts/reconcile_reference.py
REFERENCE_DATASET_NAME='分析数据_原稿(1).xlsx' npm run test:e2e
AI 与跨平台
默认 AI_ENABLED=0。启用外部模型需配置 AI_ENABLED=1、OPENAI_API_KEY、OPENAI_MODEL,并完成机构数据处理审批。密钥只在后端。用户自由输入的问题也可能含敏感信息,不能仅凭不发送原始行就认定无隐私风险。模型不能提交 SQL、修改金额或修改账号;输出只允许受控意图,异常/拒答关闭执行。
前端为标准 Web,API 无平台耦合,可由后续 PWA、Tauri 或 Capacitor 客户端复用。当前没有打包桌面/移动客户端,也不在浏览器离线缓存财务数据。
AG Grid Community 默认可运行;设置 VITE_AG_GRID_LICENSE_KEY 后注册 Enterprise 模块并启用已接入的侧栏/状态栏。必须自行取得合适的商用许可证。SSRM / Pivot / Master-Detail 尚未接入,不能因存在 Enterprise 依赖就声称已完成这些功能。