刘光辉
4 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
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
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
package jnpf.elnService.impl;
 
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jnpf.audit.model.AuditEventDTO;
import jnpf.audit.sdk.AuditClient;
import jnpf.eln.RecordFillSaveCommand;
import jnpf.eln.RecordFillSaveOperation;
import jnpf.elnEntity.field.ElnFieldVersionEntity;
import jnpf.elnEntity.field.ElnFieldForms;
import jnpf.elnEntity.formula.ElnTemplateFormulaBindingEntity;
import jnpf.elnEntity.runtime.ElnRecordEntity;
import jnpf.elnEntity.runtime.ElnRecordFieldValueEntity;
import jnpf.elnEntity.runtime.ElnRecordFormulaCalculationResult;
import jnpf.elnEntity.runtime.ElnRecordRevisionEntity;
import jnpf.elnEntity.runtime.ElnRecordRepeatRowEntity;
import jnpf.elnEntity.runtime.ElnRecordRepeatValueEntity;
import jnpf.elnEntity.runtime.ElnRecordSaveOperationEntity;
import jnpf.elnEntity.template.ElnTemplateBindingEntity;
import jnpf.elnEntity.template.ElnTemplateVos;
import jnpf.elnMapper.ElnFieldVersionMapper;
import jnpf.elnMapper.ElnRecordFieldValueMapper;
import jnpf.elnMapper.ElnRecordMapper;
import jnpf.elnMapper.ElnRecordRevisionMapper;
import jnpf.elnMapper.ElnRecordRepeatRowMapper;
import jnpf.elnMapper.ElnRecordRepeatValueMapper;
import jnpf.elnMapper.ElnRecordSaveOperationMapper;
import jnpf.elnMapper.ElnTemplateBindingMapper;
import jnpf.elnMapper.ElnTemplateFormulaBindingMapper;
import jnpf.elnService.ElnTestWorkItemSnapshotProvider;
import jnpf.elnService.ElnRecordFillAccessService;
import jnpf.elnService.ElnRecordFormulaCalculationService;
import jnpf.elnService.ElnRecordSourceSyncDocumentConfirmation;
import jnpf.elnService.template.ElnTemplateDocumentEvidence;
import jnpf.elnService.template.ElnTemplateBindingConfirmationFacade;
import jnpf.elnService.template.ElnTemplateCheckboxGroupService;
import jnpf.elnService.template.docx.DocxControlConfigMatcher;
import jnpf.elnService.template.docx.DocxExpectedControlConfig;
import jnpf.elnService.template.docx.DocxControlSnapshot;
import jnpf.database.util.TenantDataSourceUtil;
import jnpf.exception.DataException;
import jnpf.lims.api.model.TestWorkItemSnapshotBatch;
import jnpf.lims.api.model.TestWorkItemSnapshot;
import jnpf.util.JsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
 
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
 
/** 记录文件归属与 LIMS 当前负责人/状态的双重准入。 */
@Service
public class ElnRecordFillAccessServiceImpl implements ElnRecordFillAccessService {
    private final ElnRecordMapper recordMapper;
    private final ElnRecordRevisionMapper revisionMapper;
    private final ElnTestWorkItemSnapshotProvider taskProvider;
    private final ElnRecordFieldValueMapper valueMapper;
    private final ElnTemplateBindingMapper bindingMapper;
    private final ElnFieldVersionMapper fieldVersionMapper;
    private final ElnRecordSaveOperationMapper operationMapper;
    private final ElnTemplateFormulaBindingMapper formulaBindingMapper;
    private final ElnRecordFormulaCalculationService formulaCalculationService;
    private final ElnTemplateDocumentEvidence documentEvidence;
    private final ElnRecordRepeatRowMapper repeatRowMapper;
    private final ElnRecordRepeatValueMapper repeatValueMapper;
    private final TenantRoute tenantRoute;
    private final ElnTemplateBindingConfirmationFacade confirmationFacade;
    private final Consumer<AuditEventDTO> auditRecorder;
    private final ElnRecordSourceSyncDocumentConfirmation sourceSyncConfirmation;
    private final ElnTemplateCheckboxGroupService checkboxGroupService;
 
    @Autowired
    public ElnRecordFillAccessServiceImpl(ElnRecordMapper recordMapper,
                                          ElnRecordRevisionMapper revisionMapper,
                                          ElnTestWorkItemSnapshotProvider taskProvider,
                                          ElnRecordFieldValueMapper valueMapper,
                                          ElnTemplateBindingMapper bindingMapper,
                                          ElnFieldVersionMapper fieldVersionMapper,
                                          ElnRecordSaveOperationMapper operationMapper,
                                          ElnTemplateFormulaBindingMapper formulaBindingMapper,
                                          ElnRecordFormulaCalculationService formulaCalculationService,
                                          ElnTemplateDocumentEvidence documentEvidence,
                                          ElnTemplateBindingConfirmationFacade confirmationFacade,
                                          ElnRecordRepeatRowMapper repeatRowMapper,
                                          ElnRecordRepeatValueMapper repeatValueMapper,
                                          ElnTemplateCheckboxGroupService checkboxGroupService,
                                          ElnRecordSourceSyncDocumentConfirmation sourceSyncConfirmation,
                                          ObjectProvider<AuditClient> auditClientProvider) {
        this(recordMapper, revisionMapper, taskProvider, valueMapper, bindingMapper, fieldVersionMapper,
                operationMapper, formulaBindingMapper, formulaCalculationService,
                documentEvidence, confirmationFacade, repeatRowMapper, repeatValueMapper,
                event -> {
                    AuditClient client = auditClientProvider.getIfAvailable();
                    if (client != null) client.record(event);
                },
                new TenantRoute() {
                    @Override public void switchTo(String tenantId) { TenantDataSourceUtil.switchTenant(tenantId); }
                    @Override public void clear() { TenantDataSourceUtil.clearLocalTenantInfo(); }
                }, sourceSyncConfirmation, checkboxGroupService);
    }
 
    ElnRecordFillAccessServiceImpl(ElnRecordMapper recordMapper,
                                   ElnRecordRevisionMapper revisionMapper,
                                   ElnTestWorkItemSnapshotProvider taskProvider,
                                   ElnRecordFieldValueMapper valueMapper,
                                   ElnTemplateBindingMapper bindingMapper,
                                   ElnFieldVersionMapper fieldVersionMapper,
                                   ElnRecordSaveOperationMapper operationMapper,
                                   ElnTemplateFormulaBindingMapper formulaBindingMapper,
                                   ElnRecordFormulaCalculationService formulaCalculationService,
                                   ElnTemplateDocumentEvidence documentEvidence,
                                   ElnTemplateBindingConfirmationFacade confirmationFacade,
                                   TenantRoute tenantRoute) {
        this(recordMapper, revisionMapper, taskProvider, valueMapper, bindingMapper,
                fieldVersionMapper, operationMapper, formulaBindingMapper,
                formulaCalculationService, documentEvidence, confirmationFacade,
                null, null, event -> { }, tenantRoute);
    }
 
    ElnRecordFillAccessServiceImpl(ElnRecordMapper recordMapper,
                                   ElnRecordRevisionMapper revisionMapper,
                                   ElnTestWorkItemSnapshotProvider taskProvider,
                                   ElnRecordFieldValueMapper valueMapper,
                                   ElnTemplateBindingMapper bindingMapper,
                                   ElnFieldVersionMapper fieldVersionMapper,
                                   ElnRecordSaveOperationMapper operationMapper,
                                   ElnTemplateFormulaBindingMapper formulaBindingMapper,
                                   ElnRecordFormulaCalculationService formulaCalculationService,
                                   ElnTemplateDocumentEvidence documentEvidence,
                                   ElnTemplateBindingConfirmationFacade confirmationFacade,
                                   ElnRecordRepeatRowMapper repeatRowMapper,
                                   ElnRecordRepeatValueMapper repeatValueMapper,
                                   TenantRoute tenantRoute) {
        this(recordMapper, revisionMapper, taskProvider, valueMapper, bindingMapper,
                fieldVersionMapper, operationMapper, formulaBindingMapper,
                formulaCalculationService, documentEvidence, confirmationFacade,
                repeatRowMapper, repeatValueMapper, event -> { }, tenantRoute);
    }
 
