# 薪酬考勤管理系统

本项目是一套前后端分离的薪酬考勤管理系统，用于处理钉钉月度汇总 Excel、后续钉钉实时接口、员工档案、薪资档案、提成、考勤扣款、加班费、工资汇总和统计报表。

核心目标是把人工工资核算链路拆成可维护的数据流：

```text
钉钉考勤接口 / Excel导入
  -> 数据标准化
  -> 考勤 / 请假 / 加班引擎
  -> 工时统计
  -> 薪资 / 提成 / 奖惩计算
  -> 工资汇总
  -> 工资条 / 月报表 / 财务报表
```

## 当前能力

- 支持导入钉钉月度汇总 Excel 计算工资。
- 支持实时考勤看板：按员工档案中的钉钉 userId 自动同步本月至今打卡，不需要人工输入 userId。
- 支持月度核算中心：按月份导入 Excel 或同步钉钉计算，生成异常清单、核算批次、锁定状态和导出入口。
- 自动统计出勤天数、缺勤天数、请假工时、缺卡、迟到扣款。
- 按下班打卡时间自动计算加班：17:00 下班，18:00 计 1 小时，20:30 计 3 小时。
- 自动计算：`剩余加班工时 = 打卡推算加班工时 - 请假工时`。
- 迟到规则：5 分钟内 3 次合格，超过后每次扣 30 元；超过 5 分钟每次扣 30 元。
- 缺卡规则：每次扣 20 元。
- 支持组织架构、员工维护、薪资维护、提成管理、规则配置、操作日志、统计报表。
- 薪资模式支持：包月、计时、计件、试用期。
- 加班费支持：工作日、周末、法定节假日独立单价。
- 提成支持手工维护和 Excel 导入，并按工资月份自动汇总进工资计算。
- 工资计算结果会落库为工资汇总、工资明细、考勤记录、请假记录和加班记录。
- 实时薪资预估只用于过程查看，最终工资以月度核算锁定结果为准。

## 项目结构

```text
main.py                         后端启动入口
config/                         后端配置文件
financial_system/
  api/                           FastAPI app、路由、schema、依赖注入
  core/                          配置、权限、安全、日志
  database/                      ORM、数据库会话、SQL脚本
  domain/                        领域模型、工资计算器
  integrations/                  钉钉等外部系统适配
  io/                            Excel解析、导出、文件存储
  repositories/                  数据库读写层
  services/                      业务服务层
frontend/                        Vue3 前端项目
```

分层约定：

- `routers` 只处理 HTTP、权限、参数和操作日志。
- `schemas` 只定义请求响应结构。
- `services` 只做业务编排和校验。
- `repositories` 只负责 ORM 读写。
- `domain` 只负责纯计算规则，不依赖数据库和 HTTP。

## 运行环境

后端使用 Conda 环境：

```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python main.py
```

默认访问：

- 后端接口文档：http://127.0.0.1:8000/docs
- 健康检查：http://127.0.0.1:8000/health
- 前端开发地址：http://127.0.0.1:5173

前端启动：

```bash
cd frontend
npm install
npm run dev
```

前端构建：

```bash
cd frontend
npm run build
```

## Docker 运行（外部 MySQL）

项目已提供后端镜像、前端 Nginx 镜像和 `docker-compose.yml`。数据库使用外部 MySQL，不会在 compose 里启动 MySQL 容器。

1. 准备环境变量：

```bash
cp .env.docker.example .env
```

2. 修改 `.env` 里的 `DATABASE_URL`：

```env
DATABASE_URL=mysql+pymysql://用户名:密码@MySQL地址:3306/financial_system?charset=utf8mb4
```

如果 MySQL 就运行在当前电脑，并且你使用 Docker Desktop，MySQL 地址通常写：

```env
DATABASE_URL=mysql+pymysql://root:12345678@host.docker.internal:3306/financial_system?charset=utf8mb4
```

如果 MySQL 在另一台服务器，写那台服务器的内网 IP、外网 IP 或域名。不要在容器里使用 `localhost` 连接外部 MySQL，因为 `localhost` 指向的是后端容器本身。

3. 启动：

```bash
docker compose up --build
```

默认访问：

- 前端系统：http://127.0.0.1:5173
- 后端接口：http://127.0.0.1:8000
- 接口文档：http://127.0.0.1:5173/docs

后端容器启动时会按外部 MySQL 自动建库、建表并初始化默认超级管理员。请确认外部 MySQL 已允许该账号远程连接，并且账号有创建数据库和建表权限。

## 统一配置

后端配置统一放在：

```text
config/app_settings.json
```

常用配置：

