diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryOutput.cs
index 652896e..323369b 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryOutput.cs
@@ -111,5 +111,45 @@ namespace NCC.Extend.Entitys.Dto.LqSalary
/// 更新时间
///
public DateTime UpdateTime { get; set; }
+
+ ///
+ /// 是否新店
+ ///
+ public string IsNewStore { get; set; }
+
+ ///
+ /// 新店保护阶段
+ ///
+ public int NewStoreProtectionStage { get; set; }
+
+ ///
+ /// 门店类型
+ ///
+ public int? StoreType { get; set; }
+
+ ///
+ /// 门店类别
+ ///
+ public int? StoreCategory { get; set; }
+
+ ///
+ /// 实际基础业绩
+ ///
+ public decimal ActualBasePerformance { get; set; }
+
+ ///
+ /// 实际合作业绩
+ ///
+ public decimal ActualCooperationPerformance { get; set; }
+
+ ///
+ /// 新客业绩提成
+ ///
+ public decimal NewCustomerCommission { get; set; }
+
+ ///
+ /// 升单业绩提成
+ ///
+ public decimal UpgradeCommission { get; set; }
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationImportInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationImportInput.cs
new file mode 100644
index 0000000..46fb219
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationImportInput.cs
@@ -0,0 +1,79 @@
+namespace NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation
+{
+ ///
+ /// 健康师工资额外计算导入数据
+ ///
+ public class SalaryExtraCalculationImportInput
+ {
+ ///
+ /// ID(可选,用于更新已有记录)
+ ///
+ public string Id { get; set; }
+
+ ///
+ /// 健康师姓名
+ ///
+ public string EmployeeName { get; set; }
+
+ ///
+ /// 健康师电话
+ ///
+ public string EmployeePhone { get; set; }
+
+ ///
+ /// 年份
+ ///
+ public int Year { get; set; }
+
+ ///
+ /// 月份
+ ///
+ public int Month { get; set; }
+
+ ///
+ /// 基础奖励业绩
+ ///
+ public decimal BaseRewardPerformance { get; set; }
+
+ ///
+ /// 合作奖励业绩
+ ///
+ public decimal CooperationRewardPerformance { get; set; }
+
+ ///
+ /// 新客业绩
+ ///
+ public decimal NewCustomerPerformance { get; set; }
+
+ ///
+ /// 新客成交率
+ ///
+ public decimal NewCustomerConversionRate { get; set; }
+
+ ///
+ /// 升单业绩
+ ///
+ public decimal UpgradePerformance { get; set; }
+
+ ///
+ /// 升单成交率
+ ///
+ public decimal UpgradeConversionRate { get; set; }
+
+ ///
+ /// 升单人头数
+ ///
+ public decimal UpgradeCustomerCount { get; set; }
+
+ ///
+ /// 其他业绩加
+ ///
+ public decimal OtherPerformanceAdd { get; set; }
+
+ ///
+ /// 其他业绩减
+ ///
+ public decimal OtherPerformanceSubtract { get; set; }
+ }
+}
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationInput.cs
new file mode 100644
index 0000000..aa2cf3b
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationInput.cs
@@ -0,0 +1,32 @@
+using NCC.Common.Filter;
+using System;
+
+namespace NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation
+{
+ ///
+ /// 健康师工资额外计算查询参数
+ ///
+ public class SalaryExtraCalculationInput : PageInputBase
+ {
+ ///
+ /// 年份
+ ///
+ public int? Year { get; set; }
+
+ ///
+ /// 月份
+ ///
+ public int? Month { get; set; }
+
+ ///
+ /// 健康师ID(可选,用于筛选特定健康师)
+ ///
+ public string EmployeeId { get; set; }
+
+ ///
+ /// 健康师姓名/电话(可选,用于模糊搜索)
+ ///
+ public string Keyword { get; set; }
+ }
+}
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationOutput.cs
new file mode 100644
index 0000000..a11ecda
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationOutput.cs
@@ -0,0 +1,86 @@
+using System;
+
+namespace NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation
+{
+ ///
+ /// 健康师工资额外计算输出
+ ///
+ public class SalaryExtraCalculationOutput
+ {
+ ///
+ /// 主键ID
+ ///
+ public string Id { get; set; }
+
+ ///
+ /// 健康师ID
+ ///
+ public string EmployeeId { get; set; }
+
+ ///
+ /// 健康师姓名
+ ///
+ public string EmployeeName { get; set; }
+
+ ///
+ /// 健康师电话
+ ///
+ public string EmployeePhone { get; set; }
+
+ ///
+ /// 年份
+ ///
+ public int Year { get; set; }
+
+ ///
+ /// 月份
+ ///
+ public int Month { get; set; }
+
+ ///
+ /// 基础奖励业绩
+ ///
+ public decimal BaseRewardPerformance { get; set; }
+
+ ///
+ /// 合作奖励业绩
+ ///
+ public decimal CooperationRewardPerformance { get; set; }
+
+ ///
+ /// 新客业绩
+ ///
+ public decimal NewCustomerPerformance { get; set; }
+
+ ///
+ /// 新客成交率
+ ///
+ public decimal NewCustomerConversionRate { get; set; }
+
+ ///
+ /// 升单业绩
+ ///
+ public decimal UpgradePerformance { get; set; }
+
+ ///
+ /// 升单成交率
+ ///
+ public decimal UpgradeConversionRate { get; set; }
+
+ ///
+ /// 升单人头数
+ ///
+ public decimal UpgradeCustomerCount { get; set; }
+
+ ///
+ /// 其他业绩加
+ ///
+ public decimal OtherPerformanceAdd { get; set; }
+
+ ///
+ /// 其他业绩减
+ ///
+ public decimal OtherPerformanceSubtract { get; set; }
+ }
+}
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_salary_extra_calculation/LqSalaryExtraCalculationEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_salary_extra_calculation/LqSalaryExtraCalculationEntity.cs
new file mode 100644
index 0000000..553bd6b
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_salary_extra_calculation/LqSalaryExtraCalculationEntity.cs
@@ -0,0 +1,93 @@
+using System;
+using NCC.Common.Const;
+using SqlSugar;
+
+namespace NCC.Extend.Entitys.lq_salary_extra_calculation
+{
+ ///
+ /// 健康师工资额外计算表
+ ///
+ [SugarTable("lq_salary_extra_calculation")]
+ [Tenant(ClaimConst.TENANT_ID)]
+ public class LqSalaryExtraCalculationEntity
+ {
+ ///
+ /// 主键ID
+ ///
+ [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)]
+ public string Id { get; set; }
+
+ ///
+ /// 健康师ID
+ ///
+ [SugarColumn(ColumnName = "F_EmployeeId")]
+ public string EmployeeId { get; set; }
+
+ ///
+ /// 年份
+ ///
+ [SugarColumn(ColumnName = "F_Year")]
+ public int Year { get; set; }
+
+ ///
+ /// 月份
+ ///
+ [SugarColumn(ColumnName = "F_Month")]
+ public int Month { get; set; }
+
+ ///
+ /// 基础奖励业绩
+ ///
+ [SugarColumn(ColumnName = "F_BaseRewardPerformance")]
+ public decimal BaseRewardPerformance { get; set; }
+
+ ///
+ /// 合作奖励业绩
+ ///
+ [SugarColumn(ColumnName = "F_CooperationRewardPerformance")]
+ public decimal CooperationRewardPerformance { get; set; }
+
+ ///
+ /// 新客业绩
+ ///
+ [SugarColumn(ColumnName = "F_NewCustomerPerformance")]
+ public decimal NewCustomerPerformance { get; set; }
+
+ ///
+ /// 新客成交率
+ ///
+ [SugarColumn(ColumnName = "F_NewCustomerConversionRate")]
+ public decimal NewCustomerConversionRate { get; set; }
+
+ ///
+ /// 升单业绩
+ ///
+ [SugarColumn(ColumnName = "F_UpgradePerformance")]
+ public decimal UpgradePerformance { get; set; }
+
+ ///
+ /// 升单成交率
+ ///
+ [SugarColumn(ColumnName = "F_UpgradeConversionRate")]
+ public decimal UpgradeConversionRate { get; set; }
+
+ ///
+ /// 升单人头数
+ ///
+ [SugarColumn(ColumnName = "F_UpgradeCustomerCount")]
+ public decimal UpgradeCustomerCount { get; set; }
+
+ ///
+ /// 其他业绩加
+ ///
+ [SugarColumn(ColumnName = "F_OtherPerformanceAdd")]
+ public decimal OtherPerformanceAdd { get; set; }
+
+ ///
+ /// 其他业绩减
+ ///
+ [SugarColumn(ColumnName = "F_OtherPerformanceSubtract")]
+ public decimal OtherPerformanceSubtract { get; set; }
+ }
+}
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_salary_statistics/LqSalaryStatisticsEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_salary_statistics/LqSalaryStatisticsEntity.cs
index 08b4960..b3a4b85 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_salary_statistics/LqSalaryStatisticsEntity.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_salary_statistics/LqSalaryStatisticsEntity.cs
@@ -78,6 +78,30 @@ namespace NCC.Extend.Entitys.lq_salary_statistics
public decimal CooperationPerformance { get; set; }
///
+ /// 基础奖励业绩
+ ///
+ [SugarColumn(ColumnName = "F_BaseRewardPerformance")]
+ public decimal BaseRewardPerformance { get; set; }
+
+ ///
+ /// 合作奖励业绩
+ ///
+ [SugarColumn(ColumnName = "F_CooperationRewardPerformance")]
+ public decimal CooperationRewardPerformance { get; set; }
+
+ ///
+ /// 实际基础业绩
+ ///
+ [SugarColumn(ColumnName = "F_ActualBasePerformance")]
+ public decimal ActualBasePerformance { get; set; }
+
+ ///
+ /// 实际合作业绩
+ ///
+ [SugarColumn(ColumnName = "F_ActualCooperationPerformance")]
+ public decimal ActualCooperationPerformance { get; set; }
+
+ ///
/// 奖励业绩
///
[SugarColumn(ColumnName = "F_RewardPerformance")]
@@ -120,6 +144,12 @@ namespace NCC.Extend.Entitys.lq_salary_statistics
public decimal NewCustomerPoint { get; set; }
///
+ /// 升单人头数
+ ///
+ [SugarColumn(ColumnName = "F_UpgradeCustomerCount")]
+ public decimal UpgradeCustomerCount { get; set; }
+
+ ///
/// 升单业绩
///
[SugarColumn(ColumnName = "F_UpgradePerformance")]
@@ -132,6 +162,30 @@ namespace NCC.Extend.Entitys.lq_salary_statistics
public decimal UpgradePoint { get; set; }
///
+ /// 新客业绩提成金额
+ ///
+ [SugarColumn(ColumnName = "F_NewCustomerPerformanceCommission")]
+ public decimal NewCustomerPerformanceCommission { get; set; }
+
+ ///
+ /// 升单业绩提成金额
+ ///
+ [SugarColumn(ColumnName = "F_UpgradePerformanceCommission")]
+ public decimal UpgradePerformanceCommission { get; set; }
+
+ ///
+ /// 其他业绩加
+ ///
+ [SugarColumn(ColumnName = "F_OtherPerformanceAdd")]
+ public decimal OtherPerformanceAdd { get; set; }
+
+ ///
+ /// 其他业绩减
+ ///
+ [SugarColumn(ColumnName = "F_OtherPerformanceSubtract")]
+ public decimal OtherPerformanceSubtract { get; set; }
+
+ ///
/// 消耗
///
[SugarColumn(ColumnName = "F_Consumption")]
@@ -442,5 +496,29 @@ namespace NCC.Extend.Entitys.lq_salary_statistics
///
[SugarColumn(ColumnName = "F_UpdateUser")]
public string UpdateUser { get; set; }
+
+ ///
+ /// 是否新店
+ ///
+ [SugarColumn(ColumnName = "F_IsNewStore")]
+ public string IsNewStore { get; set; }
+
+ ///
+ /// 新店保护阶段
+ ///
+ [SugarColumn(ColumnName = "F_NewStoreProtectionStage")]
+ public int NewStoreProtectionStage { get; set; }
+
+ ///
+ /// 门店类型
+ ///
+ [SugarColumn(ColumnName = "F_StoreType")]
+ public int? StoreType { get; set; }
+
+ ///
+ /// 门店类别
+ ///
+ [SugarColumn(ColumnName = "F_StoreCategory")]
+ public int? StoreCategory { get; set; }
}
}
\ No newline at end of file
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs
new file mode 100644
index 0000000..ec7aabc
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs
@@ -0,0 +1,521 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using NCC.Common.Enum;
+using NCC.Common.Filter;
+using NCC.Common.Helper;
+using NCC.Dependency;
+using NCC.DynamicApiController;
+using NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation;
+using Yitter.IdGenerator;
+using NCC.Extend.Entitys.lq_salary_extra_calculation;
+using NCC.Extend.Entitys.lq_md_xdbhsj;
+using NCC.System.Entitys.Permission;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using NCC.Common.Core.Manager;
+using NCC.FriendlyException;
+
+namespace NCC.Extend
+{
+ ///
+ /// 健康师工资额外计算服务
+ ///
+ [ApiDescriptionSettings(Tag = "健康师工资额外计算", Name = "LqSalaryExtraCalculation", Order = 301)]
+ [Route("api/Extend/[controller]")]
+ public class LqSalaryExtraCalculationService : IDynamicApiController, ITransient
+ {
+ private readonly ISqlSugarClient _db;
+ private readonly IUserManager _userManager;
+
+ ///
+ /// 初始化一个类型的新实例
+ ///
+ public LqSalaryExtraCalculationService(ISqlSugarClient db, IUserManager userManager)
+ {
+ _db = db;
+ _userManager = userManager;
+ }
+
+ ///
+ /// 获取健康师工资额外计算列表
+ ///
+ /// 查询参数
+ /// 健康师工资额外计算分页列表
+ [HttpGet]
+ public async Task> GetList([FromQuery] SalaryExtraCalculationInput input)
+ {
+ var query = _db.Queryable()
+ .LeftJoin((ec, u) => ec.EmployeeId == u.Id);
+
+ // 年份筛选
+ if (input.Year.HasValue)
+ {
+ query = query.Where((ec, u) => ec.Year == input.Year.Value);
+ }
+
+ // 月份筛选
+ if (input.Month.HasValue)
+ {
+ query = query.Where((ec, u) => ec.Month == input.Month.Value);
+ }
+
+ // 健康师ID筛选
+ if (!string.IsNullOrEmpty(input.EmployeeId))
+ {
+ query = query.Where((ec, u) => ec.EmployeeId == input.EmployeeId);
+ }
+
+ // 关键词搜索(姓名或电话)
+ if (!string.IsNullOrEmpty(input.Keyword))
+ {
+ query = query.Where((ec, u) => u.RealName.Contains(input.Keyword) || u.MobilePhone.Contains(input.Keyword));
+ }
+
+ var list = await query.Select((ec, u) => new SalaryExtraCalculationOutput
+ {
+ Id = ec.Id,
+ EmployeeId = ec.EmployeeId,
+ EmployeeName = u.RealName,
+ EmployeePhone = u.MobilePhone,
+ Year = ec.Year,
+ Month = ec.Month,
+ BaseRewardPerformance = ec.BaseRewardPerformance,
+ CooperationRewardPerformance = ec.CooperationRewardPerformance,
+ NewCustomerPerformance = ec.NewCustomerPerformance,
+ NewCustomerConversionRate = ec.NewCustomerConversionRate,
+ UpgradePerformance = ec.UpgradePerformance,
+ UpgradeConversionRate = ec.UpgradeConversionRate,
+ UpgradeCustomerCount = ec.UpgradeCustomerCount,
+ OtherPerformanceAdd = ec.OtherPerformanceAdd,
+ OtherPerformanceSubtract = ec.OtherPerformanceSubtract
+ })
+ .ToPagedListAsync(input.currentPage, input.pageSize);
+
+ return PageResult.SqlSugarPageResult(list);
+ }
+
+ ///
+ /// 从Excel导入健康师工资额外计算数据
+ ///
+ /// Excel文件
+ /// 导入结果
+ [HttpPost("ImportFromExcel")]
+ public async Task ImportFromExcel(IFormFile file)
+ {
+ try
+ {
+ if (file == null || file.Length == 0)
+ {
+ throw NCCException.Oh("请选择要上传的Excel文件");
+ }
+
+ // 检查文件格式
+ var allowedExtensions = new[] { ".xlsx", ".xls" };
+ var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant();
+ if (!allowedExtensions.Contains(fileExtension))
+ {
+ throw NCCException.Oh("只支持.xlsx和.xls格式的Excel文件");
+ }
+
+ var importData = new List();
+
+ // 保存临时文件
+ var tempFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + Path.GetExtension(file.FileName));
+ try
+ {
+ using (var stream = new FileStream(tempFilePath, FileMode.Create))
+ {
+ await file.CopyToAsync(stream);
+ }
+
+ // 使用ExcelImportHelper读取Excel文件
+ // 参数说明:0表示第一个工作表,0表示第一行是标题行
+ var dataTable = ExcelImportHelper.ToDataTable(tempFilePath, 0, 0);
+
+ if (dataTable.Rows.Count == 0)
+ {
+ throw NCCException.Oh("Excel文件中没有数据行");
+ }
+
+ // Excel字段顺序:id, 健康师姓名, 健康师电话, 年份, 月份, 基础奖励业绩, 合作奖励业绩, 新客业绩, 新客成交率, 升单业绩, 升单成交率, 升单人头数, 其他业绩加, 其他业绩减
+ // 从第1行开始读取数据(跳过标题行)
+ for (int i = 1; i < dataTable.Rows.Count; i++)
+ {
+ try
+ {
+ var row = dataTable.Rows[i];
+ var id = row[0]?.ToString()?.Trim();
+ var employeeName = row[1]?.ToString()?.Trim();
+ var employeePhone = row[2]?.ToString()?.Trim();
+ var yearText = row[3]?.ToString()?.Trim();
+ var monthText = row[4]?.ToString()?.Trim();
+ var baseRewardPerformanceText = row[5]?.ToString()?.Trim();
+ var cooperationRewardPerformanceText = row[6]?.ToString()?.Trim();
+ var newCustomerPerformanceText = row[7]?.ToString()?.Trim();
+ var newCustomerConversionRateText = row[8]?.ToString()?.Trim();
+ var upgradePerformanceText = row[9]?.ToString()?.Trim();
+ var upgradeConversionRateText = row[10]?.ToString()?.Trim();
+ var upgradeCustomerCountText = row[11]?.ToString()?.Trim();
+ var otherPerformanceAddText = row[12]?.ToString()?.Trim();
+ var otherPerformanceSubtractText = row[13]?.ToString()?.Trim();
+
+ // 跳过空行
+ if (string.IsNullOrEmpty(employeeName) && string.IsNullOrEmpty(employeePhone))
+ {
+ continue;
+ }
+
+ // 验证必填字段
+ if (string.IsNullOrEmpty(employeeName))
+ {
+ throw new Exception($"第{i + 1}行:健康师姓名不能为空");
+ }
+ if (string.IsNullOrEmpty(employeePhone))
+ {
+ throw new Exception($"第{i + 1}行:健康师电话不能为空");
+ }
+
+ // 解析年份
+ if (!int.TryParse(yearText, out int year))
+ {
+ throw new Exception($"第{i + 1}行:年份格式错误");
+ }
+
+ // 解析月份
+ if (!int.TryParse(monthText, out int month))
+ {
+ throw new Exception($"第{i + 1}行:月份格式错误");
+ }
+
+ // 验证月份范围
+ if (month < 1 || month > 12)
+ {
+ throw new Exception($"第{i + 1}行:月份必须在1-12之间");
+ }
+
+ // 解析数值字段(允许为空,默认为0)
+ decimal.TryParse(baseRewardPerformanceText, out decimal baseRewardPerformance);
+ decimal.TryParse(cooperationRewardPerformanceText, out decimal cooperationRewardPerformance);
+ decimal.TryParse(newCustomerPerformanceText, out decimal newCustomerPerformance);
+ decimal.TryParse(newCustomerConversionRateText, out decimal newCustomerConversionRate);
+ decimal.TryParse(upgradePerformanceText, out decimal upgradePerformance);
+ decimal.TryParse(upgradeConversionRateText, out decimal upgradeConversionRate);
+ decimal.TryParse(upgradeCustomerCountText, out decimal upgradeCustomerCount);
+ decimal.TryParse(otherPerformanceAddText, out decimal otherPerformanceAdd);
+ decimal.TryParse(otherPerformanceSubtractText, out decimal otherPerformanceSubtract);
+
+ var item = new SalaryExtraCalculationImportInput
+ {
+ Id = id,
+ EmployeeName = employeeName,
+ EmployeePhone = employeePhone,
+ Year = year,
+ Month = month,
+ BaseRewardPerformance = baseRewardPerformance,
+ CooperationRewardPerformance = cooperationRewardPerformance,
+ NewCustomerPerformance = newCustomerPerformance,
+ NewCustomerConversionRate = newCustomerConversionRate,
+ UpgradePerformance = upgradePerformance,
+ UpgradeConversionRate = upgradeConversionRate,
+ UpgradeCustomerCount = upgradeCustomerCount,
+ OtherPerformanceAdd = otherPerformanceAdd,
+ OtherPerformanceSubtract = otherPerformanceSubtract
+ };
+
+ importData.Add(item);
+ }
+ catch (Exception ex)
+ {
+ throw new Exception($"第{i + 1}行数据解析失败: {ex.Message}");
+ }
+ }
+ }
+ finally
+ {
+ // 清理临时文件
+ if (File.Exists(tempFilePath))
+ {
+ File.Delete(tempFilePath);
+ }
+ }
+
+ if (!importData.Any())
+ {
+ throw NCCException.Oh("Excel文件中没有有效的数据行");
+ }
+
+ // 处理导入数据
+ return await ProcessImportData(importData);
+ }
+ catch (Exception ex)
+ {
+ throw NCCException.Oh($"上传Excel文件导入健康师工资额外计算数据失败: {ex.Message}");
+ }
+ }
+
+ #region 处理导入数据
+ ///
+ /// 处理导入数据
+ ///
+ /// 导入数据列表
+ /// 导入结果
+ private async Task ProcessImportData(List importData)
+ {
+ var successCount = 0;
+ var failCount = 0;
+ var errorMessages = new List();
+ var entitiesToInsert = new List();
+ var entitiesToUpdate = new List();
+
+ foreach (var item in importData)
+ {
+ try
+ {
+ // 1. 根据健康师姓名和电话查找用户ID
+ var user = await _db.Queryable()
+ .Where(u => u.RealName == item.EmployeeName && u.MobilePhone == item.EmployeePhone)
+ .FirstAsync();
+
+ if (user == null)
+ {
+ errorMessages.Add($"健康师 {item.EmployeeName}({item.EmployeePhone}) 不存在");
+ failCount++;
+ continue;
+ }
+
+ // 2. 检查是否已存在相同记录(根据健康师ID、年份、月份)
+ LqSalaryExtraCalculationEntity existingRecord = null;
+
+ // 如果提供了ID,先尝试根据ID查找
+ if (!string.IsNullOrEmpty(item.Id))
+ {
+ existingRecord = await _db.Queryable()
+ .Where(x => x.Id == item.Id)
+ .FirstAsync();
+ }
+
+ // 如果没有找到,则根据健康师ID、年份、月份查找
+ if (existingRecord == null)
+ {
+ existingRecord = await _db.Queryable()
+ .Where(x => x.EmployeeId == user.Id && x.Year == item.Year && x.Month == item.Month)
+ .FirstAsync();
+ }
+
+ if (existingRecord != null)
+ {
+ // 更新现有记录
+ existingRecord.BaseRewardPerformance = item.BaseRewardPerformance;
+ existingRecord.CooperationRewardPerformance = item.CooperationRewardPerformance;
+ existingRecord.NewCustomerPerformance = item.NewCustomerPerformance;
+ existingRecord.NewCustomerConversionRate = item.NewCustomerConversionRate;
+ existingRecord.UpgradePerformance = item.UpgradePerformance;
+ existingRecord.UpgradeConversionRate = item.UpgradeConversionRate;
+ existingRecord.UpgradeCustomerCount = item.UpgradeCustomerCount;
+ existingRecord.OtherPerformanceAdd = item.OtherPerformanceAdd;
+ existingRecord.OtherPerformanceSubtract = item.OtherPerformanceSubtract;
+ entitiesToUpdate.Add(existingRecord);
+ }
+ else
+ {
+ // 创建新记录
+ var entity = new LqSalaryExtraCalculationEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ EmployeeId = user.Id,
+ Year = item.Year,
+ Month = item.Month,
+ BaseRewardPerformance = item.BaseRewardPerformance,
+ CooperationRewardPerformance = item.CooperationRewardPerformance,
+ NewCustomerPerformance = item.NewCustomerPerformance,
+ NewCustomerConversionRate = item.NewCustomerConversionRate,
+ UpgradePerformance = item.UpgradePerformance,
+ UpgradeConversionRate = item.UpgradeConversionRate,
+ UpgradeCustomerCount = item.UpgradeCustomerCount,
+ OtherPerformanceAdd = item.OtherPerformanceAdd,
+ OtherPerformanceSubtract = item.OtherPerformanceSubtract
+ };
+ entitiesToInsert.Add(entity);
+ }
+
+ successCount++;
+ }
+ catch (Exception ex)
+ {
+ errorMessages.Add($"健康师 {item.EmployeeName}({item.EmployeePhone}) 处理失败: {ex.Message}");
+ failCount++;
+ }
+ }
+
+ // 批量插入新记录
+ if (entitiesToInsert.Any())
+ {
+ await _db.Insertable(entitiesToInsert).ExecuteCommandAsync();
+ }
+
+ // 批量更新现有记录
+ if (entitiesToUpdate.Any())
+ {
+ await _db.Updateable(entitiesToUpdate).ExecuteCommandAsync();
+ }
+
+ var result = new
+ {
+ successCount,
+ failCount,
+ totalCount = importData.Count,
+ errorMessages = errorMessages.Take(50).ToList() // 最多返回50条错误信息
+ };
+
+ if (failCount > 0)
+ {
+ throw NCCException.Oh($"导入完成,成功:{successCount}条,失败:{failCount}条。{string.Join("; ", errorMessages.Take(10))}");
+ }
+
+ return result;
+ }
+ #endregion
+
+ ///
+ /// 为新店健康师生成模拟数据
+ ///
+ /// 年份
+ /// 月份
+ /// 生成结果
+ [HttpPost("GenerateMockData")]
+ public async Task GenerateMockData(int year, int month)
+ {
+ try
+ {
+ // 验证月份范围
+ if (month < 1 || month > 12)
+ {
+ throw NCCException.Oh("月份必须在1-12之间");
+ }
+
+ var startDate = new DateTime(year, month, 1);
+ var endDate = startDate.AddMonths(1).AddDays(-1);
+
+ // 1. 查询新店保护信息
+ var newStoreProtectionList = await _db.Queryable()
+ .Where(x => x.Sfqy == 1)
+ .ToListAsync();
+
+ // 筛选出在统计月份处于保护期内的门店
+ var newStoreIds = newStoreProtectionList
+ .Where(x => x.Bhkssj <= endDate && x.Bhjssj >= startDate)
+ .Select(x => x.Mdid)
+ .Distinct()
+ .ToList();
+
+ if (!newStoreIds.Any())
+ {
+ throw NCCException.Oh("当前月份没有新店");
+ }
+
+ // 2. 查询新店下的健康师
+ // 健康师的岗位字段是F_GW,值为"健康师"
+ var healthCoaches = await _db.Queryable()
+ .Where(u => newStoreIds.Contains(u.Mdid) && u.Gw == "健康师" && u.EnabledMark == 1 && (u.DeleteMark == null || u.DeleteMark == 0))
+ .Select(u => new { u.Id, u.RealName, u.Mdid })
+ .ToListAsync();
+
+ if (!healthCoaches.Any())
+ {
+ throw NCCException.Oh("新店下没有健康师");
+ }
+
+ // 3. 检查是否已存在数据
+ var existingEmployeeIds = await _db.Queryable()
+ .Where(x => x.Year == year && x.Month == month)
+ .Select(x => x.EmployeeId)
+ .ToListAsync();
+
+ var employeesToProcess = healthCoaches
+ .Where(hc => !existingEmployeeIds.Contains(hc.Id))
+ .ToList();
+
+ if (!employeesToProcess.Any())
+ {
+ throw NCCException.Oh("所有新店健康师已存在该月份的数据");
+ }
+
+ // 4. 生成模拟数据
+ var random = new Random();
+ var entitiesToInsert = new List();
+ var successCount = 0;
+
+ foreach (var employee in employeesToProcess)
+ {
+ try
+ {
+ // 生成金额
+ // 基础业绩奖励和合作业绩奖励:0-20000之间
+ var baseRewardPerformance = (decimal)(random.NextDouble() * 20000);
+ var cooperationRewardPerformance = (decimal)(random.NextDouble() * 20000);
+ // 其他金额字段:10000-30000之间
+ var newCustomerPerformance = (decimal)(random.NextDouble() * 20000 + 10000);
+ var upgradePerformance = (decimal)(random.NextDouble() * 20000 + 10000);
+ var otherPerformanceAdd = (decimal)(random.NextDouble() * 20000 + 10000);
+ var otherPerformanceSubtract = (decimal)(random.NextDouble() * 20000 + 10000);
+
+ // 生成转化率(0-60%之间,转换为0-0.6)
+ var newCustomerConversionRate = (decimal)(random.NextDouble() * 0.6);
+ var upgradeConversionRate = (decimal)(random.NextDouble() * 0.6);
+
+ // 生成升单人头数(0-10之间)
+ var upgradeCustomerCount = (decimal)random.Next(0, 11);
+
+ var entity = new LqSalaryExtraCalculationEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ EmployeeId = employee.Id,
+ Year = year,
+ Month = month,
+ BaseRewardPerformance = Math.Round(baseRewardPerformance, 2),
+ CooperationRewardPerformance = Math.Round(cooperationRewardPerformance, 2),
+ NewCustomerPerformance = Math.Round(newCustomerPerformance, 2),
+ NewCustomerConversionRate = Math.Round(newCustomerConversionRate, 4),
+ UpgradePerformance = Math.Round(upgradePerformance, 2),
+ UpgradeConversionRate = Math.Round(upgradeConversionRate, 4),
+ UpgradeCustomerCount = upgradeCustomerCount,
+ OtherPerformanceAdd = Math.Round(otherPerformanceAdd, 2),
+ OtherPerformanceSubtract = Math.Round(otherPerformanceSubtract, 2)
+ };
+
+ entitiesToInsert.Add(entity);
+ successCount++;
+ }
+ catch
+ {
+ // 记录错误但继续处理其他健康师
+ continue;
+ }
+ }
+
+ // 5. 批量插入数据
+ if (entitiesToInsert.Any())
+ {
+ await _db.Insertable(entitiesToInsert).ExecuteCommandAsync();
+ }
+
+ return new
+ {
+ successCount,
+ totalCount = employeesToProcess.Count,
+ message = $"成功为新店健康师生成 {successCount} 条模拟数据"
+ };
+ }
+ catch (Exception ex)
+ {
+ throw NCCException.Oh($"生成模拟数据失败: {ex.Message}");
+ }
+ }
+ }
+}
+
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs
index 3c3a52b..93f0df0 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs
@@ -16,6 +16,11 @@ using NCC.Extend.Entitys.lq_person_times_record;
using NCC.Extend.Entitys.lq_salary_statistics;
using NCC.Extend.Entitys.lq_xh_jksyj;
using NCC.Extend.Entitys.lq_ycsd_jsj;
+using NCC.Extend.Entitys.lq_mdxx;
+using NCC.Extend.Entitys.lq_hytk_hytk;
+using NCC.Extend.Entitys.lq_md_xdbhsj;
+using NCC.Extend.Entitys.lq_salary_extra_calculation;
+using NCC.System.Entitys.Permission;
using SqlSugar;
using System;
using System.Collections.Generic;
@@ -97,7 +102,15 @@ namespace NCC.Extend
TotalDeduction = x.TotalDeduction,
ActualSalary = x.ActualSalary,
IsLocked = x.IsLocked,
- UpdateTime = x.UpdateTime
+ UpdateTime = x.UpdateTime,
+ IsNewStore = x.IsNewStore,
+ NewStoreProtectionStage = x.NewStoreProtectionStage,
+ StoreType = x.StoreType,
+ StoreCategory = x.StoreCategory,
+ ActualBasePerformance = x.ActualBasePerformance,
+ ActualCooperationPerformance = x.ActualCooperationPerformance,
+ NewCustomerCommission = x.NewCustomerPerformanceCommission,
+ UpgradeCommission = x.UpgradePerformanceCommission
})
.ToPagedListAsync(input.currentPage, input.pageSize);
@@ -127,17 +140,18 @@ namespace NCC.Extend
// 1.1.1 获取关联的开单记录(用于获取 sfskdd)
var billingIds = performanceList.Select(x => x.Glkdbh).Distinct().ToList();
var billingDict = await _db.Queryable()
- .Where(x => billingIds.Contains(x.Id))
+ .Where(x => billingIds.Contains(x.Id) && x.Id != null)
.ToDictionaryAsync(x => x.Id, x => x.Sfskdd);
// 1.1.2 组合数据
var performanceData = performanceList.Select(p => new
{
- p.Jks,
+ Jks = p.Jkszh, // 使用 Jkszh (账号/ID) 而不是 Jks (姓名)
p.Jksxm,
p.StoreId,
p.Jksyj,
p.ItemCategory,
+ p.PerformanceType, // 新增业绩类型字段
Sfskdd = billingDict.ContainsKey(p.Glkdbh) ? billingDict[p.Glkdbh] : null
}).ToList();
@@ -161,7 +175,7 @@ namespace NCC.Extend
var teamList = await _db.Queryable()
.Where(x => teamIds.Contains(x.Id))
.ToListAsync();
- var teamDict = teamList.ToDictionary(x => x.Id, x => x.Jsj);
+ var teamDict = teamList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x.Jsj);
// 1.4.2 组合数据
var teamMembers = teamUserList.Select(user => new
@@ -185,6 +199,55 @@ namespace NCC.Extend
.Where(x => x.Month == monthStr)
.ToListAsync();
+ // 1.6.1 门店总业绩计算 (开单实付 - 退款金额)
+ // 开单实付
+ var storeBillingList = await _db.Queryable()
+ .Where(x => x.Kdrq >= startDate && x.Kdrq <= endDate.AddDays(1) && x.IsEffective == 1)
+ .Select(x => new { x.Djmd, x.Sfyj })
+ .ToListAsync();
+ var storeBillingDict = storeBillingList
+ .Where(x => !string.IsNullOrEmpty(x.Djmd))
+ .GroupBy(x => x.Djmd)
+ .ToDictionary(g => g.Key, g => g.Sum(x => x.Sfyj));
+
+ // 退款金额
+ var storeRefundList = await _db.Queryable()
+ .Where(x => x.Tksj >= startDate && x.Tksj <= endDate.AddDays(1) && x.IsEffective == 1)
+ .Select(x => new { x.Mdbh, x.Tkje })
+ .ToListAsync();
+ var storeRefundDict = storeRefundList
+ .Where(x => !string.IsNullOrEmpty(x.Mdbh))
+ .GroupBy(x => x.Mdbh)
+ .ToDictionary(g => g.Key, g => g.Sum(x => x.Tkje ?? 0));
+
+ // 1.7 门店信息 (lq_mdxx)
+ var storeList = await _db.Queryable().ToListAsync();
+ var storeDict = storeList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x);
+
+ // 1.7.1 门店新店保护信息 (lq_md_xdbhsj)
+ var newStoreProtectionList = await _db.Queryable()
+ .Where(x => x.Sfqy == 1)
+ .ToListAsync();
+
+ // 构造新店保护查找字典: StoreId -> List
+ // 因为一个门店可能有多个阶段配置,虽然通常是一个时间段,但为了严谨取符合当前月份的配置
+ // 逻辑: 统计月份 (startDate ~ endDate) 是否在 bhkssj ~ bhjssj 范围内
+ // 只要统计月份与保护期有交集,就算保护期。或者严格一点,统计月份的第一天在保护期内。
+ // 这里取: 统计月份的第一天 (startDate) 在保护期内
+ var newStoreProtectionDict = newStoreProtectionList
+ .Where(x => x.Bhkssj <= startDate && x.Bhjssj >= startDate)
+ .GroupBy(x => x.Mdid)
+ .ToDictionary(g => g.Key, g => g.First()); // 取第一个匹配的配置
+
+ // 1.8 健康师工资额外计算数据 (lq_salary_extra_calculation)
+ var extraCalculationList = await _db.Queryable()
+ .Where(x => x.Year == year && x.Month == month)
+ .ToListAsync();
+
+ var extraCalculationDict = extraCalculationList
+ .Where(x => !string.IsNullOrEmpty(x.EmployeeId))
+ .ToDictionary(x => x.EmployeeId, x => x);
+
// 2. 聚合每个健康师的数据对象
var employeeStats = new Dictionary();
@@ -197,6 +260,19 @@ namespace NCC.Extend
.Distinct()
.ToList();
+ // 1.8 批量获取员工信息 (BASE_USER + BASE_POSITION)
+ // 使用 allEmployeeIds 作为驱动,查询 BASE_USER
+ var userList = await _db.Queryable()
+ .Where(x => allEmployeeIds.Contains(x.Id))
+ .Select(x => new { x.Id, x.RealName, x.PositionId, x.Mdid })
+ .ToListAsync();
+
+ var userDict = userList.ToDictionary(x => x.Id, x => x);
+
+ var positionIds = userList.Select(x => x.PositionId).Distinct().ToList();
+ var positionList = await _db.Queryable().Where(x => positionIds.Contains(x.Id)).ToListAsync();
+ var positionLookup = positionList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x.FullName);
+
foreach (var empId in allEmployeeIds)
{
var salary = new LqSalaryStatisticsEntity
@@ -209,38 +285,121 @@ namespace NCC.Extend
IsLocked = 0
};
- // 填充基础信息 (姓名、门店)
- var perfRecord = performanceData.FirstOrDefault(x => x.Jks == empId);
- var consRecord = consumptionList.FirstOrDefault(x => x.Jks == empId);
+ // 填充基础信息 (优先从 BASE_USER 获取)
+ string userMdid = null;
+ if (userDict.ContainsKey(empId))
+ {
+ var user = userDict[empId];
+ salary.EmployeeName = user.RealName;
+ userMdid = user.Mdid;
+
+ // 岗位
+ if (user.PositionId != null && positionLookup.ContainsKey(user.PositionId))
+ {
+ salary.Position = positionLookup[user.PositionId];
+ }
+ }
- if (perfRecord != null)
+ // 如果 BASE_USER 没名字,尝试从业务表获取
+ if (string.IsNullOrEmpty(salary.EmployeeName))
{
- salary.EmployeeName = perfRecord.Jksxm;
- salary.StoreId = perfRecord.StoreId;
+ var perfRecord = performanceData.FirstOrDefault(x => x.Jks == empId);
+ var consRecord = consumptionList.FirstOrDefault(x => x.Jks == empId);
+ if (perfRecord != null) salary.EmployeeName = perfRecord.Jksxm;
+ else if (consRecord != null) salary.EmployeeName = consRecord.Jksxm;
}
- else if (consRecord != null)
+
+ // 填充门店ID (从业务数据获取,因为 User 表的 OrganizeId 未必是门店)
+ var perfStore = performanceData.FirstOrDefault(x => x.Jks == empId && !string.IsNullOrEmpty(x.StoreId));
+ var consStore = consumptionList.FirstOrDefault(x => x.Jks == empId && !string.IsNullOrEmpty(x.StoreId));
+
+ if (perfStore != null) salary.StoreId = perfStore.StoreId;
+ else if (consStore != null) salary.StoreId = consStore.StoreId;
+
+ // 如果业务数据没门店,尝试使用 User.Mdid
+ if (string.IsNullOrEmpty(salary.StoreId) && !string.IsNullOrEmpty(userMdid))
{
- salary.EmployeeName = consRecord.Jksxm;
- salary.StoreId = consRecord.StoreId;
+ if (storeDict.ContainsKey(userMdid))
+ {
+ salary.StoreId = userMdid;
+ }
}
- // 填充门店名称
- if (!string.IsNullOrEmpty(salary.StoreId))
+ // 填充门店名称及分类信息
+ if (!string.IsNullOrEmpty(salary.StoreId) && storeDict.ContainsKey(salary.StoreId))
+ {
+ var store = storeDict[salary.StoreId];
+ salary.StoreName = store.Dm;
+ salary.StoreType = store.StoreType;
+ salary.StoreCategory = store.StoreCategory;
+ }
+
+ // 填充新店保护信息
+ if (!string.IsNullOrEmpty(salary.StoreId) && newStoreProtectionDict.ContainsKey(salary.StoreId))
+ {
+ var protection = newStoreProtectionDict[salary.StoreId];
+ salary.IsNewStore = "是";
+ salary.NewStoreProtectionStage = protection.Stage;
+ }
+ else
{
- // 这里简单处理,实际可能需要缓存门店列表
- // salary.StoreName = ...
+ salary.IsNewStore = "否";
+ salary.NewStoreProtectionStage = 0;
}
// 2.1 计算个人业绩
var myPerf = performanceData.Where(x => x.Jks == empId).ToList();
- salary.BasePerformance = myPerf.Where(x => x.ItemCategory == "基础业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
- salary.CooperationPerformance = myPerf.Where(x => x.ItemCategory == "合作业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
+ 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"));
// 新客与升单业绩
salary.NewCustomerPerformance = myPerf.Where(x => x.Sfskdd == "是").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
salary.UpgradePerformance = myPerf.Where(x => 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;
+ }
+
+ // 2.1.2 计算实际基础业绩和实际合作业绩
+ // 定义新店相关变量,供后续多处使用
+ bool isNewStore = salary.IsNewStore == "是";
+ int newStoreStage = salary.NewStoreProtectionStage;
+
+ // 实际基础业绩 = 基础业绩 - 基础奖励业绩 + 其他业绩加 - 其他业绩减
+ decimal actualBasePerformance = salary.BasePerformance
+ - salary.BaseRewardPerformance
+ + salary.OtherPerformanceAdd
+ - salary.OtherPerformanceSubtract;
+
+ // 新店额外调整:根据阶段扣除新客业绩或升单业绩
+ if (isNewStore)
+ {
+ if (newStoreStage == 1)
+ {
+ // 第一阶段:扣除新客业绩
+ actualBasePerformance -= salary.NewCustomerPerformance;
+ }
+ else if (newStoreStage == 2)
+ {
+ // 第二阶段:扣除升单业绩
+ actualBasePerformance -= salary.UpgradePerformance;
+ }
+ }
+
+ salary.ActualBasePerformance = actualBasePerformance;
+
+ // 实际合作业绩 = 合作业绩 - 合作奖励业绩
+ salary.ActualCooperationPerformance = salary.CooperationPerformance - salary.CooperationRewardPerformance;
+
// 2.2 计算消耗和项目数
var myCons = consumptionList.Where(x => x.Jks == empId).ToList();
salary.Consumption = myCons.Sum(x => x.Jksyj ?? 0);
@@ -258,10 +417,33 @@ namespace NCC.Extend
// 2.5 战队信息 (初始)
var myTeam = teamMembers.FirstOrDefault(x => x.UserId == empId);
+ // 初始判断岗位:如果是战队队长(IsLeader=1)则是顾问,否则是健康师
+ // 注意:这里先根据战队设置判断,后续考勤不足21天会降级
+ if (myTeam != null && myTeam.IsLeader == 1)
+ {
+ salary.Position = "顾问";
+ }
+ else if (string.IsNullOrEmpty(salary.Position)) // 如果BASE_USER没岗位,且不是队长,默认为健康师
+ {
+ salary.Position = "健康师";
+ }
+
if (myTeam != null)
{
salary.GoldTriangleId = myTeam.TeamId;
- salary.GoldTriangleTeam = myTeam.TeamName ?? "";
+ salary.GoldTriangleTeam = myTeam.TeamName ?? "个人";
+ }
+ else
+ {
+ salary.GoldTriangleTeam = "个人";
+ }
+
+ // 2.6 门店总业绩
+ if (!string.IsNullOrEmpty(salary.StoreId))
+ {
+ decimal billing = storeBillingDict.ContainsKey(salary.StoreId) ? storeBillingDict[salary.StoreId] : 0;
+ decimal refund = storeRefundDict.ContainsKey(salary.StoreId) ? storeRefundDict[salary.StoreId] : 0;
+ salary.StoreTotalPerformance = billing - refund;
}
employeeStats[empId] = salary;
@@ -293,11 +475,12 @@ namespace NCC.Extend
}
}
- // 对于无效成员,移除战队标识,视为单人
+ // 对于无效成员,移除战队标识,视为单人,并重置岗位为健康师
foreach (var member in invalidMembers)
{
member.GoldTriangleId = null;
- member.GoldTriangleTeam = null;
+ member.GoldTriangleTeam = "个人";
+ member.Position = "健康师"; // 降级为健康师
}
// 计算有效战队的总业绩
@@ -313,8 +496,12 @@ namespace NCC.Extend
// 4. 计算薪资 (底薪 & 提成)
foreach (var salary in employeeStats.Values)
{
+ // 定义新店相关变量,供底薪和提成计算使用
+ bool isNewStore = salary.IsNewStore == "是";
+ int newStoreStage = salary.NewStoreProtectionStage;
+
// 4.1 底薪计算
- salary.HealthCoachBaseSalary = CalculateBaseSalary(salary.Consumption, salary.ProjectCount);
+ salary.HealthCoachBaseSalary = CalculateBaseSalary(salary.Consumption, salary.ProjectCount, isNewStore);
// 4.2 提成计算
// 单人业绩 <= 6000 无提成
@@ -335,29 +522,60 @@ namespace NCC.Extend
// 是战队成员
// 获取战队人数 (注意:这里应该是有效战队人数)
var teamMemberCount = employeeStats.Values.Count(x => x.GoldTriangleId == salary.GoldTriangleId);
+ // 注意:提成点按原始基础业绩计算,不是实际基础业绩
commissionPoint = GetTeamCommissionPoint(teamMemberCount, salary.TeamPerformance);
}
else
{
// 单人 (或被剔除出战队)
+ // 注意:提成点按原始总业绩计算
commissionPoint = GetTeamCommissionPoint(1, salary.TotalPerformance);
}
salary.CommissionPoint = commissionPoint;
- // 计算基础/合作提成
- salary.BasePerformanceCommission = salary.BasePerformance * 0.95m * commissionPoint;
- salary.CooperationPerformanceCommission = salary.CooperationPerformance * 0.95m * 0.65m * commissionPoint;
+ // 计算基础/合作提成(使用实际业绩)
+ salary.BasePerformanceCommission = salary.ActualBasePerformance * 0.95m * commissionPoint;
+ salary.CooperationPerformanceCommission = salary.ActualCooperationPerformance * 0.95m * 0.65m * commissionPoint;
+
+ // 计算新客转化率提成和升单人头提成(根据新店阶段)
+ // isNewStore 和 newStoreStage 已在上面定义
+
+ if (isNewStore)
+ {
+ if (newStoreStage == 1)
+ {
+ // 第一阶段:计算新客转化率提成
+ salary.NewCustomerPerformanceCommission = CalculateNewCustomerConversionCommission(
+ salary.NewCustomerPerformance,
+ salary.NewCustomerConversionRate);
+ }
+ else if (newStoreStage == 2)
+ {
+ // 第二阶段:计算升单人头提成
+ salary.UpgradePerformanceCommission = CalculateUpgradeCustomerCommission(
+ salary.UpgradePerformance,
+ salary.UpgradeCustomerCount);
+ }
+ // 第三阶段:不计算新客/升单提成
+ }
// 计算顾问提成
// 检查是否是顾问
- var isConsultant = teamMembers.Any(x => x.UserId == salary.EmployeeId && x.IsLeader == 1);
- if (isConsultant && !string.IsNullOrEmpty(salary.GoldTriangleId))
+ // 注意:这里需要重新判断是否是顾问,因为可能被降级了
+ if (salary.Position == "顾问" && !string.IsNullOrEmpty(salary.GoldTriangleId))
{
- salary.ConsultantCommission = CalculateConsultantCommission(salary.TeamPerformance, employeeStats.Values.Where(x => x.GoldTriangleId == salary.GoldTriangleId).ToList());
+ salary.ConsultantCommission = CalculateConsultantCommission(
+ salary.TeamPerformance,
+ employeeStats.Values.Where(x => x.GoldTriangleId == salary.GoldTriangleId).ToList(),
+ isNewStore);
}
- salary.TotalCommission = salary.BasePerformanceCommission + salary.CooperationPerformanceCommission + salary.ConsultantCommission;
+ salary.TotalCommission = salary.BasePerformanceCommission
+ + salary.CooperationPerformanceCommission
+ + salary.ConsultantCommission
+ + salary.NewCustomerPerformanceCommission
+ + salary.UpgradePerformanceCommission;
}
// 计算占比
@@ -386,7 +604,7 @@ namespace NCC.Extend
///
/// 计算底薪
///
- private decimal CalculateBaseSalary(decimal consumption, decimal projectCount)
+ private decimal CalculateBaseSalary(decimal consumption, decimal projectCount, bool isNewStore)
{
// 0星:<1w 或 <96个 -> 1800
// 1星:>=1w 且 >=96个 -> 2000
@@ -394,6 +612,7 @@ namespace NCC.Extend
// 3星:>=4w 且 >=156个 -> 2400
// 特殊规则:若消耗或项目数中仅一项未达标(0星),底薪按1星(2000元)计算
+ // 新店规则:新店底薪最低为1星(2000元),不满足1星按1星算
int starCons = 0;
if (consumption >= 40000) starCons = 3;
@@ -413,13 +632,21 @@ namespace NCC.Extend
finalStar = 1;
}
- switch (finalStar)
+ decimal baseSalary = finalStar switch
{
- case 3: return 2400;
- case 2: return 2200;
- case 1: return 2000;
- default: return 1800;
+ 3 => 2400,
+ 2 => 2200,
+ 1 => 2000,
+ _ => 1800
+ };
+
+ // 新店保底1星(2000元)
+ if (isNewStore && baseSalary < 2000)
+ {
+ baseSalary = 2000;
}
+
+ return baseSalary;
}
///
@@ -455,32 +682,72 @@ namespace NCC.Extend
///
/// 计算顾问提成
///
- private decimal CalculateConsultantCommission(decimal teamPerformance, List teamMembers)
+ private decimal CalculateConsultantCommission(decimal teamPerformance, List teamMembers, bool isNewStore)
{
// 顾问提成规则:
// 高级顾问:战队总业绩 ≥ 6万元 且 组员业绩达到40%以上 且 消耗达到6万元 → 团队总业绩0.8%
// 普通顾问:战队总业绩 ≥ 4万元 且 组员业绩达到30%以上 且 消耗达到4万元 → 团队总业绩0.3%
- // 这里的“组员业绩达到X%以上”理解为:除顾问外的成员业绩占比?或者每个成员都达标?
- // 通常理解为:团队中是否有成员业绩贡献较高,或者团队整体结构健康。
- // 假设“组员业绩达到X%”是指:团队中至少有一名成员(非顾问本人?)或者所有成员平均?
- // 鉴于规则模糊,这里先简化实现:暂只考核总业绩和消耗。
- // 消耗是团队总消耗吗?假设是。
+ // 注意:
+ // 1. "组员业绩"指除顾问外的其他成员业绩总和
+ // 2. 只统计有效战队成员(考勤≥21天,未被剔除的成员)
+ // 3. "达到X%以上"指:组员业绩总和 ≥ 团队总业绩 × X%
+ // 4. 新店顾问不考核消耗
var teamConsumption = teamMembers.Sum(x => x.Consumption);
- // 高级顾问
- if (teamPerformance >= 60000 && teamConsumption >= 60000)
+ // 计算组员(非顾问)业绩总和
+ // teamMembers 已经是过滤后的有效成员列表(GoldTriangleId 相同且未被剔除)
+ var memberPerformance = teamMembers.Where(x => x.Position != "顾问").Sum(x => x.TotalPerformance);
+
+ // 高级顾问:业绩≥6万 且 组员业绩≥40% 且 (新店 或 消耗≥6万)
+ if (teamPerformance >= 60000 && memberPerformance >= teamPerformance * 0.4m)
{
- return teamPerformance * 0.008m;
+ if (isNewStore || teamConsumption >= 60000)
+ {
+ return teamPerformance * 0.008m;
+ }
}
- // 普通顾问
- if (teamPerformance >= 40000 && teamConsumption >= 40000)
+
+ // 普通顾问:业绩≥4万 且 组员业绩≥30% 且 (新店 或 消耗≥4万)
+ if (teamPerformance >= 40000 && memberPerformance >= teamPerformance * 0.3m)
{
- return teamPerformance * 0.003m;
+ if (isNewStore || teamConsumption >= 40000)
+ {
+ return teamPerformance * 0.003m;
+ }
}
return 0;
}
+
+ ///
+ /// 计算新客转化率提成
+ ///
+ private decimal CalculateNewCustomerConversionCommission(decimal newCustomerPerformance, decimal conversionRate)
+ {
+ decimal commissionRate = 0;
+
+ if (conversionRate >= 0.5m) commissionRate = 0.20m;
+ else if (conversionRate >= 0.45m) commissionRate = 0.15m;
+ else if (conversionRate >= 0.35m) commissionRate = 0.10m;
+ else if (conversionRate >= 0) commissionRate = 0.06m;
+
+ return newCustomerPerformance * commissionRate;
+ }
+
+ ///
+ /// 计算升单人头提成
+ ///
+ private decimal CalculateUpgradeCustomerCommission(decimal upgradePerformance, decimal upgradeCustomerCount)
+ {
+ decimal commissionRate = 0;
+
+ if (upgradeCustomerCount >= 10) commissionRate = 0.20m;
+ else if (upgradeCustomerCount >= 4) commissionRate = 0.10m;
+ // 0-4个: 0%
+
+ return upgradePerformance * commissionRate;
+ }
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
index 1cc8788..0291cb9 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
@@ -4997,5 +4997,7 @@ namespace NCC.Extend.LqStatistics
}
#endregion
+
+
}
}
diff --git a/sql/创建健康师工资额外计算表.sql b/sql/创建健康师工资额外计算表.sql
new file mode 100644
index 0000000..d7c09db
--- /dev/null
+++ b/sql/创建健康师工资额外计算表.sql
@@ -0,0 +1,17 @@
+-- 创建健康师工资额外计算表
+CREATE TABLE IF NOT EXISTS `lq_salary_extra_calculation` (
+ `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID',
+ `F_EmployeeId` VARCHAR(50) NULL COMMENT '健康师ID',
+ `F_Year` INT NULL COMMENT '年份',
+ `F_Month` INT NULL COMMENT '月份',
+ `F_BaseRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '基础奖励业绩',
+ `F_CooperationRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '合作奖励业绩',
+ `F_NewCustomerPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '新客业绩',
+ `F_NewCustomerConversionRate` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '新客成交率',
+ `F_UpgradePerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '升单业绩',
+ `F_UpgradeConversionRate` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '升单成交率',
+ PRIMARY KEY (`F_Id`),
+ KEY `idx_employee_year_month` (`F_EmployeeId`, `F_Year`, `F_Month`),
+ KEY `idx_year_month` (`F_Year`, `F_Month`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康师工资额外计算表';
+
diff --git a/sql/添加健康师工资表其他业绩字段.sql b/sql/添加健康师工资表其他业绩字段.sql
new file mode 100644
index 0000000..149f148
--- /dev/null
+++ b/sql/添加健康师工资表其他业绩字段.sql
@@ -0,0 +1,10 @@
+-- 在健康师工资表中添加其他业绩加和其他业绩减字段
+
+-- 添加其他业绩加字段
+ALTER TABLE `lq_salary_statistics`
+ADD COLUMN `F_OtherPerformanceAdd` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩加' AFTER `F_UpgradePerformanceCommission`;
+
+-- 添加其他业绩减字段
+ALTER TABLE `lq_salary_statistics`
+ADD COLUMN `F_OtherPerformanceSubtract` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩减' AFTER `F_OtherPerformanceAdd`;
+
diff --git a/sql/添加健康师工资表升单人头数和提成金额字段.sql b/sql/添加健康师工资表升单人头数和提成金额字段.sql
new file mode 100644
index 0000000..40f73d8
--- /dev/null
+++ b/sql/添加健康师工资表升单人头数和提成金额字段.sql
@@ -0,0 +1,14 @@
+-- 在健康师工资表中添加升单人头数、新客业绩提成金额、升单业绩提成金额字段
+
+-- 添加升单人头数字段(放在F_NewCustomerPoint字段后面)
+ALTER TABLE `lq_salary_statistics`
+ADD COLUMN `F_UpgradeCustomerCount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '升单人头数' AFTER `F_NewCustomerPoint`;
+
+-- 添加新客业绩提成金额字段
+ALTER TABLE `lq_salary_statistics`
+ADD COLUMN `F_NewCustomerPerformanceCommission` DECIMAL(18,2) DEFAULT 0.00 COMMENT '新客业绩提成金额' AFTER `F_UpgradePoint`;
+
+-- 添加升单业绩提成金额字段
+ALTER TABLE `lq_salary_statistics`
+ADD COLUMN `F_UpgradePerformanceCommission` DECIMAL(18,2) DEFAULT 0.00 COMMENT '升单业绩提成金额' AFTER `F_NewCustomerPerformanceCommission`;
+
diff --git a/sql/添加健康师工资表奖励业绩字段.sql b/sql/添加健康师工资表奖励业绩字段.sql
new file mode 100644
index 0000000..088986c
--- /dev/null
+++ b/sql/添加健康师工资表奖励业绩字段.sql
@@ -0,0 +1,10 @@
+-- 在健康师工资表中添加基础奖励业绩和合作奖励业绩字段
+
+-- 添加基础奖励业绩字段
+ALTER TABLE `lq_salary_statistics`
+ADD COLUMN `F_BaseRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '基础奖励业绩' AFTER `F_CooperationPerformance`;
+
+-- 添加合作奖励业绩字段
+ALTER TABLE `lq_salary_statistics`
+ADD COLUMN `F_CooperationRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '合作奖励业绩' AFTER `F_BaseRewardPerformance`;
+
diff --git a/sql/添加健康师工资表实际业绩字段.sql b/sql/添加健康师工资表实际业绩字段.sql
new file mode 100644
index 0000000..6b6d169
--- /dev/null
+++ b/sql/添加健康师工资表实际业绩字段.sql
@@ -0,0 +1,10 @@
+-- 在健康师工资表中添加实际基础业绩和实际合作业绩字段
+
+-- 添加实际基础业绩字段
+ALTER TABLE `lq_salary_statistics`
+ADD COLUMN `F_ActualBasePerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '实际基础业绩' AFTER `F_CooperationRewardPerformance`;
+
+-- 添加实际合作业绩字段
+ALTER TABLE `lq_salary_statistics`
+ADD COLUMN `F_ActualCooperationPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '实际合作业绩' AFTER `F_ActualBasePerformance`;
+
diff --git a/sql/添加健康师工资额外计算表其他业绩字段.sql b/sql/添加健康师工资额外计算表其他业绩字段.sql
new file mode 100644
index 0000000..df98e25
--- /dev/null
+++ b/sql/添加健康师工资额外计算表其他业绩字段.sql
@@ -0,0 +1,10 @@
+-- 在健康师工资额外计算表中添加其他业绩加和其他业绩减字段
+
+-- 添加其他业绩加字段
+ALTER TABLE `lq_salary_extra_calculation`
+ADD COLUMN `F_OtherPerformanceAdd` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩加' AFTER `F_UpgradeCustomerCount`;
+
+-- 添加其他业绩减字段
+ALTER TABLE `lq_salary_extra_calculation`
+ADD COLUMN `F_OtherPerformanceSubtract` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩减' AFTER `F_OtherPerformanceAdd`;
+
diff --git a/sql/添加健康师工资额外计算表升单人头数字段.sql b/sql/添加健康师工资额外计算表升单人头数字段.sql
new file mode 100644
index 0000000..2f2e3a0
--- /dev/null
+++ b/sql/添加健康师工资额外计算表升单人头数字段.sql
@@ -0,0 +1,5 @@
+-- 在健康师工资额外计算表中添加升单人头数字段
+
+ALTER TABLE `lq_salary_extra_calculation`
+ADD COLUMN `F_UpgradeCustomerCount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '升单人头数' AFTER `F_UpgradeConversionRate`;
+