    ElnRecordFillAccessServiceImpl(ElnRecordMapper recordMapper,
                                   ElnRecordRevisionMapper revisionMapper,
                                   ElnTestWorkItemSnapshotProvider taskProvider,
                                   ElnRecordFieldValueMapper valueMapper,
                                   ElnTemplateBindingMapper bindingMapper,
                                   ElnFieldVersionMapper fieldVersionMapper,
                                   ElnRecordSaveOperationMapper operationMapper,
                                   ElnTemplateFormulaBindingMapper formulaBindingMapper,
                                   ElnRecordFormulaCalculationService formulaCalculationService,
                                   ElnTemplateDocumentEvidence documentEvidence,
                                   ElnTemplateBindingConfirmationFacade confirmationFacade,
                                   ElnRecordRepeatRowMapper repeatRowMapper,
                                   ElnRecordRepeatValueMapper repeatValueMapper,
                                   Consumer<AuditEventDTO> auditRecorder,
                                   TenantRoute tenantRoute) {
        this(recordMapper, revisionMapper, taskProvider, valueMapper, bindingMapper,
                fieldVersionMapper, operationMapper, formulaBindingMapper,
                formulaCalculationService, documentEvidence, confirmationFacade,
                repeatRowMapper, repeatValueMapper, auditRecorder, tenantRoute,
                ElnRecordSourceSyncDocumentConfirmation.none());
    }
 
    ElnRecordFillAccessServiceImpl(ElnRecordMapper recordMapper,
                                   ElnRecordRevisionMapper revisionMapper,
                                   ElnTestWorkItemSnapshotProvider taskProvider,
                                   ElnRecordFieldValueMapper valueMapper,
                                   ElnTemplateBindingMapper bindingMapper,
                                   ElnFieldVersionMapper fieldVersionMapper,
                                   ElnRecordSaveOperationMapper operationMapper,
                                   ElnTemplateFormulaBindingMapper formulaBindingMapper,
                                   ElnRecordFormulaCalculationService formulaCalculationService,
                                   ElnTemplateDocumentEvidence documentEvidence,
                                   ElnTemplateBindingConfirmationFacade confirmationFacade,
                                   ElnRecordRepeatRowMapper repeatRowMapper,
                                   ElnRecordRepeatValueMapper repeatValueMapper,
                                   Consumer<AuditEventDTO> auditRecorder,
                                   TenantRoute tenantRoute,
                                   ElnRecordSourceSyncDocumentConfirmation sourceSyncConfirmation) {
        this(recordMapper, revisionMapper, taskProvider, valueMapper, bindingMapper,
                fieldVersionMapper, operationMapper, formulaBindingMapper,
                formulaCalculationService, documentEvidence, confirmationFacade,
                repeatRowMapper, repeatValueMapper, auditRecorder, tenantRoute,
                sourceSyncConfirmation, null);
    }
 
    ElnRecordFillAccessServiceImpl(ElnRecordMapper recordMapper,
                                   ElnRecordRevisionMapper revisionMapper,
                                   ElnTestWorkItemSnapshotProvider taskProvider,
                                   ElnRecordFieldValueMapper valueMapper,
                                   ElnTemplateBindingMapper bindingMapper,
                                   ElnFieldVersionMapper fieldVersionMapper,
                                   ElnRecordSaveOperationMapper operationMapper,
                                   ElnTemplateFormulaBindingMapper formulaBindingMapper,
                                   ElnRecordFormulaCalculationService formulaCalculationService,
                                   ElnTemplateDocumentEvidence documentEvidence,
                                   ElnTemplateBindingConfirmationFacade confirmationFacade,
                                   ElnRecordRepeatRowMapper repeatRowMapper,
                                   ElnRecordRepeatValueMapper repeatValueMapper,
                                   Consumer<AuditEventDTO> auditRecorder,
                                   TenantRoute tenantRoute,
                                   ElnRecordSourceSyncDocumentConfirmation sourceSyncConfirmation,
                                   ElnTemplateCheckboxGroupService checkboxGroupService) {
        this.recordMapper = recordMapper;
        this.revisionMapper = revisionMapper;
        this.taskProvider = taskProvider;
        this.valueMapper = valueMapper;
        this.bindingMapper = bindingMapper;
        this.fieldVersionMapper = fieldVersionMapper;
        this.operationMapper = operationMapper;
        this.formulaBindingMapper = formulaBindingMapper;
        this.formulaCalculationService = formulaCalculationService;
        this.documentEvidence = documentEvidence;
        this.repeatRowMapper = repeatRowMapper;
        this.repeatValueMapper = repeatValueMapper;
        this.tenantRoute = tenantRoute;
        this.confirmationFacade = confirmationFacade;
        this.auditRecorder = auditRecorder;
        this.sourceSyncConfirmation = sourceSyncConfirmation;
        this.checkboxGroupService = checkboxGroupService;
    }
 
    @Override
    public boolean canFill(String tenantId, String userId, String recordId, String fileId) {
        try {
            return inTenant(tenantId, () -> resolveCurrent(tenantId, userId, recordId, fileId) != null);
        } catch (RuntimeException ignored) {
            return false;
        }
    }
 
    @Override
    public RecordFillSaveOperation preflight(RecordFillSaveCommand command) {
        return inTenant(command == null ? null : command.getTenantId(),
                () -> confirmationFacade.execute(() -> preflightInTenant(command), ignored -> { }));
    }
 
    private RecordFillSaveOperation preflightInTenant(RecordFillSaveCommand command) {
        requireSession(command);
        Current current = resolveCurrent(command.getTenantId(), command.getUserId(),
                command.getRecordId(), command.getFileId());
        ElnRecordSaveOperationEntity entity = new ElnRecordSaveOperationEntity();
        entity.setId(IdUtil.getSnowflakeNextIdStr());
        entity.setOperationCode(IdUtil.getSnowflakeNextIdStr());
        entity.setOperationStatus(RecordFillSaveOperation.PREPARED);
        entity.setDianziJiluId(current.record.getId());
        entity.setXiudingId(current.revision.getId());
        entity.setJiluWendang(command.getFileId());
        entity.setExpectedRecordVersion(current.record.getFVersion());
        entity.setSessionDocKey(command.getSessionDocKey());
        entity.setSessionVersion(command.getSessionVersion());
        entity.setOperatorUserId(command.getUserId());
        entity.setFTenantId(command.getTenantId());
        entity.setFVersion(0);
        entity.setFCreatorUserId(command.getUserId());
        entity.setFLastModifyUserId(command.getUserId());
        entity.setFDeleteMark(0);
        entity.setFCreatorTime(new Date());
        if (operationMapper.insert(entity) != 1) {
            throw new DataException("电子记录保存操作创建冲突");
        }
        return operation(entity);
    }
 
    @Override
    public RecordFillSaveOperation commit(RecordFillSaveCommand command) {
        return inTenant(command == null ? null : command.getTenantId(), () -> commitInTenant(command));
    }
 
    private RecordFillSaveOperation commitInTenant(RecordFillSaveCommand command) {
        requireOperation(command);
        ElnRecordSaveOperationEntity operation = findOperation(command.getTenantId(), command.getOperationCode());
        if (operation == null) return rejected(command, "电子记录保存操作不存在");
        if (RecordFillSaveOperation.COMMITTED.equals(operation.getOperationStatus())
                || RecordFillSaveOperation.REJECTED.equals(operation.getOperationStatus())) return operation(operation);
        try {
            return confirmationFacade.execute(() -> commitState(command),
                    ignored -> rejectPrepared(command.getTenantId(), command.getOperationCode(), ignored.getMessage()));
        } catch (DataException e) {
            ElnRecordSaveOperationEntity rejected = findOperation(command.getTenantId(), command.getOperationCode());
            if (rejected != null && RecordFillSaveOperation.REJECTED.equals(rejected.getOperationStatus())) {
                return operation(rejected);
            }
            throw e;
        }
    }
 
    private RecordFillSaveOperation commitState(RecordFillSaveCommand command) {
        ElnRecordSaveOperationEntity operation = findOperation(command.getTenantId(), command.getOperationCode());
        if (operation == null) throw new DataException("电子记录保存操作不存在");
        if (!RecordFillSaveOperation.PREPARED.equals(operation.getOperationStatus())) return operation(operation);
        assertOperationIdentity(operation, command);
        Current current = resolveCurrent(command.getTenantId(), command.getUserId(),
                command.getRecordId(), command.getFileId());
        if (!Objects.equals(current.record.getFVersion(), operation.getExpectedRecordVersion())) {
            throw new DataException("电子记录已被其他保存更新");
        }
        PreparedValues prepared = prepareValues(current, command);
        if (recordMapper.advanceVersion(command.getTenantId(), current.record.getId(),
                operation.getExpectedRecordVersion(), command.getUserId()) != 1) {
            throw new DataException("电子记录保存回调已过期");
        }
        // From this point any failure must roll back record CAS, field values and operation status together.
        saveValues(prepared.changedValues);
        saveRepeatValues(prepared.repeat);
        prepared.changedBindingIds.addAll(prepared.repeat.changedBindingIds);
        ElnRecordFormulaCalculationResult calculated =
                formulaCalculationService.recalculateAffectedWithRepeats(
                command.getTenantId(), current.record, current.revision, prepared.values,
                prepared.changedBindingIds, operation.getOperationCode(), command.getUserId());
        saveValues(calculated.getFieldValues());
        saveCalculatedRepeatValues(calculated.getRepeatValues());
        sourceSyncConfirmation.confirmSavedDocument(command.getTenantId(), command.getUserId(),
                current.record.getId(), current.revision.getId());
        if (operationMapper.finishPrepared(command.getTenantId(), operation.getOperationCode(),
                RecordFillSaveOperation.COMMITTED, null) != 1) {
            throw new DataException("电子记录保存操作状态冲突");
        }
        operation.setOperationStatus(RecordFillSaveOperation.COMMITTED);
        operation.setRejectReason(null);
        return operation(operation);
    }
 
