730 lines
20 KiB
Markdown
730 lines
20 KiB
Markdown
# 设计文档 - 员工月度绩效考核系统
|
||
|
||
## 概述
|
||
|
||
员工月度绩效考核系统是一个基于 Web 的全栈应用,采用前后端分离架构。系统集成 FastGPT AI 评分能力,实现员工绩效填报、AI 自动评分、管理层审核、数据统计分析的完整闭环。
|
||
|
||
### 技术栈选择
|
||
|
||
**后端:**
|
||
- Node.js + TypeScript
|
||
- Express.js 框架
|
||
- MySQL 数据库
|
||
- JWT 身份认证
|
||
- Axios(FastGPT API 调用)
|
||
|
||
**前端:**
|
||
- React + TypeScript
|
||
- Ant Design UI 组件库
|
||
- React Router(路由管理)
|
||
- Axios(HTTP 请求)
|
||
- ECharts(数据可视化)
|
||
|
||
### 核心设计原则
|
||
|
||
1. **角色权限分离**: 严格按照员工、管理层、总经理三种角色进行权限控制
|
||
2. **数据安全**: 敏感数据加密存储,操作日志完整记录
|
||
3. **流程自动化**: 绩效提交自动触发 AI 评分,审核完成自动归档
|
||
4. **可扩展性**: 模块化设计,便于后续功能扩展
|
||
|
||
## 架构设计
|
||
|
||
### 系统架构图
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph "前端层"
|
||
A[员工端页面]
|
||
B[管理层端页面]
|
||
C[总经理端页面]
|
||
end
|
||
|
||
subgraph "API 网关层"
|
||
D[Express API Server]
|
||
E[JWT 认证中间件]
|
||
F[权限验证中间件]
|
||
end
|
||
|
||
subgraph "业务逻辑层"
|
||
G[用户服务]
|
||
H[绩效服务]
|
||
I[AI 评分服务]
|
||
J[统计分析服务]
|
||
end
|
||
|
||
subgraph "数据访问层"
|
||
K[用户 DAO]
|
||
L[绩效 DAO]
|
||
M[AI 结果 DAO]
|
||
end
|
||
|
||
subgraph "外部服务"
|
||
N[FastGPT API]
|
||
end
|
||
|
||
subgraph "数据存储层"
|
||
O[(MySQL 数据库)]
|
||
end
|
||
|
||
A --> D
|
||
B --> D
|
||
C --> D
|
||
D --> E
|
||
E --> F
|
||
F --> G
|
||
F --> H
|
||
F --> J
|
||
H --> I
|
||
I --> N
|
||
G --> K
|
||
H --> L
|
||
I --> M
|
||
K --> O
|
||
L --> O
|
||
M --> O
|
||
```
|
||
|
||
### 数据流设计
|
||
|
||
**绩效提交流程:**
|
||
```
|
||
员工填报 → 前端验证 → API 提交 → 保存数据库 → 触发 AI 评分 → 调用 FastGPT → 解析结果 → 存储 AI 结果 → 返回成功
|
||
```
|
||
|
||
**绩效审核流程:**
|
||
```
|
||
管理层查看 → 加载绩效+AI结果 → 调整评分 → 填写意见 → 提交审核 → 计算等级奖惩 → 归档数据 → 通知员工
|
||
```
|
||
|
||
## 组件与接口
|
||
|
||
### 后端核心模块
|
||
|
||
#### 1. 用户认证模块 (AuthService)
|
||
|
||
**职责**: 处理用户登录、令牌生成与验证
|
||
|
||
**接口:**
|
||
```typescript
|
||
interface AuthService {
|
||
// 用户登录
|
||
login(username: string, password: string, role: string): Promise<LoginResult>;
|
||
|
||
// 验证令牌
|
||
verifyToken(token: string): Promise<UserInfo>;
|
||
|
||
// 刷新令牌
|
||
refreshToken(token: string): Promise<string>;
|
||
}
|
||
|
||
interface LoginResult {
|
||
token: string;
|
||
userInfo: UserInfo;
|
||
}
|
||
|
||
interface UserInfo {
|
||
userId: number;
|
||
name: string;
|
||
role: 'employee' | 'manager' | 'generalManager';
|
||
department: string;
|
||
position: string;
|
||
}
|
||
```
|
||
|
||
#### 2. 绩效管理模块 (PerformanceService)
|
||
|
||
**职责**: 处理绩效填报、查询、审核等核心业务逻辑
|
||
|
||
**接口:**
|
||
```typescript
|
||
interface PerformanceService {
|
||
// 提交绩效(暂存或提交)
|
||
submitPerformance(data: PerformanceSubmitDTO): Promise<PerformanceResult>;
|
||
|
||
// 查询员工个人绩效
|
||
getEmployeePerformance(userId: number, month?: string, page?: PageInfo): Promise<PerformanceListResult>;
|
||
|
||
// 查询管理层下属绩效
|
||
getManagerSubordinates(managerId: number, filters: PerformanceFilter, page: PageInfo): Promise<PerformanceListResult>;
|
||
|
||
// 审核绩效
|
||
reviewPerformance(perfId: number, reviewData: ReviewDTO): Promise<void>;
|
||
|
||
// 驳回绩效
|
||
rejectPerformance(perfId: number, reason: string): Promise<void>;
|
||
|
||
// 申请修改绩效
|
||
requestModification(perfId: number, reason: string): Promise<void>;
|
||
|
||
// 批准修改申请
|
||
approveModification(perfId: number): Promise<void>;
|
||
}
|
||
|
||
interface PerformanceSubmitDTO {
|
||
userId: number;
|
||
month: string;
|
||
status: 'draft' | 'submit';
|
||
selfScore: number;
|
||
attendance: AttendanceData;
|
||
workSummary: string;
|
||
performanceItems: PerformanceItemDTO[];
|
||
}
|
||
|
||
interface PerformanceItemDTO {
|
||
itemName: string;
|
||
weight: number;
|
||
userContent: string;
|
||
selfScore: number;
|
||
evidence?: string;
|
||
}
|
||
|
||
interface AttendanceData {
|
||
leave: number;
|
||
late: number;
|
||
absent: number;
|
||
lackCard: number;
|
||
remark?: string;
|
||
}
|
||
|
||
interface ReviewDTO {
|
||
perfId: number;
|
||
managerScore: number;
|
||
reviewOpinion: string;
|
||
itemScores: ItemScoreDTO[];
|
||
}
|
||
|
||
interface ItemScoreDTO {
|
||
itemName: string;
|
||
managerScore: number;
|
||
scoreExplanation: string;
|
||
}
|
||
```
|
||
|
||
#### 3. AI 评分模块 (AIEvaluationService)
|
||
|
||
**职责**: 调用 FastGPT API 进行自动评分和反馈生成
|
||
|
||
**接口:**
|
||
```typescript
|
||
interface AIEvaluationService {
|
||
// 执行 AI 评分
|
||
evaluatePerformance(perfId: number): Promise<AIResult>;
|
||
|
||
// 构建 AI 请求 Prompt
|
||
buildPrompt(performance: PerformanceRecord): string;
|
||
|
||
// 解析 AI 响应
|
||
parseAIResponse(response: string): AIScoreData;
|
||
|
||
// 重试机制
|
||
retryEvaluation(perfId: number, maxRetries: number): Promise<AIResult>;
|
||
}
|
||
|
||
interface AIResult {
|
||
aiId: number;
|
||
perfId: number;
|
||
aiScoreDetail: AIScoreItem[];
|
||
aiTotalScore: number;
|
||
aiProblems: string[];
|
||
aiSuggestions: string[];
|
||
createTime: Date;
|
||
}
|
||
|
||
interface AIScoreItem {
|
||
itemName: string;
|
||
weight: number;
|
||
aiScore: number;
|
||
scoreExplanation: string;
|
||
}
|
||
```
|
||
|
||
#### 4. 统计分析模块 (StatisticsService)
|
||
|
||
**职责**: 提供多维度数据统计和报表生成
|
||
|
||
**接口:**
|
||
```typescript
|
||
interface StatisticsService {
|
||
// 获取团队统计
|
||
getTeamStatistics(managerId: number, month: string): Promise<TeamStats>;
|
||
|
||
// 获取全公司统计
|
||
getCompanyStatistics(month: string): Promise<CompanyStats>;
|
||
|
||
// 多维度统计
|
||
getMultiDimensionStats(filters: StatFilter): Promise<MultiDimensionStats>;
|
||
|
||
// 导出 Excel
|
||
exportToExcel(filters: ExportFilter): Promise<Buffer>;
|
||
}
|
||
|
||
interface TeamStats {
|
||
averageScore: number;
|
||
excellentCount: number;
|
||
qualifiedCount: number;
|
||
needMotivationCount: number;
|
||
totalCount: number;
|
||
}
|
||
|
||
interface CompanyStats {
|
||
departmentStats: DepartmentStat[];
|
||
positionStats: PositionStat[];
|
||
levelDistribution: LevelDistribution;
|
||
}
|
||
```
|
||
|
||
### 前端核心组件
|
||
|
||
#### 1. 员工端组件
|
||
|
||
```typescript
|
||
// 绩效填报组件
|
||
interface PerformanceFormComponent {
|
||
props: {
|
||
month: string;
|
||
userInfo: UserInfo;
|
||
};
|
||
state: {
|
||
formData: PerformanceFormData;
|
||
isDraft: boolean;
|
||
};
|
||
methods: {
|
||
handleItemChange(index: number, field: string, value: any): void;
|
||
handleSave(): Promise<void>;
|
||
handleSubmit(): Promise<void>;
|
||
uploadEvidence(file: File): Promise<string>;
|
||
};
|
||
}
|
||
|
||
// 个人绩效查看组件
|
||
interface PerformanceHistoryComponent {
|
||
props: {
|
||
userId: number;
|
||
};
|
||
state: {
|
||
performanceList: PerformanceRecord[];
|
||
selectedMonth: string;
|
||
pagination: PaginationState;
|
||
};
|
||
methods: {
|
||
loadPerformanceList(): Promise<void>;
|
||
viewDetail(perfId: number): void;
|
||
filterByMonth(month: string): void;
|
||
};
|
||
}
|
||
```
|
||
|
||
#### 2. 管理层端组件
|
||
|
||
```typescript
|
||
// 下属绩效列表组件
|
||
interface SubordinateListComponent {
|
||
props: {
|
||
managerId: number;
|
||
};
|
||
state: {
|
||
subordinateList: PerformanceRecord[];
|
||
filters: PerformanceFilter;
|
||
pagination: PaginationState;
|
||
};
|
||
methods: {
|
||
loadSubordinateList(): Promise<void>;
|
||
applyFilters(filters: PerformanceFilter): void;
|
||
viewDetail(perfId: number): void;
|
||
};
|
||
}
|
||
|
||
// 绩效审核组件
|
||
interface PerformanceReviewComponent {
|
||
props: {
|
||
perfId: number;
|
||
};
|
||
state: {
|
||
performanceData: PerformanceRecord;
|
||
aiResult: AIResult;
|
||
reviewForm: ReviewFormData;
|
||
};
|
||
methods: {
|
||
loadPerformanceDetail(): Promise<void>;
|
||
adjustScore(itemName: string, score: number): void;
|
||
submitReview(): Promise<void>;
|
||
rejectPerformance(reason: string): Promise<void>;
|
||
};
|
||
}
|
||
```
|
||
|
||
#### 3. 总经理端组件
|
||
|
||
```typescript
|
||
// 全局统计组件
|
||
interface CompanyStatisticsComponent {
|
||
props: {
|
||
month: string;
|
||
};
|
||
state: {
|
||
companyStats: CompanyStats;
|
||
chartData: ChartData;
|
||
selectedDimension: string;
|
||
};
|
||
methods: {
|
||
loadStatistics(): Promise<void>;
|
||
switchDimension(dimension: string): void;
|
||
exportData(): Promise<void>;
|
||
};
|
||
}
|
||
```
|
||
|
||
## 数据模型
|
||
|
||
### 数据库表设计
|
||
|
||
#### 1. 用户表 (user)
|
||
|
||
```sql
|
||
CREATE TABLE user (
|
||
user_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
|
||
username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名(工号)',
|
||
password VARCHAR(255) NOT NULL COMMENT '密码(加密存储)',
|
||
name VARCHAR(50) NOT NULL COMMENT '姓名',
|
||
role ENUM('employee', 'manager', 'generalManager') NOT NULL COMMENT '角色',
|
||
department VARCHAR(50) NOT NULL COMMENT '部门',
|
||
position VARCHAR(50) NOT NULL COMMENT '岗位',
|
||
manager_id INT COMMENT '直属管理层ID',
|
||
status ENUM('active', 'inactive') DEFAULT 'active' COMMENT '状态',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
INDEX idx_role (role),
|
||
INDEX idx_manager (manager_id),
|
||
FOREIGN KEY (manager_id) REFERENCES user(user_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
|
||
```
|
||
|
||
#### 2. 绩效主表 (performance_month)
|
||
|
||
```sql
|
||
CREATE TABLE performance_month (
|
||
perf_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '绩效记录ID',
|
||
user_id INT NOT NULL COMMENT '员工ID',
|
||
month VARCHAR(7) NOT NULL COMMENT '考核月份(YYYY-MM)',
|
||
status ENUM('draft', 'submitted', 'under_review', 'completed', 'rejected') NOT NULL COMMENT '状态',
|
||
self_score DECIMAL(5,2) COMMENT '员工自评总分',
|
||
ai_score DECIMAL(5,2) COMMENT 'AI评分总分',
|
||
manager_score DECIMAL(5,2) COMMENT '管理层审核总分',
|
||
total_score DECIMAL(5,2) COMMENT '最终总分',
|
||
level ENUM('excellent', 'qualified', 'need_motivation', 'unqualified') COMMENT '绩效等级',
|
||
reward_punish VARCHAR(255) COMMENT '奖惩说明',
|
||
work_summary TEXT COMMENT '工作汇总',
|
||
submit_time TIMESTAMP COMMENT '提交时间',
|
||
review_time TIMESTAMP COMMENT '审核时间',
|
||
review_opinion TEXT COMMENT '审核意见',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
UNIQUE KEY uk_user_month (user_id, month),
|
||
INDEX idx_status (status),
|
||
INDEX idx_month (month),
|
||
FOREIGN KEY (user_id) REFERENCES user(user_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='绩效主表';
|
||
```
|
||
|
||
#### 3. 绩效项明细表 (perf_item)
|
||
|
||
```sql
|
||
CREATE TABLE perf_item (
|
||
item_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '绩效项ID',
|
||
perf_id INT NOT NULL COMMENT '绩效记录ID',
|
||
item_name VARCHAR(100) NOT NULL COMMENT '考核项名称',
|
||
item_category ENUM('business', 'comprehensive') NOT NULL COMMENT '考核项类别',
|
||
weight INT NOT NULL COMMENT '权重(分数)',
|
||
user_content TEXT COMMENT '员工填写内容',
|
||
self_score DECIMAL(5,2) COMMENT '员工自评分',
|
||
ai_score DECIMAL(5,2) COMMENT 'AI评分',
|
||
ai_explanation TEXT COMMENT 'AI评分说明',
|
||
manager_score DECIMAL(5,2) COMMENT '管理层评分',
|
||
manager_explanation TEXT COMMENT '管理层评分说明',
|
||
evidence_url VARCHAR(500) COMMENT '佐证材料URL',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
INDEX idx_perf (perf_id),
|
||
FOREIGN KEY (perf_id) REFERENCES performance_month(perf_id) ON DELETE CASCADE
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='绩效项明细表';
|
||
```
|
||
|
||
#### 4. 考勤表 (attendance)
|
||
|
||
```sql
|
||
CREATE TABLE attendance (
|
||
attendance_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '考勤ID',
|
||
perf_id INT NOT NULL COMMENT '绩效记录ID',
|
||
leave_days INT DEFAULT 0 COMMENT '事假天数',
|
||
late_times INT DEFAULT 0 COMMENT '迟到次数',
|
||
absent_days INT DEFAULT 0 COMMENT '旷工天数',
|
||
lack_card_times INT DEFAULT 0 COMMENT '缺卡次数',
|
||
attendance_score DECIMAL(5,2) COMMENT '考勤得分',
|
||
remark TEXT COMMENT '备注',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
UNIQUE KEY uk_perf (perf_id),
|
||
FOREIGN KEY (perf_id) REFERENCES performance_month(perf_id) ON DELETE CASCADE
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='考勤表';
|
||
```
|
||
|
||
#### 5. AI 结果表 (ai_result)
|
||
|
||
```sql
|
||
CREATE TABLE ai_result (
|
||
ai_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'AI结果ID',
|
||
perf_id INT NOT NULL COMMENT '绩效记录ID',
|
||
ai_score_json TEXT NOT NULL COMMENT 'AI评分详情(JSON格式)',
|
||
ai_total_score DECIMAL(5,2) NOT NULL COMMENT 'AI总分',
|
||
problems TEXT COMMENT 'AI总结的问题(JSON数组)',
|
||
suggestions TEXT COMMENT 'AI改进建议(JSON数组)',
|
||
api_response TEXT COMMENT 'FastGPT原始响应',
|
||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '生成时间',
|
||
UNIQUE KEY uk_perf (perf_id),
|
||
FOREIGN KEY (perf_id) REFERENCES performance_month(perf_id) ON DELETE CASCADE
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI结果表';
|
||
```
|
||
|
||
#### 6. 操作日志表 (operation_log)
|
||
|
||
```sql
|
||
CREATE TABLE operation_log (
|
||
log_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '日志ID',
|
||
user_id INT NOT NULL COMMENT '操作人ID',
|
||
operation_type VARCHAR(50) NOT NULL COMMENT '操作类型',
|
||
target_type VARCHAR(50) COMMENT '目标类型',
|
||
target_id INT COMMENT '目标ID',
|
||
operation_detail TEXT COMMENT '操作详情',
|
||
ip_address VARCHAR(50) COMMENT 'IP地址',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
INDEX idx_user (user_id),
|
||
INDEX idx_created (created_at),
|
||
FOREIGN KEY (user_id) REFERENCES user(user_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='操作日志表';
|
||
```
|
||
|
||
### 数据关系图
|
||
|
||
```mermaid
|
||
erDiagram
|
||
USER ||--o{ PERFORMANCE_MONTH : "has"
|
||
USER ||--o{ USER : "manages"
|
||
PERFORMANCE_MONTH ||--|{ PERF_ITEM : "contains"
|
||
PERFORMANCE_MONTH ||--|| ATTENDANCE : "has"
|
||
PERFORMANCE_MONTH ||--|| AI_RESULT : "has"
|
||
USER ||--o{ OPERATION_LOG : "performs"
|
||
|
||
USER {
|
||
int user_id PK
|
||
string username
|
||
string password
|
||
string name
|
||
enum role
|
||
string department
|
||
string position
|
||
int manager_id FK
|
||
}
|
||
|
||
PERFORMANCE_MONTH {
|
||
int perf_id PK
|
||
int user_id FK
|
||
string month
|
||
enum status
|
||
decimal self_score
|
||
decimal ai_score
|
||
decimal manager_score
|
||
decimal total_score
|
||
enum level
|
||
}
|
||
|
||
PERF_ITEM {
|
||
int item_id PK
|
||
int perf_id FK
|
||
string item_name
|
||
enum item_category
|
||
int weight
|
||
text user_content
|
||
decimal self_score
|
||
decimal ai_score
|
||
decimal manager_score
|
||
}
|
||
|
||
ATTENDANCE {
|
||
int attendance_id PK
|
||
int perf_id FK
|
||
int leave_days
|
||
int late_times
|
||
int absent_days
|
||
int lack_card_times
|
||
decimal attendance_score
|
||
}
|
||
|
||
AI_RESULT {
|
||
int ai_id PK
|
||
int perf_id FK
|
||
text ai_score_json
|
||
decimal ai_total_score
|
||
text problems
|
||
text suggestions
|
||
}
|
||
```
|
||
|
||
## 正确性属性
|
||
|
||
*正确性属性是系统应该在所有有效执行中保持为真的特征或行为——本质上是关于系统应该做什么的形式化陈述。属性作为人类可读规范和机器可验证正确性保证之间的桥梁。*
|
||
|
||
### 属性 1: 认证正确性
|
||
|
||
*对于任意* 用户凭据(用户名、密码、角色),认证结果应与凭据有效性严格一致——有效凭据必须返回 token,无效凭据必须被拒绝,不存在中间状态。
|
||
|
||
**Validates: Requirements 1.1, 1.2**
|
||
|
||
---
|
||
|
||
### 属性 2: 权限隔离不变量
|
||
|
||
*对于任意* 员工用户,使用其 token 查询其他员工的绩效数据时,系统应始终返回 403 权限不足错误,不泄露任何数据。
|
||
|
||
**Validates: Requirements 1.5**
|
||
|
||
---
|
||
|
||
### 属性 3: 草稿暂存往返一致性
|
||
|
||
*对于任意* 绩效填报数据,暂存后再读取,返回的数据应与暂存时提交的数据完全一致(往返属性)。
|
||
|
||
**Validates: Requirements 2.5**
|
||
|
||
---
|
||
|
||
### 属性 4: 提交幂等性
|
||
|
||
*对于任意* 已提交状态的绩效记录,再次尝试提交同一用户同一月份的绩效时,系统应拒绝并返回错误,绩效记录状态保持不变。
|
||
|
||
**Validates: Requirements 2.6**
|
||
|
||
---
|
||
|
||
### 属性 5: 绩效等级与奖惩计算正确性
|
||
|
||
*对于任意* 最终总分(0-100),系统计算出的绩效等级和奖惩金额应严格符合以下规则:
|
||
- 分数 >= 90 → 优秀,奖励
|
||
- 80 <= 分数 <= 89 → 合格,扣 100 元
|
||
- 70 <= 分数 <= 79 → 合格,扣 200 元
|
||
- 60 <= 分数 <= 69 → 需激励,扣 300 元
|
||
- 分数 < 60 → 不合格,扣 600 元
|
||
|
||
**Validates: Requirements 5.1, 5.2, 5.3, 5.4, 5.5**
|
||
|
||
---
|
||
|
||
### 属性 6: 连续低分预警正确性
|
||
|
||
*对于任意* 员工,若其连续 N 个月(N >= 2)的最终总分均低于 60 分,系统应正确标记对应的预警状态(N=2 书面警告,N>=3 劝退处理)。
|
||
|
||
**Validates: Requirements 5.6, 5.7**
|
||
|
||
---
|
||
|
||
### 属性 7: 绩效记录查询完整性
|
||
|
||
*对于任意* 已提交或已完成的绩效记录,员工查询个人历史绩效时,该记录必须出现在结果列表中,且内容与提交时一致(插入/查询往返属性)。
|
||
|
||
**Validates: Requirements 6.1, 6.2**
|
||
|
||
---
|
||
|
||
### 属性 8: 考勤分数计算正确性
|
||
|
||
*对于任意* 考勤数据(事假天数、迟到次数、缺卡次数),系统计算的考勤分数应满足:
|
||
- 基础分 10 分
|
||
- 每天事假扣 5 分
|
||
- 每次迟到/缺卡扣 2 分
|
||
- 最终分数不低于 0 分(下限保护)
|
||
|
||
**Validates: Requirements 11.1, 11.2, 11.3, 11.5**
|
||
|
||
---
|
||
|
||
### 属性 9: AI 响应 JSON 解析往返一致性
|
||
|
||
*对于任意* 符合规范的 AI 评分 JSON 字符串,系统解析后再序列化,应得到语义等价的对象(解析往返属性)。
|
||
|
||
**Validates: Requirements 3.6, 13.3**
|
||
|
||
---
|
||
|
||
### 属性 10: AI 输出格式约束
|
||
|
||
*对于任意* AI 评分结果,`ai_problems` 和 `ai_suggestions` 数组的长度应在 3 到 5 之间(含边界值)。
|
||
|
||
**Validates: Requirements 3.3, 3.4**
|
||
|
||
---
|
||
|
||
## 错误处理
|
||
|
||
### 错误码规范
|
||
|
||
| 状态码 | 含义 | 场景 |
|
||
|--------|------|------|
|
||
| 200 | 成功 | 正常响应 |
|
||
| 400 | 参数错误 | 请求参数缺失或格式错误 |
|
||
| 401 | 未登录/Token 失效 | Token 过期或未携带 |
|
||
| 403 | 权限不足 | 越权访问他人数据 |
|
||
| 500 | 服务器异常 | 内部错误 |
|
||
|
||
### 关键错误场景处理
|
||
|
||
**AI 调用失败:**
|
||
- 超时(>10s):记录错误日志,绩效状态标记为 `ai_failed`,管理员可手动触发重试
|
||
- 返回格式异常:记录原始响应,尝试降级解析,失败则通知管理员
|
||
|
||
**数据库操作失败:**
|
||
- 使用事务保证绩效提交的原子性(绩效主表 + 绩效项 + 考勤数据同时写入)
|
||
- 失败时回滚并返回 500 错误
|
||
|
||
**并发提交冲突:**
|
||
- 利用 `UNIQUE KEY uk_user_month (user_id, month)` 数据库约束防止重复提交
|
||
- 捕获唯一键冲突异常,返回友好提示
|
||
|
||
## 测试策略
|
||
|
||
### 双重测试方法
|
||
|
||
系统采用单元测试和属性测试相结合的方式,两者互补:
|
||
|
||
- **单元测试**: 验证具体示例、边界条件和错误场景
|
||
- **属性测试**: 验证跨所有输入的通用属性
|
||
|
||
### 属性测试配置
|
||
|
||
- 使用 **fast-check**(TypeScript 属性测试库)
|
||
- 每个属性测试最少运行 **100 次迭代**
|
||
- 每个测试用注释标注对应的设计属性编号
|
||
- 标注格式: `// Feature: employee-performance-system, Property N: <属性描述>`
|
||
|
||
### 属性测试覆盖
|
||
|
||
| 属性编号 | 测试内容 | 测试类型 |
|
||
|----------|----------|----------|
|
||
| 属性 1 | 认证凭据有效性 | property |
|
||
| 属性 2 | 权限隔离 | property |
|
||
| 属性 3 | 草稿暂存往返 | property |
|
||
| 属性 4 | 提交幂等性 | property |
|
||
| 属性 5 | 等级奖惩计算 | property |
|
||
| 属性 6 | 连续低分预警 | property |
|
||
| 属性 7 | 绩效记录查询 | property |
|
||
| 属性 8 | 考勤分数计算 | property |
|
||
| 属性 9 | AI JSON 解析往返 | property |
|
||
| 属性 10 | AI 输出格式约束 | property |
|
||
|
||
### 单元测试覆盖
|
||
|
||
- 用户登录接口(具体示例:正确凭据、错误密码、不存在用户)
|
||
- 绩效提交接口(具体示例:暂存、提交、重复提交)
|
||
- 审核流程(具体示例:通过、驳回、修改申请)
|
||
- Excel 导出格式验证
|
||
- 分页查询边界条件(第一页、最后一页、空结果)
|
||
|