# 健康师薪酬计算规则梳理 - 额外数据与金三角战队业绩整合逻辑 ## 一、核心问题 健康师的额外数据(`lq_salary_extra_calculation` 表)会影响健康师的业绩计算,变动后的业绩需要整合到金三角战队业绩中。 ## 二、数据流程梳理 ### 2.1 数据来源 #### 1. 基础业绩数据 - **表名**: `lq_kd_jksyj` (健康师业绩表) - **字段**: - `jkszh`: 健康师账号/ID - `jksyj`: 健康师业绩(字符串类型,需转换) - `PerformanceType`: 业绩类型("基础业绩" 或 "合作业绩") - `yjsj`: 业绩时间 - `glkdbh`: 关联开单编号 #### 2. 额外计算数据 - **表名**: `lq_salary_extra_calculation` (健康师工资额外计算表) - **关键字段**: - `F_EmployeeId`: 健康师ID - `F_Year`: 年份 - `F_Month`: 月份 - `F_BaseRewardPerformance`: 基础奖励业绩(需从基础业绩中扣除) - `F_CooperationRewardPerformance`: 合作奖励业绩(需从合作业绩中扣除) - `F_OtherPerformanceAdd`: **其他业绩加**(需加到总业绩中) - `F_OtherPerformanceSubtract`: **其他业绩减**(需从总业绩中扣除) - `F_NewCustomerPerformance`: 新客业绩(可能被额外数据覆盖) - `F_UpgradePerformance`: 升单业绩(可能被额外数据覆盖) #### 3. 退款数据 - **表名**: `lq_hytk_jksyj` (退款健康师业绩表) - **作用**: 从总业绩中扣除退款金额 #### 4. 金三角战队数据 - **表名**: `lq_jinsanjiao_user` (金三角用户表) - **表名**: `lq_ycsd_jsj` (金三角基础信息表) - **作用**: 确定健康师所属的战队 ### 2.2 当前计算流程(代码位置:`LqSalaryService.cs` 的 `CalculateHealthCoachSalary` 方法) #### 步骤1: 计算基础业绩数据(第563-591行) ```csharp // 2.1 计算个人业绩 var myPerf = performanceData.Where(x => x.Jks == empId).ToList(); salary.BasePerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "基础业绩") .Sum(x => decimal.Parse(x.Jksyj ?? "0")); salary.CooperationPerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "合作业绩") .Sum(x => decimal.Parse(x.Jksyj ?? "0")); salary.TotalPerformance = myPerf.Sum(x => decimal.Parse(x.Jksyj ?? "0")); // 第567行 // 扣除退款 var myRefunds = refundList.Where(x => x.Jks == empId).ToList(); if (myRefunds.Any()) { decimal totalRefund = myRefunds.Sum(x => x.Jksyj ?? 0); salary.TotalPerformance -= totalRefund; // 第584行 salary.BasePerformance -= baseRefund; salary.CooperationPerformance -= cooperationRefund; } // 新客与升单业绩(从开单记录中统计) salary.NewCustomerPerformance = myPerf.Where(x => string.Equals(x.Sfskdd, "是")) .Sum(x => decimal.Parse(x.Jksyj ?? "0")); salary.UpgradePerformance = myPerf.Where(x => string.Equals(x.Sfskdd, "否")) .Sum(x => decimal.Parse(x.Jksyj ?? "0")); ``` **当前问题**: - `TotalPerformance` 只统计了开单业绩并扣除了退款 - **没有加上 `OtherPerformanceAdd`** - **没有减去 `OtherPerformanceSubtract`** #### 步骤2: 填充额外计算数据(第593-605行) ```csharp // 2.1.1 填充额外计算数据 if (extraCalculationDict.ContainsKey(empId)) { var extraData = extraCalculationDict[empId]; salary.BaseRewardPerformance = extraData.BaseRewardPerformance; salary.CooperationRewardPerformance = extraData.CooperationRewardPerformance; salary.OtherPerformanceAdd = extraData.OtherPerformanceAdd; // 第599行 salary.OtherPerformanceSubtract = extraData.OtherPerformanceSubtract; // 第600行 salary.UpgradeCustomerCount = extraData.UpgradeCustomerCount; salary.NewCustomerConversionRate = extraData.NewCustomerConversionRate; salary.NewCustomerPerformance = extraData.NewCustomerPerformance; // 可能覆盖之前的值 salary.UpgradePerformance = extraData.UpgradePerformance; // 可能覆盖之前的值 } ``` **说明**: - 额外数据被填充到工资统计对象中 - `NewCustomerPerformance` 和 `UpgradePerformance` 可能被额外数据覆盖 #### 步骤3: 计算实际基础业绩和实际合作业绩(第607-636行) ```csharp // 2.1.2 计算实际基础业绩和实际合作业绩 // 实际基础业绩 = 基础业绩 - 基础奖励业绩 + 其他业绩加 - 其他业绩减 decimal actualBasePerformance = salary.BasePerformance - salary.BaseRewardPerformance + salary.OtherPerformanceAdd // 第615行:只影响实际基础业绩 - salary.OtherPerformanceSubtract; // 第616行:只影响实际基础业绩 // 新店额外调整:根据阶段扣除新客业绩或升单业绩 if (isNewStore) { if (newStoreStage == 1) { actualBasePerformance -= salary.NewCustomerPerformance; } else if (newStoreStage == 2) { actualBasePerformance -= salary.UpgradePerformance; } } salary.ActualBasePerformance = actualBasePerformance; // 实际合作业绩 = 合作业绩 - 合作奖励业绩 salary.ActualCooperationPerformance = salary.CooperationPerformance - salary.CooperationRewardPerformance; ``` **当前问题**: - `OtherPerformanceAdd` 和 `OtherPerformanceSubtract` **只用于计算 `ActualBasePerformance`** - **没有用于调整 `TotalPerformance`** #### 步骤4: 计算金三角战队业绩(第700-753行) ```csharp // 3. 处理战队逻辑 (考勤规则) // 规则:若出勤天数 < 20天,则该健康师不计入战队,按单人计算。 // 按战队分组 var teamGroups = employeeStats.Values .Where(x => !string.IsNullOrEmpty(x.GoldTriangleId)) .GroupBy(x => x.GoldTriangleId) .ToList(); foreach (var group in teamGroups) { var validMembers = new List(); var invalidMembers = new List(); foreach (var member in group) { if (member.WorkingDays >= 20) { validMembers.Add(member); } else { invalidMembers.Add(member); } } // 对于无效成员,移除战队标识,视为单人 foreach (var member in invalidMembers) { member.GoldTriangleId = null; member.GoldTriangleTeam = "个人"; member.Position = "健康师"; } // 计算有效战队的总业绩和总消耗 var teamTotalPerformance = validMembers.Sum(x => x.TotalPerformance); // 第735行 var teamTotalConsumption = validMembers.Sum(x => x.Consumption); // 更新有效成员的战队业绩和战队总消耗 foreach (var member in validMembers) { member.TeamPerformance = teamTotalPerformance; // 第741行 member.TeamTotalConsumption = teamTotalConsumption; } } // 补充处理:对于没有战队ID的(个人,或被剔除的),战队业绩等于个人总业绩 foreach (var salary in employeeStats.Values) { if (string.IsNullOrEmpty(salary.GoldTriangleId)) { salary.TeamPerformance = salary.TotalPerformance; // 第751行 } } ``` **当前问题**: - 第735行计算战队总业绩时,使用的是 `x.TotalPerformance` - 但 `TotalPerformance` **没有包含额外数据的调整**(`OtherPerformanceAdd` 和 `OtherPerformanceSubtract`) - 因此,**金三角战队业绩没有整合变动后的业绩** ## 三、问题分析 ### 3.1 核心问题 1. **总业绩未包含额外数据调整** - `TotalPerformance` 只统计了开单业绩并扣除了退款 - 没有加上 `OtherPerformanceAdd` - 没有减去 `OtherPerformanceSubtract` 2. **金三角战队业绩计算不准确** - 战队业绩 = 所有有效成员的总业绩之和 - 但使用的是未调整的 `TotalPerformance` - 导致额外数据的变动没有反映到战队业绩中 ### 3.2 影响范围 1. **健康师个人总业绩**: 不准确(缺少额外数据调整) 2. **金三角战队业绩**: 不准确(基于不准确的个人总业绩) 3. **提成计算**: 可能受影响(如果提成计算依赖 `TotalPerformance`) 4. **业绩占比**: 不准确(个人业绩占比 = 个人总业绩 / 战队总业绩) ## 四、解决方案建议 ### 4.1 方案一:在计算总业绩时直接加上额外数据调整 **修改位置**: `LqSalaryService.cs` 第567-605行 **修改逻辑**: ```csharp // 2.1 计算个人业绩 var myPerf = performanceData.Where(x => x.Jks == empId).ToList(); salary.BasePerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "基础业绩") .Sum(x => decimal.Parse(x.Jksyj ?? "0")); salary.CooperationPerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "合作业绩") .Sum(x => decimal.Parse(x.Jksyj ?? "0")); salary.TotalPerformance = myPerf.Sum(x => decimal.Parse(x.Jksyj ?? "0")); // 扣除退款 var myRefunds = refundList.Where(x => x.Jks == empId).ToList(); if (myRefunds.Any()) { decimal totalRefund = myRefunds.Sum(x => x.Jksyj ?? 0); salary.TotalPerformance -= totalRefund; salary.BasePerformance -= baseRefund; salary.CooperationPerformance -= cooperationRefund; } // 新客与升单业绩(从开单记录中统计) salary.NewCustomerPerformance = myPerf.Where(x => string.Equals(x.Sfskdd, "是")) .Sum(x => decimal.Parse(x.Jksyj ?? "0")); salary.UpgradePerformance = myPerf.Where(x => string.Equals(x.Sfskdd, "否")) .Sum(x => decimal.Parse(x.Jksyj ?? "0")); // 2.1.1 填充额外计算数据 if (extraCalculationDict.ContainsKey(empId)) { var extraData = extraCalculationDict[empId]; salary.BaseRewardPerformance = extraData.BaseRewardPerformance; salary.CooperationRewardPerformance = extraData.CooperationRewardPerformance; salary.OtherPerformanceAdd = extraData.OtherPerformanceAdd; salary.OtherPerformanceSubtract = extraData.OtherPerformanceSubtract; salary.UpgradeCustomerCount = extraData.UpgradeCustomerCount; salary.NewCustomerConversionRate = extraData.NewCustomerConversionRate; salary.NewCustomerPerformance = extraData.NewCustomerPerformance; salary.UpgradePerformance = extraData.UpgradePerformance; // 【新增】调整总业绩:加上其他业绩加,减去其他业绩减 salary.TotalPerformance += salary.OtherPerformanceAdd; salary.TotalPerformance -= salary.OtherPerformanceSubtract; } ``` **优点**: - 简单直接,在填充额外数据后立即调整总业绩 - 确保后续所有使用 `TotalPerformance` 的地方都能获取到调整后的值 **缺点**: - 需要确认 `OtherPerformanceAdd` 和 `OtherPerformanceSubtract` 是否应该影响总业绩 - 需要确认是否应该影响基础业绩或合作业绩 ### 4.2 方案二:在计算战队业绩前调整总业绩 **修改位置**: `LqSalaryService.cs` 第700-753行 **修改逻辑**: ```csharp // 3. 处理战队逻辑 (考勤规则) // 规则:若出勤天数 < 20天,则该健康师不计入战队,按单人计算。 // 【新增】在计算战队业绩前,先调整所有健康师的总业绩(加上额外数据) foreach (var salary in employeeStats.Values) { salary.TotalPerformance += salary.OtherPerformanceAdd; salary.TotalPerformance -= salary.OtherPerformanceSubtract; } // 按战队分组 var teamGroups = employeeStats.Values .Where(x => !string.IsNullOrEmpty(x.GoldTriangleId)) .GroupBy(x => x.GoldTriangleId) .ToList(); // ... 后续逻辑不变 ``` **优点**: - 集中处理,逻辑清晰 - 确保在计算战队业绩前,所有成员的总业绩都已调整 **缺点**: - 如果其他地方也使用 `TotalPerformance`,可能也需要调整 ### 4.3 方案三:使用调整后的总业绩字段 **修改逻辑**: - 保持 `TotalPerformance` 不变(原始总业绩) - 新增 `AdjustedTotalPerformance` 字段(调整后的总业绩) - 在计算战队业绩时使用 `AdjustedTotalPerformance` **优点**: - 保留原始数据,便于追溯 - 明确区分原始业绩和调整后业绩 **缺点**: - 需要修改数据库表结构 - 需要修改所有使用总业绩的地方 ## 五、推荐方案 **推荐使用方案一**,原因: 1. 逻辑简单,在填充额外数据后立即调整总业绩 2. 确保后续所有使用 `TotalPerformance` 的地方都能获取到调整后的值 3. 不需要修改数据库表结构 4. 符合业务逻辑:额外数据应该影响总业绩 ## 六、需要确认的问题 1. **`OtherPerformanceAdd` 和 `OtherPerformanceSubtract` 是否应该影响总业绩?** - 当前代码中,这两个字段只影响 `ActualBasePerformance` - 需要确认业务规则:是否应该同时影响 `TotalPerformance` 2. **额外数据中的 `NewCustomerPerformance` 和 `UpgradePerformance` 是否应该覆盖从开单记录中统计的值?** - 当前代码中,如果存在额外数据,会覆盖之前统计的值 - 需要确认业务规则:是覆盖还是累加? 3. **额外数据是否应该影响基础业绩或合作业绩?** - 当前代码中,`OtherPerformanceAdd` 和 `OtherPerformanceSubtract` 只影响 `ActualBasePerformance` - 需要确认:是否应该同时调整 `BasePerformance` 或 `CooperationPerformance`? 4. **金三角战队业绩的计算时机** - 当前在计算战队业绩时,使用的是 `TotalPerformance` - 如果 `TotalPerformance` 没有包含额外数据调整,战队业绩就不准确 - 需要确认:是否应该在计算战队业绩前,先调整所有成员的总业绩? ## 七、代码修改建议 ### 7.1 修改位置 **文件**: `netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs` **方法**: `CalculateHealthCoachSalary` **位置**: 第593-605行(填充额外计算数据后) ### 7.2 修改内容 在填充额外计算数据后,立即调整总业绩: ```csharp // 2.1.1 填充额外计算数据 if (extraCalculationDict.ContainsKey(empId)) { var extraData = extraCalculationDict[empId]; salary.BaseRewardPerformance = extraData.BaseRewardPerformance; salary.CooperationRewardPerformance = extraData.CooperationRewardPerformance; salary.OtherPerformanceAdd = extraData.OtherPerformanceAdd; salary.OtherPerformanceSubtract = extraData.OtherPerformanceSubtract; salary.UpgradeCustomerCount = extraData.UpgradeCustomerCount; salary.NewCustomerConversionRate = extraData.NewCustomerConversionRate; salary.NewCustomerPerformance = extraData.NewCustomerPerformance; salary.UpgradePerformance = extraData.UpgradePerformance; // 【新增】调整总业绩:加上其他业绩加,减去其他业绩减 // 注意:这里调整的是总业绩,确保后续计算(包括战队业绩)使用的是调整后的值 salary.TotalPerformance += salary.OtherPerformanceAdd; salary.TotalPerformance -= salary.OtherPerformanceSubtract; } ``` ### 7.3 影响分析 **修改后的影响**: 1. ✅ 健康师个人总业绩包含额外数据调整 2. ✅ 金三角战队业绩 = 所有有效成员的调整后总业绩之和 3. ✅ 提成计算(如果依赖 `TotalPerformance`)会使用调整后的值 4. ✅ 业绩占比计算会使用调整后的值 **需要注意**: - 确保 `OtherPerformanceAdd` 和 `OtherPerformanceSubtract` 的值是正确的 - 确保额外数据表(`lq_salary_extra_calculation`)的数据是准确的 - 如果后续有其他地方也使用 `TotalPerformance`,需要确认是否应该使用调整后的值 ## 八、测试建议 1. **单元测试**: - 测试额外数据为0时,总业绩不变 - 测试 `OtherPerformanceAdd > 0` 时,总业绩增加 - 测试 `OtherPerformanceSubtract > 0` 时,总业绩减少 - 测试两者同时存在时的计算 2. **集成测试**: - 测试单个健康师的额外数据调整后,总业绩是否正确 - 测试战队中多个成员都有额外数据时,战队总业绩是否正确 - 测试战队中部分成员有额外数据时,战队总业绩是否正确 3. **数据验证**: - 对比修改前后的计算结果 - 验证金三角战队业绩是否包含额外数据调整 - 验证业绩占比计算是否正确 --- **文档版本**: v1.0 **创建日期**: 2026-01-09 **适用范围**: 健康师薪酬计算 - 额外数据与金三角战队业绩整合