    @Override
    public RecordFillSaveOperation saveStatus(String tenantId, String operationCode) {
        return inTenant(tenantId, () -> saveStatusInTenant(tenantId, operationCode));
    }
 
    @Override
    public RecordFillSaveOperation abort(RecordFillSaveCommand command) {
        return inTenant(command == null ? null : command.getTenantId(), () -> {
            requireOperation(command);
            ElnRecordSaveOperationEntity operation = findOperation(command.getTenantId(), command.getOperationCode());
            if (operation == null) return rejected(command, "电子记录保存操作不存在");
            if (!RecordFillSaveOperation.PREPARED.equals(operation.getOperationStatus())) return operation(operation);
            assertOperationIdentity(operation, command);
            return confirmationFacade.execute(() -> reject(operation, "OnlyOffice 本地写回失败"), ignored -> { });
        });
    }
 
    private RecordFillSaveOperation saveStatusInTenant(String tenantId, String operationCode) {
        if (!StringUtils.hasText(tenantId) || !StringUtils.hasText(operationCode)) {
            throw new DataException("电子记录保存操作标识不能为空");
        }
        ElnRecordSaveOperationEntity operation = findOperation(tenantId, operationCode);
        return operation == null ? null : operation(operation);
    }
 
    private Current resolveCurrent(String tenantId, String userId, String recordId, String fileId) {
        if (!StringUtils.hasText(tenantId) || !StringUtils.hasText(userId)
                || !StringUtils.hasText(recordId) || !StringUtils.hasText(fileId)) {
            throw new DataException("电子记录保存身份不完整");
        }
        ElnRecordEntity record = recordMapper.selectOne(Wrappers.<ElnRecordEntity>lambdaQuery()
                .eq(ElnRecordEntity::getFTenantId, tenantId).eq(ElnRecordEntity::getId, recordId)
                .and(q -> q.isNull(ElnRecordEntity::getFDeleteMark).or().eq(ElnRecordEntity::getFDeleteMark, 0)).last("limit 1"));
        if (record == null || !StringUtils.hasText(record.getDangqianXiudingId())) {
            throw new DataException("电子记录不存在或当前修订无效");
        }
        ElnRecordRevisionEntity revision = revisionMapper.selectOne(Wrappers.<ElnRecordRevisionEntity>lambdaQuery()
                .eq(ElnRecordRevisionEntity::getFTenantId, tenantId).eq(ElnRecordRevisionEntity::getId, record.getDangqianXiudingId())
                .eq(ElnRecordRevisionEntity::getDianziJiluId, recordId).eq(ElnRecordRevisionEntity::getJiluWendang, fileId)
                .and(q -> q.isNull(ElnRecordRevisionEntity::getFDeleteMark).or().eq(ElnRecordRevisionEntity::getFDeleteMark, 0)).last("limit 1"));
        if (revision == null) throw new DataException("电子记录当前修订与文件不匹配");
        if (!"DRAFT".equals(revision.getBizStatus())) {
            throw new DataException("电子记录当前修订不可编辑");
        }
        TestWorkItemSnapshotBatch batch = taskProvider.resolveAsActor(tenantId, userId,
                Collections.singletonList(record.getJianyanXiangId()), Collections.<String>emptyList());
        if (batch == null || batch.getItems() == null || batch.getItems().size() != 1) {
            throw new DataException("LIMS 检验任务快照返回无效");
        }
        TestWorkItemSnapshot item = batch.getItems().get(0);
        if (item == null || !record.getJianyanXiangId().equals(item.getTestWorkItemId())
                || !"SUCCESS".equals(item.getOutcomeCode())) {
            throw new DataException("当前用户或检验项状态不允许保存电子记录");
        }
        return new Current(record, revision);
    }
 
    private PreparedValues prepareValues(Current current, RecordFillSaveCommand command) {
        Map<String, ElnTemplateBindingEntity> bindings = activeBindings(command.getTenantId(), current.record.getMubanId());
        Map<String, ElnTemplateVos.CheckboxGroup> checkboxGroupRules = checkboxGroupRules(
                command.getTenantId(), current.record.getMubanId());
        Set<String> formulaResults = formulaResultBindingIds(
                command.getTenantId(), current.record.getMubanId());
        Map<String, DocxControlSnapshot> controls = controls(command.getTenantId(), command.getFileId(), bindings);
        List<ElnRecordFieldValueEntity> values = valueMapper.selectList(Wrappers.<ElnRecordFieldValueEntity>lambdaQuery()
                .eq(ElnRecordFieldValueEntity::getFTenantId, command.getTenantId())
                .eq(ElnRecordFieldValueEntity::getDianziJiluId, current.record.getId())
                .eq(ElnRecordFieldValueEntity::getXiudingId, current.revision.getId())
                .and(q -> q.isNull(ElnRecordFieldValueEntity::getFDeleteMark).or().eq(ElnRecordFieldValueEntity::getFDeleteMark, 0)));
        Map<String, ElnRecordFieldValueEntity> valuesByBinding = new LinkedHashMap<>();
        for (ElnRecordFieldValueEntity value : values) {
            if (valuesByBinding.put(value.getMubanBangdingId(), value) != null) {
                throw new DataException("电子记录存在重复主字段值");
            }
        }
        List<ElnRecordFieldValueEntity> changedValues = new java.util.ArrayList<>();
        Set<String> changedBindingIds = new LinkedHashSet<>();
        for (ElnTemplateBindingEntity binding : bindings.values()) {
            if (!"PRIMARY".equals(binding.getKongjianJuese())) continue;
            ElnRecordFieldValueEntity value = valuesByBinding.remove(binding.getId());
            if (value == null || !Objects.equals(value.getZiduanBanbenId(), binding.getZiduanBanbenId())
                    || !Objects.equals(value.getZiduanBianma(), binding.getZiduanBianma())) {
                throw new DataException("DOCX 主字段绑定与记录固定版本不一致");
            }
            DocxControlSnapshot control = controls.get(binding.getKongjianBiaoqian());
            ElnFieldVersionEntity version = fixedVersion(command.getTenantId(), value.getZiduanBanbenId());
            validateControlConfig(control, version);
            if (formulaResults.contains(binding.getId())) continue;
            if (isProviderSource(version) || "CROSS_TEMPLATE".equals(value.getZhiLaiyuan())) {
                assertProviderValueUnchanged(value, control, version);
                continue;
            }
            ValueState before = ValueState.of(value);
            applyNormalizedValue(value, control, version);
            if (!before.equals(ValueState.of(value))) {
                value.setZhiLaiyuan("MANUAL");
                value.setFLastModifyUserId(command.getUserId());
                changedValues.add(value);
                changedBindingIds.add(binding.getId());
            }
        }
        if (!valuesByBinding.isEmpty()) throw new DataException("电子记录字段值与活动主绑定不一致");
        verifyReferences(command.getTenantId(), bindings, controls);
        RepeatPrepared repeat = prepareRepeatValues(current, command, bindings, controls,
                formulaResults);
        validatePrimaryCheckboxGroups(bindings, values, checkboxGroupRules);
        validateRepeatCheckboxGroups(repeat, checkboxGroupRules);
        return new PreparedValues(values, changedValues, changedBindingIds, repeat);
    }
 
    private static void validatePrimaryCheckboxGroups(
            Map<String, ElnTemplateBindingEntity> bindings,
            List<ElnRecordFieldValueEntity> values,
            Map<String, ElnTemplateVos.CheckboxGroup> rules) {
        Map<String, ElnRecordFieldValueEntity> valuesByBinding = new LinkedHashMap<>();
        for (ElnRecordFieldValueEntity value : values) {
            valuesByBinding.put(value.getMubanBangdingId(), value);
        }
        Map<String, CheckboxGroupState> states = new LinkedHashMap<>();
        for (ElnTemplateBindingEntity binding : bindings.values()) {
            if (!"PRIMARY".equals(binding.getKongjianJuese())
                    || !"CHECKBOX".equals(binding.getKongjianLeixing())
                    || !StringUtils.hasText(binding.getCheckboxGroupCode())) {
                continue;
            }
            String key = String.valueOf(binding.getStepId()) + "\n"
                    + binding.getCheckboxGroupCode();
            CheckboxGroupState state = states.computeIfAbsent(key,
                    ignored -> new CheckboxGroupState(binding.getCheckboxGroupCode()));
            ElnRecordFieldValueEntity value = valuesByBinding.get(binding.getId());
            if (value != null && "true".equals(value.getZhiWenben())) {
                state.checked.add(binding);
            }
        }
        for (CheckboxGroupState state : states.values()) {
            validateCheckboxGroupState(state, rules.get(state.groupCode));
        }
    }
 
