41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1
2
|
# 员工门店归属快照表方案详细设计
|
db9c79c0
“wangming”
feat: punch-based...
|
3
4
|
> **建表 SQL(与本文字段、索引对齐)**:`项目文档相关/sql/2026-4-3/R-002_员工门店归属快照表_lq_employee_store_assignment.sql`。合并版改造说明见 `用户主档与薪酬考勤改造记录.md`。
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
## 📋 方案概述
通过创建门店归属快照表,记录员工每个月的门店归属历史,以时间维度为唯一标准,解决工资计算和数据补录中的门店归属问题。
---
## 🗄️ 数据模型设计
### 1. 核心表设计
#### 表名:`lq_employee_store_assignment`(员工门店归属表)
#### 字段设计
|
db9c79c0
“wangming”
feat: punch-based...
|
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
| 字段名 | 类型 | 说明 | 约束 | 备注 |
| ----------------- | ------------ | ------ | ----------- | ----------------- |
| F_Id | VARCHAR(50) | 主键ID | PRIMARY KEY | 使用YitIdHelper生成 |
| F_EmployeeId | VARCHAR(50) | 员工ID | NOT NULL | 关联BASE_USER.F_Id |
| F_StoreId | VARCHAR(50) | 门店ID | NOT NULL | 关联lq_mdxx.F_Id |
| F_StoreName | VARCHAR(200) | 门店名称 | | 冗余字段,便于查询和显示 |
| F_Year | INT | 年份 | NOT NULL | 如:2025 |
| F_Month | INT | 月份 | NOT NULL | 1-12 |
| F_StatisticsMonth | VARCHAR(6) | 统计月份 | NOT NULL | YYYYMM格式,如:202501 |
| F_StartDate | DATE | 归属开始日期 | NOT NULL | 精确到天,用于月中调店 |
| F_EndDate | DATE | 归属结束日期 | NULL | 为空表示当前仍在归属,精确到天 |
| F_CreateTime | DATETIME | 创建时间 | | |
| F_UpdateTime | DATETIME | 更新时间 | | |
| F_CreateUser | VARCHAR(50) | 创建人 | | |
| F_UpdateUser | VARCHAR(50) | 更新人 | | |
| F_IsEffective | INT | 是否有效 | DEFAULT 1 | 1=有效,0=无效(软删除) |
| F_Remark | VARCHAR(500) | 备注 | | 记录调店原因等 |
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
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
|
#### 索引设计
```sql
-- 主键索引(自动创建)
PRIMARY KEY (F_Id)
-- 员工+月份查询索引(最常用)
INDEX idx_employee_month (F_EmployeeId, F_StatisticsMonth, F_IsEffective)
-- 门店+月份查询索引(用于门店维度统计)
INDEX idx_store_month (F_StoreId, F_StatisticsMonth, F_IsEffective)
-- 日期范围查询索引(用于精确查询某天的归属)
INDEX idx_employee_date (F_EmployeeId, F_StartDate, F_EndDate)
-- 时间范围查询索引(用于查询某个时间点的归属)
INDEX idx_date_range (F_StartDate, F_EndDate, F_IsEffective)
```
#### 唯一约束
**重要考虑:是否允许一个员工在一个月内归属多个门店?**
**建议:允许**(支持月中调店场景)
**唯一约束设计:**
|
db9c79c0
“wangming”
feat: punch-based...
|
65
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
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
|
- 不设置唯一约束(允许一个员工一个月有多条记录)
- 通过业务逻辑保证:同一个员工的同一天只能有一条有效记录
- 通过应用层校验:插入/更新时检查是否有日期重叠
---
### 2. 数据关系图
```
BASE_USER (员工表)
↓ F_Id
lq_employee_store_assignment (门店归属表)
↓ F_StoreId
lq_mdxx (门店表)
↓
lq_salary_statistics (工资统计表) - 使用归属表的门店信息
lq_kd_jksyj (开单业绩表) - 使用归属表的门店信息
lq_xh_jksyj (消耗业绩表) - 使用归属表的门店信息
```
---
## 📐 业务规则设计
### 1. 基础规则
#### 1.1 时间维度原则
|
db9c79c0
“wangming”
feat: punch-based...
|
93
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
94
95
96
97
98
99
|
- **唯一标准**:以数据实际发生的时间(年、月、日)作为门店归属的判断标准
- **不可变更**:历史归属记录一旦创建,原则上不允许修改(可通过软删除+新增的方式调整)
#### 1.2 归属记录创建规则
**场景A:月初调店(整月归属一个门店)**
|
db9c79c0
“wangming”
feat: punch-based...
|
100
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
101
102
103
104
105
106
|
- 员工在1月1日从门店A调到门店B
- 创建两条记录:
- 记录1:1月,门店A,开始日期:1月1日,结束日期:1月1日(或空,如果跨月)
- 记录2:1月,门店B,开始日期:1月2日,结束日期:空(或1月31日)
**场景B:月中调店(一个月归属两个门店)**
|
db9c79c0
“wangming”
feat: punch-based...
|
107
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
108
109
110
111
112
113
|
- 员工在1月15日从门店A调到门店B
- 创建两条记录:
- 记录1:1月,门店A,开始日期:1月1日,结束日期:1月14日
- 记录2:1月,门店B,开始日期:1月15日,结束日期:1月31日(或空)
**场景C:整月归属一个门店**
|
db9c79c0
“wangming”
feat: punch-based...
|
114
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
115
116
117
118
119
120
121
|
- 员工整个月都在门店A
- 创建一条记录:
- 记录:1月,门店A,开始日期:1月1日,结束日期:1月31日(或空)
#### 1.3 归属记录查询规则
**查询某员工某月的所有门店归属:**
|
db9c79c0
“wangming”
feat: punch-based...
|
122
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
123
124
125
126
127
128
129
130
131
|
```sql
SELECT * FROM lq_employee_store_assignment
WHERE F_EmployeeId = @EmployeeId
AND F_StatisticsMonth = @StatisticsMonth
AND F_IsEffective = 1
ORDER BY F_StartDate
```
**查询某员工某天的门店归属:**
|
db9c79c0
“wangming”
feat: punch-based...
|
132
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
133
134
135
136
137
138
139
140
141
142
143
|
```sql
SELECT * FROM lq_employee_store_assignment
WHERE F_EmployeeId = @EmployeeId
AND F_StartDate <= @Date
AND (F_EndDate >= @Date OR F_EndDate IS NULL)
AND F_IsEffective = 1
ORDER BY F_StartDate DESC
LIMIT 1
```
**查询某门店某月的所有员工:**
|
db9c79c0
“wangming”
feat: punch-based...
|
144
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
```sql
SELECT DISTINCT F_EmployeeId FROM lq_employee_store_assignment
WHERE F_StoreId = @StoreId
AND F_StatisticsMonth = @StatisticsMonth
AND F_IsEffective = 1
```
### 2. 数据维护规则
#### 2.1 自动维护机制
**触发时机:员工调店时**
**操作步骤:**
|
db9c79c0
“wangming”
feat: punch-based...
|
159
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
160
161
|
1. 用户在前端操作:员工从门店A调到门店B(选择调店日期)
2. 后端处理逻辑:
|
db9c79c0
“wangming”
feat: punch-based...
|
162
163
164
165
|
- 查询当前员工的有效归属记录(结束日期为空的记录)
- 如果存在,将结束日期设置为调店日期的前一天
- 创建新的归属记录:开始日期为调店日期,结束日期为空
- 如果调店日期是月初,更新统计月份;如果是月中,保持原月份,新增记录
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
166
167
168
169
|
#### 2.2 手动维护机制
**适用场景:**
|
db9c79c0
“wangming”
feat: punch-based...
|
170
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
171
172
173
174
175
|
- 历史数据补录
- 数据修正
- 批量导入
**操作方式:**
|
db9c79c0
“wangming”
feat: punch-based...
|
176
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
177
178
179
180
181
182
183
|
- 提供管理界面:员工门店归属管理
- 支持批量导入Excel
- 支持手动添加/编辑/删除(软删除)
#### 2.3 数据校验规则
**插入/更新时的校验:**
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
184
|
|
db9c79c0
“wangming”
feat: punch-based...
|
185
186
187
|
1. **日期范围校验**:
- 开始日期不能大于结束日期
- 开始日期和结束日期必须在同一个月内(或跨月但统计月份相同)
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
188
|
2. **重叠校验**:
|
db9c79c0
“wangming”
feat: punch-based...
|
189
190
|
- 同一个员工,同一天不能有多条有效记录
- 如果存在重叠,需要先处理旧记录(结束或删除)
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
191
|
3. **完整性校验**:
|
db9c79c0
“wangming”
feat: punch-based...
|
192
193
|
- 员工ID、门店ID必须存在
- 统计月份格式正确(YYYYMM)
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
194
|
4. **业务逻辑校验**:
|
db9c79c0
“wangming”
feat: punch-based...
|
195
196
|
- 门店必须有效(未删除)
- 员工必须有效(未删除、已启用)
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
197
198
199
200
201
202
203
204
205
206
|
---
## 🔄 与现有系统集成
### 1. 工资计算集成
#### 1.1 健康师工资计算
**当前逻辑:**
|
db9c79c0
“wangming”
feat: punch-based...
|
207
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
208
209
210
211
212
213
214
|
```csharp
// 1. 优先从业绩数据中获取门店(performanceData中的StoreId)
// 2. 如果没有,从消耗数据中获取门店
// 3. 如果还没有,使用 BASE_USER.F_Mdid
```
**调整后逻辑:**
|
db9c79c0
“wangming”
feat: punch-based...
|
215
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
```csharp
// 1. 查询门店归属快照表(按统计月份)
var storeAssignments = await QueryStoreAssignment(employeeId, statisticsMonth);
// 2. 如果快照表有记录,使用快照表的门店归属
if (storeAssignments.Any())
{
// 按时间范围拆分计算(如果一个月有多个门店)
foreach (var assignment in storeAssignments)
{
// 查询该员工在该门店、该时间范围内的业绩数据
// 计算该部分工资
}
}
// 3. 如果快照表没有记录,使用当前逻辑(回退方案)
else
{
// 使用现有逻辑:从业务数据或BASE_USER获取
}
```
**特殊处理:月中调店**
|
db9c79c0
“wangming”
feat: punch-based...
|
238
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
239
240
241
242
243
|
- 如果一个月有多个门店归属,需要按时间范围拆分计算
- 每个时间段的业绩数据单独统计
- 工资计算结果需要合并(一个员工一个月可能有多条工资记录?还是合并为一条?)
**建议:合并为一条记录**
|
db9c79c0
“wangming”
feat: punch-based...
|
244
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
245
246
247
248
249
250
|
- 工资统计表仍然是一个员工一个月一条记录
- 但需要记录主要门店(归属时间最长的门店,或业绩最多的门店)
#### 1.2 其他岗位工资计算
**店长工资:**
|
db9c79c0
“wangming”
feat: punch-based...
|
251
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
252
253
254
255
|
- 店长通常是固定门店,但也要支持调店
- 计算逻辑与健康师类似
**主任工资:**
|
db9c79c0
“wangming”
feat: punch-based...
|
256
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
257
258
259
|
- 类似逻辑
**其他岗位:**
|
db9c79c0
“wangming”
feat: punch-based...
|
260
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
261
262
263
264
265
266
267
|
- 统一使用快照表逻辑
### 2. 数据补录集成
#### 2.1 开单数据补录
**当前问题:**
|
db9c79c0
“wangming”
feat: punch-based...
|
268
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
269
270
271
|
- 补录历史数据时,如果没有门店信息,可能使用当前门店(BASE_USER.F_Mdid)
**调整后逻辑:**
|
db9c79c0
“wangming”
feat: punch-based...
|
272
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
|
```csharp
// 补录开单数据时
public async Task ImportBillingData(BillingRecord record)
{
var billingDate = record.BillingDate; // 开单日期
var employeeId = record.EmployeeId; // 健康师ID
var statisticsMonth = $"{billingDate.Year}{billingDate.Month:D2}";
// 1. 查询该日期该员工的门店归属
var assignment = await QueryStoreAssignmentByDate(employeeId, billingDate);
// 2. 设置门店ID
if (assignment != null)
{
record.StoreId = assignment.StoreId;
}
else
{
// 回退方案:使用当前逻辑
record.StoreId = GetStoreIdByCurrentLogic(employeeId);
// 可选:记录警告日志,提示需要维护门店归属
_logger.LogWarning($"员工{employeeId}在{billingDate}的门店归属未找到,使用当前门店");
}
// 3. 保存数据
await SaveBillingRecord(record);
}
```
#### 2.2 消耗数据补录
**类似逻辑:**
|
db9c79c0
“wangming”
feat: punch-based...
|
306
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
307
308
309
310
311
312
313
314
|
- 根据消耗日期(Yjsj)查询门店归属
- 设置消耗记录的StoreId
### 3. 数据查询集成
#### 3.1 员工列表查询
**当前逻辑:**
|
db9c79c0
“wangming”
feat: punch-based...
|
315
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
316
317
318
|
- 直接从BASE_USER表查询,显示F_Mdid对应的门店
**调整后逻辑:**
|
db9c79c0
“wangming”
feat: punch-based...
|
319
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
320
321
322
323
324
325
326
327
328
|
- 如果要显示历史门店,需要关联查询快照表
- 当前门店:BASE_USER.F_Mdid(实时)
- 历史门店:从快照表查询
#### 3.2 门店员工列表查询
**场景:查询某门店某月的所有员工**
**当前逻辑:**
|
db9c79c0
“wangming”
feat: punch-based...
|
329
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
330
331
332
|
- 查询BASE_USER表,F_Mdid = 门店ID
**调整后逻辑:**
|
db9c79c0
“wangming”
feat: punch-based...
|
333
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
|
```sql
-- 查询某门店某月的所有员工
SELECT DISTINCT esa.F_EmployeeId, u.F_RealName
FROM lq_employee_store_assignment esa
INNER JOIN BASE_USER u ON esa.F_EmployeeId = u.F_Id
WHERE esa.F_StoreId = @StoreId
AND esa.F_StatisticsMonth = @StatisticsMonth
AND esa.F_IsEffective = 1
AND u.F_DeleteMark IS NULL
AND u.F_EnabledMark = 1
```
---
## 📊 数据迁移策略
### 1. 历史数据来源
#### 1.1 数据来源分析
**可能的数据来源:**
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
355
|
|
db9c79c0
“wangming”
feat: punch-based...
|
356
357
358
359
|
1. **工资统计表(lq_salary_statistics等)**
- 已有历史工资记录
- 工资记录中有门店ID(F_StoreId)
- 可以反向推导:员工+月份 → 门店
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
360
|
2. **业务数据(开单/消耗表)**
|
db9c79c0
“wangming”
feat: punch-based...
|
361
362
363
|
- 开单业绩表(lq_kd_jksyj)有门店ID(F_StoreId)
- 消耗表(lq_xh_jksyj)有门店ID(F_StoreId)
- 可以统计:员工+月份 → 门店(取出现最多的门店)
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
364
|
3. **BASE_USER表的变更日志(如果有)**
|
db9c79c0
“wangming”
feat: punch-based...
|
365
|
- 如果系统有记录用户表变更日志,可以追溯
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
366
|
4. **人工确认**
|
db9c79c0
“wangming”
feat: punch-based...
|
367
|
- 对于无法确定的数据,需要人工确认
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
368
369
370
371
|
#### 1.2 数据迁移优先级
**优先级1:工资统计表(最可靠)**
|
db9c79c0
“wangming”
feat: punch-based...
|
372
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
373
374
375
376
377
|
- 理由:工资记录是最终结果,门店归属相对准确
- 方法:从工资统计表提取:员工ID + 统计月份 + 门店ID
- 时间范围:最近2年的数据(或更长时间,根据业务需要)
**优先级2:业务数据统计(补充)**
|
db9c79c0
“wangming”
feat: punch-based...
|
378
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
379
380
381
382
383
|
- 理由:对于没有工资记录的月份,从业务数据推断
- 方法:统计每个员工每个月的业绩数据,取门店出现最多的
- 注意:可能存在一个月有多个门店的情况(月中调店)
**优先级3:BASE_USER当前门店(兜底)**
|
db9c79c0
“wangming”
feat: punch-based...
|
384
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
|
- 理由:对于完全没有数据的员工,使用当前门店
- 方法:BASE_USER.F_Mdid作为最后兜底
### 2. 数据迁移步骤
#### 步骤1:数据提取
```sql
-- 从工资统计表提取(健康师工资)
INSERT INTO lq_employee_store_assignment
(F_Id, F_EmployeeId, F_StoreId, F_StoreName, F_Year, F_Month, F_StatisticsMonth, F_StartDate, F_EndDate, ...)
SELECT
YitIdHelper.NextId(),
F_EmployeeId,
F_StoreId,
(SELECT F_Dm FROM lq_mdxx WHERE F_Id = F_StoreId),
YEAR(STR_TO_DATE(CONCAT(F_StatisticsMonth, '01'), '%Y%m%d')),
MONTH(STR_TO_DATE(CONCAT(F_StatisticsMonth, '01'), '%Y%m%d')),
F_StatisticsMonth,
STR_TO_DATE(CONCAT(F_StatisticsMonth, '01'), '%Y%m%d'), -- 开始日期:月初
LAST_DAY(STR_TO_DATE(CONCAT(F_StatisticsMonth, '01'), '%Y%m%d')), -- 结束日期:月末
NOW(),
NOW(),
'System',
'System',
1
FROM lq_salary_statistics
WHERE F_StoreId IS NOT NULL AND F_StoreId != ''
GROUP BY F_EmployeeId, F_StatisticsMonth, F_StoreId
```
#### 步骤2:数据清洗
**处理重复记录:**
|
db9c79c0
“wangming”
feat: punch-based...
|
419
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
420
421
422
423
|
- 如果一个员工一个月有多条记录(可能是数据异常,也可能是月中调店)
- 需要人工确认:是数据异常还是确实调店
**处理缺失数据:**
|
db9c79c0
“wangming”
feat: punch-based...
|
424
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
425
426
427
428
429
430
|
- 对于没有工资记录但可能有业务数据的员工
- 从业务数据统计推断
#### 步骤3:数据验证
**验证规则:**
|
db9c79c0
“wangming”
feat: punch-based...
|
431
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
432
433
434
435
436
437
|
1. 每个员工每个月至少有一条记录(如果有业务数据)
2. 门店ID必须存在
3. 统计月份格式正确
4. 日期范围合理
**验证方法:**
|
db9c79c0
“wangming”
feat: punch-based...
|
438
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
439
440
441
442
443
444
|
- 抽样检查:随机抽取一定比例的数据,人工验证
- 逻辑检查:检查是否有明显异常(如同一员工同一天多个门店)
#### 步骤4:数据导入
**导入方式:**
|
db9c79c0
“wangming”
feat: punch-based...
|
445
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
446
447
448
449
450
451
452
|
- 批量导入(使用事务保证一致性)
- 分批次导入(避免锁表时间过长)
- 记录导入日志(成功/失败记录)
### 3. 数据迁移时间窗口
**建议:**
|
db9c79c0
“wangming”
feat: punch-based...
|
453
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
454
455
456
457
458
459
460
461
462
463
464
465
466
|
- 选择业务低峰期(如夜间或周末)
- 分批次迁移(先迁移最近3个月,验证无误后再迁移更多)
- 保留回滚方案(如果迁移失败,可以删除导入的数据)
---
## 🎯 边界情况处理
### 1. 月中调店
**场景:** 员工在1月15日从门店A调到门店B
**处理方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
467
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
468
469
470
471
472
|
- 创建两条归属记录
- 工资计算时,按时间范围拆分计算
- 最终工资记录合并为一条(记录主要门店)
**问题:**
|
db9c79c0
“wangming”
feat: punch-based...
|
473
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
474
475
476
477
478
479
480
481
482
483
|
- 工资统计表中的门店ID如何设置?
- 方案A:设置为主要门店(归属时间最长的门店,或业绩最多的门店)
- 方案B:设置为空,或记录为"多门店"
- **建议:方案A**,记录为主要门店,在备注或明细中说明
### 2. 数据缺失
**场景:** 快照表中没有某员工某月的归属记录
**处理方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
484
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
485
486
487
488
489
490
491
492
493
|
- 回退到现有逻辑(从业务数据或BASE_USER获取)
- 记录警告日志,提示需要维护门店归属
- 可选:自动创建一条归属记录(使用当前门店或推断的门店)
### 3. 数据冲突
**场景:** 同一个员工同一天有多条归属记录(数据异常)
**处理方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
494
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
495
496
497
498
499
500
501
502
503
|
- 数据校验时发现冲突,不允许插入/更新
- 提示用户先处理冲突记录
- 提供数据修正工具
### 4. 跨月调店
**场景:** 员工在1月31日调到门店B,实际在2月1日生效
**处理方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
504
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
505
506
507
508
509
510
511
512
513
|
- 记录1月:门店A,结束日期:1月31日
- 记录2月:门店B,开始日期:2月1日
- 如果调店日期是月末最后一天,可以考虑归属到下个月
### 5. 离职员工
**场景:** 员工在月中离职
**处理方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
514
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
515
516
517
518
519
520
521
522
523
|
- 创建归属记录:结束日期设置为离职日期
- 如果离职日期是月中,归属记录只到离职日期
- 工资计算时,只计算到离职日期
### 6. 新员工入职
**场景:** 新员工在月中入职
**处理方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
524
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
525
526
527
528
529
530
531
532
533
534
|
- 创建归属记录:开始日期为入职日期
- 如果入职日期是月中,从入职日期开始归属
---
## ⚡ 性能考虑
### 1. 查询性能优化
#### 1.1 索引优化
|
db9c79c0
“wangming”
feat: punch-based...
|
535
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
536
537
538
539
540
541
|
- 已设计多个索引(见索引设计部分)
- 定期分析索引使用情况,优化索引
#### 1.2 查询优化
**批量查询:**
|
db9c79c0
“wangming”
feat: punch-based...
|
542
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
543
|
- 工资计算时,批量查询所有员工的归属记录,避免N+1查询
|
db9c79c0
“wangming”
feat: punch-based...
|
544
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
545
546
547
548
549
550
551
552
553
554
|
```csharp
// 批量查询
var employeeIds = salaryList.Select(s => s.EmployeeId).Distinct().ToList();
var assignments = await _db.Queryable<EmployeeStoreAssignmentEntity>()
.Where(x => employeeIds.Contains(x.EmployeeId) && x.StatisticsMonth == monthStr)
.ToListAsync();
var assignmentDict = assignments.GroupBy(x => x.EmployeeId).ToDictionary(g => g.Key, g => g.ToList());
```
**缓存策略:**
|
db9c79c0
“wangming”
feat: punch-based...
|
555
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
556
557
558
559
560
561
|
- 对于频繁查询的数据(如当前月的归属记录),可以使用缓存
- 缓存失效策略:归属记录更新时,清除相关缓存
### 2. 写入性能优化
#### 2.1 批量插入
|
db9c79c0
“wangming”
feat: punch-based...
|
562
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
563
564
565
566
|
- 数据迁移时,使用批量插入(如每次插入1000条)
- 使用事务保证一致性
#### 2.2 异步处理
|
db9c79c0
“wangming”
feat: punch-based...
|
567
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
568
569
570
571
572
573
|
- 数据迁移可以异步处理
- 员工调店时,归属记录的创建可以异步处理(如果对实时性要求不高)
### 3. 表分区考虑
**是否需要分区?**
|
db9c79c0
“wangming”
feat: punch-based...
|
574
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
575
576
577
578
579
580
581
582
583
584
|
- 如果数据量很大(如10年历史数据,每月几千员工),可以考虑按年份或月份分区
- 当前阶段可能不需要,但设计时要考虑扩展性
---
## 🔒 数据一致性保证
### 1. 事务处理
**关键操作使用事务:**
|
db9c79c0
“wangming”
feat: punch-based...
|
585
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
586
587
588
589
590
591
|
- 员工调店时:更新旧记录 + 创建新记录(在一个事务中)
- 数据迁移:批量导入使用事务
### 2. 数据校验
**应用层校验:**
|
db9c79c0
“wangming”
feat: punch-based...
|
592
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
593
594
595
596
|
- 插入/更新前校验数据完整性和业务逻辑
- 防止数据冲突
**数据库层约束:**
|
db9c79c0
“wangming”
feat: punch-based...
|
597
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
598
599
600
601
602
603
|
- 外键约束(如果MySQL支持,但通常不使用外键,使用应用层保证)
- 非空约束、唯一约束(根据业务需要)
### 3. 数据同步
**与BASE_USER表同步:**
|
db9c79c0
“wangming”
feat: punch-based...
|
604
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
605
606
607
608
|
- 员工调店时,同时更新BASE_USER.F_Mdid和归属表
- 或者:归属表作为数据源,BASE_USER.F_Mdid从归属表计算(当前月份的主要门店)
**建议:**
|
db9c79c0
“wangming”
feat: punch-based...
|
609
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
610
611
612
613
614
615
616
617
618
619
620
621
622
|
- BASE_USER.F_Mdid保持为当前门店(实时)
- 归属表记录历史归属
- 两者可以不一致(BASE_USER是当前状态,归属表是历史记录)
---
## 👤 用户体验设计
### 1. 管理界面
#### 1.1 员工门店归属管理页面
**功能:**
|
db9c79c0
“wangming”
feat: punch-based...
|
623
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
624
625
626
627
628
629
630
631
632
633
|
- 列表展示:员工、门店、月份、时间范围
- 查询:按员工、门店、月份查询
- 添加:手动添加归属记录
- 编辑:修改归属记录(需要校验)
- 删除:软删除归属记录
- 批量导入:Excel导入
#### 1.2 员工调店操作
**操作流程:**
|
db9c79c0
“wangming”
feat: punch-based...
|
634
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
635
636
637
638
|
1. 选择员工
2. 选择目标门店
3. 选择调店日期(默认今天)
4. 系统自动:
|
db9c79c0
“wangming”
feat: punch-based...
|
639
640
641
|
- 查询当前有效归属记录
- 更新旧记录结束日期
- 创建新记录开始日期
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
642
643
644
645
646
647
648
|
5. 确认保存
### 2. 数据展示
#### 2.1 员工详情页
**显示:**
|
db9c79c0
“wangming”
feat: punch-based...
|
649
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
650
651
652
653
654
655
|
- 当前门店:BASE_USER.F_Mdid(实时)
- 历史归属:从归属表查询,列表展示
#### 2.2 工资计算页面
**显示:**
|
db9c79c0
“wangming”
feat: punch-based...
|
656
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
657
658
659
660
661
662
663
664
665
666
|
- 如果使用了快照表的门店,显示来源标识
- 如果使用了回退逻辑,显示警告提示
---
## 🔮 扩展性考虑
### 1. 未来可能的业务变化
#### 1.1 多门店归属
|
db9c79c0
“wangming”
feat: punch-based...
|
667
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
668
669
670
|
**场景:** 一个员工可能同时归属多个门店(兼职)
**扩展方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
671
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
672
673
674
675
|
- 当前设计已经支持(一个员工一个月可以有多条记录)
- 需要调整业务逻辑:工资计算时,如何分配业绩到不同门店
#### 1.2 部门归属
|
db9c79c0
“wangming”
feat: punch-based...
|
676
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
677
678
679
|
**场景:** 不仅记录门店归属,还要记录部门归属
**扩展方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
680
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
681
682
683
684
|
- 可以增加字段:F_DepartmentId(部门ID)
- 或者创建新的表:员工部门归属表
#### 1.3 岗位变更
|
db9c79c0
“wangming”
feat: punch-based...
|
685
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
686
687
688
|
**场景:** 记录员工岗位变更历史
**扩展方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
689
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
690
691
692
693
694
695
|
- 类似设计:员工岗位归属表
- 或者:在归属表中增加字段:F_Position(岗位)
### 2. 性能扩展
#### 2.1 数据归档
|
db9c79c0
“wangming”
feat: punch-based...
|
696
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
697
698
699
700
|
- 历史数据(如3年前的数据)可以归档到历史表
- 当前表只保留最近2-3年的数据
#### 2.2 读写分离
|
db9c79c0
“wangming”
feat: punch-based...
|
701
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
702
703
704
705
706
707
708
709
710
711
712
713
|
- 如果查询压力大,可以考虑读写分离
- 归属表主要是查询操作,可以放到只读库
---
## ⚠️ 风险点与应对措施
### 1. 数据准确性风险
**风险:** 历史数据迁移可能不准确
**应对:**
|
db9c79c0
“wangming”
feat: punch-based...
|
714
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
715
716
717
718
719
720
721
722
723
|
- 多数据源验证(工资表、业务数据、人工确认)
- 分批次迁移,每批验证
- 提供数据修正工具
### 2. 性能风险
**风险:** 查询性能可能下降
**应对:**
|
db9c79c0
“wangming”
feat: punch-based...
|
724
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
725
726
727
728
729
730
731
732
733
|
- 合理设计索引
- 使用缓存
- 监控查询性能,及时优化
### 3. 数据一致性风险
**风险:** 归属表与BASE_USER表可能不一致
**应对:**
|
db9c79c0
“wangming”
feat: punch-based...
|
734
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
735
736
737
738
739
740
741
742
743
|
- 明确两者的作用:BASE_USER是当前状态,归属表是历史记录
- 提供数据同步工具(可选)
- 在关键操作时校验一致性
### 4. 业务复杂度风险
**风险:** 月中调店等复杂场景增加业务复杂度
**应对:**
|
db9c79c0
“wangming”
feat: punch-based...
|
744
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
745
746
747
748
749
750
751
752
753
|
- 提供清晰的业务规则文档
- 在管理界面提供操作指引
- 提供数据校验和提示
---
## 📋 实施 Checklist
### 阶段一:数据准备
|
db9c79c0
“wangming”
feat: punch-based...
|
754
755
756
757
758
|
- 创建归属表结构
- 设计索引
- 编写数据迁移脚本
- 准备测试数据
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
759
760
|
### 阶段二:功能开发
|
db9c79c0
“wangming”
feat: punch-based...
|
761
762
763
764
765
766
|
- 开发归属记录管理功能(CRUD)
- 开发员工调店功能(自动创建归属记录)
- 调整工资计算逻辑(集成快照表)
- 调整数据补录逻辑(集成快照表)
- 开发数据校验逻辑
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
767
768
|
### 阶段三:数据迁移
|
db9c79c0
“wangming”
feat: punch-based...
|
769
770
771
772
773
774
|
- 备份现有数据
- 执行数据迁移(小批量测试)
- 验证数据准确性
- 全量数据迁移
- 数据验证和修正
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
775
776
|
### 阶段四:测试验证
|
db9c79c0
“wangming”
feat: punch-based...
|
777
778
779
780
781
|
- 单元测试
- 集成测试
- 性能测试
- 用户验收测试
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
782
783
|
### 阶段五:上线部署
|
db9c79c0
“wangming”
feat: punch-based...
|
784
785
786
787
788
789
|
- 部署到测试环境
- 测试环境验证
- 部署到生产环境
- 监控运行情况
- 数据修正和支持
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
790
791
792
793
794
795
796
797
798
799
800
801
|
---
## 🔍 参考现有系统设计
### 1. 现有归属表分析
系统中已经存在类似的归属表设计,可以参考其设计思路:
#### 1.1 大项目部老师归属表(lq_md_major_project_teacher_assignment)
**设计特点:**
|
db9c79c0
“wangming”
feat: punch-based...
|
802
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
803
804
805
806
807
|
- 只记录月份级别(Year + Month),不记录日期范围
- 唯一约束:门店 + 年份 + 月份 + 老师ID
- 一个老师一个月只能有一条记录(不支持月中调店)
**适用场景:**
|
db9c79c0
“wangming”
feat: punch-based...
|
808
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
809
810
811
812
813
814
|
- 大项目部老师通常是整月归属一个门店
- 如果需要月中调店,需要拆分月份处理
#### 1.2 总经理门店归属表(lq_md_general_manager_lifeline)
**设计特点:**
|
db9c79c0
“wangming”
feat: punch-based...
|
815
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
816
817
818
819
820
821
|
- 记录月份级别(Month,YYYYMM格式)
- 唯一约束:门店 + 月份 + 总经理ID
- 一个总经理一个月在一个门店只能有一条记录
- 但一个总经理可以管理多个门店(通过多条记录实现)
**适用场景:**
|
db9c79c0
“wangming”
feat: punch-based...
|
822
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
823
824
825
826
827
828
829
830
|
- 总经理/经理通常管理多个门店
- 整月归属,不涉及月中调店
### 2. 设计对比与选择
#### 方案A:按月设计(参考现有归属表)
**特点:**
|
db9c79c0
“wangming”
feat: punch-based...
|
831
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
832
833
834
835
836
|
- 只记录月份级别(Year + Month)
- 一个员工一个月只能有一条记录
- 不支持月中调店(如果需要调店,需要拆分月份)
**优点:**
|
db9c79c0
“wangming”
feat: punch-based...
|
837
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
838
839
840
841
842
|
- 设计简单,与现有归属表一致
- 查询逻辑简单
- 性能好(数据量少)
**缺点:**
|
db9c79c0
“wangming”
feat: punch-based...
|
843
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
844
845
846
847
848
849
|
- 不支持月中调店场景
- 如果员工在月中调店,需要手动拆分月份
#### 方案B:按日期范围设计(推荐方案)
**特点:**
|
db9c79c0
“wangming”
feat: punch-based...
|
850
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
851
852
853
854
855
|
- 记录日期范围(StartDate + EndDate)
- 一个员工一个月可以有多条记录
- 支持月中调店
**优点:**
|
db9c79c0
“wangming”
feat: punch-based...
|
856
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
857
858
859
860
861
|
- 支持月中调店等复杂场景
- 时间维度精确
- 更灵活
**缺点:**
|
db9c79c0
“wangming”
feat: punch-based...
|
862
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
863
864
865
866
867
868
869
870
871
872
873
874
875
876
|
- 设计相对复杂
- 查询逻辑需要处理日期范围
- 数据量可能更多
### 3. 推荐方案
**建议采用方案B(按日期范围设计)**,理由:
1. **业务需求**:健康师等岗位确实存在月中调店的场景
2. **数据准确性**:日期范围设计更精确,能准确反映实际归属情况
3. **扩展性**:未来如果有更细粒度的需求,日期范围设计更容易扩展
4. **兼容性**:可以与现有按月设计的归属表共存,互不影响
**但是要注意:**
|
db9c79c0
“wangming”
feat: punch-based...
|
877
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
878
879
880
881
882
883
884
885
886
887
888
889
890
891
|
- 如果某些岗位(如大项目部老师、总经理)不需要支持月中调店,可以继续使用现有的按月设计
- 门店归属快照表主要用于健康师、店助、店长等需要精确时间范围的岗位
---
## 💭 额外思考点
### 1. 数据一致性策略
#### 1.1 BASE_USER.F_Mdid 与归属表的关系
**问题:** BASE_USER.F_Mdid 应该如何处理?
**方案A:保持独立**
|
db9c79c0
“wangming”
feat: punch-based...
|
892
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
893
894
895
896
897
|
- BASE_USER.F_Mdid 保持为当前门店(实时状态)
- 归属表记录历史归属
- 两者可以不一致(BASE_USER是当前状态,归属表是历史记录)
**方案B:同步更新**
|
db9c79c0
“wangming”
feat: punch-based...
|
898
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
899
900
901
902
|
- 员工调店时,同时更新 BASE_USER.F_Mdid 和归属表
- 保持一致
**方案C:BASE_USER.F_Mdid 从归属表计算**
|
db9c79c0
“wangming”
feat: punch-based...
|
903
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
904
905
906
907
908
|
- BASE_USER.F_Mdid 不再手动维护
- 从归属表计算当前月份的主要门店
- 查询时实时计算或定期同步
**推荐:方案A**
|
db9c79c0
“wangming”
feat: punch-based...
|
909
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
910
911
912
913
914
915
916
917
|
- 理由:BASE_USER.F_Mdid 是实时状态,归属表是历史记录,用途不同
- BASE_USER.F_Mdid 用于当前业务查询(如:查询当前门店的员工)
- 归属表用于历史数据查询(如:计算历史月份工资)
- 两者可以不一致,但需要明确各自的作用
#### 1.2 数据同步策略
**如果采用方案A,需要考虑:**
|
db9c79c0
“wangming”
feat: punch-based...
|
918
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
919
920
921
922
923
924
925
926
|
- 员工调店时,BASE_USER.F_Mdid 和归属表的更新是否需要在同一个事务中?
- 建议:在同一个事务中更新,保证一致性
### 2. 数据维护的便利性
#### 2.1 自动维护 vs 手动维护
**自动维护:**
|
db9c79c0
“wangming”
feat: punch-based...
|
927
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
928
929
930
931
932
|
- 员工调店时,自动创建归属记录
- 优点:减少人工操作,数据准确
- 缺点:如果调店操作本身有问题,归属记录也会有问题
**手动维护:**
|
db9c79c0
“wangming”
feat: punch-based...
|
933
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
934
935
936
937
938
|
- 需要手动创建/编辑归属记录
- 优点:可控性强
- 缺点:增加操作复杂度,可能遗漏
**推荐:混合模式**
|
db9c79c0
“wangming”
feat: punch-based...
|
939
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
940
941
942
943
944
945
946
947
948
|
- 员工调店时,自动创建归属记录(默认)
- 提供手动维护界面,可以修改/补录
- 支持批量导入历史数据
#### 2.2 数据修正机制
**场景:发现历史归属数据有误**
**处理方案:**
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
949
|
|
db9c79c0
“wangming”
feat: punch-based...
|
950
951
952
953
|
1. **不允许修改历史数据**(严格模式)
- 如果需要修正,只能删除(软删除)+ 新增
- 优点:保持数据可追溯性
- 缺点:操作复杂
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
954
|
2. **允许修改历史数据**(灵活模式)
|
db9c79c0
“wangming”
feat: punch-based...
|
955
956
957
|
- 可以直接修改历史记录
- 优点:操作简单
- 缺点:无法追溯修改历史
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
958
959
|
**推荐:方案1(严格模式)**
|
db9c79c0
“wangming”
feat: punch-based...
|
960
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
961
962
963
964
965
966
967
968
969
970
971
|
- 历史数据一旦创建,不允许直接修改
- 需要修正时,软删除旧记录,创建新记录
- 记录修正原因和操作人
### 3. 查询性能优化
#### 3.1 批量查询优化
**场景:计算某月所有员工的工资**
**优化策略:**
|
db9c79c0
“wangming”
feat: punch-based...
|
972
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
|
```csharp
// 一次性查询所有员工的归属记录
var employeeIds = allEmployees.Select(e => e.Id).ToList();
var assignments = await _db.Queryable<EmployeeStoreAssignmentEntity>()
.Where(x => employeeIds.Contains(x.EmployeeId) && x.StatisticsMonth == monthStr)
.ToListAsync();
// 转换为字典,便于快速查找
var assignmentDict = assignments
.GroupBy(x => x.EmployeeId)
.ToDictionary(g => g.Key, g => g.OrderBy(x => x.StartDate).ToList());
```
#### 3.2 缓存策略
**缓存哪些数据?**
|
db9c79c0
“wangming”
feat: punch-based...
|
989
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
990
991
992
993
|
- 当前月的归属记录(变化较少)
- 员工当前门店(BASE_USER.F_Mdid)
**缓存失效策略:**
|
db9c79c0
“wangming”
feat: punch-based...
|
994
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
995
996
997
998
999
1000
|
- 归属记录更新时,清除相关缓存
- 定时刷新(如每天凌晨)
#### 3.3 数据分区考虑
**是否需要分区?**
|
db9c79c0
“wangming”
feat: punch-based...
|
1001
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
|
- 如果数据量很大(如10年历史,每月几千员工),可以考虑按年份分区
- 当前阶段可能不需要,但设计时要考虑扩展性
### 4. 异常情况处理
#### 4.1 数据冲突检测
**场景:同一个员工同一天有多条归属记录**
**检测时机:**
|
db9c79c0
“wangming”
feat: punch-based...
|
1012
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1013
1014
1015
1016
|
- 插入/更新归属记录时
- 数据迁移时
**处理方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
1017
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1018
1019
1020
1021
|
- 应用层校验:插入前检查是否有日期重叠
- 如果发现冲突,拒绝操作,提示用户处理
**校验SQL示例:**
|
db9c79c0
“wangming”
feat: punch-based...
|
1022
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
|
```sql
-- 检查是否存在日期重叠
SELECT COUNT(*)
FROM lq_employee_store_assignment
WHERE F_EmployeeId = @EmployeeId
AND F_IsEffective = 1
AND (
(F_StartDate <= @EndDate AND F_EndDate >= @StartDate)
OR (F_StartDate IS NULL AND F_EndDate IS NULL)
)
```
#### 4.2 数据缺失处理
**场景:快照表中没有某员工某月的归属记录**
**处理方案:**
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1040
|
|
db9c79c0
“wangming”
feat: punch-based...
|
1041
1042
1043
1044
|
1. **回退到现有逻辑**(推荐)
- 从业务数据或BASE_USER获取
- 记录警告日志
- 可选:自动创建一条归属记录(使用当前门店或推断的门店)
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1045
|
2. **报错处理**
|
db9c79c0
“wangming”
feat: punch-based...
|
1046
1047
|
- 如果快照表没有记录,直接报错
- 要求用户先维护归属记录
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1048
1049
|
**推荐:方案1**
|
db9c79c0
“wangming”
feat: punch-based...
|
1050
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1051
1052
1053
1054
1055
1056
1057
1058
|
- 保持系统的容错性
- 记录警告,便于后续补录数据
### 5. 与现有系统的兼容性
#### 5.1 渐进式迁移
**迁移策略:**
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1059
|
|
db9c79c0
“wangming”
feat: punch-based...
|
1060
1061
1062
1063
|
1. **第一阶段:新建表,不影响现有逻辑**
- 创建归属表
- 数据迁移
- 但工资计算仍使用现有逻辑
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1064
|
2. **第二阶段:双写模式**
|
db9c79c0
“wangming”
feat: punch-based...
|
1065
1066
|
- 工资计算时,同时使用新逻辑和旧逻辑
- 对比结果,验证新逻辑的正确性
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1067
|
3. **第三阶段:切换到新逻辑**
|
db9c79c0
“wangming”
feat: punch-based...
|
1068
1069
|
- 新逻辑验证无误后,切换到新逻辑
- 保留旧逻辑作为回退方案
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1070
|
4. **第四阶段:移除旧逻辑**
|
db9c79c0
“wangming”
feat: punch-based...
|
1071
|
- 新逻辑稳定运行一段时间后,移除旧逻辑
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1072
1073
1074
1075
|
#### 5.2 数据迁移的准确性
**迁移数据来源优先级:**
|
db9c79c0
“wangming”
feat: punch-based...
|
1076
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1077
1078
1079
1080
1081
|
1. 工资统计表(最可靠)
2. 业务数据统计(补充)
3. BASE_USER当前门店(兜底)
**迁移后的验证:**
|
db9c79c0
“wangming”
feat: punch-based...
|
1082
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
|
- 抽样检查:随机抽取一定比例的数据,人工验证
- 逻辑检查:检查是否有明显异常
- 对比验证:对比迁移前后的工资计算结果
### 6. 未来扩展性
#### 6.1 多门店归属
**场景:一个员工同时归属多个门店(兼职)**
**扩展方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
1094
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1095
1096
1097
1098
1099
1100
1101
1102
1103
|
- 当前设计已经支持(一个员工一个月可以有多条记录)
- 需要调整工资计算逻辑:如何分配业绩到不同门店
- 可能需要增加字段:业绩分配比例
#### 6.2 部门归属
**场景:不仅记录门店归属,还要记录部门归属**
**扩展方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
1104
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1105
1106
1107
1108
1109
1110
1111
1112
1113
|
- 可以增加字段:F_DepartmentId(部门ID)
- 或者创建新表:员工部门归属表
- 或者:在归属表中增加字段:F_DepartmentId(如果需要同时记录门店和部门)
#### 6.3 岗位变更历史
**场景:记录员工岗位变更历史**
**扩展方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
1114
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1115
1116
1117
1118
1119
1120
1121
1122
|
- 可以创建新表:员工岗位归属表(类似设计)
- 或者:在归属表中增加字段:F_Position(岗位)
#### 6.4 其他业务属性
**场景:需要记录其他业务属性(如:职级、薪资等级等)**
**扩展方案:**
|
db9c79c0
“wangming”
feat: punch-based...
|
1123
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1124
1125
1126
1127
|
- 可以在归属表中增加字段
- 或者创建关联表
**设计原则:**
|
db9c79c0
“wangming”
feat: punch-based...
|
1128
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
|
- 优先考虑在归属表中增加字段(如果属性是时间相关的)
- 如果属性与门店归属无关,考虑创建新表
---
## 📝 总结
门店归属快照表方案是一个相对完善的解决方案,能够:
1. **解决核心问题**:以时间维度为唯一标准,准确记录历史门店归属
2. **支持复杂场景**:月中调店、跨月调店等
3. **保证数据完整性**:通过多数据源验证和数据校验
4. **考虑性能**:合理设计索引和查询优化
5. **易于扩展**:设计时考虑了未来的业务变化
**关键成功因素:**
|
db9c79c0
“wangming”
feat: punch-based...
|
1145
|
|
41c75b24
“wangming”
refactor: 移除所有薪酬服...
|
1146
1147
1148
1149
1150
1151
1152
1153
1154
|
- 数据迁移的准确性
- 业务规则的清晰性
- 用户操作的简便性
- 系统性能的稳定性
---
**文档版本:** v1.0
**创建日期:** 2026-01-09
|
db9c79c0
“wangming”
feat: punch-based...
|
1155
|
**文档性质:** 详细设计方案(仅思考,不修改代码)
|