# 月度核算中心与实时考勤看板 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 导入。