    private Map<String, ElnTemplateVos.CheckboxGroup> checkboxGroupRules(
            String tenantId, String templateId) {
        Map<String, ElnTemplateVos.CheckboxGroup> result = new LinkedHashMap<>();
        if (checkboxGroupService == null) return result;
        List<ElnTemplateVos.CheckboxGroup> groups = checkboxGroupService.list(tenantId, templateId);
        if (groups == null) throw new DataException("电子记录选择组数量规则无效");
        for (ElnTemplateVos.CheckboxGroup group : groups) {
            if (group == null || !StringUtils.hasText(group.getGroupCode())
                    || !validCheckboxGroupRule(group)
                    || result.put(group.getGroupCode(), group) != null) {
                throw new DataException("电子记录选择组数量规则无效");
            }
        }
        return result;
    }
 
    private static boolean validCheckboxGroupRule(ElnTemplateVos.CheckboxGroup group) {
        Integer minSelected = group.getMinSelected();
        Integer maxSelected = group.getMaxSelected();
        return minSelected != null && minSelected >= 0
                && (maxSelected == null
                || (maxSelected >= 1 && minSelected <= maxSelected));
    }
 
    private static void validateCheckboxGroupState(
            CheckboxGroupState state, ElnTemplateVos.CheckboxGroup configuredRule) {
        int minSelected = configuredRule == null ? 0 : configuredRule.getMinSelected();
        Integer maxSelected = configuredRule == null
                ? Integer.valueOf(1) : configuredRule.getMaxSelected();
        int selected = state.checked.size();
        if (selected < minSelected) {
            throw new DataException("选择组“" + state.groupCode + "”至少选择 "
                    + minSelected + " 项,当前已选择 " + selected + " 项");
        }
        if (maxSelected != null && selected > maxSelected) {
            throw new DataException("选择组“" + state.groupCode + "”最多选择 "
                    + maxSelected + " 项,当前已选择 " + selected + " 项:"
                    + checkboxMembers(state.checked));
        }
    }
 
    private static void validateRepeatCheckboxGroups(RepeatPrepared prepared,
            Map<String, ElnTemplateVos.CheckboxGroup> rules) {
        for (CheckboxGroupState state : prepared.checkboxGroups.values()) {
            validateCheckboxGroupState(state, rules.get(state.groupCode));
        }
    }
 
    private static String checkboxMembers(List<ElnTemplateBindingEntity> bindings) {
        List<String> members = new java.util.ArrayList<>();
        for (ElnTemplateBindingEntity binding : bindings) {
            String code = binding.getZiduanBianma();
            String alias = binding.getKongjianBieming();
            members.add(StringUtils.hasText(alias) ? alias + "(" + code + ")" : code);
        }
        return String.join("、", members);
    }
 
