83a6fd1f
“wangming”
feat: 添加员工确认状态及相关...
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
# 工资导入逻辑说明
## 导入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. 判断是更新还是新增
```csharp
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. 保护逻辑(已锁定或已确认的记录不能导入覆盖)
```csharp
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(主键),格式如下:
```csharp
[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个工资服务中实现导入功能:
1. `LqSalaryService` - 健康师
2. `LqTechTeacherSalaryService` - 科技部老师
3. `LqAssistantSalaryService` - 店助/店助主任
4. `LqStoreManagerSalaryService` - 店长
5. `LqDirectorSalaryService` - 主任
6. `LqMajorProjectTeacherSalaryService` - 大项目部老师
7. `LqMajorProjectDirectorSalaryService` - 大项目主管
8. `LqTechGeneralManagerSalaryService` - 科技部总经理
9. `LqBusinessUnitManagerSalaryService` - 事业部总经理/经理
---
## 实施步骤
1. ✅ 确认导入逻辑:通过ID判断更新/新增
2. ⏳ 修改导出功能:确保第一列是ID
3. ⏳ 实现/修改导入功能:
- 读取Excel,第一列为ID
- 有ID且存在 → 检查锁定/确认状态 → 更新
- 有ID但不存在 → 新增(使用该ID)
- 无ID → 新增(自动生成ID)
4. ⏳ 添加保护逻辑:已锁定或已确认的记录跳过
---
## 关键点
1. **Excel第一列必须是ID**:这样导入时才能准确匹配记录
2. **ID的处理**:
- Excel有ID且数据库存在 → 更新(检查锁定/确认状态)
- Excel有ID但数据库不存在 → 新增(使用Excel中的ID)
- Excel无ID → 新增(自动生成新ID)
3. **保护机制**:
- 已锁定(IsLocked = 1)的记录不能导入覆盖
- 已确认(EmployeeConfirmStatus = 1)的记录不能导入覆盖
- 只有未锁定且未确认的记录才能导入覆盖
4. **导入后重置确认状态**:如果记录被导入覆盖,确认状态会被重置为0
---
## 工作流程说明
### 完整的工资处理流程
1. **系统计算工资** → 生成工资数据(IsLocked = 0, EmployeeConfirmStatus = 0)
2. **导出Excel** → 第一列是ID,后续列是业务字段
3. **线下梳理处理** → 在Excel中调整数据
4. **导入Excel** → 通过ID匹配,覆盖未锁定且未确认的记录
5. **管理员锁定工资** → 设置 IsLocked = 1(准备让员工确认)
6. **员工查看工资条** → 只能查看已锁定的工资条
7. **员工确认工资条** → 只能确认已锁定的工资条(IsLocked = 1 且 EmployeeConfirmStatus = 0)
8. **发工资** → 确认后(EmployeeConfirmStatus = 1)才会去发工资
### 状态流转
```
初始状态:IsLocked = 0, EmployeeConfirmStatus = 0
↓ 管理员锁定
已锁定状态:IsLocked = 1, EmployeeConfirmStatus = 0
↓ 员工确认
已确认状态:IsLocked = 1, EmployeeConfirmStatus = 1
↓ 发工资
```
|