Blame view

项目文档相关/docs/用户主档与薪酬考勤改造记录.md 54.3 KB
db9c79c0   “wangming”   feat: punch-based...
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
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
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
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
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
  # 用户主档与薪酬考勤改造记录
  
  > **文档说明**:本文档由原《用户主档与薪酬考勤改造记录》与《员工门店归属变更问题分析与解决方案》**合并**而成,统一描述用户主档(`BASE_USER`)、**门店主档(`lq_mdxx`)与门店归属、薪酬、考勤相关的问题分析、方案、改造项**,以及**主档变更对计算与历史查看的影响**。  
  > **更新原则**:每确认一项需求或方案结论,在「改造项清单」追加或更新状态。  
  > **文档版本**:v2.4(R-003 门店主档版本化)  
  > **创建/合并日期**:2026-01-09(门店归属分析) / 2026-04-03(R-001/R-002) / 2026-04-03(R-003 门店版本)
  
  ---
  
  ## 目录
  
  1. [关联文档](#关联文档)
  2. [改造项清单](#改造项清单)
  3. [R-001 用户档案字段级变更履历(审计)](#r-001-用户档案字段级变更履历审计)
  4. [R-001 详细设计(完整)](#r-001-详细设计完整)
  5. [用户信息修改后对数据计算的影响(闭环梳理)](#用户信息修改后对数据计算的影响闭环梳理)
  6. [员工门店归属变更:问题描述与现状](#员工门店归属变更问题描述与现状)
  7. [员工门店归属:解决方案与对比](#员工门店归属解决方案与对比)
  8. [员工门店归属:技术实现要点与注意事项](#员工门店归属技术实现要点与注意事项)
  9. [员工门店归属:总结](#员工门店归属总结)
  10. [R-003 门店主档版本化(完整设计)](#r-003-门店主档版本化完整设计)
  11. [附录:正式环境设计审视与评审结论](#附录正式环境设计审视与评审结论)
  12. [数据库变更与 SQL 脚本](#数据库变更与-sql-脚本)
  
  ---
  
  ## 关联文档
  
  
  | 主题                      | 文档                   |
  | ----------------------- | -------------------- |
  | 门店归属快照表字段、索引、业务规则(详细设计) | `员工门店归属快照表方案详细设计.md` |
  | 数据库表与字段说明(维护时同步)        | `数据库说明.md`           |
  
  
  ---
  
  ## 改造项清单
  
  
  | 序号    | 状态          | 提出/更新日期    | 主题                  | 摘要                                                                                         |
  | ----- | ----------- | ---------- | ------------------- | ------------------------------------------------------------------------------------------ |
  | R-001 | 详细设计已完成·待开发 | 2026-04-03 | **用户档案字段级变更履历(审计)** | 见下文 [R-001 详细设计(完整)](#r-001-详细设计完整);表 `lq_user_profile_audit`,与 `UsersService` 等写入点同事务落库。  |
  | R-002 | 待实施         | 2026-01-09 | **员工门店归属快照表(时间维度)** | 见本文第六节起;详细表结构见 `员工门店归属快照表方案详细设计.md`。                                                       |
  | R-003 | 详细设计已完成·待开发 | 2026-04-03 | **门店主档时点版本**        | 见 [R-003 门店主档版本化(完整设计)](#r-003-门店主档版本化完整设计);表 `lq_store_profile_version`;解决考勤回看与算薪取历史门店属性。 |
  
  
  ---
  
  ## R-001 用户档案字段级变更履历(审计)
  
  ### 业务目标
  
  - 管理员在用户详情/编辑相关界面可查看该用户的**历史修改记录**
  - 每条记录至少包含:**操作时间****操作人**(用户 ID 或可解析姓名)、**字段标识/中文名****变更前值****变更后值**(敏感字段如密码仅记「已修改」或脱敏策略另定)。
  
  ### 现状(结论)
  
  - `UsersService` 更新用户时仅维护 `LastModifyTime`、`LastModifyUserId`**无**字段级历史表或审计中间件。
  - `BASE_SYSLOG` 等系统日志**不保证**按字段拆解旧值/新值,且与用户详情未做产品级串联。
  - 通用表 `lq_business_operation_log` 当前**未**接入用户编辑流程。
  
  ### 概要方案
  
  采用 **独立表 `lq_user_profile_audit`(方案 A,推荐)**:一行(或多行)记录一次业务动作中的**单个字段**差异;`F_BatchId` 聚合同一次保存。完整表结构、枚举、接口、触发点见下一节 **「R-001 详细设计(完整)」**。可选 **方案 B** 复用 `lq_business_operation_log`,见附录与 R-001 SQL 文件注释。
  
  ### 闭环验收
  
  - 修改门店/组织/岗位/考勤组后,历史中可见对应字段旧→新。
  - 多次连续修改可追溯时间线。
  - 与算薪/考勤排查可对照(不要求自动反算工资,仅要求**可查因**)。
  
  ---
  
  ## R-001 详细设计(完整)
  
  ### 1. 目标与范围
  
  
  | 项       | 说明                                                                                       |
  | ------- | ---------------------------------------------------------------------------------------- |
  | **目标**  | 对 `BASE_USER` 的变更保留**可查询**的字段级履历:时间、操作者、字段、旧值、新值(敏感信息按规则处理)。                             |
  | **范围**  | 所有经后端**持久化到 `BASE_USER`** 的写入路径(含管理员改人、本人改资料、岗位/角色批量维护、密码重置等);**不含**仅内存或缓存变更。            |
  | **非目标** | 不自动反算工资;不替代 `BASE_SYSLOG` 请求日志;不记录用户关系表 `BASE_USER_RELATION` 明细(若需可二期加 `F_Remark` 或扩展表)。 |
  
  
  ### 2. 方案选型
  
  
  | 方案        | 做法                                                                                     | 优点                 | 缺点                    |
  | --------- | -------------------------------------------------------------------------------------- | ------------------ | --------------------- |
  | **A(推荐)** | 新建 `lq_user_profile_audit`                                                             | 列表查询简单、索引清晰、字段语义固定 | 多一张表                  |
  | **B**     | `lq_business_operation_log`,`F_BizType=base_user`,`F_BizId=用户Id`,diff 放 `F_DetailJson` | 无新表                | 列表需解析 JSON;与考勤等业务日志混表 |
  
  
  **生产建议**:采用 **方案 A**;方案 B 仅作资源极度受限时的退路。
  
  ### 3. 数据模型(与 SQL 一致)
  
  **脚本**`项目文档相关/sql/2026-4-3/R-001_用户主档变更审计表_lq_user_profile_audit.sql`
  
  
  | 字段                                        | 类型             | 说明                                                     |
  | ----------------------------------------- | -------------- | ------------------------------------------------------ |
  | `F_Id`                                    | varchar(50) PK | `YitIdHelper`                                          |
  | `F_TargetUserId`                          | varchar(50)    | 被变更用户                                                  |
  | `F_OperatorUserId` / `F_OperatorUserName` | varchar        | 操作人;本人改资料时二者均为本人                                       |
  | `F_OperateTime`                           | datetime       | 默认 `CURRENT_TIMESTAMP`,应用可显式传入与事务一致                    |
  | `F_Action`                                | varchar(64)    | 见 §4,区分业务场景                                            |
  | `F_BatchId`                               | varchar(50)    | 单次请求内 `Guid`;无多字段可空                                    |
  | `F_FieldKey`                              | varchar(64)    | 与 `UserEntity` 属性名一致(PascalCase),如 `Mdid`、`OrganizeId` |
  | `F_FieldLabel`                            | varchar(64)    | 中文名,便于 UI                                              |
  | `F_OldValue` / `F_NewValue`               | text           | 统一 **字符串化**(日期 `yyyy-MM-dd`、数字直接 `ToString`);敏感见 §6    |
  | `F_Source`                                | varchar(32)    | `API` / `RELATION_SERVICE` / `OAUTH` / `PORTAL` 等      |
  | `F_ClientIp` / `F_UserAgent`              | varchar        | 可选,从 `HttpContext` 取                                   |
  | `F_Remark`                                | varchar(500)   | 可选                                                     |
  
  
  **索引**`(F_TargetUserId, F_OperateTime)` 主查询;`F_BatchId`;`(F_OperatorUserId, F_OperateTime)`;`(F_Action, F_OperateTime)` 运维分析。
  
  ### 4. `F_Action` 枚举(建议代码内用常量类)
  
  
  | 值                                                       | 含义                         | 典型入口                                 |
  | ------------------------------------------------------- | -------------------------- | ------------------------------------ |
  | `PROFILE_CREATE`                                        | 新建用户                       | `UsersService.CreateSimple`          |
  | `PROFILE_UPDATE`                                        | 管理员全量资料更新                  | `UsersService.Update`                |
  | `PROFILE_DELETE_SOFT`                                   | 软删除                        | `UsersService.Delete`                |
  | `ENABLE_TOGGLE`                                         | 启用/停用切换                    | `UsersService.UpdateState`           |
  | `ADMIN_RESET_PASSWORD`                                  | 管理员重置密码                    | `UsersService.ResetPassword`         |
  | `CURRENT_MODIFY_PASSWORD`                               | 本人改密                       | `UsersCurrentService.ModifyPassword` |
  | `CURRENT_BASE_INFO`                                     | 本人改资料                      | `UsersCurrentService.UpdateBaseInfo` |
  | `CURRENT_THEME` / `CURRENT_LANGUAGE` / `CURRENT_AVATAR` | 主题/语言/头像                   | `UsersCurrentService` 对应 Put         |
  | `RELATION_BATCH_POSITION`                               | 岗位关系批量增删导致 `PositionId` 变化 | `UserRelationService`                |
  | `RELATION_BATCH_ROLE`                                   | 角色关系批量增删导致 `RoleId` 变化     | `UserRelationService`                |
  | `OAUTH_UPDATE`                                          | 第三方登录回写用户                  | `OAuthService`(若写 `BASE_USER`)       |
  | `PORTAL_UPDATE`                                         | 门户改用户                      | `PortalService`(若写 `BASE_USER`)      |
  
  
  密码类动作:除 `FieldKey=Password` 一行外,`F_OldValue`/`F_NewValue` 均 **不写明文**,填占位如 `[REDACTED]` 或空 + 仅靠 `F_Action` 表达。
  
  ### 5. 字段比对与写入策略
  
  1. **时机**:在 `Updateable`/`Insertable` **执行前**读取库中**旧实体**(更新/删除);新增无旧实体则只写 `F_NewValue`(`F_OldValue` 用 `null` 或 `(空)`)。
  2. **参与比对的列**:仅业务会**真正更新**的列(与 `UsersService.Update` 的 `UpdateColumns` 列表对齐,避免把未提交字段算成变更)。
  3. **相等判定**`null` 与 `""` 在展示层可按业务约定视为相同(可选归一化后再比);日期比较建议统一到日或 UTC。
  4. **输出**:每个变化字段插入 **一行** `lq_user_profile_audit`,共享同一 `F_BatchId`
  5. **无字段变化**:可不插审计行;若需审计「空保存」,可插一行 `F_FieldKey=__META__`,`F_Remark=NoFieldChange`(可选)。
  6. **软删除**:至少记 `DeleteMark`(若实体上有)或记 `F_Action=PROFILE_DELETE_SOFT` + `F_FieldKey=__META__` + `F_Remark`
  
  ### 6. 敏感字段与脱敏
  
  
  | 类型   | `F_FieldKey`         | 存储规则                           |
  | ---- | -------------------- | ------------------------------ |
  | 密码   | `Password`           | 不存哈希;`F_NewValue`=`[已重置]` 或空   |
  | 手机号  | `MobilePhone`        | 建议 `138****8000`(保留前3后4)       |
  | 证件号  | `CertificatesNumber` | 仅后4位或 `[已变更]`                  |
  | 邮箱   | `Email`              | 可脱敏 `a***@domain.com` 或全量(按合规) |
  | 银行卡等 | —                    | 若未来有字段,同证件策略                   |
  
  
  查询接口应对 **无额外权限** 的用户再次脱敏(双保险)。
  
  ### 7. `F_FieldKey` / 中文标签与审计级别(建议配置表或字典)
  
  **高优先级(算薪/考勤/权限强相关,必记)**
  
  
  | F_FieldKey        | 建议 F_FieldLabel | DB 列                  |
  | ----------------- | --------------- | --------------------- |
  | Mdid              | 门店              | `F_MDID`              |
  | OrganizeId        | 组织              | `F_ORGANIZEID`        |
  | Gw                | 岗位              | `F_GW`                |
  | PositionId        | 职位ID            | `F_POSITIONID`        |
  | RoleId            | 角色              | `F_ROLEID`            |
  | AttendanceGroupId | 考勤分组            | `F_AttendanceGroupId` |
  | EntryDate         | 入职日期            | `F_ENTRYDATE`         |
  | IsOnJob           | 是否在职            | `F_IsOnJob`           |
  | EnabledMark       | 启用标记            | `F_ENABLEDMARK`       |
  | Account           | 账号              | `F_ACCOUNT`           |
  | RealName          | 姓名              | `F_REALNAME`          |
  
  
  **中优先级(人事资料,建议记)**`Gender`、`Birthday`、`MobilePhone`、`Email`、`CertificatesType`、`CertificatesNumber`、`Nation`、`NativePlace`、`Education`、`UrgentContacts`、`UrgentTelePhone`、`PostalAddress`、`TelePhone`、`Landline`、`ManagerId`、`SortCode`、`Description`、`HeadIcon`、`Zw`、`Fyft`、`Gwfl`、`OpenId` 等。
  
  **低优先级 / 默认忽略(降噪)**`QuickQuery`(随姓名自动生成可不计或合并到 `RealName`)、`LastModifyTime`、`LastModifyUserId`、`Password`、`Secretkey`、各类 **登录统计字段**(`FirstLogTime`、`LastLogTime`、`LogSuccessCount` 等)、`ChangePasswordDate`(若已单独记密码动作可忽略)、`PropertyJson`/`Theme`/`Language`/`CommonMenu` 等按产品要求:**主题语言头像若需审计已包含在 `CURRENT_*` 动作中**
  
  > 实现建议:维护 `HashSet<string> IgnoredPropertyNames` + `Dictionary<string,string> FieldLabels`,与 `UserEntity` 反射名一致。
  
  ### 8. 写入触发点(须全部接入同一审计服务)
  
  
  | #   | 类/方法                                                   | 行为                       | F_Action                             |
  | --- | ------------------------------------------------------ | ------------------------ | ------------------------------------ |
  | 1   | `UsersService.CreateSimple`                            | Insert                   | `PROFILE_CREATE`                     |
  | 2   | `UsersService.Update`                                  | UpdateColumns 多字段        | `PROFILE_UPDATE`                     |
  | 3   | `UsersService.Delete`                                  | 软删                       | `PROFILE_DELETE_SOFT`                |
  | 4   | `UsersService.UpdateState`                             | `EnabledMark` 切换         | `ENABLE_TOGGLE`                      |
  | 5   | `UsersService.ResetPassword`                           | 密码                       | `ADMIN_RESET_PASSWORD`               |
  | 6   | `UsersCurrentService.ModifyPassword`                   | 密码                       | `CURRENT_MODIFY_PASSWORD`            |
  | 7   | `UsersCurrentService.UpdateBaseInfo`                   | 个人资料                     | `CURRENT_BASE_INFO`                  |
  | 8   | `UsersCurrentService` 主题/语言/头像                         | 对应更新                     | `CURRENT_*`                          |
  | 9   | `UserRelationService` 批量增删岗位/角色                        | 更新 `PositionId`/`RoleId` | `RELATION_BATCH_*`                   |
  | 10  | `OAuthService`                                         | 若更新用户表                   | `OAUTH_UPDATE`                       |
  | 11  | `PortalService`                                        | 若更新用户表                   | `PORTAL_UPDATE`                      |
  | 12  | `LqReimbursementWorkflowConfigService` 等 **Insert 用户** | 若存在                      | `PROFILE_CREATE` + `F_Source=SYSTEM` |
  
  
  **要求**:新增任何 `Updateable<UserEntity>` 必须 Code Review 是否接审计;禁止脚本直连改 `BASE_USER` 而不留痕。
  
  ### 9. 事务与一致性
  
  - 审计插入与 **用户表同一数据库事务**:审计失败则 **整单回滚**(推荐)。  
  - 批量 `UserRelationService` 多用户更新:每个用户独立事务块内写审计,或整批一个大事务(与现网一致)。
  
  ### 10. 后端接口设计(建议挂在权限模块)
  
  
  | 项        | 约定                                                                                                                                                                                               |
  | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
  | **路由**   | `GET api/permission/Users/{targetUserId}/profile-audit-logs`(挂在 `Users`/`UsersService`,与现有用户模块同权限体系)                                                                                             |
  | **查询参数** | `current`(页码)、`pageSize`、`fieldKey`(可选)、`action`(可选)、`startTime`、`endTime`(可选)                                                                                                                   |
  | **权限**   | 与 **编辑该用户** 相同数据范围:`user.dataScope` 含目标用户 `organizeId` 的 `Edit`,或管理员;可增设 `user:audit:view`                                                                                                       |
  | **响应行**  | `id`、`operateTime`、`operatorUserId`、`operatorUserName`、`action`、`fieldKey`、`fieldLabel`、`oldValue`、`newValue`、`batchId`、`source`、`clientIp`(可选);可选 `oldDisplay`/`newDisplay`(门店/组织/职位等解析名,服务端组装) |
  
  
  **传参约定**:若管理端 axios 对 GET 统一走 **data** 而服务端仅绑 Query,需二选一:**(1)** 前端将该接口改为 query string;**(2)** 增加 `POST .../profile-audit-logs/query`,Body 承载分页与筛选(与 `PageInputBase` 一致)。实现前与现网 `permission/user` API 风格对齐。
  
  ### 11. 前端设计(管理端 `antis-ncc-admin`)
  
  
  | 项       | 说明                                   |
  | ------- | ------------------------------------ |
  | **入口**  | 用户管理—编辑用户弹窗/详情增加 **Tab「变更记录」** 或独立抽屉 |
  | **表格列** | 操作时间、操作人、动作、字段中文名、变更前、变更后            |
  | **筛选**  | 时间范围、字段、动作(二期)                       |
  | **空态**  | 「暂无变更记录」                             |
  | **性能**  | 分页 `pageSize` 默认 20,最大 100           |
  
  
  ### 12. 非功能
  
  
  | 项      | 建议                                          |
  | ------ | ------------------------------------------- |
  | **性能** | 单次保存字段 diff 通常 <30 行插入,同步写入;若压测瓶颈再考虑 Outbox |
  | **容量** | 按员工数 × 年均变更 × 行数估算;超期归档冷表(策略同附录评审)          |
  | **安全** | 接口防纵向越权(禁止通过遍历 id 猜用户);审计接口同样鉴权             |
  
  
  ### 13. 测试用例(最小集)
  
  
  | 场景             | 预期                                      |
  | -------------- | --------------------------------------- |
  | 改门店+岗位一次保存     | 2 行审计,同 `F_BatchId`                     |
  | 管理员重置密码        | `ADMIN_RESET_PASSWORD` + `Password` 无明文 |
  | 本人改手机          | 脱敏后旧/新                                  |
  | 无权限用户访问他人审计接口  | 403 / 业务错误码                             |
  | 岗位批量调整         | 多用户多条 `RELATION_BATCH_POSITION`         |
  | 事务回滚(模拟审计插入失败) | 用户表未更新                                  |
  
  
  ---
  
  ## 用户信息修改后对数据计算的影响(闭环梳理)
  
  以下基于当前后端实现口径归纳:**用户主档变更后,哪些模块会按「当前库里的用户表」参与计算或统计**。历史月份**重算工资**时,若仍读 `BASE_USER` 而非当月快照,会出现与「当时实际情况」不一致的风险(与第五节门店归属问题同源)。
  
  ### 1. 按主档字段分项说明
  
  
  | 修改字段                               | 主要影响(计算/统计/规则)                                                                                                                                                                                                                                         | 说明与风险                                                               |
  | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
  | **门店 `F_Mdid`**                    | **考勤**:打卡/补卡/列表默认门店、月汇总与人关联的门店展示(如 `LqAttendanceRecordService` 取用户门店)。**工资**:店长、主任、店助、科技老师等按用户 `Mdid` 做门店维度聚合;**健康师工资**在业绩/消耗明细无门店时**回退到 `User.Mdid`**。**看板**:如事业部驾驶舱按「店长/健康师」且 `Mdid` 落在门店集合内筛选人员。                                                    | 调店后重算历史月:分组与兜底门店可能变成**新门店**;与第五节「时间维度 vs 当前归属」矛盾。                   |
  | **组织 `F_OrganizeId`**              | **权限**`@organizeId`、组织及子组织数据范围(`AuthorizeService`)。**工资**:科技部总经理、大项目部主管按 **组织 ID 是否在科技部/大项目部组织列表** 且岗位匹配进池;与 `lq_md_target` 当月科技部/大项目部字段联动门店范围。**大项目部老师工资**:用 `OrganizeId` 映射部门名,统计行上 `StoreId` 曾用部门维度。**科技部相关报表入口**:如总监驾驶舱按用户组织判断是否科技部并按 target 筛门店。 | 调部门后:能否进某类工资池、科技部/大项维度统计范围会变;**不等于门店**,勿与 `Mdid` 混用。                |
  | **岗位 `F_Gw`(及展示用 `F_PositionId`)** | **工资入池**`Gw` 字符串决定是否进入对应薪酬服务(如健康师、店长、主任、店助/店助主任、科技老师、科技部总经理/总经理、大项目部主管等)。**健康师**等还会用 `PositionId` → 职位表填展示岗位名。                                                                                                                                        | `Gw` 与 `PositionId` 不一致时,可能出现「入了算薪池但展示名不同」或反之;改岗后**重算**会改变是否计入某类工资。 |
  | **考勤分组 `F_AttendanceGroupId`**     | **考勤规则**:迟到早退、补卡、批量生成/重算等,记录上可带分组快照,缺省回**用户当前分组**`LqAttendanceRecordService`)。**设置**:分组下绑定人数统计。                                                                                                                                                       | 改分组主要影响**之后**生成/重算的记录;已落库且带快照的记录行为以记录为准。                            |
  | **在职 `F_IsOnJob`(及启用等)**           | **工资**:多处服务过滤 `DeleteMark`、`EnabledMark`;部分统计用在职标识(如事业总工资里 `IsTerminated` 来自用户在职状态)。**列表与选人**:一般仅在职/未删用户。                                                                                                                                              | 标离职后可能不再进入算薪名单;若需「离职当月仍计薪」需单独业务规则(本文不展开)。                           |
  | **入职时间 `F_EntryDate`**             | **假期/工龄等业务**(用户编辑已强调必填):与请假额度、工龄类规则相关;若其它模块按入职日算系数需单独核对。                                                                                                                                                                                               | 改入职日会改变**按入职推算**的统计;与门店/岗位无关但影响合规类计算。                               |
  | **角色 `F_RoleId`**                  | **权限与菜单**,不直接参与薪酬公式;间接影响「谁能操作算薪/导入」而非个人工资数额。                                                                                                                                                                                                           | 一般不计入个人业绩/工资算法。                                                     |
  
  
  ### 2. 按业务模块汇总(便于测试回归)
  
  
  | 模块                                               | 与用户主档的关系                                                                              |
  | ------------------------------------------------ | ------------------------------------------------------------------------------------- |
  | 健康师工资 `LqSalaryService`                          | `Gw` 过滤入池;门店优先明细,否则 `Mdid`。                                                           |
  | 健康师额外计算 `LqSalaryExtraCalculationService`        | 新店等场景下 `Mdid` + `Gw`(健康师)。                                                            |
  | 店长 / 主任 / 店助工资                                   | `Gw` 入池;按 `Mdid` 关联门店与业绩。                                                             |
  | 科技老师工资 `LqTechTeacherSalaryService`              | `Gw`;按 `Mdid` 等。                                                                      |
  | 科技部总经理工资 `LqTechGeneralManagerSalaryService`     | `Gw` + `OrganizeId`;`lq_md_target`。                                                   |
  | 大项目部主管工资 `LqMajorProjectDirectorSalaryService`   | `Gw`(主管)+ `OrganizeId`;`lq_md_target`。                                                |
  | 大项目部老师工资 `LqMajorProjectTeacherSalaryService`    | 名单来自 `lq_md_major_project_teacher_assignment`;用户表补姓名、`OrganizeId`、在职;**不依赖 `Gw` 入池**。 |
  | 事业部总经理/经理工资 `LqBusinessUnitManagerSalaryService` | 名单来自 `lq_md_general_manager_lifeline`;用户表主要补姓名、账号、在职。                                 |
  | 考勤记录/汇总                                          | `Mdid`、`AttendanceGroupId`;汇总表按 `UserId` 关联。                                          |
  | 事业部驾驶舱等                                          | `Gw` + `Mdid` 组合筛选「店长/健康师」等。                                                          |
  
  
  ### 3. 闭环建议(与改造项对应)
  
  1. **门店**:实施 **R-002 归属快照** 后,算薪与补录优先按**发生月/日**查归属,减少对当前 `F_Mdid` 的依赖。
  2. **审计**:实施 **R-001** 后,主档变更可追溯,便于解释「为何重算结果变化」。
  3. **组织/岗位**:科技部、大项主管等若需历史一致,需明确是否引入**按月任职/组织快照**或在工资结果表中固化当时维度(与快照表同类思路)。
  
  ---
  
  ## 员工门店归属变更:问题描述与现状
  
  ### 业务场景
  
  员工每个月的归属门店可能会发生变化。当发生改变后,在以下场景中会遇到门店归属问题:
  
  1. **工资计算场景**:员工 A 在 1 月在门店 A 工作,2 月调到门店 B(`BASE_USER.F_Mdid` 更新为门店 B),计算 1 月工资时如何确定该员工 1 月的归属门店?
  2. **数据补录场景**:员工 A 在 1 月在门店 A 产生开单/消耗数据,2 月调到门店 B;若 1 月数据在 2 月补录,应归属哪个门店?
  
  ### 核心矛盾
  
  - **时间维度**:数据应按实际发生时间归属门店。
  - **当前归属**`BASE_USER.F_Mdid` 只记录当前门店,无法追溯历史。
  - **数据完整性**:补录历史数据需要正确的门店归属。
  
  ### 当前数据存储情况
  
  1. **员工门店归属**`BASE_USER.F_Mdid`,仅当前门店 ID。
  2. **开单/消耗数据**`lq_kd_jksyj` / `lq_xh_jksyj` 有 `F_StoreId`;开单主表 `lq_kd_kdjlb` 有 `djmd`
  3. **工资计算逻辑**(以健康师工资为例):优先业绩 `StoreId` → 其次消耗 `StoreId` → 再回退 `BASE_USER.F_Mdid`
  
  ### 存在的问题
  
  1. 业务数据无门店时回退到**当前** `F_Mdid`,历史月可能错档。
  2. 补录历史数据时门店易被错归。
  3. 历史月工资、门店统计、新店判断等可能偏差。
  
  ---
  
  ## 员工门店归属:解决方案与对比
  
  ### 方案一:时间维度优先 + 门店归属快照表(推荐)
  
  **核心思路**:以时间为标准,用门店归属快照表记录员工每月(可精确到日)的归属。
  
  **实现要点**
  
  1. 创建门店归属表(表名建议 `lq_employee_store_assignment`;字段与索引见 `员工门店归属快照表方案详细设计.md`)。
  2. 调店时自动维护记录;支持月中调店(同一月多条记录);支持历史导入。
  3. **工资计算**:按统计月(及日期范围)查快照 → 无记录再回退现有逻辑。
  4. **数据补录**:按单据/业绩发生日期查当月归属,写入 `StoreId`
  
  **优点**:时间清晰、可追溯、支持月中调店、对现有表侵入小。  
  **缺点**:需维护额外表与历史迁移。
  
  ### 方案二:业务数据中强制记录门店
  
  工资只信业务数据门店;无门店则报错或跳过;不再用 `F_Mdid` 回退。
  
  **缺点**:历史补录、缺失门店时难以闭环。
  
  ### 方案三:在 BASE_USER 上存历史(JSON 或附属表)
  
  **缺点**:改核心用户表或 JSON 查询性能;附属表与方案一类似。
  
  ### 方案四:仅依赖工资统计表已有 StoreId
  
  **缺点**:首次计算与补录仍缺「当时归属」依据。
  
  ### 方案对比表
  
  
  | 方案          | 实施难度 | 数据完整性 | 可追溯性 | 扩展性 | 推荐度   |
  | ----------- | ---- | ----- | ---- | --- | ----- |
  | 方案一:快照表     | 中等   | 高     | 高    | 高   | ★★★★★ |
  | 方案二:业务强制    | 低    | 中     | 中高   | 中   | ★★★   |
  | 方案三:USER 扩展 | 高    | 中高    | 中高   | 中高  | ★★★   |
  | 方案四:工资表     | 低    | 低     | 中    | 低   | ★★    |
  
  
  ### 推荐与实施阶段(摘要)
  
  **推荐方案一**。建议阶段:数据准备(建表、历史整理)→ 功能开发(维护入口、算薪与补录改读快照)→ 迁移与校验 → 性能与监控。
  
  ---
  
  ## 员工门店归属:技术实现要点与注意事项
  
  ### 1. 建表示例(**勿直接用于生产**)
  
  > **正式 DDL 以脚本为准**:`项目文档相关/sql/2026-4-3/R-002_员工门店归属快照表_lq_employee_store_assignment.sql`(已与详细设计字段、索引对齐)。下文块仅为阅读摘要;迁移脚本**不可**在 MySQL 中调用 `YitIdHelper`,ID 须在应用层生成。
  
  ```sql
  CREATE TABLE lq_employee_store_assignment (
      F_Id VARCHAR(50) PRIMARY KEY,
      F_EmployeeId VARCHAR(50) NOT NULL COMMENT '员工ID',
      F_StoreId VARCHAR(50) NOT NULL COMMENT '门店ID',
      F_StoreName VARCHAR(200) COMMENT '门店名称',
      F_Year INT NOT NULL COMMENT '年份',
      F_Month INT NOT NULL COMMENT '月份',
      F_StatisticsMonth VARCHAR(6) NOT NULL COMMENT '统计月份YYYYMM',
      F_StartDate DATE COMMENT '归属开始日期',
      F_EndDate DATE COMMENT '归属结束日期',
      F_CreateTime DATETIME,
      F_UpdateTime DATETIME,
      F_CreateUser VARCHAR(50),
      INDEX idx_employee_month (F_EmployeeId, F_StatisticsMonth),
      INDEX idx_store_month (F_StoreId, F_StatisticsMonth)
  ) COMMENT '员工门店归属表';
  ```
  
  ### 2. 工资计算逻辑调整要点(伪代码)
  
  ```csharp
  // 伪代码示例
  public async Task CalculateSalary(int year, int month)
  {
      var monthStr = $"{year}{month:D2}";
  
      var storeAssignments = await _db.Queryable<EmployeeStoreAssignmentEntity>()
          .Where(x => x.StatisticsMonth == monthStr)
          .ToListAsync();
  
      if (!storeAssignments.Any())
      {
          // 回退:现有逻辑(业务数据或 BASE_USER)
      }
  
      foreach (var assignment in storeAssignments)
      {
          // 按归属查询业绩并计算
      }
  }
  ```
  
  ### 3. 数据补录逻辑调整要点(伪代码)
  
  ```csharp
  public async Task ImportBillingData(DateTime billingDate, string employeeId)
  {
      var monthStr = $"{billingDate.Year}{billingDate.Month:D2}";
  
      var assignment = await _db.Queryable<EmployeeStoreAssignmentEntity>()
          .Where(x => x.EmployeeId == employeeId && x.StatisticsMonth == monthStr)
          .Where(x => billingDate >= x.StartDate && (x.EndDate == null || billingDate <= x.EndDate))
          .FirstAsync();
  
      billingRecord.StoreId = assignment?.StoreId ?? GetStoreIdByCurrentLogic();
  }
  ```
  
  ### 4. 注意事项
  
  - 快照数据必须准确,定期校验。  
  - 索引与批量查询,避免 N+1。  
  - 无快照时必须有回退策略。  
  - 月中调店:一月多条记录,算薪按日期拆分。  
  - 历史迁移需人工抽检。
  
  ---
  
  ## 员工门店归属:总结
  
  核心矛盾是**时间维度**`**BASE_USER` 仅保存当前门店**之间的矛盾。推荐 **方案一(门店归属快照表)**,与 **R-002** 对应;详细字段与业务规则以 `员工门店归属快照表方案详细设计.md` 为准。
  
  实施重点:数据准确性、性能、回退方案、历史迁移。
  
  ---
  
  ## R-003 门店主档版本化(完整设计)
  
  ### 1. 问题与目标
  
  
  | 问题        | 说明                                                                                                                                                           |
  | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
  | **考勤回看**  | `lq_attendance_record` 仅存 `F_StoreName` 等少量快照;**围栏 / Wi-Fi / 经纬度 / 营业时间**等打卡规则来自**当前** `lq_mdxx`。门店一改,历史日期的「当时按何规则判定正常/外勤」无法严格还原。                            |
  | **算薪与费用** | 店长/主任等工资服务从 `lq_mdxx` 取 `**F_StoreType`、`F_StoreCategory`** 参与提成与分类逻辑;`**zxzt`(最新状态)**影响日报等统计是否纳入「开店」门店;**`dhhm`(热线)**在部分业务检索中使用。若只读当前主档,**重算历史月**会与当时口径不一致。 |
  
  
  **目标**:门店主档每次**影响考勤判定或算薪口径**的变更,落一条**不可变版本行**;历史查询与重算按**业务日期/时点**解析到对应版本。
  
  ### 2. 与 R-002 的边界
  
  
  | 维度    | R-002 员工门店归属 | R-003 门店主档版本    |
  | ----- | ------------ | --------------- |
  | 回答的问题 | 员工**在哪一家店**  | 该店**当时属性是什么**   |
  | 主键维度  | 员工 + 时间段     | 门店 + 版本号 + 有效时间 |
  
  
  二者配合:算薪可先定「人→店」(R-002),再定「店→类型/类别/状态」(R-003)。
  
  ### 3. 数据模型
  
  **表名**`lq_store_profile_version`(脚本:`项目文档相关/sql/2026-4-3/R-003_门店主档版本快照表_lq_store_profile_version.sql`
  
  
  | 字段                                        | 说明                                                                                                                                                |
  | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `F_StoreId`                               | `lq_mdxx.F_Id`                                                                                                                                    |
  | `F_VersionNo`                             | 单店内从 1 递增                                                                                                                                         |
  | `F_ValidFrom`                             | 本版本生效时刻(建议用**保存成功**的服务器时间)                                                                                                                        |
  | `F_ValidTo`                               | 下一版本写入前,将上一行 `F_ValidTo` 置为**新版本**的 `F_ValidFrom`(左闭右开区间)                                                                                         |
  | `F_OperatorUserId` / `F_OperatorUserName` | 谁改的                                                                                                                                               |
  | `F_ChangeTrigger`                         | `STORE_UPDATE` / `TARGETS_UPDATE` / `IMPORT` 等                                                                                                    |
  | **算薪常用列(冗余,便于 SQL)**                      | `F_Dm`、`F_StoreType`、`F_StoreCategory`、`F_Zxzt`、`F_Dhhm`、`F_Status`                                                                               |
  | `F_AttendanceSnapshotJson`                | 与 `LqAttendanceRecordService.BuildStoreFenceSnapshot` **同结构**并含扩展:围栏、WiFi 成对、经纬度、地址、`AttendanceCheckFence`/`Wifi`、`BusinessHours`、`TrafficTips` 等 |
  | `F_FullExtensionJson`                     | 其余字段可选打包(图片、标签、目标等),按需填充                                                                                                                          |
  
  
  **查询某日 `d` 当天有效版本**
  
  ```text
  WHERE F_StoreId = @sid
    AND F_ValidFrom <= @dEndOfDay
    AND (F_ValidTo IS NULL OR F_ValidTo > @dStartOfDay)
  ```
  
  (若一天内多次变更,应用层应保证**同一自然日**仅一条有效或约定「按最后一次」;推荐 **ValidFrom 精确到秒**,同日多条时取 `MAX(F_ValidFrom)` 仍落在当日的行。)
  
  ### 4. 纳入版本化的字段集合(首期)
  
  **必须进入 `F_AttendanceSnapshotJson`(或等价列)**`longitude`、`latitude`、`fence_polygons`、`F_AttendanceCheckFence`、`F_AttendanceCheckWifi`、`F_AttendanceWifiPairs`、`F_AttendanceWifiVerifyPair`、`dm`、`dz`、`F_BusinessHours`、`F_TrafficTips`
  
  **必须进入冗余列(算薪/统计)**`F_StoreType`、`F_StoreCategory`、`zxzt → F_Zxzt`、`dhhm → F_Dhhm`、`status → F_Status`、`dm → F_Dm`
  
  **可选进 `F_FullExtensionJson`**:事业部/教育部/科技部/大项目部归属、目标字段、门店介绍等——**仅当**后续算法依赖时再纳入,避免版本行过大。
  
  ### 5. 写入触发点(与 `lq_mdxx` 写库对齐)
  
  
  | 入口      | 方法(约)                                            | `F_ChangeTrigger`    |
  | ------- | ------------------------------------------------ | -------------------- |
  | 门店资料保存  | `LqMdxxService.Update`                           | `STORE_UPDATE`       |
  | 门店目标等   | `LqMdxxService.UpdateTargets`、同文件其它 `Updateable` | `TARGETS_UPDATE` 或细分 |
  | 批量导入/脚本 | 凡写 `lq_mdxx` 的通道                                 | `IMPORT` / `SYSTEM`  |
  
  
  **策略**`Update` **成功提交前**在**同一事务**内:(1) 读当前门店实体旧值;(2) 与即将写入的字段做 diff,若**版本集合内任一字段变化**则:关闭上一版本 `F_ValidTo = now`;`F_VersionNo = max+1`;插入新行并填充快照 JSON;(3) 再执行 `lq_mdxx` 的 `Update`。若**仅非版本字段**变更(如纯展示图),可不产生新版本(需在实现里列白名单)。
  
  ### 6. 考勤侧改造要点
  
  
  | 项                      | 说明                                                                                                                                                                                           |
  | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | **记录落库**               | 在打卡/生成记录/重算考勤写入 `RuleSnapshotJson` 时,将 `**F_AttendanceSnapshotJson`** 嵌入 `F_AllRuleSnapshotJson` 的节点 `storeFenceSnapshot`(或新增列 `F_StorePunchSnapshotJson` 二选一);推荐**先扩 JSON** 减少 `ALTER` 表次数。 |
  | **详情接口**               | 考勤记录详情展示「当时规则」时:**优先**用记录内快照;**legacy 无快照**则按 `AttendanceDate` + `StoreId` **回查** `lq_store_profile_version`。                                                                                |
  | **CurrentPunchConfig** | 仍读当前 `lq_mdxx`;无需版本表。                                                                                                                                                                        |
  
  
  ### 7. 算薪侧改造要点
  
  
  | 项             | 说明                                                                                                                                                                                                          |
  | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | **取门店属性**     | 各 `*SalaryService` 在 `Queryable<LqMdxxEntity>` 取 `StoreType/StoreCategory` 处,改为:对统计月取 **时点** `T`(如月末最后一秒或业务规定的「计薪锁定日」),`LEFT JOIN` 或子查询 `lq_store_profile_version` 解析该日有效版本;**无版本行**则回退当前 `lq_mdxx`(兼容冷启动)。 |
  | **已落库工资表**    | 历史结果行若已写 `StoreType/StoreCategory`,以结果表为准做展示;**重算**时才用 R-003 纠正口径。                                                                                                                                          |
  | `**zxzt` 过滤** | 如 `LqDailyReportService` 等 SQL 中 `store.zxzt = '开店'`:历史报表若需严格一致,应改为「事实日 + 版本表 `F_Zxzt`」或接受「按当前主档」的产品口径并在文档声明。                                                                                               |
  
  
  ### 8. 冷启动与回填
  
  1. 建表后对每个门店插入 **V1**`F_ValidFrom = 门店创建时间或系统上线日`,`F_ValidTo = NULL`,内容从**当前** `lq_mdxx` 拷贝。
  2. 之后每次变更按 §5 追加版本。
  3. **不保证**回填前历史与真实完全一致,需在发布说明中声明;争议月可人工插版本行修正。
  
  ### 9. 可选增强
  
  - `**lq_mdxx.F_ProfileVersionNo`**:指向当前最新 `F_VersionNo`,便于与版本表对账(脚本内注释)。  
  - **字段级审计**:门店维度可复用 `lq_business_operation_log` 或另建 `lq_store_field_audit`(与 R-001 同思路);**R-003 解决时点属性**,审计解决「谁改了哪一列」。
  
  ### 10. 验收要点
  
  - 修改围栏/Wi-Fi 后,旧考勤记录在详情中仍显示**修改前**规则(或能从版本表解析一致)。  
  - 修改门店类别/类型后,重算**过去某月**工资时使用该月时点版本(与产品约定 T 取日)。  
  - 修改 `zxzt`/`dhhm` 后,依赖字段的报表或算薪可按日期追溯(若产品要求)。
  
  ---
  
  ## 数据库变更与 SQL 脚本
  
  > **执行注意**:主键一律在应用层用 `YitIdHelper.NextId().ToString()` 生成;不要在 MySQL 存储过程/脚本里调用 C#。生产执行前在测试库验证;`utf8mb4` / 存储引擎与现网一致。
  
  ### 变更总览
  
  
  | 改造项            | 数据库对象                          | 变更类型                        | 脚本文件                                                                   |
  | -------------- | ------------------------------ | --------------------------- | ---------------------------------------------------------------------- |
  | R-001(方案 A,推荐) | `lq_user_profile_audit`        | **新建表**                     | `项目文档相关/sql/2026-4-3/R-001_用户主档变更审计表_lq_user_profile_audit.sql`        |
  | R-001(方案 B,可选) | `lq_business_operation_log`    | 仅**可选追加索引**                 | 见 R-001 脚本内注释                                                          |
  | R-002          | `lq_employee_store_assignment` | **新建表**                     | `项目文档相关/sql/2026-4-3/R-002_员工门店归属快照表_lq_employee_store_assignment.sql` |
  | R-003          | `lq_store_profile_version`     | **新建表**                     | `项目文档相关/sql/2026-4-3/R-003_门店主档版本快照表_lq_store_profile_version.sql`     |
  | `lq_mdxx`      | 可选                             | **可选** `F_ProfileVersionNo` | 见 R-003 脚本注释                                                           |
  | `BASE_USER`    | —                              | **首期不要求 ALTER**             | 与 R-002 双写 `F_Mdid` 等,不强制加列                                            |
  
  
  ### R-001 表结构说明(`lq_user_profile_audit`,与 R-001 详细设计一致)
  
  
  | 字段                                                          | 说明                                               |
  | ----------------------------------------------------------- | ------------------------------------------------ |
  | `F_Action`                                                  | 操作类型枚举,如 `PROFILE_UPDATE`、`ADMIN_RESET_PASSWORD` |
  | `F_TargetUserId`                                            | 被改用户                                             |
  | `F_OperatorUserId` / `F_OperatorUserName` / `F_OperateTime` | 操作者与时间                                           |
  | `F_BatchId`                                                 | 同一次保存多字段共用                                       |
  | `F_FieldKey` / `F_FieldLabel`                               | 属性名与中文名                                          |
  | `F_OldValue` / `F_NewValue`                                 | 脱敏后前后值                                           |
  | `F_Source`                                                  | `API` / `RELATION_SERVICE` 等                     |
  | `F_ClientIp` / `F_UserAgent`                                | 可选                                               |
  | `F_Remark`                                                  | 可选                                               |
  
  
  **索引**`(F_TargetUserId, F_OperateTime)`、`F_BatchId`、`(F_OperatorUserId, F_OperateTime)`、`(F_Action, F_OperateTime)`
  
  ### R-002 表结构说明
  
  `员工门店归属快照表方案详细设计.md` 及 `R-002_...sql` 一致(含 `F_IsEffective`、`F_Remark`、`F_UpdateUser` 等)。
  
  ### R-003 表结构说明(`lq_store_profile_version`)
  
  [R-003 门店主档版本化(完整设计)](#r-003-门店主档版本化完整设计):版本区间、`F_AttendanceSnapshotJson`、算薪冗余列 `F_StoreType` / `F_StoreCategory` / `F_Zxzt` / `F_Dhhm` 等。
  
  ### 建议执行顺序
  
  1. R-002 建表 → 维护与双写(可先不改算薪读路径)。
  2. R-003 建表 → 门店保存接版本写入 → 考勤快照嵌入 / 算薪 JOIN 版本(可分阶段开关)。
  3. R-001 建表 → 各用户写入点接审计。
  4. 方案 B 时按需 `ALTER` 业务操作日志索引。
  
  ### 对账类 SQL(参考)
  
  见 R-002 脚本末尾注释(有效段重叠检测)。
  
  ---
  
  **文档性质**:问题分析、改造记录与影响梳理;具体代码变更以评审与迭代为准。
  
  ---
  
  ## 附录:正式环境设计审视与评审结论
  
  > 本节由任务协调视角对 v2.0 正文做**生产已上线**前提下的需求/设计细化,便于技术评审、排期与发版;实施时仍以代码与详细设计为准。
  
  ### A. 文档问题清单(已处理项 + 持续关注)
  
  
  | 类型           | 说明                                                                                                         |
  | ------------ | ---------------------------------------------------------------------------------------------------------- |
  | **DDL 一致性**  | 主文档第七节建表仅为摘要;**生产建表必须以** `员工门店归属快照表方案详细设计.md` **全字段与索引为准**(含 `F_UpdateUser`、`F_IsEffective`、`F_Remark` 等)。 |
  | **语义边界**     | 「历史不可改」指**不直接改历史行语义**;允许对**当前开放归属段****EndDate 截断 + 新段插入**(切段)。是否与**已锁定工资月**冲突见下文开放问题。                    |
  | **伪代码**      | 工资示例中按 `StatisticsMonth` 拉全表assignment 易误导性能;实现时应 **按参与算薪的员工 ID 集合批量查询**,与详细设计一致。                          |
  | **迁移脚本**     | 不得在 MySQL 中调用 C# `YitIdHelper`;ID 在应用层生成或按项目 DB 规范执行。                                                      |
  | **现网基线**     | 当前生产算薪/补录仍以 `BASE_USER.F_Mdid` 与业务明细门店为主;R-002 上线前需在发布说明中写明 **能力差距与回退开关**。                                 |
  | **多租户**      | 若 R-001 复用或对齐 `lq_business_operation_log`,须遵守租户隔离(实体上 `[Tenant]`),查询必带租户条件。                                |
  | **Markdown** | 影响矩阵表内错误嵌套已在 v2.1 修正。                                                                                      |
  
  
  ### B. R-001 用户档案审计(生产级要点)
  
  1. **触发范围**:凡最终 `Updateable<UserEntity>` / 写 `BASE_USER` 的路径均需纳入(含批量);须做**静态清单 + Code Review**;禁止旁路 SQL 改用户表或统一走带审计门面。
  2. **字段策略**:白名单至少含主文档所列薪酬/考勤敏感字段 + `RealName`/`Account`(排查串改);密码等**永不记原值**;手机等脱敏;可忽略 `LastModifyTime` 等纯元数据噪声。
  3. **事务**:默认 **审计与主档更新同事务**,失败则回滚;若主档优先,须书面接受审计缺失并配补偿/告警。
  4. **权限**:查询与用户编辑数据范围一致;敏感列可拆子权限;导出需额外约束。
  5. **性能与留存**:高负载再评估异步(Outbox,禁止裸 fire-and-forget);索引 `(UserId, OperateTime)` 或 `(BizType, BizId, OperateTime)`;保留期与归档策略由人事/法务定。
  
  ### C. R-002 门店归属快照(生产级要点)
  
  1. **双写真值**:建议 `**F_Mdid` = 当前归属**;**历史月算薪/补录以快照段为准**。列表/选人是否展示「当前」仍以 `F_Mdid` 为主,除非产品另定。
  2. **调店切段(事务内)**:截断当前有效开放段 `EndDate = 调店日-1`(**当日归属旧店或新店须业务拍板**)→ 插入新段 `StartDate = 调店日` → 更新 `F_Mdid`
  3. **回退优先级(建议定稿)**:P1 快照覆盖业务日 → P2 当月有快照但日无覆盖须**可观测**(告警/人工,勿静默乱兜底)→ P3 多月多段时 **主门店合并规则** 全服务统一 → P4 无快照则现有逻辑(明细门店 → `F_Mdid`)→ P5 仍无则业务定报错或待补队列。
  4. **历史回填**:优先从**工资结果**反推;注意一月多店时单行工资会丢中段;再用业务众数等补洞;当前 `F_Mdid` 仅冷启动兜底;全量后跑对账报表。
  5. **发布阶段**:Phase0 建表+维护+只写快照不算薪 → Phase1 调店双写+对账 → Phase2 算薪/补录双读与开关 → Phase3 全量新逻辑+fallback 监控 → Phase4 收敛。建议**按模块**灰度(如先补录、再某一类工资)。
  6. **对账思路**:段重叠、与在职区间空洞、`F_Mdid` vs 最新开放段、锁定月跳过重算 diff 等。
  
  ### D. R-001 与 R-002 发布顺序
  
  - **无强依赖**,可并行开发。  
  - **建议**:M1 先 R-002 表 + 回填 + 维护 + 双写(**不改变算薪结果**)→ M2 读快照 + 双读校验 + 开关 → M3 R-001(或合规优先时 R-001 提前)。切段操作宜与审计联动(归属变更可追溯)。
  
  ### E. 测试与验收清单(勾选用)
  
  **用户主档与权限**:改各敏感字段后展示与数据范围正确;(R-001)有审计且敏感不脱敏明文。  
  
  **R-002**:月初/月中/月末调店无重叠、无空洞(在约定定义下);软删/更正流程正确;批量导入失败策略。  
  
  **工资**:健康师(有/无业绩店、有/无快照、月中两店);店长/主任/店助调店月;科技部/大项与 `lq_md_target` 组合;**锁定月策略**符合拍板结论。  
  
  **考勤**:打卡/补卡与快照日一致;已落库分组快照不受改分组影响。  
  
  **补录**:历史日期写入 `StoreId` 与快照一致;回退路径可观测。  
  
  **非功能**:大批量算薪性能、无 N+1、对账抽样。  
  
  ### F. 待业务/财务拍板(开放问题)
  
  1. **调店当日**业绩/考勤/工资算旧店还是新店(决定 `EndDate`/`StartDate` 是否含当日)。
  2. **工资月已锁定**后是否允许改快照、是否允许重算、审批链。
  3. **月中多店**时工资汇总表 **主门店** 规则(业绩最大 / 归属天数最长 / 其他财务口径)。
  4. **无快照且无业务门店**:报错阻断还是兜底 `F_Mdid` 并打待补标。
  5. **历史回填**以工资表为准还是以人事台账为准;争议数据谁签字确认。
  6. **R-001 保留年限**、是否允许导出审计。
  7. **兼职/多店同时有效**是否首期支持。
  8. **离职当月**:归属段是否截到离职日、与「离职当月仍计薪」是否冲突。