    private RepeatPrepared prepareRepeatValues(Current current, RecordFillSaveCommand command,
            Map<String, ElnTemplateBindingEntity> bindings,
            Map<String, DocxControlSnapshot> controls, Set<String> formulaResults) {
        List<ElnTemplateBindingEntity> repeatBindings = new java.util.ArrayList<>();
        for (ElnTemplateBindingEntity binding : bindings.values()) {
            if ("REPEAT".equals(binding.getKongjianJuese())) repeatBindings.add(binding);
        }
        boolean hasRepeatControl = controls.keySet().stream()
                .anyMatch(tag -> tag.startsWith("eln.repeat."));
        if (repeatBindings.isEmpty()) {
            if (hasRepeatControl) throw new DataException("DOCX 存在未绑定的重复字段控件");
            return RepeatPrepared.empty();
        }
        if (repeatRowMapper == null || repeatValueMapper == null) {
            throw new DataException("重复行持久化服务不可用");
        }
        Map<String, List<ElnTemplateBindingEntity>> bindingsByGroup = new LinkedHashMap<>();
        Map<String, Map<String, ElnTemplateBindingEntity>> bindingsByGroupAndField =
                new LinkedHashMap<>();
        Map<String, DocxControlSnapshot> prototypesByGroup = new LinkedHashMap<>();
        for (ElnTemplateBindingEntity binding : repeatBindings) {
            String groupCode = binding.getRepeatGroupCode();
            if (!StringUtils.hasText(groupCode)
                    || !"prototype".equals(binding.getRepeatPrototypeRowId())
                    || !StringUtils.hasText(binding.getZiduanBianma())) {
                throw new DataException("模板重复组绑定无效");
            }
            Map<String, ElnTemplateBindingEntity> bindingsByField =
                    bindingsByGroupAndField.computeIfAbsent(groupCode,
                            ignored -> new LinkedHashMap<>());
            if (bindingsByField.put(binding.getZiduanBianma(), binding) != null) {
                throw new DataException("模板重复组绑定无效");
            }
            bindingsByGroup.computeIfAbsent(groupCode,
                    ignored -> new java.util.ArrayList<>()).add(binding);
            DocxControlSnapshot control = controls.get(binding.getKongjianBiaoqian());
            requireRepeatCell(control, binding);
            DocxControlSnapshot prototype = prototypesByGroup.putIfAbsent(groupCode, control);
            if (prototype != null && !samePhysicalRow(prototype, control)) {
                throw new DataException("重复组原型列不在同一物理行");
            }
        }
 
        List<ElnRecordRepeatRowEntity> rows = repeatRowMapper.selectList(
                Wrappers.<ElnRecordRepeatRowEntity>lambdaQuery()
                        .eq(ElnRecordRepeatRowEntity::getFTenantId, command.getTenantId())
                        .eq(ElnRecordRepeatRowEntity::getDianziJiluId, current.record.getId())
                        .eq(ElnRecordRepeatRowEntity::getXiudingId, current.revision.getId())
                        .in(ElnRecordRepeatRowEntity::getOperationStatus,
                                "PREPARED", "ACTIVE", "DELETE_PREPARED")
                        .and(q -> q.isNull(ElnRecordRepeatRowEntity::getFDeleteMark)
                                .or().eq(ElnRecordRepeatRowEntity::getFDeleteMark, 0))
                        .orderByAsc(ElnRecordRepeatRowEntity::getRowOrder));
        Map<String, ElnRecordRepeatRowEntity> rowsByIdentity = new LinkedHashMap<>();
        Map<String, List<ElnRecordRepeatRowEntity>> rowsByGroup = new LinkedHashMap<>();
        for (ElnRecordRepeatRowEntity row : rows) {
            if (row == null || !Objects.equals(current.record.getMubanId(), row.getMubanId())
                    || !bindingsByGroup.containsKey(row.getRepeatGroupCode())
                    || !StringUtils.hasText(row.getRowId())
                    || rowsByIdentity.put(repeatRowKey(
                            row.getRepeatGroupCode(), row.getRowId()), row) != null) {
                throw new DataException("重复行身份数据无效");
            }
            rowsByGroup.computeIfAbsent(row.getRepeatGroupCode(),
                    ignored -> new java.util.ArrayList<>()).add(row);
        }
 
        Map<String, DocxControlSnapshot> runtimeControls = new LinkedHashMap<>();
        Map<String, DocxControlSnapshot> physicalRows = new LinkedHashMap<>();
        for (DocxControlSnapshot control : controls.values()) {
            if (!control.getTag().startsWith("eln.repeat.")) continue;
            RepeatTag tag = RepeatTag.parse(control.getTag());
            if (tag == null) {
                throw new DataException("DOCX 重复字段 Tag 无效");
            }
            Map<String, ElnTemplateBindingEntity> bindingsByField =
                    bindingsByGroupAndField.get(tag.groupCode);
            if (bindingsByField == null) throw new DataException("DOCX 重复字段 Tag 无效");
            ElnTemplateBindingEntity binding = bindingsByField.get(tag.fieldCode);
            if (binding == null) throw new DataException("DOCX 存在未知重复字段");
            if ("prototype".equals(tag.rowId)) continue;
            String rowKey = repeatRowKey(tag.groupCode, tag.rowId);
            ElnRecordRepeatRowEntity row = rowsByIdentity.get(rowKey);
            if (row == null) throw new DataException("DOCX 存在未分配身份的重复行");
            requireRepeatCell(control, binding);
            DocxControlSnapshot firstInRow = physicalRows.putIfAbsent(rowKey, control);
            if (firstInRow != null && !samePhysicalRow(firstInRow, control)) {
                throw new DataException("同一重复行的字段不在同一物理行");
            }
            if (runtimeControls.put(repeatCellKey(
                    tag.groupCode, tag.rowId, binding.getId()), control) != null) {
                throw new DataException("DOCX 重复行存在重复字段列");
            }
        }
 
        List<ElnRecordRepeatValueEntity> existing = repeatValueMapper.selectList(
                Wrappers.<ElnRecordRepeatValueEntity>lambdaQuery()
                        .eq(ElnRecordRepeatValueEntity::getFTenantId, command.getTenantId())
                        .eq(ElnRecordRepeatValueEntity::getDianziJiluId, current.record.getId())
                        .eq(ElnRecordRepeatValueEntity::getXiudingId, current.revision.getId())
                        .and(q -> q.isNull(ElnRecordRepeatValueEntity::getFDeleteMark)
                                .or().eq(ElnRecordRepeatValueEntity::getFDeleteMark, 0)));
        Map<String, ElnRecordRepeatValueEntity> valuesByCell = new LinkedHashMap<>();
        for (ElnRecordRepeatValueEntity value : existing) {
            String rowKey = repeatRowKey(value.getRepeatGroupCode(), value.getRepeatRowId());
            String key = repeatCellKey(value.getRepeatGroupCode(),
                    value.getRepeatRowId(), value.getMubanBangdingId());
            Map<String, ElnTemplateBindingEntity> groupBindings =
                    bindingsByGroupAndField.get(value.getRepeatGroupCode());
            if (!rowsByIdentity.containsKey(rowKey)
                    || groupBindings == null
                    || groupBindings.values().stream().noneMatch(binding ->
                            Objects.equals(binding.getId(), value.getMubanBangdingId()))
                    || valuesByCell.put(key, value) != null) {
                throw new DataException("重复字段值与行模型不一致");
            }
        }
 
        RepeatPrepared prepared = new RepeatPrepared();
        for (Map.Entry<String, List<ElnTemplateBindingEntity>> groupEntry
                : bindingsByGroup.entrySet()) {
            String groupCode = groupEntry.getKey();
            List<ElnTemplateBindingEntity> groupBindings = groupEntry.getValue();
            DocxControlSnapshot prototype = prototypesByGroup.get(groupCode);
            int previousPhysicalRow = prototype.getTableRowIndex();
            for (ElnRecordRepeatRowEntity row : rowsByGroup.getOrDefault(
                    groupCode, Collections.emptyList())) {
                String rowKey = repeatRowKey(groupCode, row.getRowId());
                DocxControlSnapshot firstInRow = physicalRows.get(rowKey);
                if (firstInRow == null) {
                    if ("PREPARED".equals(row.getOperationStatus())) continue;
                    if ("DELETE_PREPARED".equals(row.getOperationStatus())) {
                        for (ElnTemplateBindingEntity binding : groupBindings) {
                            String key = repeatCellKey(
                                    groupCode, row.getRowId(), binding.getId());
                            ElnRecordRepeatValueEntity value = valuesByCell.remove(key);
                            if (value == null) {
                                throw new DataException("待删除重复行缺少持久化字段值");
                            }
                            requireRepeatValueIdentity(value, current, row, binding);
                            prepared.valuesToDelete.add(value);
                            prepared.changedBindingIds.add(binding.getId());
                        }
                        prepared.rowsToDelete.add(row);
                        continue;
                    }
                    throw new DataException("DOCX 缺少已激活的重复行");
                }
                if (!Objects.equals(prototype.getPartName(), firstInRow.getPartName())
                        || !Objects.equals(prototype.getTableIndex(), firstInRow.getTableIndex())
                        || firstInRow.getTableRowIndex() <= previousPhysicalRow) {
                    throw new DataException("DOCX 重复行未按末尾顺序追加");
                }
                previousPhysicalRow = firstInRow.getTableRowIndex();
                for (ElnTemplateBindingEntity binding : groupBindings) {
                    String key = repeatCellKey(groupCode, row.getRowId(), binding.getId());
                    DocxControlSnapshot control = runtimeControls.remove(key);
                    if (control == null) throw new DataException("DOCX 重复行缺少字段列");
                    ElnFieldVersionEntity version = fixedVersion(
                            command.getTenantId(), binding.getZiduanBanbenId());
                    if (isProviderSource(version)) {
                        throw new DataException("当前阶段重复字段仅支持人工填写");
                    }
                    validateControlConfig(control, version);
                    if (formulaResults.contains(binding.getId())) {
                        ElnRecordRepeatValueEntity value = valuesByCell.remove(key);
                        if (value == null) {
                            value = newRepeatValue(current, command, row, binding,
                                    new ElnRecordFieldValueEntity());
                            value.setZhiLaiyuan("FORMULA");
                            prepared.inserts.add(value);
                        } else {
                            requireRepeatValueIdentity(value, current, row, binding);
                        }
                        continue;
                    }
                    ElnRecordFieldValueEntity normalized = new ElnRecordFieldValueEntity();
                    applyNormalizedValue(normalized, control, version);
                    if ("CHECKBOX".equals(binding.getKongjianLeixing())
                            && StringUtils.hasText(binding.getCheckboxGroupCode())) {
                        String checkboxGroupKey = groupCode + "\n" + row.getRowId()
                                + "\n" + binding.getCheckboxGroupCode();
                        CheckboxGroupState state = prepared.checkboxGroups.computeIfAbsent(
                                checkboxGroupKey, ignored -> new CheckboxGroupState(
                                        binding.getCheckboxGroupCode()));
                        if ("true".equals(normalized.getZhiWenben())) {
                            state.checked.add(binding);
                        }
                    }
                    ElnRecordRepeatValueEntity value = valuesByCell.remove(key);
                    if (value == null) {
                        value = newRepeatValue(current, command, row, binding, normalized);
                        prepared.inserts.add(value);
                        prepared.changedBindingIds.add(binding.getId());
                    } else {
                        requireRepeatValueIdentity(value, current, row, binding);
                        if (!sameValue(value, normalized)) {
                            copyValue(value, normalized);
                            value.setZhiLaiyuan("MANUAL");
                            value.setFLastModifyUserId(command.getUserId());
                            value.setFLastModifyTime(new Date());
                            prepared.updates.add(value);
                            prepared.changedBindingIds.add(binding.getId());
                        }
                    }
                }
                if ("PREPARED".equals(row.getOperationStatus())) prepared.rowsToActivate.add(row);
            }
        }
        if (!runtimeControls.isEmpty() || !valuesByCell.isEmpty()) {
            throw new DataException("DOCX 重复字段与持久化值不一致");
        }
        return prepared;
    }
 
    private Set<String> formulaResultBindingIds(String tenantId, String templateId) {
        Set<String> result = new LinkedHashSet<>();
        for (ElnTemplateFormulaBindingEntity formula : formulaBindingMapper.selectList(
                Wrappers.<ElnTemplateFormulaBindingEntity>lambdaQuery()
                        .eq(ElnTemplateFormulaBindingEntity::getFTenantId, tenantId)
                        .eq(ElnTemplateFormulaBindingEntity::getMubanId, templateId)
                        .eq(ElnTemplateFormulaBindingEntity::getBangdingZhuangtai, "ACTIVE")
                        .and(q -> q.isNull(ElnTemplateFormulaBindingEntity::getFDeleteMark)
                                .or().eq(ElnTemplateFormulaBindingEntity::getFDeleteMark, 0)))) {
            if (!result.add(formula.getJieguoBangdingId())) {
                throw new DataException("公式结果字段被重复写入");
            }
        }
        return result;
    }
 
    private void saveValues(List<ElnRecordFieldValueEntity> values) {
        for (ElnRecordFieldValueEntity value : values) {
            if (valueMapper.updateById(value) != 1) {
                throw new DataException("电子记录字段保存冲突");
            }
        }
    }
 
    private void saveRepeatValues(RepeatPrepared prepared) {
        for (ElnRecordRepeatValueEntity value : prepared.inserts) {
            if (repeatValueMapper.insert(value) != 1) {
                throw new DataException("重复字段值创建冲突");
            }
        }
        for (ElnRecordRepeatValueEntity value : prepared.updates) {
            if (repeatValueMapper.updateById(value) != 1) {
                throw new DataException("重复字段值保存冲突");
            }
        }
        Date deletedAt = new Date();
        for (ElnRecordRepeatValueEntity value : prepared.valuesToDelete) {
            ElnRecordRepeatRowEntity row = prepared.rowsToDelete.stream()
                    .filter(item -> Objects.equals(item.getRepeatGroupCode(),
                                    value.getRepeatGroupCode())
                            && Objects.equals(item.getRowId(), value.getRepeatRowId()))
                    .findFirst().orElseThrow(() -> new DataException("待删除重复字段缺少行身份"));
            value.setFDeleteMark(1);
            value.setFDeleteUserId(row.getDeleteRequestedUserId());
            value.setFDeleteTime(deletedAt);
            value.setFLastModifyUserId(row.getDeleteRequestedUserId());
            value.setFLastModifyTime(deletedAt);
            if (repeatValueMapper.updateById(value) != 1) {
                throw new DataException("重复字段值逻辑删除冲突");
            }
        }
        for (ElnRecordRepeatRowEntity row : prepared.rowsToActivate) {
            row.setOperationStatus("ACTIVE");
            row.setFLastModifyTime(new Date());
            if (repeatRowMapper.updateById(row) != 1) {
                throw new DataException("重复行激活冲突");
            }
            auditRepeat(row, row.getOperationCode(),
                    "ELN_RECORD_REPEAT_APPEND_ACTIVATE", "激活电子记录重复行", "activate");
        }
        for (ElnRecordRepeatRowEntity row : prepared.rowsToDelete) {
            row.setOperationStatus("DELETED");
            row.setFDeleteMark(1);
            row.setFDeleteUserId(row.getDeleteRequestedUserId());
            row.setFDeleteTime(deletedAt);
            row.setFLastModifyUserId(row.getDeleteRequestedUserId());
            row.setFLastModifyTime(deletedAt);
            if (repeatRowMapper.updateById(row) != 1) {
                throw new DataException("重复行逻辑删除冲突");
            }
            auditRepeat(row, row.getDeleteOperationCode(),
                    "ELN_RECORD_REPEAT_DELETE", "删除电子记录重复行", "delete");
        }
    }
 