| 配置 | 说明 |
| --- | --- |
| `server.host` / `server.port` | 后端监听地址和端口 |
| `database.url` | 数据库连接 |
| 系统时区 | 后端统一使用 `Asia/Shanghai`，MySQL 会话启动时自动设置为 `+08:00` |
| `dingtalk.app_key` / `dingtalk.app_secret` | 钉钉开放平台凭证 |
| `storage.upload_dir` | 上传文件目录 |
| `storage.output_dir` | 导出工资文件目录 |
| `storage.avatar_dir` | 头像上传目录 |
| `logging.*` | info/error 日志文件和滚动策略 |
| `auth.secret_key` | Token 签名密钥 |
| `bootstrap_superuser.*` | 首次启动超级管理员 |
| `cors.allowed_origins` | 前端跨域来源 |

默认 MySQL：

```json
{
  "database": {
    "url": "mysql+pymysql://root:12345678@localhost:3306/financial_system?charset=utf8mb4"
  }
}
```

如需使用其他配置文件：

```bash
APP_CONFIG_FILE=/path/to/app_settings.json /Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python main.py
```

## 数据库

本地 MySQL：

```text
host: localhost
port: 3306
user: root
password: 12345678
database: financial_system
```

完整初始化：

```bash
mysql -h localhost -P 3306 -u root -p12345678 < financial_system/database/sql/init_mysql.sql
```

已有旧库升级到当前版本：

```bash
mysql -h localhost -P 3306 -u root -p12345678 financial_system < financial_system/database/sql/upgrade_20260617_payroll_modules.sql
mysql -h localhost -P 3306 -u root -p12345678 financial_system < financial_system/database/sql/upgrade_20260618_monthly_payroll_center.sql
```

如果历史页面时间整体少 8 小时，说明旧数据曾按 UTC 写入，可确认后执行一次修正脚本：

```bash
mysql -h localhost -P 3306 -u root -p12345678 financial_system < financial_system/database/sql/upgrade_20260623_timezone_shanghai.sql
```

SQL 文件说明：

| 文件 | 说明 |
| --- | --- |
| `schema.sql` | 建库建表脚本，包含所有表和字段注释 |
| `seed.sql` | 初始化默认超级管理员 |
| `init_mysql.sql` | 一键初始化脚本，包含建库、建表、默认配置、默认管理员 |
| `upgrade_20260617_payroll_modules.sql` | 当前薪酬考勤完整模块升级脚本 |
| `upgrade_20260618_monthly_payroll_center.sql` | 实时考勤同步、薪资预估、月度核算批次和异常清单升级脚本 |
| `upgrade_20260623_timezone_shanghai.sql` | 一次性修正历史 UTC 时间为北京时间，确认整体少 8 小时后再执行 |

主要业务表：

| 表 | 说明 |
| --- | --- |
| `users` | 系统用户 |
| `departments` | 部门及上下级关系 |
| `positions` | 部门下的岗位 |
| `employees` | 员工档案 |
| `salary_profiles` | 员工薪资档案 |
| `attendance_record` | 标准化考勤记录 |
| `leave_record` | 请假/调休记录 |
| `overtime_record` | 加班记录 |
| `commission_record` | 提成/奖金记录 |
| `payroll_jobs` | 工资计算任务 |
| `payroll_results` | 员工工资计算结果 |
| `attendance_sync_jobs` | 钉钉/Excel 考勤同步批次 |
| `salary_preview` | 本月进行中的薪资预估 |
| `monthly_payroll_runs` | 月度正式核算批次 |
| `payroll_exceptions` | 月度核算异常清单 |
| `salary_record` | 月度工资汇总 |
| `salary_detail` | 工资明细项 |
| `salary_config` | 规则配置中心 |
| `operation_logs` | 操作日志 |

服务启动时会执行 ORM 建表检查，并补齐默认超级管理员和规则配置。

组织架构启动时会按内置层级补齐部门和岗位：

- 部门顺序按总经办、行政人事部、财务部、订单部、工程部、杭州运营中心、华南/华中/西南运营中心、生产中心等层级维护。
- `工程部-安装队`、`工程部-项目预算` 会挂到自动补齐的 `工程部` 父级下。
- `杭州运营中心-销售部` 会挂到自动补齐的 `杭州运营中心` 父级下。
- `生产中心-采购部/仓库/品管部/生产部` 会挂到 `生产中心` 下。
- `西南运营中心-办事处/销售部` 会挂到 `西南运营中心` 下。
- 岗位编码由系统自动生成，页面只展示编码，不需要人工维护。

员工维护中，员工编号同样由系统自动生成，默认格式为 `ZA0001`、`ZA0002` 递增，前端只展示不手工维护。

## 规则配置

规则配置既可以通过前端“规则配置”页面维护，也可以参考：

