# 员工工资查询接口逻辑梳理 ## 📋 概述 所有薪酬服务都提供了根据员工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 **适用范围**: 所有薪酬服务的员工工资查询接口