    private void saveCalculatedRepeatValues(List<ElnRecordRepeatValueEntity> values) {
        for (ElnRecordRepeatValueEntity value : values) {
            if (repeatValueMapper.updateById(value) != 1) {
                throw new DataException("重复公式结果字段保存冲突");
            }
        }
    }
 
    private void auditRepeat(ElnRecordRepeatRowEntity row, String operationCode,
            String actionCode, String actionLabel, String eventSuffix) {
        if (!StringUtils.hasText(operationCode)) return;
        Map<String, Object> snapshot = new LinkedHashMap<>();
        snapshot.put("groupCode", row.getRepeatGroupCode());
        snapshot.put("rowId", row.getRowId());
        snapshot.put("rowOrder", row.getRowOrder());
        snapshot.put("status", row.getOperationStatus());
        snapshot.put("deleteReason", row.getDeleteReason());
        auditRecorder.accept(AuditEventDTO.builder()
                .clientEventId(ElnRepeatAuditEventIds.of(operationCode, eventSuffix))
                .operationId(operationCode)
                .eventTime(new Date())
                .tenantId(row.getFTenantId())
                .operatorId(row.getFLastModifyUserId())
                .eventType("DATA_CHANGE")
                .actionCode(actionCode)
                .actionLabel(actionLabel)
                .bizModule("eln")
                .bizType("eln_record_repeat_row")
                .bizCode(row.getDianziJiluId())
                .targetTable("eln_record_repeat_row")
                .targetId(row.getId())
                .dataSnapshot(JsonUtil.getObjectToString(snapshot))
                .build());
    }
 
    private static void requireRepeatCell(DocxControlSnapshot control,
            ElnTemplateBindingEntity binding) {
        if (control == null || !"REPEAT".equals(control.getControlRole())
                || !Objects.equals(binding.getKongjianLeixing(), control.getControlType())
                || !"word/document.xml".equals(control.getPartName())
                || control.getTableIndex() == null || control.getTableRowIndex() == null) {
            throw new DataException("重复字段必须位于正文表格数据行内");
        }
        if (control.isNestedTable()) throw new DataException("当前阶段不支持嵌套重复表格");
        if (control.isVerticalMerge()) throw new DataException("重复数据区不支持跨行合并");
    }
 
    private static boolean samePhysicalRow(DocxControlSnapshot left,
            DocxControlSnapshot right) {
        return Objects.equals(left.getPartName(), right.getPartName())
                && Objects.equals(left.getTableIndex(), right.getTableIndex())
                && Objects.equals(left.getTableRowIndex(), right.getTableRowIndex());
    }
 
    private static String repeatRowKey(String groupCode, String rowId) {
        return groupCode + "\n" + rowId;
    }
 
    private static String repeatCellKey(String groupCode, String rowId, String bindingId) {
        return repeatRowKey(groupCode, rowId) + "\n" + bindingId;
    }
 
    private static ElnRecordRepeatValueEntity newRepeatValue(Current current,
            RecordFillSaveCommand command, ElnRecordRepeatRowEntity row,
            ElnTemplateBindingEntity binding, ElnRecordFieldValueEntity normalized) {
        Date now = new Date();
        ElnRecordRepeatValueEntity value = new ElnRecordRepeatValueEntity();
        value.setId(IdUtil.getSnowflakeNextIdStr());
        value.setDianziJiluId(current.record.getId());
        value.setXiudingId(current.revision.getId());
        value.setMubanId(current.record.getMubanId());
        value.setRepeatRowId(row.getRowId());
        value.setRepeatGroupCode(row.getRepeatGroupCode());
        value.setMubanBangdingId(binding.getId());
        value.setZiduanDingyiId(binding.getZiduanDingyiId());
        value.setZiduanBianma(binding.getZiduanBianma());
        value.setZiduanBanbenId(binding.getZiduanBanbenId());
        value.setBangdingJuese("REPEAT");
        value.setBangdingZhuangtai("ACTIVE");
        copyValue(value, normalized);
        value.setZhiLaiyuan("MANUAL");
        value.setFTenantId(command.getTenantId());
        value.setFVersion(0);
        value.setFCreatorUserId(command.getUserId());
        value.setFCreatorTime(now);
        value.setFLastModifyUserId(command.getUserId());
        value.setFLastModifyTime(now);
        value.setFDeleteMark(0);
        return value;
    }
 
    private static void requireRepeatValueIdentity(ElnRecordRepeatValueEntity value,
            Current current, ElnRecordRepeatRowEntity row,
            ElnTemplateBindingEntity binding) {
        if (!Objects.equals(current.record.getId(), value.getDianziJiluId())
                || !Objects.equals(current.revision.getId(), value.getXiudingId())
                || !Objects.equals(current.record.getMubanId(), value.getMubanId())
                || !Objects.equals(row.getRepeatGroupCode(), value.getRepeatGroupCode())
                || !Objects.equals(binding.getZiduanDingyiId(), value.getZiduanDingyiId())
                || !Objects.equals(binding.getZiduanBianma(), value.getZiduanBianma())
                || !Objects.equals(binding.getZiduanBanbenId(), value.getZiduanBanbenId())
                || !"REPEAT".equals(value.getBangdingJuese())
                || !"ACTIVE".equals(value.getBangdingZhuangtai())) {
            throw new DataException("重复字段值固定绑定已漂移");
        }
    }
 
    private static boolean sameValue(ElnRecordRepeatValueEntity value,
            ElnRecordFieldValueEntity normalized) {
        return Objects.equals(value.getZhiWenben(), normalized.getZhiWenben())
                && Objects.equals(value.getZhiShuzhi(), normalized.getZhiShuzhi())
                && Objects.equals(value.getZhiRiqi(), normalized.getZhiRiqi());
    }
 
    private static void copyValue(ElnRecordRepeatValueEntity target,
            ElnRecordFieldValueEntity source) {
        target.setZhiWenben(source.getZhiWenben());
        target.setZhiShuzhi(source.getZhiShuzhi());
        target.setZhiRiqi(source.getZhiRiqi());
    }
 
    private Map<String, ElnTemplateBindingEntity> activeBindings(String tenantId, String templateId) {
        List<ElnTemplateBindingEntity> list = bindingMapper.selectList(Wrappers.<ElnTemplateBindingEntity>lambdaQuery()
                .eq(ElnTemplateBindingEntity::getFTenantId, tenantId).eq(ElnTemplateBindingEntity::getMubanId, templateId)
                .eq(ElnTemplateBindingEntity::getBangdingZhuangtai, "ACTIVE")
                .and(q -> q.isNull(ElnTemplateBindingEntity::getFDeleteMark).or().eq(ElnTemplateBindingEntity::getFDeleteMark, 0)));
        Map<String, ElnTemplateBindingEntity> result = new LinkedHashMap<>();
        for (ElnTemplateBindingEntity binding : list) {
            if (result.put(binding.getId(), binding) != null) throw new DataException("活动绑定重复");
        }
        return result;
    }
 
    private Map<String, DocxControlSnapshot> controls(String tenantId, String fileId,
                                                       Map<String, ElnTemplateBindingEntity> bindings) {
        Map<String, DocxControlSnapshot> result = new LinkedHashMap<>();
        for (DocxControlSnapshot control : documentEvidence.readControls(tenantId, fileId)) {
            if (result.put(control.getTag(), control) != null) throw new DataException("DOCX 存在重复字段 Tag");
        }
        for (ElnTemplateBindingEntity binding : bindings.values()) {
            DocxControlSnapshot control = result.get(binding.getKongjianBiaoqian());
            if (control == null || !Objects.equals(binding.getKongjianJuese(), control.getControlRole())
                    || !Objects.equals(binding.getKongjianLeixing(), control.getControlType())) {
                throw new DataException("DOCX 与活动绑定不一致");
            }
        }
        for (DocxControlSnapshot control : result.values()) {
            String tag = control.getTag();
            if ((tag.startsWith("eln.field.") || tag.startsWith("eln.ref."))
                    && !hasActiveBindingTag(bindings, tag)) {
                throw new DataException("DOCX 存在未绑定的 ELN 字段控件");
            }
        }
        return result;
    }
 
