Files
fa-agent/README.md
Jayway Liu dd8912f7ae
Verify / verify (pull_request) Failing after 0s
AI Review / review (pull_request) Successful in 9s
Verify / verify (push) Failing after 1s
feat(ai): 页面可配置云端或本地 OpenAI 兼容模型
- 新增 AIConfig 模型(每组织一份,迁移 0005)与 /api/ai-config、
  /api/ai-models 接口;密钥只存后端、读取脱敏、修改记审计
- 支持 OpenAI 及本地 OpenAI 兼容服务(Ollama/LM Studio/vLLM),
  可从 /v1/models 一键获取模型列表多选一;数据库配置优先于环境变量
- 助手调用走生效配置,Copilot 文案同步更新
- 全权限管理员可在「管理 → AI 模型」配置;补接口级测试
2026-10-11 09:37:05 +08:00

127 lines
10 KiB
Markdown
Raw Permalink 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.
# TraceIQ 财务分析工作台
响应式 Web 前端 + Django API + PostgreSQL。基于分享会话、医院财务 Excel 原始附件及 AG Grid 开发计划实现,当前交付是可运行、经过本地回归的核心业务版本,不等于全部需求完成或已获生产上线认证。缺口与上线门禁见 [验收状态](docs/acceptance.md)。
## 已实现
- 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。所有示例从仓库根目录执行。
快速启动本地页面(迁移、管理员初始化、前端构建、API 与导入 worker 一条命令完成,健康检查通过后自动打开 `http://127.0.0.1:8000`;`Ctrl-C` 或 `scripts/dev_up.sh --stop` 停止,`--no-open` 跳过自动打开浏览器):
```bash
scripts/dev_up.sh
```
未设置 `DATABASE_URL` 时该脚本使用 SQLite 轻量开发库 `var/db.sqlite3`,仅限页面测试与开发回归;金额聚合与财务验收需按下方配置 PostgreSQL。手动分步流程如下:
```bash
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:
```bash
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:
```bash
/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 仅作轻量开发后备,不保证高精度金额聚合,禁止财务验收和生产使用。
## 生产部署
1. 在独立服务器配置域名 DNS、防火墙及 TLS,创建 `.env`,参考 `.env.example`。生成至少 50 字符随机 SECRET_KEY、数据库强密码,两处数据库密码需一致;密码有特殊字符时 DATABASE_URL 中必须 URL 编码。
2. 设置准确的 `DOMAIN`、`ALLOWED_HOSTS`、`CSRF_TRUSTED_ORIGINS`。仅开放 Caddy 80/443;数据库与 Gunicorn 不发布主机端口。
3. 执行 `docker compose --env-file .env config --quiet`,再执行 `docker compose up --build -d`。migrate 服务成功后才启动 web/worker,Caddy 自动申请 HTTPS 证书。
4. 执行 `docker compose exec web python manage.py bootstrap --username admin --organization '医院财务中心'`,交互输入管理员密码,无默认生产密码。
5. 执行 `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 均为虚构数据。
```bash
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` 数据集,不可指向正式生产环境。`.gitea/workflows/ci.yml` 在独立 PostgreSQL 服务容器上执行同样流程(Gitea act_runner 的 job 跑在容器内,服务主机名用服务名 `postgres` 而非 localhost)。`.gitea/workflows/ai-review.yml` 对每个 PR 运行对抗式 AI 审查与自动修复,引擎在 vertai-tech/ai-review-engine(v1 标签),本仓库审查侧重见 `.gitea/ai-review.md`。
原附件只在本机使用,不在仓库中:
```bash
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 模型」配置服务商(OpenAI 或本地 OpenAI 兼容服务如 Ollama / LM Studio / vLLM)、Base URL、API Key 与模型,可从 `/v1/models` 一键获取本地服务模型列表多选一;或配置环境变量 `AI_ENABLED=1`、`OPENAI_API_KEY`、`OPENAI_MODEL`(可选 `OPENAI_BASE_URL`)。均需完成机构数据处理审批。密钥只在后端:读取接口不回传密钥原文,配置修改记入审计(不含密钥)。用户自由输入的问题也可能含敏感信息,不能仅凭不发送原始行就认定无隐私风险。模型不能提交 SQL、修改金额或修改账号;输出只允许受控意图,异常/拒答关闭执行。
前端为标准 Web,API 无平台耦合,可由后续 PWA、Tauri 或 Capacitor 客户端复用。当前没有打包桌面/移动客户端,也不在浏览器离线缓存财务数据。
AG Grid Community 默认可运行;设置 `VITE_AG_GRID_LICENSE_KEY` 后注册 Enterprise 模块并启用已接入的侧栏/状态栏。必须自行取得合适的商用许可证。SSRM / Pivot / Master-Detail 尚未接入,不能因存在 Enterprise 依赖就声称已完成这些功能。