```text
config/salary_rules.example.json
```

默认关键规则：

- 上班时间：08:30
- 下班时间：17:00
- 加班取整：60 分钟向下取整
- 标准日工时：8 小时
- 5 分钟内迟到免扣次数：3 次
- 迟到扣款：30 元/次
- 缺卡扣款：20 元/次
- 周末定义：周六、周日
- 法定节假日：可在规则配置中维护日期数组

工资公式：

```text
应发工资 = 基础工资 + 提成 + 加班费 - 迟到扣款 - 缺卡扣款 - 其他扣款
```

基础工资按薪资模式计算：

- 包月：固定底薪
- 计时：实际工作工时 × 小时单价
- 计件：计件数量 × 计件单价
- 试用期：底薪 × 固定比例，或固定金额

## 前端页面

| 页面 | 路由 | 说明 |
| --- | --- | --- |
| 登录 | `/login` | 用户登录 |
| 工作台 | `/dashboard` | 系统概览 |
| 实时考勤 | `/attendance/realtime` | 查看今日打卡、迟到、缺卡、请假、加班和本月薪资预估 |
| 月度核算 | `/payroll/monthly` | 按月导入/同步、计算、异常处理、锁定和导出 |
| 工资计算 | `/payroll` | Excel 导入和钉钉实时计算 |
| 计算记录 | `/payroll/jobs` | 查看历史计算任务 |
| 提成管理 | `/payroll/commissions` | 手工维护和 Excel 导入提成 |
| 统计报表 | `/reports` | 工资汇总、部门汇总、考勤工时 |
| 组织架构 | `/system/organization` | 维护部门上下级和部门岗位 |
| 员工维护 | `/system/employees` | 员工分页维护 |
| 薪资维护 | `/system/salary-profiles` | 员工薪资模式和加班单价 |
| 规则配置 | `/system/configs` | 考勤和薪资规则 |
| 用户管理 | `/system/users` | 超级用户创建账号 |
| 操作日志 | `/system/operation-logs` | 查看系统操作审计 |

前端支持主题色切换、侧边栏收起、个人资料修改、头像上传、修改密码。

## 登录与权限

首次启动默认超级管理员：

```text
用户名：admin
密码：Admin@123456
角色：superuser
```

角色：

| 角色 | 说明 |
| --- | --- |
| `superuser` | 超级用户，拥有全部菜单和操作权限 |
| `manager` | 管理者，可维护组织架构、员工、薪资、提成、规则、报表和工资计算 |
| `viewer` | 查看用户，可查看计算记录、下载结果和统计报表 |

登录示例：

```bash
curl -X POST "http://127.0.0.1:8000/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"Admin@123456"}'
```

## 常用接口

上传 Excel 计算工资：

```bash
curl -X POST "http://127.0.0.1:8000/api/payroll/excel" \
  -H "Authorization: Bearer <access_token>" \
  -F "file=@/path/to/月度汇总.xlsx" \
  -F "export_excel=true"
```

实时考勤看板同步钉钉：

```bash
curl -X POST "http://127.0.0.1:8000/api/attendance/realtime/sync" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"attendance_date":"2026-06-22","include_salary_preview":true}'
```

查看实时考勤看板：

```bash
curl "http://127.0.0.1:8000/api/attendance/realtime/today?date=2026-06-22" \
  -H "Authorization: Bearer <access_token>"
```

月度核算同步钉钉并计算：

```bash
curl -X POST "http://127.0.0.1:8000/api/payroll/monthly/dingtalk" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"salary_month":"2026-06","source_type":"dingtalk","export_excel":true}'
```

旧版钉钉实时计算接口仍可使用，但需要手动传 user_ids：

```bash
curl -X POST "http://127.0.0.1:8000/api/payroll/dingtalk/realtime" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"user_ids":["001","002"],"start_date":"2026-05-01","end_date":"2026-05-31","export_excel":true}'
```

新增提成：

```bash
curl -X POST "http://127.0.0.1:8000/api/commissions" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"employee_id":1,"commission_month":"2026-06","commission_type":"销售提成","amount":1000,"source_type":"manual","business_ref":"","remark":""}'
```

查询统计报表：

```bash
curl "http://127.0.0.1:8000/api/reports/payroll-summary?salary_month=2026-06" \
  -H "Authorization: Bearer <access_token>"
```

## 日志

日志统一由 `financial_system/core/logger.py` 封装。

默认文件：

| 文件 | 说明 |
| --- | --- |
| `logs/info.log` | 正常流程日志 |
| `logs/error.log` | 错误和异常日志 |

修改 `config/app_settings.json` 中 `logging.*` 后重启服务即可生效。
