- 经理聚合分成放大 100 倍:参数按百分数存储(12.5=12.5%),parse_fraction 后调用方又 ×100、函数内部再 ÷100,净效应「绩效×12.5」;去掉调用方 ×100,rate_text 改用实际参数原文(原硬编码 12.5%+5% 参数调整后失真) - 审批联动只认终审:财务节点通过(1→2)不再提前 mark_locked/调整单生效, 否则领导驳回后 locked_at/locked_by 残留、两级审批形同虚设;驳回在任何 节点即时联动;mark_rejected 补清锁定字段 - 调整单新建不可用:ENABLE_CODE_BY_TYPE 未登记 type=3 → is_enabled 恒 400; 补登记 approval.adjustment.enabled。另修复叠加缺陷——开关关闭分支手工 ApprovalStep(order_id=调整单id),而该外键指向 approval_orders,必然 errno 1452;统一改走 submit_order(自动通过留痕由它负责) - 重算双重计提:业务级一次性计提的「进度登记」回款无生效台账行,重算会 凭空落新行;无旧行一律 400 - 前端重算传错 id:台账行主键当 receipt_id 传给重算接口,会作废无关回款 的台账;改传 row.receipt_id(聚合行防御) - quantize 显式 ROUND_HALF_UP:9 处金额 quantize 缺省银行家舍入, x.xx5 与四舍五入口径差 1 分(docstring 却声称 HALF_UP) 测试:显式控制审批开关(V8 起种子默认开启,用例不再依赖默认值); 新增回归 4 例(经理聚合比例/调整单即生效/进度登记重算 400/财务节点不提前 锁定),test_calc+test_settlements 22 passed Co-Authored-By: Claude Code <noreply@anthropic.com> |
||
|---|---|---|
| backend | ||
| db | ||
| docker | ||
| docs | ||
| frontend | ||
| scripts | ||
| .editorconfig | ||
| .gitattributes | ||
| .gitignore | ||
| .markdownlint.json | ||
| README.md | ||
衍水集团营销人员工资管理系统
营销人员绩效工资核算系统。后端 Python/FastAPI,前端 Vue 3,数据存 MySQL,会话与缓存在 Redis。
当前进度:登录鉴权与系统管理(用户/角色/操作日志/日志查询)已完成,前端具备应用布局框架。业务模块(基础数据、业务数据、绩效核算、报表)待需求澄清后开发。
快速开始
0. 前置条件
| 工具 | 版本 | 说明 |
|---|---|---|
| Docker Desktop | — | 提供 MySQL 8.0 与 Redis 7,必须先启动 |
| Python | 3.12 | 由 uv 自动获取,无需手动安装 |
| uv | ≥ 0.12 | Python 依赖与虚拟环境管理 |
| Node.js / pnpm | 22 / 12 | 前端 |
国内网络建议先配镜像。 直连 pypi.org 实测约 4 KB/s,一次
uv sync要几分钟。 配在 uv 的用户级配置(%APPDATA%\uv\uv.toml,Linux/macOS 为~/.config/uv/uv.toml), 不要写进pyproject.toml—— 那会被uv.lock记录为包来源,仓库就此绑死在该镜像上。[[index]] url = "https://pypi.tuna.tsinghua.edu.cn/simple" default = true前端已默认走 npmmirror,无需额外配置。
1. 首次准备
# 从模板创建配置文件,填入真实值(.env 不入库)
cp docker/.env.example docker/.env
cp backend/.env.example backend/.env
# 生成后端密钥并填入 backend/.env 的 APP_SECRET_KEY
python -c "import secrets; print(secrets.token_urlsafe(48))"
# 两处 MYSQL_PASSWORD / REDIS_PASSWORD 需保持一致
2. 启动(三步)
# ① 数据库与缓存(首次会自动执行 db/init/init-dev.sql 建表)
docker compose -f docker/docker-compose.dev.yml up -d
# ② 后端:装依赖 → 应用迁移 → 建管理员 → 起服务
cd backend
uv sync
uv run alembic upgrade head
uv run python -m app.cli create-admin --username admin # 密码交互输入
uv run uvicorn app.main:app --reload --port 8000
# ③ 前端(另开终端)
cd frontend
pnpm install
pnpm dev
打开 http://localhost:5173 即可登录。接口文档在 http://localhost:8000/api/docs。
务必加
--reload:不加的话改了后端代码进程不会重载,而前端仍能正常访问, 会出现「改了没生效但看不出哪里错」的困惑。
日常开发
# 后端测试与代码检查
cd backend
uv run pytest tests/unit -q # 纯单元,无需容器,秒级
uv run pytest -q # 全部(集成部分需容器在跑)
uv run ruff check . # 代码检查
uv run ruff format . # 代码格式化
# 前端
cd frontend
pnpm dev # 开发服务
pnpm type-check # 类型检查
pnpm build # 类型检查 + 生产构建
# 浏览器端到端(需容器、后端、前端都在运行)
python e2e/login_flow.py # 登录链路 8 项
python e2e/system_management.py # 系统管理 13 项
目录结构
.
├── backend/ 后端 FastAPI(六层:api/core/models/repositories/schemas/services)
│ ├── app/ 应用代码
│ ├── alembic/ 数据库迁移
│ ├── tests/ unit(无需容器)/ integration(需容器)
│ └── README.md 模块说明
├── frontend/ 前端 Vue 3 + TS
│ ├── src/ 应用代码
│ ├── e2e/ Playwright 端到端脚本
│ └── README.md 模块说明(含视觉设计约定)
├── db/
│ ├── init/ 开发环境初始化(仅供参考,见下方说明)
│ ├── migration/ 生产迁移脚本,按序编号
│ └── queries/ 业务 SQL 留存
├── docker/ 容器编排
├── docs/ 需求文档
└── ai-aux/ AI 辅助上下文(不纳入版本管理)
几个容易踩的点
db/init/init-dev.sql 改了不生效
它只在 MySQL 数据卷为空时的首次启动执行。数据卷已存在时改动它没有任何提示。
需要重置时:docker compose -f docker/docker-compose.dev.yml down -v 再 up -d(会清空数据)。
生产路径是 db/migration/,那个才必须保证正确。
环境变量分两处,重复项要同步改
docker/.env 管编排凭据(数据库 root 密码、业务账号密码、Redis 密码);
backend/.env 管应用配置(连接串、密钥、JWT、验证码、T+ 凭据)。
MYSQL_PASSWORD / REDIS_PASSWORD 两处都有,改一处要改两处。
后端 401 与登录失败是两回事
401 只表示「登录态失效」;登录接口自身失败返回 400/403。
前端拦截器把 401 一律当作「清 token + 跳登录页」,两者混用会导致「输错密码就被弹回登录页」。
登录失败后必须刷新验证码
后端用 Redis GETDEL 消费验证码,任何一次提交后它都已失效——前端已自动处理,改动登录页时注意保留。
相关文档
| 文档 | 内容 |
|---|---|
backend/README.md |
后端启动、分层约定、接口清单 |
backend/tests/README.md |
测试约定、用例清单、运行记录、已修复缺陷 |
frontend/README.md |
前端版本约束、视觉设计约定、端到端测试 |
docs/ |
需求文档与工资算法方案 |