工资导入逻辑说明.md
6.06 KB
工资导入逻辑说明
导入Excel文件结构
Excel文件位置
- 路径:
ExportFiles/工资导入/ - 文件命名:
{岗位名称}工资_{时间戳}.xlsx - 工作表名称:与岗位名称对应(如"健康师工资"、"店长工资"等)
Excel文件结构
重要:Excel文件的第一列(A列)必须是 ID(主键)
A列: ID(主键,F_Id)
B列: 门店名称
C列: 员工姓名
... 其他业务字段
当前Excel文件状态
根据查看的文件,目前Excel文件的第一列还不是ID,而是"门店名称"。需要:
- 修改导出功能,确保第一列是ID
- 修改导入功能,通过ID来判断更新还是新增
导入逻辑
1. 读取Excel
- 使用
ExcelImportHelper.ToDataTable()读取Excel文件 - 第一列(索引0)为ID字段
- 从第二行开始读取数据(第一行是表头)
2. 判断是更新还是新增
foreach (var row in dataTable.Rows)
{
var id = row[0]?.ToString()?.Trim(); // 第一列是ID
if (!string.IsNullOrWhiteSpace(id))
{
// 有ID → 查找现有记录
var existing = await _db.Queryable<SalaryEntity>()
.Where(x => x.Id == id)
.FirstAsync();
if (existing != null)
{
// 记录存在 → 检查是否可以更新
// ... 更新逻辑
}
else
{
// 记录不存在 → 新增
// ... 新增逻辑
}
}
else
{
// 没有ID → 新增
// ... 新增逻辑
}
}
3. 保护逻辑(已锁定或已确认的记录不能导入覆盖)
if (existing != null)
{
// 检查是否已锁定(已锁定的不能导入覆盖)
if (existing.IsLocked == 1)
{
failMessages.Add($"员工 {existing.EmployeeName} (ID: {id}) 的工资已锁定,不能导入覆盖");
failCount++;
continue; // 跳过
}
// 检查是否已确认(已确认的不能导入覆盖)
if (existing.EmployeeConfirmStatus == 1)
{
failMessages.Add($"员工 {existing.EmployeeName} (ID: {id}) 的工资已确认,不能导入覆盖");
failCount++;
continue; // 跳过
}
// 可以更新 → 覆盖现有记录(未锁定且未确认)
existing.StoreName = storeName;
existing.EmployeeName = employeeName;
// ... 更新所有字段
// 注意:导入后重置确认状态(如果被覆盖)
existing.EmployeeConfirmStatus = 0;
existing.EmployeeConfirmTime = null;
existing.EmployeeConfirmRemark = null;
recordsToUpdate.Add(existing);
}
else
{
// 新增记录
var newRecord = new SalaryEntity
{
Id = string.IsNullOrWhiteSpace(id) ? YitIdHelper.NextId().ToString() : id,
// ... 其他字段
EmployeeConfirmStatus = 0,
IsLocked = 0
};
recordsToInsert.Add(newRecord);
}
导出功能修改
需要修改的导出功能
确保导出时,第一列是ID(主键),格式如下:
[HttpGet("Actions/Export")]
public async Task<dynamic> Export([FromQuery] SalaryInput input)
{
var exportData = await this.GetNoPagingList(input);
// 配置导出字段,确保第一列是ID
List<ParamsModel> paramList = new List<ParamsModel>
{
new ParamsModel { value = "ID", field = "id" }, // 第一列必须是ID
new ParamsModel { value = "门店名称", field = "storeName" },
new ParamsModel { value = "员工姓名", field = "employeeName" },
// ... 其他字段
};
// ... Excel导出逻辑
}
涉及的服务
需要在以下9个工资服务中实现导入功能:
LqSalaryService- 健康师LqTechTeacherSalaryService- 科技部老师LqAssistantSalaryService- 店助/店助主任LqStoreManagerSalaryService- 店长LqDirectorSalaryService- 主任LqMajorProjectTeacherSalaryService- 大项目部老师LqMajorProjectDirectorSalaryService- 大项目主管LqTechGeneralManagerSalaryService- 科技部总经理LqBusinessUnitManagerSalaryService- 事业部总经理/经理
实施步骤
- ✅ 确认导入逻辑:通过ID判断更新/新增
- ⏳ 修改导出功能:确保第一列是ID
- ⏳ 实现/修改导入功能:
- 读取Excel,第一列为ID
- 有ID且存在 → 检查锁定/确认状态 → 更新
- 有ID但不存在 → 新增(使用该ID)
- 无ID → 新增(自动生成ID)
- ⏳ 添加保护逻辑:已锁定或已确认的记录跳过
关键点
- Excel第一列必须是ID:这样导入时才能准确匹配记录
- ID的处理:
- Excel有ID且数据库存在 → 更新(检查锁定/确认状态)
- Excel有ID但数据库不存在 → 新增(使用Excel中的ID)
- Excel无ID → 新增(自动生成新ID)
- 保护机制:
- 已锁定(IsLocked = 1)的记录不能导入覆盖
- 已确认(EmployeeConfirmStatus = 1)的记录不能导入覆盖
- 只有未锁定且未确认的记录才能导入覆盖
- 导入后重置确认状态:如果记录被导入覆盖,确认状态会被重置为0
工作流程说明
完整的工资处理流程
- 系统计算工资 → 生成工资数据(IsLocked = 0, EmployeeConfirmStatus = 0)
- 导出Excel → 第一列是ID,后续列是业务字段
- 线下梳理处理 → 在Excel中调整数据
- 导入Excel → 通过ID匹配,覆盖未锁定且未确认的记录
- 管理员锁定工资 → 设置 IsLocked = 1(准备让员工确认)
- 员工查看工资条 → 只能查看已锁定的工资条
- 员工确认工资条 → 只能确认已锁定的工资条(IsLocked = 1 且 EmployeeConfirmStatus = 0)
- 发工资 → 确认后(EmployeeConfirmStatus = 1)才会去发工资
状态流转
初始状态:IsLocked = 0, EmployeeConfirmStatus = 0
↓ 管理员锁定
已锁定状态:IsLocked = 1, EmployeeConfirmStatus = 0
↓ 员工确认
已确认状态:IsLocked = 1, EmployeeConfirmStatus = 1
↓ 发工资