financial_system/docs/plans/2026-06-18-monthly-payroll-center.md
2026-06-22 13:23:04 +08:00

1413 lines
30 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.

# 月度核算中心与实时考勤看板 Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 将当前薪酬考勤系统从“多个功能页分散操作”升级为“实时考勤看板 + 月度核算中心”的主流程系统支持钉钉实时同步、Excel 导入、考勤标准化、薪资预估、月度工资复核、锁定和导出。
**Architecture:** 后端继续沿用 FastAPI 分层架构router 只处理 HTTP 和权限schema 只定义请求响应service 负责编排repository 负责 ORM 读写domain 负责纯计算。新增实时考勤和月度核算模块时,复用现有 `PayrollCalculator`、`MonthlySummaryParser`、`DingTalkAttendanceAdapter`、`attendance_record`、`payroll_results` 等能力,只补齐同步批次、实时预估、月度核算状态和异常记录。
**Tech Stack:** Python 3 / FastAPI / SQLAlchemy / MySQL / openpyxl / Vue 3 / Pinia / Vue Router / TypeScript / Vite / lucide-vue。
---
## 目标业务流
```text
实时考勤看板
-> 读取在职员工和钉钉 userId
-> 同步今天或本月至今的钉钉打卡
-> 标准化为每日考勤
-> 展示今日打卡、迟到、缺卡、请假、加班和本月薪资预估
月度核算中心
-> 选择工资月份
-> 同步钉钉或导入 Excel
-> 标准化考勤
-> 生成异常清单
-> 计算工资
-> 复核员工明细
-> 锁定本月工资
-> 导出工资表和报表
```
## 阶段 0开发前保护
### Task 0.1: 建立当前基线
**Files:**
- Read: `README.md`
- Read: `financial_system/database/orm.py`
- Read: `financial_system/services/payroll_service.py`
- Read: `financial_system/api/routers/payroll.py`
- Read: `frontend/src/router/index.ts`
- Read: `frontend/src/components/SidebarNav.vue`
- Read: `frontend/src/views/PayrollView.vue`
**Step 1: 查看工作区状态**
Run:
```bash
git status --short
```
Expected: 确认哪些文件已有用户改动。不要回滚任何非本任务改动。
**Step 2: 编译当前后端**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: `financial_system` 编译成功。
**Step 3: 构建当前前端**
Run:
```bash
cd frontend
npm run build
```
Expected: Vite build 通过。
**Step 4: Commit**
本任务只做基线验证,不需要 commit。
---
## 阶段 1数据库模型与 SQL
### Task 1.1: 增加实时同步和月度核算 ORM
**Files:**
- Modify: `financial_system/database/orm.py`
- Modify: `financial_system/database/sql/schema.sql`
- Modify: `financial_system/database/sql/init_mysql.sql`
- Create: `financial_system/database/sql/upgrade_20260618_monthly_payroll_center.sql`
**Step 1: 设计新增表**
新增 4 张表,所有表和字段都必须有 MySQL 注释:
```text
attendance_sync_jobs
id
source_type
sync_mode
attendance_date
salary_month
start_date
end_date
status
employee_count
record_count
error_message
created_by
created_at
updated_at
salary_preview
id
sync_job_id
employee_id
employee_no
employee_name
department
position
salary_month
attendance_days
actual_work_hours
punch_overtime_hours
leave_hours
remaining_overtime_hours
late_deduction
missing_card_deduction
commission_amount
overtime_pay
estimated_gross_salary
estimated_net_salary
status
note
created_at
updated_at
monthly_payroll_runs
id
salary_month
source_type
source_job_id
status
employee_count
gross_total
net_total
deduction_total
overtime_total
locked_by
locked_at
created_by
created_at
updated_at
payroll_exceptions
id
run_id
sync_job_id
employee_id
employee_no
employee_name
salary_month
exception_type
severity
message
status
created_at
resolved_at
```
**Step 2: 修改 ORM**
`financial_system/database/orm.py` 中新增:
```python
class AttendanceSyncJobORM(Base):
"""钉钉或 Excel 考勤同步批次,用于实时看板和月度核算追踪。"""
__tablename__ = "attendance_sync_jobs"
...
class SalaryPreviewORM(Base):
"""本月进行中的薪资预估结果,不作为最终工资发放依据。"""
__tablename__ = "salary_preview"
...
class MonthlyPayrollRunORM(Base):
"""月度正式核算批次,支持复核、锁定和导出。"""
__tablename__ = "monthly_payroll_runs"
...
class PayrollExceptionORM(Base):
"""核算异常记录,例如缺员工、缺薪资档案、缺卡、请假无法识别。"""
__tablename__ = "payroll_exceptions"
...
```
字段类型沿用现有风格:金额用 `Float`,月份用 `String(7)`,状态用 `String(32)`,时间用 `DateTime`
**Step 3: 更新 SQL**
`schema.sql``init_mysql.sql` 中增加建表语句。新增 `upgrade_20260618_monthly_payroll_center.sql`,包含 `CREATE TABLE IF NOT EXISTS`,可对已有库安全执行。
**Step 4: 编译验证**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: PASS。
**Step 5: Commit**
```bash
git add financial_system/database/orm.py financial_system/database/sql/schema.sql financial_system/database/sql/init_mysql.sql financial_system/database/sql/upgrade_20260618_monthly_payroll_center.sql
git commit -m "feat: add monthly payroll center tables"
```
---
### Task 1.2: 增加权限和菜单
**Files:**
- Modify: `financial_system/core/permissions.py`
- Modify: `frontend/src/router/index.ts`
- Modify: `frontend/src/components/SidebarNav.vue`
**Step 1: 增加权限码**
`financial_system/core/permissions.py` 增加:
```python
PERMISSION_ATTENDANCE_REALTIME_VIEW = "attendance:realtime:view"
PERMISSION_ATTENDANCE_REALTIME_SYNC = "attendance:realtime:sync"
PERMISSION_MONTHLY_PAYROLL_VIEW = "monthly_payroll:view"
PERMISSION_MONTHLY_PAYROLL_CALCULATE = "monthly_payroll:calculate"
PERMISSION_MONTHLY_PAYROLL_LOCK = "monthly_payroll:lock"
PERMISSION_MONTHLY_PAYROLL_EXPORT = "monthly_payroll:export"
```
**Step 2: 增加菜单**
新增菜单:
```python
"attendance_realtime": MenuPermission(
code="attendance_realtime",
name="实时考勤",
path="/attendance/realtime",
),
"monthly_payroll": MenuPermission(
code="monthly_payroll",
name="月度核算",
path="/payroll/monthly",
),
```
超级用户和管理者可见两个菜单;查看权限至少可见 `实时考勤` 和报表,是否能看月度工资按业务需要决定,默认仅查看 `实时考勤`
**Step 3: 前端路由占位**
`frontend/src/router/index.ts` 先注册页面:
```typescript
{
path: "attendance/realtime",
name: "attendance-realtime",
component: RealtimeAttendanceView,
meta: { permission: "attendance:realtime:view" },
},
{
path: "payroll/monthly",
name: "monthly-payroll",
component: MonthlyPayrollView,
meta: { permission: "monthly_payroll:view" },
},
```
**Step 4: 侧边栏图标**
`frontend/src/components/SidebarNav.vue` 中增加图标映射:
```typescript
attendance_realtime: CalendarClock,
monthly_payroll: Calculator,
```
**Step 5: 验证**
Run:
```bash
cd frontend
npm run build
```
Expected: PASS。
**Step 6: Commit**
```bash
git add financial_system/core/permissions.py frontend/src/router/index.ts frontend/src/components/SidebarNav.vue
git commit -m "feat: add realtime attendance and monthly payroll menus"
```
---
## 阶段 2后端实时考勤看板
### Task 2.1: 增加实时考勤 schema
**Files:**
- Create: `financial_system/api/schemas/realtime_attendance.py`
- Modify: `financial_system/api/schemas/__init__.py`
**Step 1: 编写 schema**
Create `financial_system/api/schemas/realtime_attendance.py`:
```python
from __future__ import annotations
from datetime import date, datetime
from pydantic import BaseModel
class RealtimeAttendanceSyncRequest(BaseModel):
attendance_date: date
include_salary_preview: bool = True
class TodayAttendanceItem(BaseModel):
employee_id: int | None
employee_no: str
employee_name: str
department: str
position: str
first_punch: str
last_punch: str
attendance_status: str
late_minutes: int
missing_card_count: int
leave_hours: float
today_overtime_hours: float
month_remaining_overtime_hours: float
estimated_net_salary: float | None
note: str
class RealtimeAttendanceSummary(BaseModel):
attendance_date: date
employee_total: int
checked_in_count: int
unchecked_count: int
late_count: int
leave_count: int
missing_card_count: int
estimated_overtime_count: int
estimated_net_total: float
class RealtimeAttendanceResponse(BaseModel):
sync_job_id: str | None
synced_at: datetime | None
summary: RealtimeAttendanceSummary
items: list[TodayAttendanceItem]
```
**Step 2: 编译验证**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: PASS。
**Step 3: Commit**
```bash
git add financial_system/api/schemas/realtime_attendance.py financial_system/api/schemas/__init__.py
git commit -m "feat: add realtime attendance schemas"
```
---
### Task 2.2: 增加实时考勤 Repository
**Files:**
- Create: `financial_system/repositories/realtime_attendance_repository.py`
- Modify: `financial_system/repositories/__init__.py`
**Step 1: 实现 repository**
Repository 负责:
- 查询在职员工和钉钉 userId。
- 创建 `attendance_sync_jobs`
- 保存 `attendance_record`
- 保存 `salary_preview`
- 查询某日考勤记录。
- 查询某月薪资预估。
核心方法:
```python
class RealtimeAttendanceRepository:
def list_active_employees_with_dingtalk(self) -> list[EmployeeORM]: ...
def create_sync_job(...) -> AttendanceSyncJobORM: ...
def mark_sync_completed(...) -> None: ...
def mark_sync_failed(...) -> None: ...
def replace_attendance_records(...) -> None: ...
def replace_salary_previews(...) -> None: ...
def list_today_records(...) -> list[AttendanceRecordORM]: ...
def list_salary_previews(...) -> list[SalaryPreviewORM]: ...
```
**Step 2: 注意幂等**
同一天多次同步时,应按 `source_type=dingtalk + work_date + employee_id` 替换当天记录,避免列表越来越多。
**Step 3: 编译验证**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: PASS。
**Step 4: Commit**
```bash
git add financial_system/repositories/realtime_attendance_repository.py financial_system/repositories/__init__.py
git commit -m "feat: add realtime attendance repository"
```
---
### Task 2.3: 增加实时薪资预估服务
**Files:**
- Create: `financial_system/services/realtime_attendance_service.py`
- Modify: `financial_system/services/__init__.py`
- Modify: `financial_system/api/dependencies.py`
**Step 1: 实现服务职责**
`RealtimeAttendanceService` 负责:
- 从数据库拿在职员工。
- 自动收集员工 `dingtalk_user_id`,不再让用户手填。
- 调用 `DingTalkClient.list_attendance_records`
- 使用 `DingTalkAttendanceAdapter` 转成 `EmployeeAttendance`
- 使用 `PayrollCalculator` 计算本月薪资预估。
- 写入 `attendance_record``salary_preview`
- 输出今日考勤列表和汇总。
**Step 2: 保持预估语义**
返回字段命名使用 `estimated_*`,页面文案必须展示“预估金额,最终以月度核算锁定结果为准”。
**Step 3: 缺少钉钉配置**
复用 `PayrollApplicationService._dingtalk_settings()` 的逻辑,或者抽出公共 helper。缺少 `app_key/app_secret` 时返回 400提示用户修改 `config/app_settings.json`
**Step 4: 编译验证**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: PASS。
**Step 5: Commit**
```bash
git add financial_system/services/realtime_attendance_service.py financial_system/services/__init__.py financial_system/api/dependencies.py
git commit -m "feat: add realtime attendance service"
```
---
### Task 2.4: 增加实时考勤 API
**Files:**
- Create: `financial_system/api/routers/realtime_attendance.py`
- Modify: `financial_system/api/app.py`
**Step 1: 新增路由**
Create router prefix:
```python
router = APIRouter(prefix="/api/attendance/realtime", tags=["realtime-attendance"])
```
Endpoints:
```text
GET /api/attendance/realtime/today?date=YYYY-MM-DD
POST /api/attendance/realtime/sync
```
**Step 2: 权限**
- `GET today` 使用 `PERMISSION_ATTENDANCE_REALTIME_VIEW`
- `POST sync` 使用 `PERMISSION_ATTENDANCE_REALTIME_SYNC`
**Step 3: 操作日志**
同步成功记录:
```text
module=attendance
action=attendance.realtime.sync
target_type=attendance_sync_job
```
查看成功记录:
```text
module=attendance
action=attendance.realtime.view
```
**Step 4: 注册路由**
`financial_system/api/app.py` 导入并 `include_router(realtime_attendance.router)`
**Step 5: 编译验证**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: PASS。
**Step 6: Commit**
```bash
git add financial_system/api/routers/realtime_attendance.py financial_system/api/app.py
git commit -m "feat: add realtime attendance api"
```
---
## 阶段 3后端月度核算中心
### Task 3.1: 增加月度核算 schema
**Files:**
- Create: `financial_system/api/schemas/monthly_payroll.py`
- Modify: `financial_system/api/schemas/__init__.py`
**Step 1: 定义请求响应**
Create:
```python
class MonthlyPayrollCreateRequest(BaseModel):
salary_month: str
source_type: str
export_excel: bool = True
class MonthlyPayrollImportExcelResponse(BaseModel):
run_id: int
status: str
employee_count: int
exception_count: int
class MonthlyPayrollRunResponse(BaseModel):
id: int
salary_month: str
source_type: str
status: str
employee_count: int
gross_total: float
net_total: float
deduction_total: float
overtime_total: float
locked_by: str
locked_at: datetime | None
created_at: datetime
updated_at: datetime
class MonthlyPayrollDetailResponse(BaseModel):
run: MonthlyPayrollRunResponse
results: list[PayrollResultItem]
exceptions: list[PayrollExceptionItem]
```
`PayrollResultItem` 可复用现有 `financial_system/api/schemas/payroll.py` 中的结果 schema避免重复字段。
**Step 2: 编译验证**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: PASS。
**Step 3: Commit**
```bash
git add financial_system/api/schemas/monthly_payroll.py financial_system/api/schemas/__init__.py
git commit -m "feat: add monthly payroll schemas"
```
---
### Task 3.2: 增加月度核算 Repository
**Files:**
- Create: `financial_system/repositories/monthly_payroll_repository.py`
- Modify: `financial_system/repositories/__init__.py`
**Step 1: 实现方法**
Repository 负责:
```python
class MonthlyPayrollRepository:
def create_run(...) -> MonthlyPayrollRunORM: ...
def get_run(run_id: int) -> MonthlyPayrollRunORM | None: ...
def get_run_by_month(salary_month: str) -> MonthlyPayrollRunORM | None: ...
def update_run_totals(...) -> None: ...
def lock_run(...) -> None: ...
def replace_exceptions(...) -> None: ...
def list_exceptions(...) -> list[PayrollExceptionORM]: ...
```
**Step 2: 锁定保护**
如果 `monthly_payroll_runs.status == "locked"`,任何重新计算或覆盖结果都必须拒绝。
**Step 3: 编译验证**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: PASS。
**Step 4: Commit**
```bash
git add financial_system/repositories/monthly_payroll_repository.py financial_system/repositories/__init__.py
git commit -m "feat: add monthly payroll repository"
```
---
### Task 3.3: 增加异常识别规则
**Files:**
- Create: `financial_system/domain/payroll_exceptions.py`
**Step 1: 实现纯函数**
Create:
```python
def detect_payroll_exceptions(results: list[PayrollResult]) -> list[PayrollException]:
...
```
异常类型:
```text
missing_salary_profile
missing_employee_match
missing_dingtalk_user_id
missing_card
late_penalty
negative_remaining_overtime
empty_attendance
```
**Step 2: 规则建议**
- `base_pay is None` -> `missing_salary_profile`
- `missing_card_count > 0` -> `missing_card`
- `penalized_late_count > 0` -> `late_penalty`
- `remaining_overtime_hours < 0` -> `negative_remaining_overtime`
- `attendance_days == 0` -> `empty_attendance`
**Step 3: 编译验证**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: PASS。
**Step 4: Commit**
```bash
git add financial_system/domain/payroll_exceptions.py
git commit -m "feat: add payroll exception detection"
```
---
### Task 3.4: 增加月度核算服务
**Files:**
- Create: `financial_system/services/monthly_payroll_service.py`
- Modify: `financial_system/services/__init__.py`
- Modify: `financial_system/api/dependencies.py`
**Step 1: 服务职责**
`MonthlyPayrollService` 负责:
- Excel 导入并创建月度 run。
- 钉钉全量同步并创建月度 run。
- 复用 `PayrollApplicationService` 或直接复用 parser/calculator。
- 生成工资结果。
- 生成异常记录。
- 更新 run 汇总金额。
- 锁定工资。
- 导出工资文件。
**Step 2: 状态机**
状态只保留必要值:
```text
draft 草稿/已导入
calculated 已计算
reviewed 已复核
locked 已锁定
failed 失败
```
**Step 3: 锁定规则**
锁定后:
- 不允许重新计算。
- 不允许覆盖结果。
- 允许导出。
- 操作日志必须记录锁定人。
**Step 4: 编译验证**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: PASS。
**Step 5: Commit**
```bash
git add financial_system/services/monthly_payroll_service.py financial_system/services/__init__.py financial_system/api/dependencies.py
git commit -m "feat: add monthly payroll service"
```
---
### Task 3.5: 增加月度核算 API
**Files:**
- Create: `financial_system/api/routers/monthly_payroll.py`
- Modify: `financial_system/api/app.py`
**Step 1: 新增路由**
Create router prefix:
```python
router = APIRouter(prefix="/api/payroll/monthly", tags=["monthly-payroll"])
```
Endpoints:
```text
GET /api/payroll/monthly/runs?month=YYYY-MM
GET /api/payroll/monthly/runs/{run_id}
POST /api/payroll/monthly/excel
POST /api/payroll/monthly/dingtalk
POST /api/payroll/monthly/runs/{run_id}/recalculate
POST /api/payroll/monthly/runs/{run_id}/lock
GET /api/payroll/monthly/runs/{run_id}/export
```
**Step 2: 权限**
- 查看:`PERMISSION_MONTHLY_PAYROLL_VIEW`
- 计算:`PERMISSION_MONTHLY_PAYROLL_CALCULATE`
- 锁定:`PERMISSION_MONTHLY_PAYROLL_LOCK`
- 导出:`PERMISSION_MONTHLY_PAYROLL_EXPORT`
**Step 3: 操作日志**
记录以下动作:
```text
monthly_payroll.view
monthly_payroll.excel.import
monthly_payroll.dingtalk.calculate
monthly_payroll.recalculate
monthly_payroll.lock
monthly_payroll.export
```
**Step 4: 注册路由**
`financial_system/api/app.py` 导入并 include。
**Step 5: 编译验证**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: PASS。
**Step 6: Commit**
```bash
git add financial_system/api/routers/monthly_payroll.py financial_system/api/app.py
git commit -m "feat: add monthly payroll api"
```
---
## 阶段 4前端信息架构和 API
### Task 4.1: 重组菜单展示
**Files:**
- Modify: `frontend/src/components/SidebarNav.vue`
- Modify: `frontend/src/assets/styles.css`
**Step 1: 分组菜单**
将菜单视觉分组为:
```text
工作台
实时考勤
月度核算
工资核算
计算记录
提成录入
员工薪资
员工维护
薪资档案
组织架构
报表分析
工资报表
系统管理
规则配置
用户管理
操作日志
```
**Step 2: 收起侧栏体验**
收起时隐藏分组文字,只保留图标;悬停显示 `title`。保持现在的平滑过渡变量:
```css
--motion-duration: 280ms;
--motion-easing: cubic-bezier(0.22, 1, 0.36, 1);
```
**Step 3: 构建验证**
Run:
```bash
cd frontend
npm run build
```
Expected: PASS。
**Step 4: Commit**
```bash
git add frontend/src/components/SidebarNav.vue frontend/src/assets/styles.css
git commit -m "feat: group sidebar navigation"
```
---
### Task 4.2: 增加前端 API 类型
**Files:**
- Modify: `frontend/src/api/types.ts`
- Create: `frontend/src/api/realtimeAttendance.ts`
- Create: `frontend/src/api/monthlyPayroll.ts`
**Step 1: 增加类型**
`types.ts` 增加:
```typescript
export interface RealtimeAttendanceSummary { ... }
export interface TodayAttendanceItem { ... }
export interface RealtimeAttendanceResponse { ... }
export interface MonthlyPayrollRun { ... }
export interface PayrollException { ... }
export interface MonthlyPayrollDetailResponse { ... }
```
**Step 2: 增加 API 方法**
Create `realtimeAttendance.ts`:
```typescript
export function getTodayAttendance(date: string): Promise<RealtimeAttendanceResponse> { ... }
export function syncRealtimeAttendance(date: string): Promise<RealtimeAttendanceResponse> { ... }
```
Create `monthlyPayroll.ts`:
```typescript
export function listMonthlyRuns(month?: string): Promise<MonthlyPayrollRun[]> { ... }
export function getMonthlyRun(runId: number): Promise<MonthlyPayrollDetailResponse> { ... }
export function calculateMonthlyFromExcel(...): Promise<MonthlyPayrollDetailResponse> { ... }
export function calculateMonthlyFromDingTalk(month: string): Promise<MonthlyPayrollDetailResponse> { ... }
export function lockMonthlyRun(runId: number): Promise<MonthlyPayrollRun> { ... }
export function exportMonthlyRun(runId: number): Promise<void> { ... }
```
**Step 3: 构建验证**
Run:
```bash
cd frontend
npm run build
```
Expected: PASS。
**Step 4: Commit**
```bash
git add frontend/src/api/types.ts frontend/src/api/realtimeAttendance.ts frontend/src/api/monthlyPayroll.ts
git commit -m "feat: add realtime and monthly payroll api clients"
```
---
## 阶段 5实时考勤页面
### Task 5.1: 创建实时考勤页面
**Files:**
- Create: `frontend/src/views/RealtimeAttendanceView.vue`
- Modify: `frontend/src/router/index.ts`
**Step 1: 页面布局**
页面结构:
```text
顶部:今日考勤与薪资预估 / 日期选择 / 同步钉钉 / 自动刷新开关
摘要:应到人数、已打卡、未打卡、迟到、请假、预估加班
主表:员工今日考勤
右侧抽屉:员工本月明细
```
**Step 2: 主表字段**
```text
员工
部门/岗位
上班打卡
下班打卡
今日状态
迟到分钟
缺卡
请假工时
今日加班
本月剩余加班
本月预估实发
```
**Step 3: 文案**
页面必须展示:
```text
预估金额,最终以月度核算锁定结果为准
```
**Step 4: 视觉要求**
- 不使用大面积营销式卡片。
- 摘要卡片压扁,适合后台扫描。
- 表格上方保留紧凑筛选栏。
- 员工明细使用右侧抽屉,不跳页面。
**Step 5: 构建验证**
Run:
```bash
cd frontend
npm run build
```
Expected: PASS。
**Step 6: Commit**
```bash
git add frontend/src/views/RealtimeAttendanceView.vue frontend/src/router/index.ts
git commit -m "feat: add realtime attendance view"
```
---
### Task 5.2: 接入实时考勤页面数据
**Files:**
- Modify: `frontend/src/views/RealtimeAttendanceView.vue`
**Step 1: 加载数据**
页面 mounted 后默认请求今天:
```typescript
const today = new Date().toISOString().slice(0, 10);
await getTodayAttendance(today);
```
**Step 2: 同步钉钉**
点击 `同步钉钉` 调用:
```typescript
await syncRealtimeAttendance(selectedDate.value);
```
同步时按钮显示 loading成功后刷新摘要和表格。
**Step 3: 错误处理**
钉钉未配置时显示:
```text
钉钉配置缺失,请先在 config/app_settings.json 中配置 app_key 和 app_secret
```
**Step 4: 构建验证**
Run:
```bash
cd frontend
npm run build
```
Expected: PASS。
**Step 5: Commit**
```bash
git add frontend/src/views/RealtimeAttendanceView.vue
git commit -m "feat: connect realtime attendance data"
```
---
## 阶段 6月度核算中心页面
### Task 6.1: 创建月度核算中心页面
**Files:**
- Create: `frontend/src/views/MonthlyPayrollView.vue`
- Modify: `frontend/src/router/index.ts`
**Step 1: 页面结构**
```text
顶部:月份选择 / 当前状态 / 同步钉钉 / 导入 Excel / 导出 / 锁定
步骤条:数据导入 -> 考勤转换 -> 异常处理 -> 工资计算 -> 复核锁定
摘要条:员工数、应发、实发、扣款、剩余加班
主表:员工工资核算结果
右侧抽屉:员工每日考勤和工资拆分
异常面板:缺薪资档案、缺卡、迟到扣款、未匹配员工
```
**Step 2: 状态文案**
```text
draft: 草稿
calculated: 已计算
reviewed: 已复核
locked: 已锁定
failed: 失败
```
**Step 3: 锁定按钮**
`locked` 状态下禁用重新计算和导入,只保留导出。
**Step 4: 构建验证**
Run:
```bash
cd frontend
npm run build
```
Expected: PASS。
**Step 5: Commit**
```bash
git add frontend/src/views/MonthlyPayrollView.vue frontend/src/router/index.ts
git commit -m "feat: add monthly payroll center view"
```
---
### Task 6.2: 接入月度核算 API
**Files:**
- Modify: `frontend/src/views/MonthlyPayrollView.vue`
**Step 1: 默认月份**
默认当前月份:
```typescript
const selectedMonth = ref(new Date().toISOString().slice(0, 7));
```
**Step 2: 加载 run**
进入页面或月份变化时加载:
```typescript
await listMonthlyRuns(selectedMonth.value);
```
如果已有 run自动打开最新 run否则显示空状态和操作按钮。
**Step 3: Excel 导入**
导入按钮使用隐藏 file input不让用户填配置路径。
**Step 4: 钉钉计算**
点击 `同步钉钉并计算`,后端自动读取在职员工 `dingtalk_user_id`
**Step 5: 锁定确认**
锁定前弹窗确认:
```text
锁定后本月工资不能重新计算,只能导出。确认锁定?
```
**Step 6: 构建验证**
Run:
```bash
cd frontend
npm run build
```
Expected: PASS。
**Step 7: Commit**
```bash
git add frontend/src/views/MonthlyPayrollView.vue
git commit -m "feat: connect monthly payroll center api"
```
---
## 阶段 7工作台改造
### Task 7.1: 工作台改为流程总览
**Files:**
- Modify: `frontend/src/views/DashboardView.vue`
- Modify: `frontend/src/assets/styles.css`
**Step 1: 改成经营总览**
工作台展示:
```text
今日考勤状态
本月工资预估
本月核算状态
待处理异常
最近同步任务
```
**Step 2: 快捷入口**
保留按钮:
```text
进入实时考勤
进入月度核算
提成录入
员工维护
```
**Step 3: 移除假图表**
现在 `chartBars` 是静态假数据。要么接后端报表,要么先移除,避免误导。
**Step 4: 构建验证**
Run:
```bash
cd frontend
npm run build
```
Expected: PASS。
**Step 5: Commit**
```bash
git add frontend/src/views/DashboardView.vue frontend/src/assets/styles.css
git commit -m "feat: redesign dashboard around payroll workflow"
```
---
## 阶段 8文档与验证
### Task 8.1: 更新 README
**Files:**
- Modify: `README.md`
- Modify: `frontend/README.md`
**Step 1: README 增加模块说明**
补充:
```text
实时考勤看板
月度核算中心
薪资预估和正式工资的区别
锁定规则
新增 SQL 升级脚本
```
**Step 2: 更新运行说明**
说明后端仍然使用:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python main.py
```
前端仍然使用:
```bash
cd frontend
npm run dev
```
**Step 3: Commit**
```bash
git add README.md frontend/README.md
git commit -m "docs: update payroll center documentation"
```
---
### Task 8.2: 全量验证
**Files:**
- No source edits unless verification fails.
**Step 1: 后端编译**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python -m compileall financial_system
```
Expected: PASS。
**Step 2: 前端构建**
Run:
```bash
cd frontend
npm run build
```
Expected: PASS。
**Step 3: 启动后端**
Run:
```bash
/Users/jiaolongyan/miniconda3/envs/Financial_System/bin/python main.py
```
Expected: 后端启动成功,`/health` 返回正常。
**Step 4: 启动前端**
Run:
```bash
cd frontend
npm run dev
```
Expected: 前端启动成功。
**Step 5: 浏览器验收**
检查:
```text
/attendance/realtime
/payroll/monthly
/dashboard
/system/employees
/system/salary-profiles
/payroll/commissions
```
验收点:
- 菜单显示分组合理。
- 实时考勤能进入,未配置钉钉时提示明确。
- 月度核算能进入Excel 导入入口存在。
- 锁定状态不会允许重新计算。
- 侧栏收起/展开仍然平滑。
- 控制台无 error/warn。
**Step 6: Commit**
若修复了验证问题,按实际文件提交:
```bash
git add <changed-files>
git commit -m "fix: stabilize payroll center flow"
```
---
## 实施顺序建议
优先级从高到低:
1. 阶段 1数据库和菜单权限。
2. 阶段 2实时考勤后端。
3. 阶段 5实时考勤前端。
4. 阶段 3月度核算后端。
5. 阶段 6月度核算前端。
6. 阶段 7工作台改造。
7. 阶段 8文档和全量验证。
这样可以先让你看到“今天打卡和实时薪资预估”的效果,再继续完善月底正式核算。
## 风险点
- 钉钉接口真实字段可能和当前 `DingTalkAttendanceAdapter` 的假设不完全一致,需要用真实返回样例校准。
- 实时薪资只能作为预估,不能覆盖正式工资结果。
- 包月员工的“本月预估工资”要确认是否按自然日、工作日进度、还是直接展示已产生工资。
- 计件员工如果没有每日计件数据,只能显示已录入计件金额,不能凭考勤自动估算。
- 锁定工资后是否允许管理员解锁,需要单独确认。当前计划默认不提供解锁。
## 后续可选增强
- 自动同步定时任务:每天 09:30、12:00、18:30 同步钉钉。
- 异常提醒:缺卡、迟到、未绑定钉钉 userId 自动出现在工作台。
- 工资条:锁定后生成员工工资条。
- 审批流:人事核算、财务复核、老板确认。
- 导入计件数据:为计件员工增加单独的产量录入或 Excel 导入。