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((ec, u) => new JoinQueryInfos(
JoinType.Left, u.Id == SqlFunc.ToString(ec.EmployeeId)))
.Where((ec, u) => u.Id != null && (u.DeleteMark == null || u.DeleteMark == 0));
// 年份筛选
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 data = 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
})
.MergeTable()
.OrderByIF(!string.IsNullOrEmpty(input.sidx), input.sidx + " " + input.sort)
.ToPagedListAsync(input.currentPage, input.pageSize);
return PageResult.SqlSugarPageResult(data);
}
///
/// 从Excel导入健康师工资额外计算数据
///
/// Excel文件
/// 是否需要清理导入月份数据(默认:true,清理)
/// 导入结果
[HttpPost("ImportFromExcel")]
public async Task ImportFromExcel(IFormFile file, bool clearBeforeImport = true)
{
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 GetColumnValue = new Func((colIndex) =>
{
if (colIndex < row.ItemArray.Length)
{
return row[colIndex]?.ToString()?.Trim() ?? "";
}
return "";
});
var id = GetColumnValue(0);
var employeeName = GetColumnValue(1);
var employeePhone = GetColumnValue(2);
var yearText = GetColumnValue(3);
var monthText = GetColumnValue(4);
var baseRewardPerformanceText = GetColumnValue(5);
var cooperationRewardPerformanceText = GetColumnValue(6);
var newCustomerPerformanceText = GetColumnValue(7);
var newCustomerConversionRateText = GetColumnValue(8);
var upgradePerformanceText = GetColumnValue(9);
var upgradeConversionRateText = GetColumnValue(10);
var upgradeCustomerCountText = GetColumnValue(11);
var otherPerformanceAddText = GetColumnValue(12);
var otherPerformanceSubtractText = GetColumnValue(13);
// 跳过空行
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之间");
}
// 辅助方法:清理数值字符串(去除千分位分隔符、货币符号等)
Func CleanNumericString = (str) =>
{
if (string.IsNullOrWhiteSpace(str))
return "0";
// 去除常见的非数字字符(保留小数点和负号)
return str.Trim()
.Replace(",", "") // 去除千分位分隔符
.Replace(",", "") // 去除中文逗号
.Replace("¥", "") // 去除人民币符号
.Replace("$", "") // 去除美元符号
.Replace("元", "") // 去除"元"字
.Replace(" ", ""); // 去除空格
};
// 解析数值字段(允许为空,默认为0)
decimal.TryParse(CleanNumericString(baseRewardPerformanceText), out decimal baseRewardPerformance);
decimal.TryParse(CleanNumericString(cooperationRewardPerformanceText), out decimal cooperationRewardPerformance);
decimal.TryParse(CleanNumericString(newCustomerPerformanceText), out decimal newCustomerPerformance);
decimal.TryParse(CleanNumericString(newCustomerConversionRateText), out decimal newCustomerConversionRate);
decimal.TryParse(CleanNumericString(upgradePerformanceText), out decimal upgradePerformance);
decimal.TryParse(CleanNumericString(upgradeConversionRateText), out decimal upgradeConversionRate);
decimal.TryParse(CleanNumericString(upgradeCustomerCountText), out decimal upgradeCustomerCount);
decimal.TryParse(CleanNumericString(otherPerformanceAddText), out decimal otherPerformanceAdd);
decimal.TryParse(CleanNumericString(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文件中没有有效的数据行");
}
// 如果需要清理导入月份数据,先删除导入数据中所有涉及的年份+月份组合的数据
if (clearBeforeImport)
{
// 获取导入数据中所有唯一的年份+月份组合
var yearMonthPairs = importData
.Select(x => new { x.Year, x.Month })
.Distinct()
.ToList();
foreach (var pair in yearMonthPairs)
{
await _db.Deleteable()
.Where(x => x.Year == pair.Year && x.Month == pair.Month)
.ExecuteCommandAsync();
}
}
// 处理导入数据
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();
// 用于跟踪已处理的员工记录,避免重复(Key: EmployeeId_Year_Month)
var processedEmployeeRecords = new Dictionary();
foreach (var item in importData)
{
try
{
// 1. 查找用户ID(优先使用Excel中的id作为员工ID,否则通过姓名和电话查找)
UserEntity user = null;
LqSalaryExtraCalculationEntity existingRecord = null;
// 优先使用Excel中的id作为员工ID(EmployeeId)直接查找用户
if (!string.IsNullOrEmpty(item.Id))
{
// 先尝试将id作为员工ID查找用户
user = await _db.Queryable()
.Where(u => u.Id == item.Id)
.FirstAsync();
// 如果找不到,尝试将id作为记录ID查找现有记录,获取EmployeeId
if (user == null)
{
existingRecord = await _db.Queryable()
.Where(x => x.Id == item.Id)
.FirstAsync();
if (existingRecord != null)
{
// 通过EmployeeId查找用户
user = await _db.Queryable()
.Where(u => u.Id == existingRecord.EmployeeId)
.FirstAsync();
}
}
}
// 如果还没有找到用户,通过姓名和电话查找
if (user == null)
{
// 处理姓名:去除"A"前缀
var cleanName = item.EmployeeName;
if (cleanName.StartsWith("A"))
{
cleanName = cleanName.Substring(1).Trim();
}
// 处理电话:如果电话是"无"或空,只按姓名查找
var phoneIsEmpty = string.IsNullOrWhiteSpace(item.EmployeePhone) ||
item.EmployeePhone.Trim() == "无" ||
item.EmployeePhone.Trim() == "00000000" ||
item.EmployeePhone.Trim().Length < 8; // 排除明显无效的电话
if (phoneIsEmpty)
{
// 只按姓名查找(支持原姓名和去除前缀后的姓名)
user = await _db.Queryable()
.Where(u => u.RealName == item.EmployeeName || u.RealName == cleanName)
.FirstAsync();
}
else
{
// 同时匹配姓名和电话(支持原姓名和去除前缀后的姓名)
user = await _db.Queryable()
.Where(u => (u.RealName == item.EmployeeName || u.RealName == cleanName) &&
u.MobilePhone == item.EmployeePhone)
.FirstAsync();
}
}
if (user == null)
{
errorMessages.Add($"健康师 {item.EmployeeName}({item.EmployeePhone}) 不存在");
failCount++;
continue;
}
// 2. 检查是否已经在本次导入中处理过该员工(避免重复,使用员工ID而不是姓名)
var recordKey = $"{user.Id}_{item.Year}_{item.Month}";
if (processedEmployeeRecords.ContainsKey(recordKey))
{
// 如果已经处理过,更新记录而不是创建新记录
existingRecord = processedEmployeeRecords[recordKey];
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,因为已经在processedEmployeeRecords中,会在最后统一处理
successCount++;
continue;
}
// 3. 如果还没有找到现有记录,则根据健康师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);
// 记录到已处理字典中
processedEmployeeRecords[recordKey] = 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);
// 记录到已处理字典中(注意:这里需要创建一个临时对象,因为entity还没有ID)
processedEmployeeRecords[recordKey] = 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())
{
// 根据主键ID去重,确保每个记录只更新一次
var uniqueEntitiesToUpdate = entitiesToUpdate
.GroupBy(x => x.Id)
.Select(g => g.First())
.ToList();
// 明确指定要更新的字段,确保所有字段都被更新(包括升单业绩)
// 注意:Updateable接收实体列表时,会自动根据主键更新,不需要Where条件
await _db.Updateable(uniqueEntitiesToUpdate)
.UpdateColumns(it => new
{
it.BaseRewardPerformance,
it.CooperationRewardPerformance,
it.NewCustomerPerformance,
it.NewCustomerConversionRate,
it.UpgradePerformance,
it.UpgradeConversionRate,
it.UpgradeCustomerCount,
it.OtherPerformanceAdd,
it.OtherPerformanceSubtract
})
.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() * 10000);
var cooperationRewardPerformance = (decimal)(random.NextDouble() * 10000);
// 其他金额字段:10000-30000之间
var newCustomerPerformance = (decimal)(random.NextDouble() * 10000);
var upgradePerformance = (decimal)(random.NextDouble() * 10000);
var otherPerformanceAdd = (decimal)(random.NextDouble() * 10000);
var otherPerformanceSubtract = (decimal)(random.NextDouble() * 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}");
}
}
}
}