# 员工工资查询接口逻辑梳理
## 📋 概述
所有薪酬服务都提供了根据员工ID和月份查询工资的接口,供员工查看自己的工资条。本文档梳理了所有薪酬服务中的查询逻辑。
---
## 🔍 接口列表
### 1. 健康师工资查询
- **服务**: `LqSalaryService`
- **路由**: `GET /api/Extend/lqsalary/query-by-employee`
- **方法**: `GetSalaryByEmployee`
- **返回类型**: `HealthCoachSalaryOutput`
- **数据表**: `lq_salary_statistics`
### 2. 店长工资查询
- **服务**: `LqStoreManagerSalaryService`
- **路由**: `GET /api/Extend/lqstoremanagersalary/query-by-employee`
- **方法**: `GetSalaryByEmployee`
- **返回类型**: `StoreManagerSalaryOutput`
- **数据表**: `lq_store_manager_salary_statistics`
### 3. 主任工资查询
- **服务**: `LqDirectorSalaryService`
- **路由**: `GET /api/Extend/lqdirectorsalary/query-by-employee`
- **方法**: `GetSalaryByEmployee`
- **返回类型**: `DirectorSalaryOutput`
- **数据表**: `lq_director_salary_statistics`
### 4. 店助工资查询
- **服务**: `LqAssistantSalaryService`
- **路由**: `GET /api/Extend/lqassistantsalary/query-by-employee`
- **方法**: `GetSalaryByEmployee`
- **返回类型**: `AssistantSalaryOutput`
- **数据表**: `lq_assistant_salary_statistics`
### 5. 事业部总经理/经理工资查询
- **服务**: `LqBusinessUnitManagerSalaryService`
- **路由**: `GET /api/Extend/lqbusinessunitmanagersalary/query-by-employee`
- **方法**: `GetSalaryByEmployee`
- **返回类型**: `BusinessUnitManagerSalaryOutput`
- **数据表**: `lq_business_unit_manager_salary_statistics`
### 6. 科技部老师工资查询
- **服务**: `LqTechTeacherSalaryService`
- **路由**: `GET /api/Extend/lqtechteachersalary/query-by-employee`
- **方法**: `GetSalaryByEmployee`
- **返回类型**: `TechTeacherSalaryOutput`
- **数据表**: `lq_tech_teacher_salary_statistics`
### 7. 科技部总经理工资查询
- **服务**: `LqTechGeneralManagerSalaryService`
- **路由**: `GET /api/Extend/lqtechgeneralmanagersalary/query-by-employee`
- **方法**: `GetSalaryByEmployee`
- **返回类型**: `TechGeneralManagerSalaryOutput`
- **数据表**: `lq_tech_general_manager_salary_statistics`
### 8. 大项目主管工资查询
- **服务**: `LqMajorProjectDirectorSalaryService`
- **路由**: `GET /api/Extend/lqmajorprojectdirectorsalary/query-by-employee`
- **方法**: `GetSalaryByEmployee`
- **返回类型**: `MajorProjectDirectorSalaryOutput`
- **数据表**: `lq_major_project_director_salary_statistics`
### 9. 大项目部老师工资查询
- **服务**: `LqMajorProjectTeacherSalaryService`
- **路由**: `GET /api/Extend/lqmajorprojectteachersalary/query-by-employee`
- **方法**: `GetSalaryByEmployee`
- **返回类型**: `MajorProjectTeacherSalaryOutput`
- **数据表**: `lq_major_project_teacher_salary_statistics`
---
## 📝 统一查询逻辑
### 输入参数
所有接口都使用相同的输入参数类:`SalaryQueryByEmployeeInput`
```csharp
public class SalaryQueryByEmployeeInput
{
///
/// 年份
///
public int Year { get; set; }
///
/// 月份
///
public int Month { get; set; }
///
/// 员工ID
///
public string EmployeeId { get; set; }
}
```
### 查询条件
所有接口的查询条件都相同:
```csharp
.Where(x =>
x.StatisticsMonth == monthStr // 统计月份匹配
&& x.EmployeeId == input.EmployeeId // 员工ID匹配
&& x.IsLocked == 1 // 只查询已锁定的工资
&& x.EmployeeConfirmStatus != 1 // 只查询未确认的工资
)
```
**关键点**:
- ✅ **只查询已锁定的工资**:`IsLocked == 1`
- ✅ **只查询未确认的工资**:`EmployeeConfirmStatus != 1`(已确认的工资无法查询)
- ✅ **员工ID匹配**:`EmployeeId == input.EmployeeId`
- ✅ **月份匹配**:`StatisticsMonth == monthStr`(格式:YYYYMM)
**重要说明**:
- 员工只能查看**已锁定但未确认**的工资记录
- 一旦员工确认工资后(`EmployeeConfirmStatus = 1`),该工资记录将无法通过此接口查询
- 这个设计确保员工在确认工资后,无法再次查看已确认的工资记录
### 参数验证
所有接口都进行相同的参数验证:
```csharp
// 1. 验证年份和月份
if (input.Year <= 0 || input.Month <= 0 || input.Month > 12)
{
throw NCCException.Oh("年份和月份参数不正确");
}
// 2. 验证员工ID
if (string.IsNullOrWhiteSpace(input.EmployeeId))
{
throw NCCException.Oh("员工ID不能为空");
}
// 3. 格式化月份
var monthStr = $"{input.Year}{input.Month:D2}"; // 例如:202512
```
### 查询结果处理
```csharp
// 查询工资记录
var salary = await _db.Queryable()
.Where(x => x.StatisticsMonth == monthStr
&& x.EmployeeId == input.EmployeeId
&& x.IsLocked == 1)
.Select(x => new SalaryOutput { /* 字段映射 */ })
.FirstAsync();
// 如果未找到,抛出异常
if (salary == null)
{
throw NCCException.Oh($"未找到员工{input.EmployeeId}在{input.Year}年{input.Month}月的工资记录");
}
return salary;
```
---
## 🔐 安全机制
### 1. 锁定机制
- **只查询已锁定的工资**:`IsLocked == 1`
- **目的**:确保员工只能查看已完成的工资数据,避免查看未完成计算的工资
- **业务逻辑**:工资计算完成后,管理员需要先锁定工资,员工才能查看
### 1.1 确认状态限制
- **只查询未确认的工资**:`EmployeeConfirmStatus != 1`
- **目的**:员工确认工资后,该工资记录将无法再次查询
- **业务逻辑**:
1. 工资计算完成 → 管理员锁定(`IsLocked = 1`)
2. 员工可以查看工资(`IsLocked = 1` 且 `EmployeeConfirmStatus != 1`)
3. 员工确认工资(`EmployeeConfirmStatus = 1`)
4. 确认后无法再次查询(`EmployeeConfirmStatus = 1` 的记录被排除)
### 2. 员工ID匹配
- **精确匹配**:`EmployeeId == input.EmployeeId`
- **目的**:确保员工只能查看自己的工资,不能查看其他员工的工资
- **实现方式**:
- 后端:通过SQL查询条件 `EmployeeId == input.EmployeeId` 实现精确匹配
- 前端:从本地存储获取当前登录用户的ID,自动填充到查询参数中
### 3. 月份限制
- **格式验证**:月份必须在 1-12 之间
- **目的**:确保查询参数的有效性
### 4. 权限验证说明
#### 当前实现
- **后端**:接口**没有**验证当前登录用户,只通过 `EmployeeId` 参数查询
- **前端**:从 `uni.getStorageSync('userInfo')` 获取用户ID,自动填充到查询参数
- **安全依赖**:依赖前端确保传入的 `EmployeeId` 是当前登录用户的ID
#### 潜在安全问题
- **风险**:如果前端被篡改,可能会查询到其他员工的工资
- **现状**:目前通过SQL查询条件 `EmployeeId == input.EmployeeId` 实现精确匹配,但**没有验证** `input.EmployeeId` 是否与当前登录用户ID一致
#### 建议改进
可以在后端增加权限验证,确保员工只能查询自己的工资:
```csharp
// 获取当前登录用户ID
var currentUserId = _userManager.UserId;
// 验证:员工只能查询自己的工资
if (input.EmployeeId != currentUserId && !_userManager.IsAdministrator)
{
throw NCCException.Oh("您只能查询自己的工资记录");
}
```
**注意**:管理员可能需要查询所有员工的工资,所以需要判断 `IsAdministrator`
---
## 📊 数据流程
### 查询流程
```
1. 接收请求参数(Year, Month, EmployeeId)
↓
2. 参数验证
- 年份和月份有效性检查
- 员工ID非空检查
↓
3. 格式化月份(YYYYMM格式)
↓
4. 查询数据库
- 条件:StatisticsMonth == monthStr
- 条件:EmployeeId == input.EmployeeId
- 条件:IsLocked == 1(已锁定)
- 条件:EmployeeConfirmStatus != 1(未确认)
↓
5. 数据映射(Entity → Output DTO)
↓
6. 结果验证
- 如果未找到,抛出异常
↓
7. 返回工资记录
```
### 数据表结构
每个薪酬服务对应一个工资统计表:
| 服务 | 数据表 | 主键字段 | 员工ID字段 | 月份字段 | 锁定字段 |
|------|--------|---------|-----------|---------|---------|
| 健康师 | `lq_salary_statistics` | `F_Id` | `F_EmployeeId` | `F_StatisticsMonth` | `F_IsLocked` |
| 店长 | `lq_store_manager_salary_statistics` | `F_Id` | `F_EmployeeId` | `F_StatisticsMonth` | `F_IsLocked` |
| 主任 | `lq_director_salary_statistics` | `F_Id` | `F_EmployeeId` | `F_StatisticsMonth` | `F_IsLocked` |
| 店助 | `lq_assistant_salary_statistics` | `F_Id` | `F_EmployeeId` | `F_StatisticsMonth` | `F_IsLocked` |
| 事业部总经理/经理 | `lq_business_unit_manager_salary_statistics` | `F_Id` | `F_EmployeeId` | `F_StatisticsMonth` | `F_IsLocked` |
| 科技部老师 | `lq_tech_teacher_salary_statistics` | `F_Id` | `F_EmployeeId` | `F_StatisticsMonth` | `F_IsLocked` |
| 科技部总经理 | `lq_tech_general_manager_salary_statistics` | `F_Id` | `F_EmployeeId` | `F_StatisticsMonth` | `F_IsLocked` |
| 大项目主管 | `lq_major_project_director_salary_statistics` | `F_Id` | `F_EmployeeId` | `F_StatisticsMonth` | `F_IsLocked` |
| 大项目部老师 | `lq_major_project_teacher_salary_statistics` | `F_Id` | `F_EmployeeId` | `F_StatisticsMonth` | `F_IsLocked` |
---
## 🔄 与其他功能的关系
### 1. 工资计算
- **关系**:查询接口依赖工资计算接口生成的数据
- **流程**:先执行计算接口(`calculate/*`),生成工资记录,然后才能查询
### 2. 工资锁定
- **关系**:查询接口只返回已锁定的工资
- **流程**:工资计算完成后,需要锁定(`IsLocked = 1`),员工才能查看
### 3. 员工确认
- **关系**:查询接口返回的数据包含确认状态(`EmployeeConfirmStatus`)
- **流程**:员工查看工资后,可以确认工资条
---
## ⚠️ 注意事项
### 1. 锁定状态
- **必须锁定**:只有已锁定的工资才能被员工查询
- **未锁定处理**:如果工资未锁定,查询接口会返回404错误
### 1.1 确认状态
- **必须未确认**:只有未确认的工资才能被员工查询
- **已确认处理**:如果工资已确认(`EmployeeConfirmStatus = 1`),查询接口会返回404错误
- **业务含义**:员工确认工资后,该工资记录将无法再次查询,确保数据安全
### 2. 员工ID匹配
- **精确匹配**:必须使用正确的员工ID
- **安全考虑**:接口不验证当前登录用户,需要前端或中间件确保员工只能查询自己的工资
### 3. 月份格式
- **格式要求**:月份必须格式化为 YYYYMM(如:202512)
- **验证**:月份必须在 1-12 之间
### 4. 数据完整性
- **字段映射**:每个服务的Output DTO字段可能不同
- **空值处理**:如果未找到记录,返回404错误
---
## 📋 接口调用示例
### 健康师工资查询
```http
GET /api/Extend/lqsalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
```
### 店长工资查询
```http
GET /api/Extend/lqstoremanagersalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
```
### 主任工资查询
```http
GET /api/Extend/lqdirectorsalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
```
### 店助工资查询
```http
GET /api/Extend/lqassistantsalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
```
### 事业部总经理/经理工资查询
```http
GET /api/Extend/lqbusinessunitmanagersalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
```
### 科技部老师工资查询
```http
GET /api/Extend/lqtechteachersalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
```
### 科技部总经理工资查询
```http
GET /api/Extend/lqtechgeneralmanagersalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
```
### 大项目主管工资查询
```http
GET /api/Extend/lqmajorprojectdirectorsalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
```
### 大项目部老师工资查询
```http
GET /api/Extend/lqmajorprojectteachersalary/query-by-employee?Year=2025&Month=12&EmployeeId=员工ID
```
---
## 🔍 代码实现对比
### 共同点
所有服务的查询逻辑都相同:
1. **参数验证**:年份、月份、员工ID验证
2. **月份格式化**:`$"{input.Year}{input.Month:D2}"`
3. **查询条件**:`StatisticsMonth == monthStr && EmployeeId == input.EmployeeId && IsLocked == 1`
4. **异常处理**:未找到记录时抛出异常
5. **返回类型**:返回对应的Output DTO
### 差异点
1. **数据表不同**:每个服务查询不同的工资统计表
2. **Output DTO不同**:每个服务返回的字段可能不同
3. **字段映射不同**:根据岗位不同,返回的工资字段不同
---
## 📝 总结
### 核心逻辑
1. **统一接口**:所有薪酬服务都提供 `query-by-employee` 接口
2. **统一参数**:都使用 `SalaryQueryByEmployeeInput` 作为输入参数
3. **统一条件**:都查询已锁定(`IsLocked == 1`)的工资记录
4. **统一验证**:都进行相同的参数验证和异常处理
### 安全机制
1. **锁定检查**:只查询已锁定的工资
2. **员工匹配**:精确匹配员工ID
3. **参数验证**:验证年份、月份、员工ID的有效性
### 使用场景
1. **员工查看工资条**:员工通过小程序或PC端查看自己的工资
2. **工资确认**:员工查看工资后,可以确认工资条
3. **历史查询**:员工可以查询历史月份的工资记录
---
**文档版本**: v1.0
**创建日期**: 2026-01-09
**适用范围**: 所有薪酬服务的员工工资查询接口