    private static boolean hasActiveBindingTag(Map<String, ElnTemplateBindingEntity> bindings, String tag) {
        for (ElnTemplateBindingEntity binding : bindings.values()) {
            if (Objects.equals(binding.getKongjianBiaoqian(), tag)) return true;
        }
        return false;
    }
 
    private void verifyReferences(String tenantId, Map<String, ElnTemplateBindingEntity> bindings,
                                  Map<String, DocxControlSnapshot> controls) {
        for (ElnTemplateBindingEntity reference : bindings.values()) {
            if (!"REFERENCE".equals(reference.getKongjianJuese())) continue;
            ElnTemplateBindingEntity primary = bindings.get(reference.getYinyongShiliId());
            if (primary == null || !"PRIMARY".equals(primary.getKongjianJuese())) {
                throw new DataException("活动引用缺少对应主字段");
            }
            if (!Objects.equals(primary.getZiduanDingyiId(), reference.getZiduanDingyiId())
                    || !Objects.equals(primary.getZiduanBianma(), reference.getZiduanBianma())
                    || !Objects.equals(primary.getZiduanBanbenId(), reference.getZiduanBanbenId())) {
                throw new DataException("活动引用与主字段固定版本不一致");
            }
            DocxControlSnapshot primaryControl = controls.get(primary.getKongjianBiaoqian());
            DocxControlSnapshot referenceControl = controls.get(reference.getKongjianBiaoqian());
            ElnFieldVersionEntity version = fixedVersion(tenantId, primary.getZiduanBanbenId());
            validateControlConfig(primaryControl, version);
            validateControlConfig(referenceControl, version);
            String expected = normalizedText(primaryControl, version);
            String observed = normalizedText(referenceControl, version);
            if (!Objects.equals(expected, observed)) throw new DataException("DOCX 引用字段与主字段值不一致");
        }
    }
 
    private String normalizedText(DocxControlSnapshot control, ElnFieldVersionEntity version) {
        ElnRecordFieldValueEntity temporary = new ElnRecordFieldValueEntity();
        applyNormalizedValue(temporary, control, version);
        return temporary.getZhiWenben();
    }
 
    @SuppressWarnings("unchecked")
    private void applyNormalizedValue(ElnRecordFieldValueEntity value, DocxControlSnapshot control,
                                      ElnFieldVersionEntity version) {
        String type = version.getShujuLeixing() == null ? "" : version.getShujuLeixing().toUpperCase();
        String raw = control.getCurrentValue();
        value.setZhiShuzhi(null);
        value.setZhiRiqi(null);
        if ("DECIMAL".equals(type) || "NUMBER".equals(type)) {
            if (!StringUtils.hasText(raw)) {
                value.setZhiWenben(null);
                return;
            }
            try {
                BigDecimal number = new BigDecimal(raw.trim());
                value.setZhiShuzhi(number);
                value.setZhiWenben(number.toPlainString());
                return;
            } catch (NumberFormatException e) {
                throw new DataException("DOCX 数值字段格式无效");
            }
        }
        if ("DATE".equals(type) || "DATETIME".equals(type)) {
            if (!StringUtils.hasText(raw)) {
                value.setZhiWenben(null);
                return;
            }
            String format = "DATETIME".equals(type) ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd";
            try {
                SimpleDateFormat parser = new SimpleDateFormat(format);
                parser.setLenient(false);
                Date date = parser.parse(raw.trim());
                value.setZhiRiqi(date);
                value.setZhiWenben(new SimpleDateFormat(format).format(date));
                return;
            } catch (ParseException e) {
                try {
                    Date date = Date.from(Instant.parse(raw.trim()));
                    value.setZhiRiqi(date);
                    value.setZhiWenben(new SimpleDateFormat(format).format(date));
                    return;
                } catch (DateTimeParseException ignored) {
                    throw new DataException("DOCX 日期字段格式无效");
                }
            }
        }
        if ("CHECKBOX".equals(type) || "BOOLEAN".equals(type)) {
            if (!"true".equalsIgnoreCase(raw) && !"false".equalsIgnoreCase(raw)) {
                throw new DataException("DOCX 勾选字段状态无效");
            }
            value.setZhiWenben(raw.toLowerCase());
            return;
        }
        if ("SELECT".equals(type) || "DROPDOWN".equals(type)) {
            Map<String, String> options = new LinkedHashMap<>();
            Map<String, Object> config = StringUtils.hasText(version.getKongjianPeizhi())
                    ? JsonUtil.getJsonToBean(version.getKongjianPeizhi(), Map.class) : Collections.<String, Object>emptyMap();
            Object optionConfig = config.get("options");
            if (!(optionConfig instanceof List)) throw new DataException("下拉字段配置无效");
            for (Object option : (List<?>) optionConfig) {
                if (!(option instanceof Map)) throw new DataException("下拉字段配置无效");
                Map<?, ?> item = (Map<?, ?>) option;
                String optionValue = String.valueOf(item.get("value"));
                String display = String.valueOf(item.get("display"));
                if (options.containsKey(optionValue) || options.containsValue(display)) throw new DataException("下拉显示值必须唯一");
                options.put(optionValue, display);
            }
            if (!StringUtils.hasText(raw)) {
                value.setZhiWenben(null);
                return;
            }
            for (Map.Entry<String, String> option : options.entrySet()) {
                if (Objects.equals(option.getValue(), raw)) {
                    value.setZhiWenben(option.getKey());
                    return;
                }
            }
            throw new DataException("DOCX 下拉显示值不在字段固定配置中");
        }
        value.setZhiWenben(raw);
    }
 
    private void validateControlConfig(DocxControlSnapshot control, ElnFieldVersionEntity version) {
        Map<String, Object> expected = DocxExpectedControlConfig.from(version.getShujuLeixing(), version.getKongjianPeizhi());
        if (!DocxControlConfigMatcher.matches(control.getControlType(), expected, control.getControlConfig())) {
            throw new DataException("DOCX 控件配置与固定字段版本不一致");
        }
    }
 
    private ElnFieldVersionEntity fixedVersion(String tenantId, String versionId) {
        ElnFieldVersionEntity version = fieldVersionMapper.selectOne(Wrappers.<ElnFieldVersionEntity>lambdaQuery()
                .eq(ElnFieldVersionEntity::getFTenantId, tenantId).eq(ElnFieldVersionEntity::getId, versionId).last("limit 1"));
        if (version == null) throw new DataException("固定字段版本不存在");
        return version;
    }
 
    private boolean isProviderSource(ElnFieldVersionEntity version) {
        ElnFieldForms.Binding binding;
        try {
            binding = StringUtils.hasText(version.getBangdingPeizhi())
                    ? JsonUtil.getJsonToBean(version.getBangdingPeizhi(), ElnFieldForms.Binding.class) : null;
        } catch (RuntimeException e) {
            throw new DataException("字段绑定配置无效");
        }
        String source = binding == null || binding.getRead() == null
                ? null : binding.getRead().getSource();
        return "LIMS_TASK".equals(source) || "SYSTEM".equals(source);
    }
 
    private void assertProviderValueUnchanged(
            ElnRecordFieldValueEntity value, DocxControlSnapshot control,
            ElnFieldVersionEntity version) {
        boolean emptyConfiguredSnapshot = value.getYuanShuxingBanben() != null
                && value.getZhiWenben() == null && value.getZhiShuzhi() == null && value.getZhiRiqi() == null;
        if (emptyConfiguredSnapshot) {
            String documentValue = control.getCurrentValue();
            if (documentValue == null || ("CHECKBOX".equals(version.getShujuLeixing())
                    && "false".equalsIgnoreCase(documentValue))) {
                return;
            }
        }
        ElnRecordFieldValueEntity observed = new ElnRecordFieldValueEntity();
        boolean configuredDate = value.getYuanShuxingBanben() != null
                && ("DATE".equals(version.getShujuLeixing()) || "DATETIME".equals(version.getShujuLeixing()));
        if (configuredDate) {
            observed.setZhiRiqi(configuredDocumentDate(control.getCurrentValue(), version.getShujuLeixing()));
        } else {
            applyNormalizedValue(observed, control, version);
        }
        boolean unchanged = Objects.equals(value.getZhiWenben(), observed.getZhiWenben());
        if (value.getYuanShuxingBanben() != null && "DECIMAL".equals(version.getShujuLeixing())) {
            BigDecimal storedNumber = value.getZhiShuzhi();
            BigDecimal documentNumber = observed.getZhiShuzhi();
            unchanged = storedNumber == null ? documentNumber == null
                    : documentNumber != null && storedNumber.compareTo(documentNumber) == 0;
        } else if (configuredDate) {
            Date storedDate = value.getZhiRiqi();
            Date documentDate = observed.getZhiRiqi();
            unchanged = storedDate == null ? documentDate == null
                    : documentDate != null && storedDate.getTime() == documentDate.getTime();
        }
        if (!unchanged) {
            throw new DataException("外部来源字段不允许人工修改,请使用重新同步");
        }
    }
 
    private static Date configuredDocumentDate(String raw, String type) {
        if (!StringUtils.hasText(raw)) return null;
        String input = raw.trim();
        boolean calendarDate = "DATE".equals(type);
        try {
            return calendarDate ? java.sql.Date.valueOf(LocalDate.parse(input))
                    : java.sql.Timestamp.valueOf(LocalDateTime.parse(input,
                            DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss").withResolverStyle(ResolverStyle.STRICT)));
        } catch (DateTimeParseException invalidLocal) {
            try {
                Instant instant = Instant.parse(input);
                if (instant.getNano() != 0) throw new DataException("DOCX 来源日期时间不能丢失小数秒精度");
                return calendarDate ? java.sql.Date.valueOf(instant.atZone(ZoneId.systemDefault()).toLocalDate())
                        : Date.from(instant);
            } catch (DateTimeParseException invalidInstant) {
                throw new DataException("DOCX 来源日期字段格式无效");
            }
        }
    }
 
    private void requireSession(RecordFillSaveCommand command) {
        if (command == null || !StringUtils.hasText(command.getSessionDocKey())
                || command.getSessionVersion() == null || command.getSessionVersion() < 1
                || (command.getCallbackStatus() != null && command.getCallbackStatus() != 2 && command.getCallbackStatus() != 6)) {
            throw new DataException("电子记录保存会话无效");
        }
    }
 
    private void requireOperation(RecordFillSaveCommand command) {
        requireSession(command);
        if (!StringUtils.hasText(command.getOperationCode())) throw new DataException("电子记录保存操作码不能为空");
    }
 
    private void assertOperationIdentity(ElnRecordSaveOperationEntity operation, RecordFillSaveCommand command) {
        if (!Objects.equals(operation.getDianziJiluId(), command.getRecordId())
                || !Objects.equals(operation.getJiluWendang(), command.getFileId())
                || !Objects.equals(operation.getOperatorUserId(), command.getUserId())
                || !Objects.equals(operation.getSessionDocKey(), command.getSessionDocKey())
                || !Objects.equals(operation.getSessionVersion(), command.getSessionVersion())) {
            throw new DataException("电子记录保存操作身份已漂移");
        }
    }
 
    private ElnRecordSaveOperationEntity findOperation(String tenantId, String operationCode) {
        return operationMapper.selectOne(Wrappers.<ElnRecordSaveOperationEntity>lambdaQuery()
                .eq(ElnRecordSaveOperationEntity::getFTenantId, tenantId)
                .eq(ElnRecordSaveOperationEntity::getOperationCode, operationCode)
                .and(q -> q.isNull(ElnRecordSaveOperationEntity::getFDeleteMark)
                        .or().eq(ElnRecordSaveOperationEntity::getFDeleteMark, 0)).last("limit 1"));
    }
 
    private RecordFillSaveOperation reject(ElnRecordSaveOperationEntity operation, String reason) {
        if (operationMapper.finishPrepared(operation.getFTenantId(), operation.getOperationCode(),
                RecordFillSaveOperation.REJECTED, reason) == 1) {
            operation.setOperationStatus(RecordFillSaveOperation.REJECTED);
            operation.setRejectReason(reason);
            return operation(operation);
        }
        ElnRecordSaveOperationEntity refreshed = findOperation(operation.getFTenantId(), operation.getOperationCode());
        return refreshed == null ? rejected(null, reason) : operation(refreshed);
    }
 
    private void rejectPrepared(String tenantId, String operationCode, String reason) {
        operationMapper.finishPrepared(tenantId, operationCode, RecordFillSaveOperation.REJECTED, reason);
    }
 
    private RecordFillSaveOperation rejected(RecordFillSaveCommand command, String reason) {
        RecordFillSaveOperation result = new RecordFillSaveOperation();
        result.setOperationCode(command == null ? null : command.getOperationCode());
        result.setStatus(RecordFillSaveOperation.REJECTED);
        result.setReason(reason);
        return result;
    }
 
    private static RecordFillSaveOperation operation(ElnRecordSaveOperationEntity entity) {
        RecordFillSaveOperation result = new RecordFillSaveOperation();
        result.setOperationCode(entity.getOperationCode());
        result.setStatus(entity.getOperationStatus());
        result.setExpectedRecordVersion(entity.getExpectedRecordVersion());
        result.setReason(entity.getRejectReason());
        return result;
    }
 
    private static final class Current {
        private final ElnRecordEntity record;
        private final ElnRecordRevisionEntity revision;
        private Current(ElnRecordEntity record, ElnRecordRevisionEntity revision) { this.record = record; this.revision = revision; }
    }
 
    private <T> T inTenant(String tenantId, TenantAction<T> action) {
        if (!StringUtils.hasText(tenantId)) throw new DataException("电子记录租户不能为空");
        try {
            tenantRoute.switchTo(tenantId.trim());
            return action.get();
        } finally {
            tenantRoute.clear();
        }
    }
 
    @FunctionalInterface
    interface TenantAction<T> { T get(); }
 
    interface TenantRoute {
        void switchTo(String tenantId);
        void clear();
    }
 
    private static final class PreparedValues {
        private final List<ElnRecordFieldValueEntity> values;
        private final List<ElnRecordFieldValueEntity> changedValues;
        private final Set<String> changedBindingIds;
        private final RepeatPrepared repeat;
 
        private PreparedValues(List<ElnRecordFieldValueEntity> values,
                               List<ElnRecordFieldValueEntity> changedValues,
                               Set<String> changedBindingIds, RepeatPrepared repeat) {
            this.values = values;
            this.changedValues = changedValues;
            this.changedBindingIds = changedBindingIds;
            this.repeat = repeat;
        }
    }
 
    private static final class RepeatPrepared {
        private final List<ElnRecordRepeatValueEntity> inserts = new java.util.ArrayList<>();
        private final List<ElnRecordRepeatValueEntity> updates = new java.util.ArrayList<>();
        private final List<ElnRecordRepeatValueEntity> valuesToDelete = new java.util.ArrayList<>();
        private final List<ElnRecordRepeatRowEntity> rowsToActivate = new java.util.ArrayList<>();
        private final List<ElnRecordRepeatRowEntity> rowsToDelete = new java.util.ArrayList<>();
        private final Set<String> changedBindingIds = new LinkedHashSet<>();
        private final Map<String, CheckboxGroupState> checkboxGroups =
                new LinkedHashMap<>();
 
        private static RepeatPrepared empty() { return new RepeatPrepared(); }
    }
 
    private static final class CheckboxGroupState {
        private final String groupCode;
        private final List<ElnTemplateBindingEntity> checked = new java.util.ArrayList<>();
 
        private CheckboxGroupState(String groupCode) {
            this.groupCode = groupCode;
        }
    }
 
    private static final class RepeatTag {
        private final String groupCode;
        private final String rowId;
        private final String fieldCode;
 
        private RepeatTag(String groupCode, String rowId, String fieldCode) {
            this.groupCode = groupCode;
            this.rowId = rowId;
            this.fieldCode = fieldCode;
        }
 
        private static RepeatTag parse(String value) {
            String[] parts = value == null ? new String[0] : value.split("\\.", -1);
            if (parts.length != 5 || !"eln".equals(parts[0]) || !"repeat".equals(parts[1])
                    || !token(parts[2]) || !token(parts[3]) || !token(parts[4])) {
                return null;
            }
            return new RepeatTag(parts[2], parts[3], parts[4]);
        }
 
        private static boolean token(String value) {
            return value != null && value.matches("[A-Za-z0-9_-]{1,80}");
        }
    }
 
    private static final class ValueState {
        private final String text;
        private final BigDecimal decimal;
        private final Date date;
 
        private ValueState(String text, BigDecimal decimal, Date date) {
            this.text = text;
            this.decimal = decimal;
            this.date = date;
        }
 
        private static ValueState of(ElnRecordFieldValueEntity value) {
            return new ValueState(value.getZhiWenben(), value.getZhiShuzhi(),
                    value.getZhiRiqi());
        }
 
        @Override
        public boolean equals(Object other) {
            if (this == other) return true;
            if (!(other instanceof ValueState)) return false;
            ValueState that = (ValueState) other;
            return Objects.equals(text, that.text)
                    && Objects.equals(decimal, that.decimal)
                    && Objects.equals(date, that.date);
        }
 
        @Override
        public int hashCode() {
            return Objects.hash(text, decimal, date);
        }
    }
}