云
2026-09-29 e85e3baa6e1b09bcb02d8abb79da13ad41e1e61c
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
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MOM 工厂数字化平台 · 项目业务全景</title>
<style>
:root{
  --bg:#0b1220; --bg2:#101a2e; --panel:#131f36; --panel2:#182742;
  --line:#24344f; --text:#dbe4f0; --muted:#8fa1b8;
  --c-base:#8a94a6; --c-sale:#4f9cff; --c-sup:#c9a86a; --c-erp:#5fc79e;
  --c-prod:#e0b34f; --c-wm:#7e6bd6; --c-qc:#e06c6c; --c-dv:#59a8b8;
  --c-as:#c78bd6; --c-hrm:#9db85a; --c-bpm:#e6a23c; --c-ai:#56c8d8; --c-fin:#3ec9a7; --c-ums:#d6955c; --c-res:#e070a8;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{background:var(--bg);color:var(--text);font-family:"Segoe UI","Microsoft YaHei","PingFang SC",sans-serif;font-size:14px;line-height:1.6}
header{position:fixed;top:0;left:0;right:0;height:64px;background:linear-gradient(180deg,#0e1728,#0b1220);border-bottom:1px solid var(--line);display:flex;align-items:center;gap:24px;padding:0 24px;z-index:50}
header .logo{width:30px;height:30px;border-radius:6px;background:linear-gradient(135deg,#2c5cff,#19c2d8);display:flex;align-items:center;justify-content:center;font-weight:700;color:#fff;font-size:13px}
header h1{font-size:17px;font-weight:600;letter-spacing:.5px}
header .sub{color:var(--muted);font-size:12px}
#globalSearchWrap{margin-left:auto;position:relative;width:280px}
#globalSearch{width:100%;background:var(--bg2);border:1px solid var(--line);color:var(--text);border-radius:6px;padding:7px 12px;font-size:13px;outline:none}
#globalSearch:focus{border-color:#2c5cff}
#searchResult{position:absolute;top:38px;right:0;width:430px;max-height:60vh;overflow:auto;background:var(--panel);border:1px solid var(--line);border-radius:8px;display:none;box-shadow:0 12px 40px rgba(0,0,0,.5)}
#searchResult .sr-item{padding:10px 14px;border-bottom:1px solid var(--line);cursor:pointer}
#searchResult .sr-item:hover{background:var(--panel2)}
#searchResult .sr-type{font-size:11px;color:var(--muted)}
#searchResult .sr-meta{font-size:12px;color:var(--muted);margin-top:2px}
nav{position:fixed;top:64px;left:0;bottom:0;width:210px;background:var(--bg2);border-right:1px solid var(--line);padding:14px 0;overflow:auto;z-index:40}
nav .group{color:#5f7188;font-size:11px;padding:12px 18px 4px;letter-spacing:1px}
nav a{display:flex;align-items:center;gap:8px;padding:8px 18px;color:var(--muted);text-decoration:none;font-size:13px;border-left:3px solid transparent;cursor:pointer}
nav a:hover{color:var(--text);background:#152238}
nav a.active{color:#fff;background:#16294a;border-left-color:#2c5cff}
nav a .dot{width:8px;height:8px;border-radius:2px;flex:none}
main{margin:64px 0 0 210px;padding:26px 32px 80px;max-width:1500px}
section.page{display:none}
section.page.on{display:block}
h2.pt{font-size:20px;font-weight:600;margin-bottom:4px}
p.desc{color:var(--muted);margin-bottom:18px;max-width:980px}
.grid{display:grid;gap:14px}
.stats{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}
.stat{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:14px 16px}
.stat b{display:block;font-size:24px;font-weight:700;color:#fff}
.stat span{color:var(--muted);font-size:12px}
.cards{grid-template-columns:repeat(auto-fill,minmax(300px,1fr))}
.card{background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--acc,#2c5cff);border-radius:10px;padding:16px 18px;cursor:pointer;transition:border-color .15s,transform .15s}
.card:hover{transform:translateY(-2px);border-color:#3a5a8f}
.card h3{font-size:15px;color:#fff;margin-bottom:2px}
.card h3 .tag{font-size:10px;color:var(--acc,#2c5cff);border:1px solid currentColor;padding:1px 6px;border-radius:10px;margin-left:8px;vertical-align:2px}
.card p{color:var(--muted);font-size:12.5px;margin:6px 0}
.card .objs{font-size:12px;color:#a9b8cc}
.pill{display:inline-block;background:#1b2c4a;border:1px solid var(--line);color:#bcd0ea;border-radius:12px;padding:2px 10px;font-size:12px;margin:3px 4px 0 0;cursor:pointer}
.pill:hover{border-color:#3a5a8f;background:#22375c}
.banner{background:linear-gradient(90deg,#12213c,#0f1a2e);border:1px solid var(--line);border-radius:10px;padding:14px 18px;margin-bottom:18px}
.banner .chain{font-size:13.5px;color:#cfe0f5}
.banner .chain b{color:var(--c-prod)}
.fig{background:var(--panel);border:1px solid var(--line);border-radius:10px;margin:14px 0;overflow:hidden}
.fig .bar{display:flex;align-items:center;gap:8px;padding:8px 14px;border-bottom:1px solid var(--line);background:#101c31}
.fig .bar .name{font-size:13px;color:#cfe0f5;font-weight:600}
.fig .bar .btns{margin-left:auto;display:flex;gap:6px}
.fig .bar button{background:#1a2a46;border:1px solid var(--line);color:#9fb2ca;font-size:12px;border-radius:5px;padding:3px 10px;cursor:pointer}
.fig .bar button:hover{color:#fff;border-color:#3a5a8f}
.fig .canvas{overflow:auto;padding:16px;background:var(--panel)}
.fig .canvas .stage{transform-origin:top left;transition:transform .15s}
.fig:fullscreen{width:100vw;height:100vh;border-radius:0;background:var(--bg)}
.fig:fullscreen .canvas{height:calc(100vh - 45px)}
.mermaid-wrap svg{max-width:100%}
table.steps{width:100%;border-collapse:collapse;margin:10px 0}
table.steps th{font-size:12px;color:var(--muted);text-align:left;font-weight:600;padding:7px 10px;border-bottom:1px solid var(--line);background:#101c31}
table.steps td{padding:8px 10px;border-bottom:1px solid #1b2a44;vertical-align:top;font-size:13px}
table.steps td.stage{white-space:nowrap;color:var(--acc,#7ea6e8);font-weight:600}
table.steps td.role{white-space:nowrap;color:var(--muted);font-size:12px}
table.data{width:100%;border-collapse:collapse;background:var(--panel);border:1px solid var(--line);border-radius:10px;overflow:hidden}
table.data th{font-size:12px;color:var(--muted);text-align:left;padding:9px 12px;background:#101c31;border-bottom:1px solid var(--line)}
table.data td{padding:9px 12px;border-bottom:1px solid #1a2942;font-size:13px;vertical-align:top}
table.data tr:hover td{background:#16243c;cursor:pointer}
.detail{background:var(--panel2);border:1px solid #2c4468;border-radius:10px;padding:16px 20px;margin:12px 0}
.detail h4{color:#fff;margin-bottom:8px}
.detail .kv{margin:4px 0;font-size:13px}
.detail .kv b{color:var(--muted);font-weight:600;margin-right:8px}
.muted{color:var(--muted)} .small{font-size:12px}
#objDetail:empty{display:none}
h3.sec{font-size:15px;color:#cfe0f5;margin:26px 0 10px;padding-left:10px;border-left:3px solid #2c5cff}
footer{position:fixed;bottom:0;left:210px;right:0;background:#0d1526ee;border-top:1px solid var(--line);padding:8px 32px;color:#5f7188;font-size:12px;z-index:30}
.warn li{margin-left:18px;padding:3px 0;font-size:13px;color:#d9c4a0}
@media (max-width:1100px){nav{width:170px}main,footer{margin-left:0}}
</style>
</head>
<body>
<header>
  <div class="logo">MOM</div>
  <div>
    <h1 id="hProject">项目业务全景</h1>
    <div class="sub" id="hSub"></div>
  </div>
  <div id="globalSearchWrap">
    <input id="globalSearch" placeholder="搜索业务:销售订单 / 工单 / IQC / 库存 / 回款…" autocomplete="off">
    <div id="searchResult"></div>
  </div>
</header>
 
<nav id="nav">
  <div class="group">总览</div>
  <a data-t="overview" class="active"><span class="dot" style="background:#2c5cff"></span>项目概览</a>
  <a data-t="panorama"><span class="dot" style="background:#56c8d8"></span>业务全景图</a>
  <div class="group">核心业务流程</div>
  <a data-t="flow-sales"><span class="dot" style="background:var(--c-sale)"></span>销售流程</a>
  <a data-t="flow-purchase"><span class="dot" style="background:var(--c-erp)"></span>采购流程</a>
  <a data-t="flow-production"><span class="dot" style="background:var(--c-prod)"></span>生产流程</a>
  <a data-t="flow-quality"><span class="dot" style="background:var(--c-qc)"></span>质量流程</a>
  <a data-t="flow-warehouse"><span class="dot" style="background:var(--c-wm)"></span>仓储流程</a>
  <a data-t="flow-aftersales"><span class="dot" style="background:var(--c-as)"></span>售后流程</a>
  <a data-t="flow-finance"><span class="dot" style="background:var(--c-fin)"></span>财务报销流程</a>
  <a data-t="flow-ums"><span class="dot" style="background:var(--c-ums)"></span>会员入会流程</a>
  <a data-t="flow-research"><span class="dot" style="background:var(--c-res)"></span>课题管理流程</a>
  <div class="group">结构与数据</div>
  <a data-t="relation"><span class="dot" style="background:var(--c-bpm)"></span>系统关系</a>
  <a data-t="dataflow"><span class="dot" style="background:var(--c-fin)"></span>数据流</a>
  <a data-t="objects"><span class="dot" style="background:var(--c-base)"></span>业务对象</a>
  <a data-t="roles"><span class="dot" style="background:var(--c-hrm)"></span>角色权限</a>
  <div class="group">扩展</div>
  <a data-t="ai"><span class="dot" style="background:var(--c-ai)"></span>AI 业务</a>
  <a data-t="pending"><span class="dot" style="background:var(--c-sup)"></span>待确认事项</a>
</nav>
 
<main>
<section class="page on" id="overview"></section>
<section class="page" id="panorama">
  <h2 class="pt">业务全景图</h2>
  <p class="desc">按业务意义(而非代码包结构)归类的模块-能力思维导图。</p>
  <div class="fig" data-fig="01-business-mindmap">
    <div class="bar"><span class="name">业务全景思维导图</span><div class="btns"><button data-z="-">-</button><button data-z="+">+</button><button data-z="0">重置</button><button data-fs>全屏</button></div></div>
    <div class="canvas"><div class="stage mermaid-wrap"><pre class="mermaid" data-mmd="01-business-mindmap"></pre></div></div>
  </div>
</section>
<section class="page" id="flow-sales"></section>
<section class="page" id="flow-purchase"></section>
<section class="page" id="flow-production"></section>
<section class="page" id="flow-quality"></section>
<section class="page" id="flow-warehouse"></section>
<section class="page" id="flow-aftersales"></section>
<section class="page" id="flow-finance"></section>
<section class="page" id="flow-ums"></section>
<section class="page" id="flow-research"></section>
<section class="page" id="relation">
  <h2 class="pt">系统关系</h2>
  <p class="desc">模块之间的调用与数据依赖:实线=单据/数据流向,虚线=审批回调与辅助能力。</p>
  <div class="fig" data-fig="08-module-relation">
    <div class="bar"><span class="name">模块系统关系</span><div class="btns"><button data-z="-">-</button><button data-z="+">+</button><button data-z="0">重置</button><button data-fs>全屏</button></div></div>
    <div class="canvas"><div class="stage mermaid-wrap"><pre class="mermaid" data-mmd="08-module-relation"></pre></div></div>
  </div>
</section>
<section class="page" id="dataflow">
  <h2 class="pt">数据流</h2>
  <p class="desc">跨模块单据的生成与回写关系(字段带入、数量状态回写、事件通知)。</p>
  <div class="fig" data-fig="09-data-flow">
    <div class="bar"><span class="name">跨模块数据流</span><div class="btns"><button data-z="-">-</button><button data-z="+">+</button><button data-z="0">重置</button><button data-fs>全屏</button></div></div>
    <div class="canvas"><div class="stage mermaid-wrap"><pre class="mermaid" data-mmd="09-data-flow"></pre></div></div>
  </div>
</section>
<section class="page" id="objects">
  <h2 class="pt">核心业务对象</h2>
  <p class="desc">点击对象行查看作用、上游、下游与数据表。对象关系见文末 ER 图(仅核心业务对象)。</p>
  <div class="grid" id="objGrid" style="grid-template-columns:1fr"></div>
  <div id="objDetail" class="detail"></div>
  <h3 class="sec">对象关系图(ER)</h3>
  <div class="fig" data-fig="10-business-object">
    <div class="bar"><span class="name">核心业务对象关系</span><div class="btns"><button data-z="-">-</button><button data-z="+">+</button><button data-z="0">重置</button><button data-fs>全屏</button></div></div>
    <div class="canvas"><div class="stage mermaid-wrap"><pre class="mermaid" data-mmd="10-business-object"></pre></div></div>
  </div>
</section>
<section class="page" id="roles"></section>
<section class="page" id="ai">
  <h2 class="pt">AI 业务</h2>
  <p class="desc">项目 AI 统一采用「业务侧组装 Prompt → 模型 → 结构化 JSON → 人工确认」模式,无 Function Calling;AI 不可用时各功能回退人工。</p>
  <div class="grid cards" id="aiGrid"></div>
</section>
<section class="page" id="pending">
  <h2 class="pt">待确认事项</h2>
  <p class="desc">源码走读中发现与文档/预期不一致、疑似未完成或需业务确认的点,均标注了出处行为。</p>
  <ul class="warn" id="pendingList"></ul>
</section>
</main>
<footer id="footer"></footer>
 
<script type="application/json" id="data-business">{
  "project": {
    "name": "MOM 工厂数字化平台(mom-pro2)",
    "type": "混合系统",
    "mainSystem": "MOM 制造运营管理系统",
    "subSystems": [
      "MES(生产·仓储·质量·设备)",
      "ERP(进销存·财务)",
      "FIN(发票报销,独立模块)",
      "UMS(学会会员,独立模块)",
      "RES(科研课题,独立模块)",
      "CRM(客户关系)",
      "SRM(供应商关系)",
      "HRM(人力资源)",
      "BPM(Flowable 协同审批)",
      "AI(通义千问 + Milvus RAG)",
      "BI(SQL 配置式大屏)"
    ],
    "description": "基于 yudao(ruoyi-vue-pro)二次开发的制造业一体化数字工厂平台,覆盖『销售接单 → 采购/生产 → 质检 → 仓储 → 发货 → 回款 → 售后』全链路,内置审批流、主数据、附件、AI 辅助与 BI 驾驶舱。",
    "businessPositioning": "让工厂的订单、物料、生产、库存、质量、人员、资金在一个系统内闭环流转,管理层可通过 BI 大屏掌握全局。",
    "coreChain": "CRM 商机/合同 → ERP 销售订单 → MES 主生产计划/工单/报工 → MES 质检与仓储 → 销售出库发货 → 开票回款 → 售后闭环",
    "techBase": "Spring Boot + MyBatis + Flowable + Redis + Quartz/RocketMQ/Kafka + Milvus 向量库 + 通义千问",
    "database": "MySQL 8.4,运行库 mom-jssm 共 427 张表(含 system_*、bpm_*、act_* 底座表与 mes_*/erp_*/crm_*/srm_*/hrm_*/mdm_*/ai_*/finance_*/ums_*/research_* 业务表),多租户已关闭",
    "frontend": "mom-pro2-before(Vue3 管理后台,同仓库存放,只读参考)"
  },
  "stats": {
    "businessModules": 16,
    "coreObjects": 56,
    "documentFlows": 9,
    "approvalListeners": 15,
    "aiCapabilities": 8,
    "wmBillTypes": 15
  },
  "businessAnswers": [
    [
      "项目解决什么问题",
      "离散制造工厂的产供销人财物一体化管理:从客户订单到生产交付、库存资金全链路数字化闭环"
    ],
    [
      "核心业务",
      "销售订单履约(合同→订单→生产→质检→入库→发货→回款)与采购供应(申请→订单→到货→IQC→入库→付款)"
    ],
    [
      "业务主线",
      "CRM 合同 → ERP 销售订单 → MES MPS/工单 → 报工/IPQC → 完工入库 → 发货通知 → 销售出库(OQC) → 开票 → 回款"
    ],
    [
      "销售如何影响生产",
      "销售订单审批通过后,需生产明细自动创建 MES 主生产计划(MPS),MPS 下发驱动工单与任务"
    ],
    [
      "采购如何影响库存",
      "采购订单『确认收货』生成 MES 到货通知 → IQC 合格/特采 → 采购入库单完成 → 库存台账 IN 事务+N"
    ],
    [
      "生产如何产生库存",
      "报工审批通过后自动生成产品产出单入库(质检工序先待检、IPQC 后拆行入库);倒冲工序按 BOM 扣线边库"
    ],
    [
      "质量在哪里介入",
      "四道闸口:IQC(到货/外协入库)、IPQC(报工质检工序)、OQC(销售出库前)、RQC(退料/退货),不合格走 NCR 处置"
    ],
    [
      "售后如何闭环",
      "工单判定退货 → 退货申请审核 → 自动建 MES 销售退货单 → RQC 质检/入库回调 → 退款回调 → 工单完结关闭"
    ],
    [
      "AI 在哪里参与",
      "发票/报价 OCR 预填、质检结果建议、缺料/时长/风险/交付四类预测;均为『业务组装 Prompt→模型→JSON→人工确认』模式"
    ],
    [
      "模块间数据关系",
      "MDM 主数据统一下发;CRM↔ERP 合同生单;ERP↔MES 单据互建与数量回写;BPM 事件回调解锁状态;HRM 交接扩展数据可见性"
    ]
  ],
  "aiCapabilities": [
    {
      "name": "采购发票 OCR",
      "flow": "上传 blobId → 图片走 chatWithImage 视觉模型 / PDF·文本走 chat → 解析发票号·抬头·供应商·价税合计·税率 → 预填采购来票表单",
      "callsApis": [
        "SystemStorageBlobService"
      ]
    },
    {
      "name": "报价单 OCR",
      "flow": "PDF/Word/Excel/图片 → POI/PDFBox/视觉模型 → 报价头+产品明细 JSON → 预填报价单",
      "callsApis": [
        "SystemStorageBlobService"
      ]
    },
    {
      "name": "质检结果智能判定",
      "flow": "质检单+指标结果+缺陷记录 组装 Prompt → chat → 建议{合格/特采/退货/报废+理由}",
      "callsApis": [
        "MdmItemApi",
        "SrmSupplierApi",
        "CrmCustomerApi"
      ]
    },
    {
      "name": "物料短缺预测",
      "flow": "工单 BOM 需求 vs 库存(在库-冻结-占用) vs 已投料 → chat → 风险等级+缺口清单",
      "callsApis": [
        "MdmItemApi"
      ]
    },
    {
      "name": "生产时长预测",
      "flow": "工序标准工时+设备小时产量+传送倍率 → chat → 预测总时长+工序分解",
      "callsApis": [
        "MdmItemApi"
      ]
    },
    {
      "name": "生产风险预测",
      "flow": "IPQC 缺陷率+设备保养点检+物料可用率<20%+关键工序 → chat → 综合风险+因素",
      "callsApis": [
        "MdmItemApi"
      ]
    },
    {
      "name": "交付预测",
      "flow": "进度+剩余天数+任务完成状态+客户交期 → chat → 能否按时+置信度+延误因素",
      "callsApis": [
        "MdmItemApi",
        "CrmCustomerApi"
      ]
    },
    {
      "name": "RAG 知识库",
      "flow": "文档 → POI/Tika 提文本 → 切片(800token) → 向量化入 Milvus → 检索接口(topK3,阈值0.7)【当前无业务模块消费,预留能力】",
      "callsApis": []
    }
  ],
  "pendingConfirm": [
    "仓库类型/合格仓改造:需求称仓库应有 type 字段(原料/成品/半成品/线边/合格仓),但 MesWmWarehouseDO 当前无 type 字段,仅硬编码虚拟线边库,改造似未完成【待确认】",
    "HrmUserHandoverApi.expandUserIds 接口已实现,但 CRM/ERP/MES 查询『我的数据』处均未实际调用,交接可见性功能未真正生效【与项目规则文档存在落差】",
    "回款(crm_receivable)未接 BPM,创建即审批中(10)靠手动更新;合同/开票均已接【待确认是否有意简化】",
    "MES 任务暂停 pauseTask 仅允许草稿状态停工,疑似校验逻辑反转 bug",
    "付款单 BPM 驳回回退到 PROCESS(10) 而非 REJECT(35),与其他单据不一致",
    "财务勾稽断裂:updatePurchasePrice/updateSalePrice 方法体为空,入库/出库金额不会自动回写收付款",
    "生产入库单(ProductReceipt) 与报工产出(ProductProduce) 并存,存在重复入库风险需业务确认",
    "AI 模型工厂仅实现通义千问,DeepSeek/OpenAI/Ollama 枚举预留未实现",
    "售后退货单 AUDITING(10) 状态无写入代码,疑似预留未启用",
    "BI 图表 querySql 以字符串拼接参数执行(仅单引号转义),存在 SQL 注入风险面",
    "MesWmSnDO 新增的 uuid 字段生成/消费逻辑未在核心 Service 中确认",
    "转正申请(hrm_regularize_application)、培训证书等仅有 SQL 与菜单初始化,Java 侧 Service/Controller 未实现",
    "NCR 处置结果(返工/退货)未回写触发源单据,仅台账记录",
    "MPS 状态枚举独立编号(0/1/2/3),与通用单据常量(0/1/4/5)不一致",
    "财务报销:菜单仅授予 super_admin(超管由代码直返全部菜单),普通角色未配 system_role_menu,非超管登录看不到「报销单」菜单【待确认是否补授权】",
    "会员管理:会员管理菜单(1075490~1075503)仅 super_admin 可见,普通角色未配 system_role_menu,非超管登录看不到会员管理目录【待确认是否补授权】",
    "会员管理:审批通过自动建系统账号用「联系电话」同时作用户名与初始密码,若联系电话为空会用字面量 unknown 兜底,存在弱口令与重名风险【待确认是否改造】",
    "会员管理:自动建档不搬运 contactUser/unitIntro/expireDate(ums_application 无对应字段),单位会员的「联系人/单位简介/有效期」只能在会员列表手工补录【待确认是否需要申请单增字段】",
    "会员管理:单位会员的等级字典 ums_unit_grade 仅到「会长单位」(4),但校验逻辑额外硬编码禁止单位选「理事长」(grade=5)——该值本不在单位字典内,属防御性冗余校验【待确认是否精简】",
    "课题管理:专家评分权重(选题0.3/方案0.3/团队0.2/成果0.2)与「加权均分≥60 且 多数结论为推荐立项」的通过阈值是占位规则,需学会最终确认;已集中为常量便于调整【待确认,改动只需改常量】",
    "课题管理:经费拨付已对接财务报销(1:1 生成 finance_reimbursement 并自动提交审批),但台账与报销单是软关联(无外键,仅靠 reimbursement_id),财务侧直接删除报销单会留下台账的悬空 reimbursement_id;已用 FUND_REIMBURSEMENT_NOT_EXISTS 精确提示兜底,不做数据库级约束【已接受的风险】",
    "课题管理:新建的财务报销审批流程 finance_reimbursement_approve 为 2 节点(财务审核 → 财务主管审批),节点默认审批人策略为「角色 = 超级管理员(1)、依次审批」;上线前需在 BPM 设计器中按学会/财务实际审批人调整,否则该角色下多用户会变成逐人会签【环境配置项,非代码缺陷】",
    "课题管理:经费拨付台账的「已拨付(20)」等价于报销单「已通过(20)」,不含出纳实际付款动作;若日后需要独立的「已付款」状态,需另增状态与审批节点【二期可扩展】",
    "课题管理:申报单与 ums_member 是软关联(无外键),会员被删除后申报单会留下悬空 memberId;成员列表需容错展示「会员已删除」【待确认是否加外键或删除保护】",
    "课题管理:BPM 流程 research_topic_approve 三节点默认审批人为「角色=超级管理员(1) + 依次审批」,该 dev 库超级管理员角色下有 4 个用户(admin/张娟/马芳/马贞),每个节点需 4 人会签才推进;全新安装库该角色仅 admin 一人、流程可开箱即用。dev 库需减少超级管理员用户或改在 BPM 设计器中为每节点指定审批人【环境配置项,非代码缺陷】",
    "课题管理:立项通知复用 system_notice 系统公告,不新增 system_notify_template、不发站内信;如需短信/站内信定向通知需另行配置【待确认通知渠道】"
  ],
  "generatedAt": "2026-09-29",
  "analysisBasis": "基于 dev_business 分支 87bf4a99 源码,6 组并行源码走读(MES 生产质量 / MES 仓储 / ERP / CRM+SRM / 售后+HRM / AI+BI+MDM+BPM+底座);2026-09-28 增量补充 yudao-module-finance 独立财务报销模块(报销单 + 明细 + 发票附件 + BPM 审批 + 归档)与 yudao-module-ums 独立会员管理模块(入会申请 + BPM 两级审批 + 审批通过自动建会员与登录账号 + 会员列表/通讯录);2026-09-28 增量:BPM 流程定义 ums_membership_approve(入会审批)已在 mom-jssm 部署并并入 config_export_all 阶段一(分类 ums_membership_approve,秘书处初审 → 理事会审议,两节点审批人均为「角色」策略、角色=超级管理员(1) 兜底,表单类型=自定义表单,查看路径 /ums/application/detail/approve/index.vue);同时修复 mom-jssm 由 CREATE TABLE ... LIKE 克隆导致的 47 个指向 ruoyi-vue-pro 的 act_*/flw_* 跨库外键(重建为指向 mom-jssm 自身,孤儿行预检为 0);2026-09-28 增量:新增 yudao-module-research 独立科研课题模块一期(课题指南发布 + 在线申报 + 专家评审明细 + BPM 三级立项审批 + 立项通知公告 + 经费拨付记录,4 张 research_* 表 / 5 个字典 / bpm_category id=8002 / 流程 research_topic_approve / 菜单 1075510~1075553),并并入 config_export_all_20260928 阶段一;申报人取 ums_member(软关联)、立项通知复用 system_notice 公告、拨款凭证走 system 附件中心 recordType=research_fund_record;2026-09-29 增量:课题经费拨付对接财务报销——新建 yudao-module-finance-api(FinanceReimbursementApi:建单+提交/改单+重提/删除/查询四方法,本地 Spring Bean 调用,无需 Feign),课题侧登记拨款时 1:1 自动生成 finance_reimbursement(系统生成 1 行明细:费用类型=其他 5、发票金额=拨付金额、发票日期=拨付日期、说明=「课题〔名〕第 N 期拨款」)并立即提交 BPM,回写 reimbursement_id/reimbursement_no、台账置「报销审批中(15)」;凭证改为登记时必填并双绑(research_fund_record + finance_reimbursement);新增 ResearchFundStatusListener(第 15 个业务监听器,与 finance 自己的 FinanceReimbursementStatusListener 订阅同一流程 key 并行触发)按审批结果回写台账:通过→已拨付(20)、驳回→已驳回(30)+驳回原因/时间、取消流程→退回待拨付(0)可改后可重新发起(发起人撤回只把实例退回发起节点、流程未结束,不触发回写,台账保持 15);新造 2 节点 BPMN docs/sql/finance_reimbursement_approve.bpmn(财务审核 → 财务主管审批,category id=216 已存在),2026-09-29 已在 mom-jssm 部署为版本 1(form_type=20、自定义表单创建路径 /finance/reimbursement/index、查看路径 /finance/reimbursement/detail/approve/index.vue)并并入 config_export_all_20260929 阶段一,两级审批全通过/驳回/取消三条回写链路均已端到端验证"
}</script>
<script type="application/json" id="data-modules">{
  "modules": [
    {"key": "mdm", "name": "基础主数据", "color": "#8a94a6", "tag": "MDM", "flow": null,
     "positioning": "全系统统一口径的主数据权威源",
     "problem": "物料、单位、仓库在 MES/ERP/CRM 多处重复定义导致数据打架",
     "features": ["物料与SKU(含批次属性配置12开关)", "物料分类树", "计量单位与换算", "品牌", "仓库主数据(含type字段)", "MES 物料经 MdmItemSyncApi 反向同步"],
     "objects": ["物料", "SKU", "计量单位", "仓库"],
     "roles": ["主数据管理员"],
     "upstream": [], "downstream": ["生产", "进销存", "销售", "采购"],
     "coreTables": ["mdm_item", "mdm_item_sku", "mdm_item_category", "mdm_unit_measure", "mdm_brand", "mdm_warehouse"]},
    {"key": "crm", "name": "销售管理(客户关系)", "color": "#4f9cff", "tag": "CRM", "flow": "sales",
     "positioning": "销售前端:从线索到回款的客户经营",
     "problem": "客户资产沉淀、商机转化、合同审批与开票回款管控",
     "features": ["线索转化客户", "公海池与领取上限", "联系人", "商机自定义阶段", "跟进记录", "报价单(AI OCR)", "合同(BPM审批→生成销售订单)", "开票(BPM)→回款(先开票校验)", "回款计划", "销售目标", "数据权限三级(负责人/只读/读写)"],
     "objects": ["线索", "客户", "联系人", "商机", "报价单", "合同", "发票", "回款", "销售目标"],
     "roles": ["销售内勤", "销售经理", "财务(开票回款)"],
     "upstream": ["主数据(物料)"], "downstream": ["进销存(销售订单)", "售后(客户)", "AI(报价OCR)"],
     "coreTables": ["crm_clue", "crm_customer", "crm_contact", "crm_business", "crm_sale_quotation", "crm_contract", "crm_invoice", "crm_receivable", "crm_receivable_plan", "crm_permission"]},
    {"key": "srm", "name": "供应商管理", "color": "#c9a86a", "tag": "SRM", "flow": "purchase",
     "positioning": "供应商全生命周期:准入-资质-寻源-绩效",
     "problem": "供应商合规准入、阳光寻源(招标/比价)与绩效量化",
     "features": ["供应商档案与分类", "准入申请(BPM 采购/质量/财务会签)", "资质证书有效期", "招标:公开/邀请/竞谈完整版+比价简易版", "投标·报价·开标·评标·定标", "定标自动生成采购订单", "四维绩效(质量/交付/价格/服务)"],
     "objects": ["供应商", "准入申请", "资质证书", "招标项目", "投标", "定标", "绩效评价"],
     "roles": ["采购开发", "供应商管理员", "评审专家"],
     "upstream": ["主数据(物料)"], "downstream": ["进销存(采购订单)"],
     "coreTables": ["srm_supplier", "srm_supplier_apply", "srm_supplier_certificate", "srm_tender_project", "srm_tender_bid", "srm_bid_award", "srm_supplier_evaluation"]},
    {"key": "erp", "name": "进销存与财务", "color": "#5fc79e", "tag": "ERP", "flow": "purchase",
     "positioning": "商贸与资金单据中枢,向 MES 发起实物收发",
     "problem": "采购销售单据审批、来票付款控制、订单执行进度回写",
     "features": ["采购申请(BPM)→订单(自动/手动生成)", "确认收货→MES到货通知", "入库/退货数量状态回写", "采购来票(金额≤订单)", "付款单(必须关联来票·BPM)", "销售订单(BPM·审批通过自动建MPS)", "出库状态回写", "收款单(关联CRM客户)", "结算账户", "AI发票OCR"],
     "objects": ["采购申请", "采购订单", "采购来票", "付款单", "销售订单", "收款单", "结算账户"],
     "roles": ["采购员", "销售员", "出纳", "会计"],
     "upstream": ["CRM(合同)", "SRM(定标)", "主数据"], "downstream": ["生产(MPS)", "仓储(收发)"],
     "coreTables": ["erp_purchase_request", "erp_purchase_order", "erp_purchase_invoice", "erp_finance_payment", "erp_sale_order", "erp_finance_receipt", "erp_account"]},
    {"key": "fin", "name": "财务报销(独立)", "color": "#3ec9a7", "tag": "FIN", "flow": "finance",
     "positioning": "独立的发票报销单据与归档",
     "problem": "纸质发票难归集、报销审批无留痕、归档后无法追溯",
     "features": ["报销单(主表 + 1..n 明细行)", "发票附件多文件上传(附件中心多态关联 storage_attachment)", "单号 Redis 生成 BX+yyyyMMdd+6位流水·每日重置", "明细随主表一次性提交(先删后增全量覆盖)", "BPM 审批(通过20/驳回35/取消退回草稿0)", "已驳回可改发票后重新提交", "归档终态 40 并记录归档时间", "费用类型/报销状态双字典 + Excel 导出", "对外提供 FinanceReimbursementApi(yudao-module-finance-api)供课题管理经费拨付自动建单并提交(1 行明细·费用类型=其他·金额一致)"],
     "objects": ["报销单", "报销明细"],
     "roles": ["报销人", "财务", "审批人"],
     "upstream": ["课题管理(经费拨付 1:1 自动生成报销单·走 yudao-module-finance-api·本地 Bean 调用)"], "downstream": [],
     "coreTables": ["finance_reimbursement", "finance_reimbursement_item"]},
    {"key": "pro", "name": "生产管理", "color": "#e0b34f", "tag": "MES-PRO", "flow": "production",
     "positioning": "生产执行核心:计划-工单-任务-报工",
     "problem": "订单到车间执行的逐层拆解与过程可控",
     "features": ["MPS主生产计划(可由销售订单自动创建·合并下发)", "工单(按工艺路线自动生成工序任务)", "报工(按工序顺序控制·BPM审批)", "倒冲扣线边库/产出入库", "安灯", "工艺路线·工序·工单BOM", "研发设计项目(PD·审批归档)", "AI 四类预测"],
     "objects": ["MPS", "生产工单", "生产任务", "报工单", "工艺路线", "工单BOM", "设计项目"],
     "roles": ["计划员", "车间主任", "操作工", "工艺员"],
     "upstream": ["进销存(销售订单)"], "downstream": ["仓储(领料/入库)", "质量(IPQC)"],
     "coreTables": ["mes_pro_mps", "mes_pro_work_order", "mes_pro_task", "mes_pro_feedback", "mes_pro_route", "mes_pro_process", "mes_pro_work_order_bom", "mes_pd_project"]},
    {"key": "wm", "name": "仓储管理", "color": "#7e6bd6", "tag": "MES-WM", "flow": "warehouse",
     "positioning": "厂内 WMS:单据驱动的库存账实一致",
     "problem": "多来源出入库、批次/SN 追溯、库存精确到库位",
     "features": ["15种出入库单据", "库存台账(物料×仓库×库区×库位×批次×供应商)", "事务流水只读留痕", "调拨成对事务", "盘点(静态/动态·盲盘·盘盈盘亏调整)", "批次/SN/条码/装箱嵌套", "虚拟线边库(允许负库存)", "库存占用(5类)", "四级冻结", "WMS审批配置+BPM", "对外 WmsStockApi/WmsTransactionApi"],
     "objects": ["到货通知", "采购入库", "领料", "完工入库", "销售出库", "发货通知", "销售退货", "供应商退货", "调拨", "盘点", "库存台账", "批次", "SN"],
     "roles": ["仓管员", "拣货员", "盘点员"],
     "upstream": ["采购(收货)", "生产(领退料)", "销售(发货)", "售后(退货)"], "downstream": ["进销存(数量回写)", "财务(成本基础)"],
     "coreTables": ["mes_wm_warehouse", "mes_wm_material_stock", "mes_wm_transaction", "mes_wm_item_receipt", "mes_wm_product_issue", "mes_wm_product_receipt", "mes_wm_product_sales", "mes_wm_return_sales", "mes_wm_transfer", "mes_wm_stock_taking_task", "mes_wm_batch", "mes_wm_sn", "mes_wm_stock_reserve"]},
    {"key": "qc", "name": "质量管理", "color": "#e06c6c", "tag": "MES-QC", "flow": "quality",
     "positioning": "四道检验闸口 + 不合格品闭环处置",
     "problem": "来料/过程/出货/退货检验标准化,不合格可追溯处置",
     "features": ["IQC 来料(到货/外协)", "IPQC 过程(联动报工)", "OQC 出货(联动销售出库)", "RQC 退货(联动退料/售后)", "质检方案·指标·缺陷库", "检测结果:合格/特采/退货/报废", "NCR:评审→处置(退货/报废/返工/让步/降级)→关闭", "AI 判定建议"],
     "objects": ["IQC", "IPQC", "OQC", "RQC", "质检方案", "NCR", "缺陷记录"],
     "roles": ["质检员(IQC/IPQC/OQC)", "质量工程师"],
     "upstream": ["仓储(到货)", "生产(报工)", "销售(出库)", "售后(退货)"], "downstream": ["仓储(合格入库/报废出库)", "生产(NCR返工)"],
     "coreTables": ["mes_qc_iqc", "mes_qc_ipqc", "mes_qc_oqc", "mes_qc_rqc", "mes_qc_template", "mes_qc_indicator", "mes_qc_defect", "mes_qc_ncr"]},
    {"key": "qcreport", "name": "智能质检报告", "color": "#b06ce0", "tag": "QC-REPORT", "flow": "quality",
     "positioning": "可视化设计报告模板 → 一键出件并冻结历史报告",
     "problem": "各行业质检报告格式各异,排版硬编码改一次发一次版;且历史报告必须可复现、不随业务数据变化",
     "features": ["GrapesJS 可视化设计器(质检组件走注册机制,不硬编码进编辑器)", "模板 + 多版本:草稿0/已发布1/已停用2,已发布版本内容不可覆盖", "组件数据绑定与独立判定规则引擎(禁用 eval / ScriptEngine)", "与前端逐字对齐的 Java 渲染引擎,出站 HTML 剥离 on* 事件属性与 javascript:/vbscript:/data: 协议", "出件接口:preview 只渲染不落库不消耗编号 / generate 渲染+落库+冻结 data_snapshot / regenerate 用冻结快照与同一版本重渲", "报告编号 QR+yyyyMMdd+4位流水,按天独立,可显式指定(撞号会报出具体编号)", "缺口清单只在 preview/generate/regenerate 响应里返回,不落库", "PDF(Playwright) 归档与 MES 四种质检单归一化对接待后续轮次"],
     "objects": ["报告模板", "报告实例"],
     "roles": ["报告设计员", "质检员(出件)"],
     "upstream": ["质量(质检单上下文,本轮由调用方传入)"], "downstream": ["附件中心(PDF 归档·后续轮次)", "BI"],
     "coreTables": ["qc_report_template", "qc_report_template_version", "qc_report_instance", "qc_report_data_source", "qc_report_rule", "qc_report_render_record"]},
    {"key": "dv", "name": "设备与工装排班", "color": "#59a8b8", "tag": "MES-DV/TM/CAL", "flow": null,
     "positioning": "设备资产健康与生产资源保障",
     "problem": "设备点检保养维修可执行、工装状态可控、班组排班有依据",
     "features": ["设备台账(停机/生产中/保养中)", "点检·保养方案与记录", "维修工单(草稿→维修→待验收→已确认)", "计量器具检定", "工装夹具(在库/领用/维修/报废)", "班组·班次·排班计划·节假日"],
     "objects": ["设备", "点检记录", "维修工单", "工装", "排班计划"],
     "roles": ["设备员", "维修工", "工装管理员", "排班员"],
     "upstream": [], "downstream": ["生产(AI 风险预测消费设备数据)", "薪酬(排班工时)"],
     "coreTables": ["mes_dv_machinery", "mes_dv_check_plan", "mes_dv_check_record", "mes_dv_repair", "mes_tm_tool", "mes_cal_plan", "mes_cal_team", "mes_cal_shift"]},
    {"key": "aftersales", "name": "售后服务", "color": "#c78bd6", "tag": "AFTER", "flow": "aftersales",
     "positioning": "客户反馈到退货/维修/退款闭环",
     "problem": "售后工单流转、退货与质检入库退款的跨系统联动",
     "features": ["工单六态(草稿→处理中→待入库→待退款→完结→关闭)", "问题判定(一般/维修/退货)", "维修(现场/返厂/远程)", "退货申请→自动建 MES 销售退货单", "质检结论/入库/退款三重回调", "行级数据权限四视图"],
     "objects": ["售后工单", "维修记录", "退货申请"],
     "roles": ["客服", "维修工程师", "售后管理员"],
     "upstream": ["CRM(客户)", "ERP(销售订单)"], "downstream": ["仓储(销售退货)", "质量(RQC)", "财务(退款)"],
     "coreTables": ["after_sale_ticket", "after_sale_ticket_item", "after_sale_repair", "after_sale_return", "after_sale_permission"]},
    {"key": "hrm", "name": "人力资源", "color": "#9db85a", "tag": "HRM", "flow": null,
     "positioning": "员工入转调离与考勤薪酬",
     "problem": "人事流程审批化、离职数据不丢(交接)、算薪自动化",
     "features": ["员工档案(入职自动建系统账号)", "请假/加班/调岗/离职 BPM 审批", "离职交接映射 expandUserIds", "考勤(打卡·规则·排班·补卡审批·月度汇总)", "薪酬(应发-社保公积金-个税-请假扣款·1.5/2/3倍加班费·发放台账)", "培训证书(SQL先行待实现)"],
     "objects": ["员工", "请假单", "离职申请", "交接映射", "考勤记录", "薪酬核算"],
     "roles": ["HR", "部门主管", "员工本人"],
     "upstream": [], "downstream": ["各模块(数据可见性扩展)", "system(账号)"],
     "coreTables": ["hrm_employee", "hrm_leave_application", "hrm_resignation_application", "hrm_user_handover", "hrm_attendance_record", "hrm_salary_calculation", "hrm_salary_payment"]},
    {"key": "ums", "name": "会员管理(独立)", "color": "#d6955c", "tag": "UMS", "flow": "ums",
     "positioning": "学会/协会型会员全生命周期:入会申请审批 → 自动建档 → 会员列表与通讯录",
     "problem": "会员入会靠线下表格与人工建档,会员等级口径混乱,会员信息无法按地区/行业检索",
     "features": ["入会申请单(个人/单位二型·字段分叉)", "申请等级两级口径(个人 ums_personal_grade / 单位 ums_unit_grade)", "申请编号 SQ+yyyyMMdd+6位流水·会员编号 HY+同日流水(Redis 每日重置)", "BPM 两级审批(秘书处初审→理事会审议)·状态 0草稿/15审批中/20已通过/35已驳回", "审批通过自动建会员 + 自动建系统登录账号(AdminUserApi.createUser·用户名与初始密码=联系电话)", "会员等级与类型匹配校验(单位会员不可选『理事长』)", "会员通讯录只读检索(按地区编码/行业)", "证明材料走 system 附件中心(recordType=ums_application / ums_member)"],
     "objects": ["入会申请单", "会员"],
     "roles": ["入会申请人", "会员管理员", "秘书处(初审)", "理事会(审议)"],
     "upstream": [], "downstream": ["system(自动创建登录账号)"],
     "coreTables": ["ums_application", "ums_member"]},
    {"key": "res", "name": "课题管理(独立)", "color": "#e070a8", "tag": "RES", "flow": "research",
     "positioning": "学会/协会型科研课题全生命周期:指南发布 → 在线申报 → 专家评审 → 立项审批 → 经费拨付(立项通知走系统公告)",
     "problem": "课题指南靠线下发布、申报书靠纸质或邮件收集、专家评分散落在表格里无法自动汇总排名、立项与拨款无留痕",
     "features": ["课题指南(重点1/一般2 分类标注)", "指南状态 0草稿/20已发布/30已截止·发布/截止两个动作", "指南编号 GL+yyyyMMdd+6位流水(Redis 每日重置)", "在线申报书(选指南+选会员带出申报人信息+课题组成员+申请经费+申报书正文)", "申报编号 SB+同日流水·类别冗余自指南不信任前端", "申报状态 0草稿/15审批中/20已立项/35已驳回", "提交前校验指南已发布且在申报期内(逐条报具体维度与时间)", "专家评审明细子表(一位专家一行·四项加权 0.3/0.3/0.2/0.2)", "后端自动重算加权合计与排名·回写主单均分与推荐结论", "BPM 三级审批 research_topic_approve(秘书处形式审查→专家评审→学会立项审批)", "审批通过生成立项编号 LX+同日流水 与立项时间(幂等)", "发布立项通知(生成 system_notice 公告·写回 noticeId 防重复)", "经费拨付记录(批次/金额/日期/收款单位/备注/凭证附件)·凭证必填·金额不得超申请经费", "经费拨付 1:1 自动生成并提交财务报销单(FinanceReimbursementApi·1 行明细·费用类型=其他)", "拨付状态 0待拨付/15报销审批中/20已拨付/30已驳回·由 BPM 监听器回写(含驳回原因与时间)", "已驳回可改金额/凭证后重新发起报销·待拨付可删并联动清理财务侧草稿报销单", "附件走 system 附件中心(recordType=research_guideline / research_application / research_fund_record)"],
     "objects": ["课题指南", "课题申报书", "专家评审明细", "经费拨付记录"],
     "roles": ["课题申报人(会员)", "课题管理员", "评审专家", "学会秘书处", "学会理事会", "学会财务"],
     "upstream": ["会员管理(ums_member 取申报人姓名/单位/职称/电话)", "BPM(流程定义 research_topic_approve / finance_reimbursement_approve)"],
     "downstream": ["system(system_notice 立项通知公告)", "system(附件中心)", "财务(经费拨付生成 finance_reimbursement 报销单·已对接)"],
     "coreTables": ["research_guideline", "research_application", "research_topic_review", "research_fund_record"]},
    {"key": "bpm", "name": "协同审批", "color": "#e6a23c", "tag": "BPM", "flow": null,
     "positioning": "全系统单据审批中枢(Flowable)",
     "problem": "多模块单据需要灵活可配的多级审批",
     "features": ["BPMN+钉钉风格双设计器", "流程定义部署", "审批事件统一回调(BpmProcessInstanceStatusEvent)", "15个业务监听器(同一流程 key 允许多个监听器并行·各自按 key 过滤)", "表单·用户组·抄送·加签"],
     "objects": ["流程模型", "流程实例", "审批任务"],
     "roles": ["审批人(按流程配置)", "流程管理员"],
     "upstream": ["各业务提交审批"], "downstream": ["回调更新各业务单据状态"],
     "coreTables": ["bpm_process_definition_info", "bpm_process_instance", "bpm_task", "act_*", "flowable_*"]},
    {"key": "ai", "name": "AI 智能", "color": "#56c8d8", "tag": "AI", "flow": "ai",
     "positioning": "单据录入提效与生产决策辅助",
     "problem": "人工录发票/报价慢、质检与排产依赖经验",
     "features": ["chat/chatWithImage 双通道", "OCR 预填(发票/报价)", "质检判定建议", "缺料/时长/风险/交付四类预测", "RAG 知识库(Milvus)", "AI不可用时 fallback 人工"],
     "objects": ["知识库", "AI模型配置", "对话记录"],
     "roles": ["采购员", "销售", "质检员", "计划员"],
     "upstream": ["各业务提供上下文数据"], "downstream": ["结构化JSON建议返回业务侧"],
     "coreTables": ["ai_chat_conversation", "ai_chat_message", "ai_model", "ai_knowledge", "ai_knowledge_document", "ai_knowledge_segment"]}
  ],
  "platform": {
    "name": "系统底座",
    "capabilities": ["JWT+OAuth2 认证·滑块验证码", "RBAC 角色-菜单-数据范围(全部/部门/本部门及下级/仅本人/自定义)", "字典", "附件中心(storage-blob 文件 + storage-attachment 按 recordType+recordId 多态关联,全模块强制使用)", "编码规则(各业务自实现 Redis 前缀+日期+流水)", "Quartz 定时任务·RocketMQ/Kafka/Redis Stream MQ·WebSocket", "多租户已关闭", "实际 system_role 仅 4 个:超级管理员/普通角色/CRM管理员/租户管理员,业务分工靠数据权限而非多角色"],
    "unused": ["im 模块(IM/RTC,业务未消费)", "member 模块(C端商城会员,遗留;与 UMS 学会会员无关,勿混用)"]
  }
}</script>
<script type="application/json" id="data-objects">{
  "objects": [
    {"name": "物料", "module": "基础主数据", "table": "mdm_item", "doc": "MDM 权威物料/产品/半成品档案,含 SKU、批次属性、价格、安全库存", "upstream": ["MES 物料反向同步"], "downstream": ["报价单", "销售订单", "采购订单", "工单BOM", "库存台账"], "keywords": "物料 产品 原材料 item"},
    {"name": "仓库", "module": "基础主数据", "table": "mdm_warehouse", "doc": "仓库主数据,type: 1原料/2成品/3半成品/4线边;MES 自有 mes_wm_warehouse 承载库区库位", "upstream": [], "downstream": ["库存台账", "出入库单据"], "keywords": "仓库 warehouse 原料仓 成品仓 线边仓"},
    {"name": "客户", "module": "销售管理", "table": "crm_customer", "doc": "客户主档,公海池/锁定/成交状态,负责人数据权限", "upstream": ["线索"], "downstream": ["联系人", "商机", "报价单", "合同", "售后工单", "收款单"], "keywords": "客户 customer 公海"},
    {"name": "线索", "module": "销售管理", "table": "crm_clue", "doc": "原始销售线索,仅一次转化为客户", "upstream": [], "downstream": ["客户"], "keywords": "线索 clue"},
    {"name": "联系人", "module": "销售管理", "table": "crm_contact", "doc": "客户下联系人,与商机多对多", "upstream": ["客户"], "downstream": ["商机"], "keywords": "联系人 contact"},
    {"name": "商机", "module": "销售管理", "table": "crm_business", "doc": "销售机会,DB 配置式阶段推进,结束态: 赢/输/无效", "upstream": ["客户"], "downstream": ["报价单", "合同"], "keywords": "商机 business 赢单 输单 阶段"},
    {"name": "报价单", "module": "销售管理", "table": "crm_sale_quotation", "doc": "QT编号,选 MDM 物料报价,AI OCR 录入,草稿→已转合同", "upstream": ["商机"], "downstream": ["合同"], "keywords": "报价 quotation QT"},
    {"name": "合同", "module": "销售管理", "table": "crm_contract", "doc": "HT编号,BPM 审批(0草稿/10审批中/20通过/30驳回/40取消/50作废),通过后可生成销售订单", "upstream": ["报价单"], "downstream": ["销售订单", "回款计划", "开票"], "keywords": "合同 contract 审批"},
    {"name": "发票", "module": "销售管理", "table": "crm_invoice", "doc": "KP编号,BPM 审批,累计开票≤合同额,附件必传", "upstream": ["合同"], "downstream": ["回款"], "keywords": "开票 发票 invoice"},
    {"name": "回款", "module": "销售管理", "table": "crm_receivable", "doc": "HK编号,创建即审批中(手动审核),强制先开票,金额双校验", "upstream": ["合同", "发票"], "downstream": ["收款单(关联)"], "keywords": "回款 收款计划 receivable"},
    {"name": "销售目标", "module": "销售管理", "table": "crm_sales_target", "doc": "年/季/月×个人/部门/产品维度的合同回款等目标", "upstream": [], "downstream": ["业绩统计"], "keywords": "目标 target 业绩"},
    {"name": "供应商", "module": "供应商管理", "table": "srm_supplier", "doc": "供应商档案:执照/税号/银行/联系人,ERP 与 MES 共用", "upstream": ["准入申请"], "downstream": ["采购订单", "到货通知", "供应商退货", "绩效评价"], "keywords": "供应商 supplier vendor"},
    {"name": "准入申请", "module": "供应商管理", "table": "srm_supplier_apply", "doc": "AP编号,BPM 审批(采购/质量/财务),通过自动建供应商档案", "upstream": [], "downstream": ["供应商"], "keywords": "准入 供应商申请 apply"},
    {"name": "招标项目", "module": "供应商管理", "table": "srm_tender_project", "doc": "完整版(发布→投标→开标→评标→定标)与简易比价两种模式", "upstream": [], "downstream": ["定标→采购订单"], "keywords": "招标 投标 比价 定标 tender 寻源"},
    {"name": "采购申请", "module": "进销存", "table": "erp_purchase_request", "doc": "CGSQ编号,BPM 审批,通过后有供应商自动生成订单", "upstream": ["生产缺口(人工)"], "downstream": ["采购订单"], "keywords": "采购申请 请购 request"},
    {"name": "采购订单", "module": "进销存", "table": "erp_purchase_order", "doc": "CGDD编号,BPM 审批(15审批中/20已审核),入库状态:未0部分1全部2", "upstream": ["采购申请", "定标"], "downstream": ["到货通知", "采购来票", "供应商退货(约束物料)"], "keywords": "采购订单 purchase"},
    {"name": "采购来票", "module": "进销存", "table": "erp_purchase_invoice", "doc": "FPD编号,AI OCR 预填,累计≤订单总价,付款单的前置", "upstream": ["采购订单"], "downstream": ["付款单"], "keywords": "来票 发票 采购发票"},
    {"name": "付款单", "module": "财务", "table": "erp_finance_payment", "doc": "FKD编号,必须关联来票+附件,累计≤来票金额,BPM 审批", "upstream": ["采购来票"], "downstream": ["结算账户"], "keywords": "付款 应付 payment"},
    {"name": "收款单", "module": "财务", "table": "erp_finance_receipt", "doc": "SKD编号,关联 CRM 客户校验,无 BPM(手动审核)", "upstream": ["销售(弱关联)"], "downstream": ["结算账户"], "keywords": "收款 应收 receipt"},
    {"name": "销售订单", "module": "进销存", "table": "erp_sale_order", "doc": "XSDD编号,BPM 审批,需生产行自动建 MPS,出库状态:未0已预留1部分2全部3,生产完成事件回写", "upstream": ["合同"], "downstream": ["MPS", "发货通知", "销售出库", "售后工单"], "keywords": "销售订单 sale"},
    {"name": "主生产计划", "module": "生产", "table": "mes_pro_mps", "doc": "MPS计划,草稿0→已下发1→完成2/取消3,可合并下发", "upstream": ["销售订单"], "downstream": ["生产工单"], "keywords": "MPS 主计划 生产计划"},
    {"name": "生产工单", "module": "生产", "table": "mes_pro_work_order", "doc": "草稿0→已确认1→完成2/取消3,确认时按工艺路线生成任务,最后一道工序报工回写已生产数量", "upstream": ["MPS"], "downstream": ["生产任务", "领料", "完工入库"], "keywords": "工单 生产工单 work order"},
    {"name": "生产任务", "module": "生产", "table": "mes_pro_task", "doc": "工序粒度,预备0→生产中1→部分2→完成4,可暂停9取消5", "upstream": ["生产工单"], "downstream": ["报工"], "keywords": "任务 task 工序"},
    {"name": "报工单", "module": "生产", "table": "mes_pro_feedback", "doc": "草稿0→审批中2→待检3/完成4,按工序顺序校验(上道工序报满才可报),联动倒冲消耗/产出入库/IPQC", "upstream": ["生产任务"], "downstream": ["产品产出", "物料消耗", "IPQC"], "keywords": "报工 feedback"},
    {"name": "工艺路线", "module": "生产", "table": "mes_pro_route", "doc": "工序顺序+质检/关键/倒冲标记,驱动工单任务与报工规则", "upstream": [], "downstream": ["生产工单"], "keywords": "工艺 路线 route 工序"},
    {"name": "到货通知单", "module": "仓储", "table": "mes_wm_arrival_notice", "doc": "采购订单确认收货生成,草稿0→待质检2→待入库3→完成4", "upstream": ["采购订单"], "downstream": ["IQC", "采购入库"], "keywords": "到货 收货 arrival"},
    {"name": "采购入库单", "module": "仓储", "table": "mes_wm_item_receipt", "doc": "草稿0→待上架2→待执行3→完成4,完成后库存+并回写 ERP inCount/inStatus", "upstream": ["到货通知"], "downstream": ["库存台账", "采购订单(回写)"], "keywords": "采购入库 收货入库 receipt"},
    {"name": "生产领料单", "module": "仓储", "table": "mes_wm_product_issue", "doc": "草稿0→待拣货2→待执行3→完成4,库存-", "upstream": ["生产工单"], "downstream": ["库存台账"], "keywords": "领料 issue"},
    {"name": "销售出库单", "module": "仓储", "table": "mes_wm_product_sales", "doc": "发货通知审核后生成,提交→OQC检测→拣货→发运10→待执行3→完成4,回写销售订单出库数与状态", "upstream": ["发货通知", "销售订单"], "downstream": ["库存台账", "OQC"], "keywords": "销售出库 发货 出库单 sales"},
    {"name": "销售退货单", "module": "仓储", "table": "mes_wm_return_sales", "doc": "草稿→待检验(RQC)→待上架→待入库→完成,售后与手工两来源", "upstream": ["售后退货申请"], "downstream": ["库存台账", "RQC"], "keywords": "销售退货 退货入库"},
    {"name": "供应商退货单", "module": "仓储", "table": "mes_wm_return_vendor", "doc": "IQC 不合格退货发起,待拣货→待执行→完成,库存-", "upstream": ["IQC"], "downstream": ["库存台账", "采购订单(returnCount)"], "keywords": "供应商退货 退料 退货 vendor"},
    {"name": "调拨单", "module": "仓储", "table": "mes_wm_transfer", "doc": "内部/外部调拨,成对 MOVE_OUT+MOVE_IN 事务,在途数量", "upstream": [], "downstream": ["库存台账"], "keywords": "调拨 移库 transfer"},
    {"name": "盘点任务", "module": "仓储", "table": "mes_wm_stock_taking_task", "doc": "静态/动态盘点,行结果:正常/盘盈(ADJUST_IN)/盘亏(ADJUST_OUT),可冻结库存、盲盘", "upstream": ["库存台账"], "downstream": ["库存台账(调整)"], "keywords": "盘点 盘盈 盘亏 stock taking"},
    {"name": "库存台账", "module": "仓储", "table": "mes_wm_material_stock", "doc": "物料×仓库×库区×库位×批次×供应商六元组,数量/冻结/占用/在途四量", "upstream": ["全部入库单"], "downstream": ["全部出库单", "事务流水"], "keywords": "库存 台账 stock 可用量"},
    {"name": "批次", "module": "仓储", "table": "mes_wm_batch", "doc": "关联物料/工单/供应商/客户/质量状态;SN 码含批次 uuid;条码支持18种业务对象", "upstream": ["入库单据"], "downstream": ["库存台账"], "keywords": "批次 batch SN 条码 追溯"},
    {"name": "IQC来料检验", "module": "质量", "table": "mes_qc_iqc", "doc": "到货通知/外协入库触发,草稿0→完成4,结论回写来源单据;行操作「出报告」仅在已完成时可见", "upstream": ["到货通知", "外协入库"], "downstream": ["采购入库", "供应商退货", "NCR", "报告实例(出报告)"], "keywords": "IQC 来料检验 出报告"},
    {"name": "IPQC过程检验", "module": "质量", "table": "mes_qc_ipqc", "doc": "报工质检工序产出行触发,完成回调推进报工与产出入库;行操作「出报告」仅在已完成时可见", "upstream": ["报工"], "downstream": ["产出入库", "NCR", "报告实例(出报告)"], "keywords": "IPQC 过程检验 巡检 出报告"},
    {"name": "OQC出货检验", "module": "质量", "table": "mes_qc_oqc", "doc": "销售出库提交触发,完成推进出库单待拣货;行操作「出报告」仅在已完成时可见", "upstream": ["销售出库单"], "downstream": ["销售出库单(推进)", "报告实例(出报告)"], "keywords": "OQC 出货检验 出厂检验 出报告"},
    {"name": "RQC退货检验", "module": "质量", "table": "mes_qc_rqc", "doc": "生产退料/销售退货/售后退货触发,结论回调售后;行操作「出报告」仅在已完成时可见。措辞已统一为「退货检验」(原菜单父项与 mes_qc_type 字典写「退料检验」,与后端枚举/表注释/菜单权限子项不一致;RQC 三者都覆盖,故取「退货检验」)", "upstream": ["退料", "退货"], "downstream": ["退货入库", "售后回调", "报告实例(出报告)"], "keywords": "RQC 退货检验 退料检验 出报告"},
    {"name": "NCR不合格品", "module": "质量", "table": "mes_qc_ncr", "doc": "待评审0→已评审6→已处置7→已关闭8,处置:退货/报废/返工/让步接收/降级", "upstream": ["IQC/IPQC/OQC/RQC"], "downstream": ["退货/报废/返工(台账)"], "keywords": "NCR 不合格 处置 特采 报废"},
    {"name": "设备", "module": "设备", "table": "mes_dv_machinery", "doc": "台账状态:停机/生产中/保养中;点检·保养·维修工单;AI 风险预测消费其数据", "upstream": [], "downstream": ["点检", "维修", "生产风险预测"], "keywords": "设备 点检 保养 维修 machinery"},
    {"name": "售后工单", "module": "售后", "table": "after_sale_ticket", "doc": "草稿0→处理中10→待入库30→待退款40→完结50→关闭60/取消99,判定:一般/维修/退货", "upstream": ["客户反馈(人工录入)"], "downstream": ["维修记录", "退货申请"], "keywords": "售后 工单 ticket 维修"},
    {"name": "退货申请", "module": "售后", "table": "after_sale_return", "doc": "草稿0→已审核20(自动建MES退货单)→已质检40→已入库50→已退款60→关闭70;类型:退款退货/换货/仅退款", "upstream": ["售后工单"], "downstream": ["销售退货单", "退款"], "keywords": "售后退货 return"},
    {"name": "员工", "module": "人力", "table": "hrm_employee", "doc": "在职1/试用2/离职3,入职自动建系统账号", "upstream": [], "downstream": ["交接映射", "考勤", "薪酬"], "keywords": "员工 入职 employee"},
    {"name": "离职交接", "module": "人力", "table": "hrm_user_handover", "doc": "from→to 映射,已完成20 后 expandUserIds 扩展交接人可见数据(当前各业务模块尚未实际接入调用)", "upstream": ["离职申请"], "downstream": ["各模块我的数据查询"], "keywords": "交接 离职 handover expandUserIds"},
    {"name": "排班计划", "module": "设备", "table": "mes_cal_plan", "doc": "班组×班次×日期排班,供生产与考勤引用", "upstream": [], "downstream": ["生产任务", "考勤"], "keywords": "排班 班组 班次 日历"},
    {"name": "报告模板", "module": "质检报告", "table": "qc_report_template", "doc": "智能质检报告模板,启用0/停用1;画布内容存在 qc_report_template_version(草稿0/已发布1/已停用2,已发布不可覆盖),currentVersion 指向当前已发布版本;源文件经 AI 导入识别成草稿后由人工确认才成为版本;同一模板重复导入按【替换】处理——先删旧附件(连带 blob 行与磁盘文件)再绑新的,不留孤儿文件;删除模板会连带清附件", "upstream": ["人工设计(GrapesJS 设计器)", "AI 导入草稿(qc-report/ai-import/draft)"], "downstream": ["模板版本", "报告实例"], "keywords": "报告模板 质检报告 template 设计器 版本 AI导入 识别 草稿 源文件替换"},
    {"name": "报告实例", "module": "质检报告", "table": "qc_report_instance", "doc": "出件产物,编号 QR+yyyyMMdd+4位流水按天独立且业务唯一,可显式指定覆盖(注意 uk_report_no 不区分软删除,删掉的号不回收);两条出件入口:通用 generate(调用方自带 context)与 generate-from-qc(数据从 MES 质检单取,权限码 qc-report:instance:generate 已入 system_menu id=1075416,五道校验按业务优先级只报第一处:105_000 类型非法 / 105_001 单据不存在 / 105_002 未完成 / 105_003 未判定 / 105_005 模板类型不符 / 105_006 无可用检验项;四张质检单列表的行操作「出报告」仅在 status=已完成时可见,弹窗只列 reportType 一致、模板启用、且 currentVersion 指向的版本已发布的模板(后端 /qc-report/template/selectable 判定——停用版本不清空 currentVersion,前端拿分页结果自己推导会漏),结果就地展示并跳本页列表);冻结 data_snapshot(判定后数据,含后端算好的 PASS/FAIL 与合格率)与 render_html;regenerate 只重渲排版、数据与编号不变;business_id/business_type 存 MES 表名(mes_qc_iqc 等)与单据 ID,可反查、一单可出多份;报告头另带单据原判定 qcResult(数)/qcResultText(中文,合格/特采/不合格退货/不合格报废),与引擎三态判定并存互不覆盖;响应带 undecidableCount=快照中 result 为空串的项数与 warnings(分组标题跳过/指标已删/未录实测值/样品超3),两者都不落库、事后看不到", "upstream": ["报告模板版本", "质检单上下文(通用 generate 由调用方传入)", "MES 质检单 IQC/IPQC/OQC/RQC(generate-from-qc 经 MesQcReportApi 取数)"], "downstream": ["前端实例页 /qc/report-instance(列表·iframe预览·快照页签·导出PDF)", "PDF归档(system_storage_attachment recordType=qc_report_instance)"], "keywords": "报告实例 出件 QR编号 快照 复现 regenerate PDF归档 报告实例页 导出PDF generate-from-qc 质检单出报告 出报告弹窗 qcResult undecidableCount warnings 待判定 反查 业务单据筛选"},
    {"name": "报销单", "module": "财务报销", "table": "finance_reimbursement", "doc": "单号可自定义(中间不允许空格,与已有单号重复则报错);新增时留空或纯空格由系统生成 BX+yyyyMMdd+6位流水(Redis 每日重置),修改时留空表示保持不变。status: 0草稿/15审批中/20已通过/35已驳回/40已归档;仅草稿可删、草稿与已驳回可改、仅已通过可归档;提交后由 BPM 监听器按结果回写状态(取消退回草稿),审批中 processInstanceId 被回写。来源两条:① 财务模块页面人工填报 ②课题管理「经费拨付」经 FinanceReimbursementApi(yudao-module-finance-api)自动建单并立即提交(1:1、金额一致、系统固定 1 行明细·费用类型=其他)——后者的审批结果由课题侧 ResearchFundStatusListener 订阅同一流程回写拨款台账,报销单本身不回写任何外部单据", "upstream": ["课题管理(经费拨付·经 FinanceReimbursementApi 自动建单)", "人工填报"], "downstream": ["报销明细", "发票附件(storage_attachment·recordType=finance_reimbursement)", "课题拨款台账(仅经 BPM 事件回写状态)"], "keywords": "报销单 报销 发票 reimbursement BX 归档 财务 费用 自定义单号 课题经费拨付 自动建单 FinanceReimbursementApi"},
    {"name": "报销明细", "module": "财务报销", "table": "finance_reimbursement_item", "doc": "报销单的费用行:费用类型(1差旅/2办公/3招待/4交通/5其他)、发票号码、发票金额、发票日期、开票方、费用说明;随主表一次性提交(先删后增全量覆盖),提交时必须剥掉行 id 与 reimbursementId —— 明细删除是软删,回传原 id 会与已软删行主键冲突", "upstream": ["报销单"], "downstream": [], "keywords": "报销明细 费用明细 费用类型 发票金额 item 明细行 差旅"},
    {"name": "入会申请单", "module": "会员管理", "table": "ums_application", "doc": "单号 SQ+yyyyMMdd+6位流水(Redis 每日重置),后端生成不可自定义;memberType 1个人/2单位决定字段分叉(个人用 workUnit/title,单位用 legalPerson/organizationCode);memberGrade 按类型取不同字典;status: 0草稿/15审批中/20已通过/35已驳回。仅草稿可删(连带清附件)、草稿与已驳回可改可重提;提交后由 BPM 监听器按结果回写(取消退回草稿),审批中回写 processInstanceId;审批通过触发自动建会员与系统账号。独立模块,不与其它业务模块产生单据关联", "upstream": ["人工填报(申请人)", "证明材料附件(storage_attachment·recordType=ums_application)"], "downstream": ["会员(审批通过自动创建)", "BPM 流程实例(ums_membership_approve 分类)"], "keywords": "入会申请 入会 会员申请 application SQ 秘书处 理事会 审批 会员类型 个人 单位 法定代表人 研究方向 申请等级"},
    {"name": "会员", "module": "会员管理", "table": "ums_member", "doc": "单号 HY+yyyyMMdd+6位流水(Redis 每日重置);两个来源:①入会申请审批通过自动创建(回填 applicationId 与 userName/系统账号) ②会员列表手工新增。status: 1正常/0暂停;memberGrade 按 memberType 取对应字典,校验规则是单位会员(type=2)不可选『理事长』(grade=5)。自动建档时入会日期=当天、不搬运 contactUser/unitIntro/expireDate(申请单无对应字段);自动建的系统账号用户名为联系电话且初始密码同为联系电话,部门固定为「学会会员」;手机号重复会明确报错且整单事务回滚", "upstream": ["入会申请单(审批通过)", "人工新增", "附件(storage_attachment·recordType=ums_member)"], "downstream": ["会员通讯录(按 regionCode/industry 检索)", "system_users(登录账号)"], "keywords": "会员 member HY 会员编号 会员等级 会员类型 通讯录 地区 行业 入会日期 有效期"},
    {"name": "课题指南", "module": "课题管理", "table": "research_guideline", "doc": "指南编号 GL+yyyyMMdd+6位流水(Redis 每日重置);category 1重点课题/2一般课题;publishStatus 0草稿/20已发布/30已截止,仅草稿可改可删,publish 发布回写 publishTime,close 截止置 30;已产生申报单(applicationMapper.selectCountByGuidelineId 大于 0)的指南禁止删除并在提示中报出该指南下已有多少张申报单;正文 content 为长文本;附件 recordType=research_guideline", "upstream": ["人工维护(学会秘书处)", "指南附件(storage_attachment·recordType=research_guideline)"], "downstream": ["课题申报书(申报时引用且必须已发布并在申报期内)"], "keywords": "课题指南 指南 发布 截止 重点课题 一般课题 GL 指南编号 申报期 申报起止"},
    {"name": "课题申报书", "module": "课题管理", "table": "research_application", "doc": "申报编号 SB+yyyyMMdd+6位流水(Redis 每日重置);guidelineId 关联指南、memberId 关联 ums_member(软关联无外键,会员删除后会留下悬空 memberId 需容错显示);applicantName/workUnit/jobTitle/contactPhone/contactEmail 由选会员自动带出可手改;category 冗余自指南且不信任前端传值;budgetAmount 申请经费为拨付上限;status: 0草稿/15审批中/20已立项/35已驳回。仅草稿可删(连带清附件)、草稿与已驳回可改可重提;submit 前逐条校验「指南已发布」与「现在在申报期内」,任一不满足即拒绝并按维度给出指南名与实际值;审批通过由监听器生成立项编号 researchNo(LX+同日流水)与 approveTime(已有编号幂等跳过);reviewAvgScore/reviewPass 为评审汇总回写值,明细删空时清成未汇总;noticeId 记录已发布的立项通知公告,重复发布被拦截", "upstream": ["课题指南(必须已发布且在申报期内)", "会员(ums_member·带出申报人信息)", "申报附件(storage_attachment·recordType=research_application)"], "downstream": ["专家评审明细", "经费拨付记录", "立项通知(system_notice 公告)", "BPM 流程实例(research_topic_approve 分类)", "附件中心"], "keywords": "课题申报 申报书 申报单 SB 申报编号 立项 立项编号 LX 申请经费 申报书正文 课题组成员 专家评审 评审汇总 立项通知 发布通知 提交审批 驳回 重提"},
    {"name": "专家评审明细", "module": "课题管理", "table": "research_topic_review", "doc": "一位专家一行的一主多行子表:evaluatorName/evaluatorUnit/evaluatorTitle + topicScore 选题价值与意义(权重0.3) + planScore 研究方案可行性(0.3) + teamScore 团队与研究基础(0.2) + resultScore 预期成果与效益(0.2) + conclusion 1推荐立项/2不推荐立项 + opinion 评审意见 + reviewTime。compositeScore 由后端 weightedScore 计算、ranking 由后端按加权分降序重排,均不接受前端传值;每次增删改后调 recalculate 回写主单 reviewAvgScore(全部专家加权分均值·保留2位)与 reviewPass(均分达标 且 多数结论为推荐立项时为 1);四项分值超出 0~100 时提示指出具体维度与实际值;仅在申报单 status=15 审批中可增删改", "upstream": ["课题申报书(仅审批中可写)"], "downstream": ["课题申报书(回写 reviewAvgScore/reviewPass)"], "keywords": "专家评审 评审明细 评分 加权 权重 排名 ranking compositeScore 推荐立项 评审意见 均分"},
    {"name": "经费拨付记录", "module": "课题管理", "table": "research_fund_record", "doc": "一主多行的拨款台账:batchName 批次名称/amount 拨付金额/payDate 拨付日期/payee 收款单位/remark,凭证附件走 system 附件中心 recordType=research_fund_record(表内不存文件地址)。status: 0待拨付/15报销审批中/20已拨付/30已驳回,由系统与 BPM 驱动,新增/修改接口已移除 status 入参(防伪造「已拨付」)。登记即一条龙:落台账 → 同一批 blob 双绑 → 经 FinanceReimbursementApi 自动生成 1:1 的 finance_reimbursement 并立即提交 BPM → 回写 reimbursement_id/reimbursement_no 并置 15;审批结果由监听器回写:通过→20、驳回→30(+reject_reason/reject_time)、取消流程→退回 0(发起人撤回只退回发起节点、流程未结束,状态不变)。仅申报单 status=20 已立项可登记;凭证必填;金额校验:已登记金额(该单全部记录求和) + 本次金额 不得超出申报单 budgetAmount,超出时提示报出本次、已登记、合计、预算四项数值;仅「待拨付」可删(联动删除财务侧草稿报销单,非草稿则拒绝),「待拨付」「已驳回」可改并重新发起报销(已驳回改后清空 reject_reason/reject_time),编辑时把本行金额加回可用余额展示避免误解。与报销单为软关联(无外键),财务侧直接删单会留下悬空 reimbursement_id,此时修改报「报销单已不存在,请删除本条记录后重新登记」", "upstream": ["课题申报书(必须已立项)", "财务报销单(1:1 自动生成·软关联 reimbursement_id)"], "downstream": ["拨付凭证(storage_attachment·recordType=research_fund_record)", "财务报销单(生成并提交出账·审批结果回写本台账)"], "keywords": "经费拨付 拨款 拨付记录 批次 拨付金额 凭证 待拨付 报销审批中 已拨付 已驳回 驳回原因 财务报销单号 reimbursement_id 收款单位 资助金额 预算 超额 重新发起报销"}
  ]
}</script>
<script type="application/json" id="data-flows">{
  "flows": [
    {"id": "sales", "name": "销售流程", "mermaid": "mermaid/02-sales-flow.mmd", "color": "#4f9cff",
     "summary": "线索转化客户 → 商机推进 → 报价(可AI识别) → 合同 BPM 审批 → 生成 ERP 销售订单并审批 → 需生产行自动建 MPS → 发货通知 → 销售出库(OQC)回写 → 开票(BPM) → 回款(先开票校验)。",
     "steps": [
       {"stage": "获客", "action": "线索转化客户、建联系人、立项商机", "roles": ["销售内勤"]},
       {"stage": "报价", "action": "商机赢单后建报价单(AI OCR 预填),转合同", "roles": ["销售内勤"]},
       {"stage": "签约", "action": "合同提交 BPM 审批(可选流程定义),通过=20", "roles": ["销售", "审批人"]},
       {"stage": "接单", "action": "合同一键生成销售订单(XSDD),销售订单再走 BPM;审批通过自动为 needProduction 明细创建 MPS", "roles": ["销售", "计划员"]},
       {"stage": "交付", "action": "发货通知审核(占用)→ 销售出库 OQC → 发运 → 出库回写部分/全部出库", "roles": ["仓管", "质检"]},
       {"stage": "回款", "action": "开票 BPM(累计≤合同额) → 回款强制先开票(累计≤合同剩余与开票剩余)", "roles": ["财务", "销售"]}]},
    {"id": "purchase", "name": "采购流程", "mermaid": "mermaid/03-purchase-flow.mmd", "color": "#5fc79e",
     "summary": "SRM 准入/定标供源 → 采购申请 BPM → 采购订单 BPM → 确认收货生成 MES 到货通知 → IQC(合格/特采入库·退货/报废) → 采购入库回写订单与申请 → 来票(AI OCR) → 付款单 BPM。",
     "steps": [
       {"stage": "寻源", "action": "供应商准入(BPM)建档;招标完整版或简易比价,定标审批通过自动生成采购订单", "roles": ["采购开发"]},
       {"stage": "申请", "action": "采购申请(CGSQ)提交 BPM,驳回可重置回草稿;通过且有供应商自动生成订单", "roles": ["采购员", "审批人"]},
       {"stage": "下单", "action": "采购订单(CGDD)自动提交 BPM(15→20)", "roles": ["采购员"]},
       {"stage": "收货", "action": "确认收货生成到货通知(0→待质检2/待入库3→4);需检物料自动创建 IQC", "roles": ["仓管", "IQC 质检员"]},
       {"stage": "入库", "action": "IQC 合格/特采→采购入库(待上架→待执行→完成)库存+,回写订单 inCount/inStatus 与申请明细", "roles": ["仓管"]},
       {"stage": "退货", "action": "IQC 不合格退货→供应商退货出库(选料锁定到订单行);报废走 NCR", "roles": ["仓管"]},
       {"stage": "付款", "action": "采购来票(FPD, AI OCR, ≤订单总价) → 付款单(FKD 关联来票+附件, ≤来票金额) BPM 审批", "roles": ["财务", "审批人"]}]},
    {"id": "production", "name": "生产流程", "mermaid": "mermaid/04-production-flow.mmd", "color": "#e0b34f",
     "summary": "销售订单驱动 MPS → 下发工单 → 按工艺路线拆工序任务 → 领料/线边备料 → 报工(工序顺序硬校验+BPM) → 倒冲消耗/产出入库/IPQC → 末道工序回写进度 → 自动完工 → ProductionFinishedEvent 回写销售订单。",
     "steps": [
       {"stage": "计划", "action": "MPS 创建(手工或销售订单自动)→下发或合并下发为工单", "roles": ["计划员"]},
       {"stage": "派工", "action": "工单确认按工艺路线逐工序生成任务,可指派/暂停/取消", "roles": ["车间主任"]},
       {"stage": "备料", "action": "生产领料单出库或线边虚拟库备料", "roles": ["仓管"]},
       {"stage": "报工", "action": "提交报工:上一道工序未报满直接拦截;启用 BPM 走审批", "roles": ["操作工"]},
       {"stage": "联动", "action": "审批通过按工序标记: 倒冲→自动物料消耗扣线边库(允许负库存);质检→产出待检行等 IPQC;关键非检→直接产出入库", "roles": ["系统自动"]},
       {"stage": "完工", "action": "仅末道工序回写工单已生产数量,达标自动完成;MPS 累计判断完成并发事件给销售订单", "roles": ["系统自动"]}]},
    {"id": "quality", "name": "质量流程", "mermaid": "mermaid/05-quality-flow.mmd", "color": "#e06c6c",
     "summary": "四道检验闸口(IQC/IPQC/OQC/RQC,二态 0→4) + 判定四果(合格/特采/退货/报废) + NCR 评审处置关闭;AI 提供判定建议;NCR 当前仅台账不触发返工单;检验单可由智能质检报告平台一键出件(generate-from-qc:数据从 MES 取,五道校验后渲染落库)。",
     "steps": [
       {"stage": "来料", "action": "到货通知/外协入库→IQC,结论回写推进待入库或触发退货", "roles": ["IQC 质检员"]},
       {"stage": "过程", "action": "报工质检工序产出行→IPQC,合格拆行入库、不合格生成 NCR", "roles": ["IPQC 质检员"]},
       {"stage": "出货", "action": "销售出库提交→OQC,完成后出库单进入待拣货", "roles": ["OQC 质检员"]},
       {"stage": "退货", "action": "退料/销售退货/售后退货→RQC,结论回调单据与售后", "roles": ["RQC 质检员"]},
       {"stage": "不合格", "action": "NCR: 待评审→评审→处置(退货/报废/返工/让步接收/降级)→关闭", "roles": ["质量工程师"]},
       {"stage": "出件", "action": "两条出件入口:①通用 generate/preview(调用方自带上下文) 渲染 HTML 并冻结数据快照,编号 QR+日期+4位流水按天独立,preview 只渲染不落库不消耗编号,regenerate 用冻结快照重渲且数据与编号不变并作废已归档 PDF,导出 PDF 由后端调 Chromium 打印实例已存的 HTML 后归档附件中心;②generate-from-qc 由质检单直接出件(数据经 MesQcReportApi 从 MES 取,前端不拼上下文),出件前五道校验按业务优先级只报第一处:类型非法/单据不存在/未完成检验/未填判定/模板类型不符/无可用检验项,报告头另带单据原判定 qcResult+qcResultText 与引擎判定并存,响应回 undecidableCount 待判定项数与 warnings(分组标题跳过/指标已删/未录实测值/样品超3),同一张单据可反复出多份", "roles": ["质检员"]},
       {"stage": "建模板", "action": "AI 导入:上传既有检验报告文件(扫描件/照片、电子版 PDF、Word/Excel) → 识别成模板草稿(不落库) → 人工在设计器确认调整后保存为版本;.doc/.xls 旧格式不支持", "roles": ["质检员", "质量工程师"]}]},
    {"id": "warehouse", "name": "仓储流程", "mermaid": "mermaid/06-warehouse-flow.mmd", "color": "#7e6bd6",
     "summary": "15 类单据驱动库存台账(六元组、四量)变动,全部经事务流水留痕;支持调拨在途、盘点调整、批次/SN 追溯、库存占用与四级冻结、线边虚拟库负库存;对外 WmsStockApi 供 ERP 收发。",
     "steps": [
       {"stage": "入库", "action": "采购入库/生产入库/产出入库/退货入库/外协入库/杂项入库 → IN 事务,批次混放校验、四级冻结校验", "roles": ["仓管"]},
       {"stage": "出库", "action": "销售出库/领料/消耗/外协发料/供应商退货/杂项出库 → OUT 事务,checkFlag 控制可否负库存", "roles": ["仓管", "系统自动"]},
       {"stage": "移库", "action": "内部/外部调拨成对 MOVE 事务,在途数量跟踪", "roles": ["仓管"]},
       {"stage": "盘点", "action": "方案→任务→明细行→结果,盘盈 ADJUST_IN、盘亏 ADJUST_OUT,可冻结与盲盘", "roles": ["盘点员"]},
       {"stage": "追溯", "action": "批次/SN(uuid)/条码(18类)/装箱嵌套支撑正反向追溯", "roles": ["质量", "售后"]}]},
    {"id": "aftersales", "name": "售后流程", "mermaid": "mermaid/07-after-sales-flow.mmd", "color": "#c78bd6",
     "summary": "工单(六态+问题判定) → 维修三类型或退货申请 → 审核自动建 MES 销售退货单 → RQC 质检/入库/退款三回调逐级推进 → 工单完结关闭。",
     "steps": [
       {"stage": "受理", "action": "人工录入工单(关联客户/订单/合同),分配处理人进入处理中", "roles": ["客服"]},
       {"stage": "判定", "action": "问题判定: 一般/维修/退货,严重度 0-3", "roles": ["售后工程师"]},
       {"stage": "维修", "action": "现场/返厂/远程维修,费用与结果记录,完成联动工单完结", "roles": ["维修工程师"]},
       {"stage": "退货", "action": "退货申请审核→自动建 MES 销售退货单(RQC)→质检/入库回调→退款回调→关闭", "roles": ["售后", "质检", "财务"]}]},
    {"id": "finance", "name": "财务报销流程", "mermaid": "mermaid/12-finance-flow.mmd", "color": "#3ec9a7",
     "summary": "独立财务模块(yudao-module-finance):报销单(主表 + 1..n 明细行 + 发票附件) 一次性保存 → 提交 BPM 审批 → 通过后归档终态;驳回可改发票重新提交。附件走 system 附件中心按 recordType=finance_reimbursement 绑定,模块不与其他业务模块产生任何单据关联或字段带入。",
     "steps": [
       {"stage": "填报", "action": "主表(报销标题/报销总金额/申请日期/备注) + 至少 1 行明细(费用类型/发票号码/发票金额/发票日期/开票方/费用说明);总金额由明细求和回填,用户可手改,后端不校验一致性", "roles": ["报销人"]},
       {"stage": "附件", "action": "发票先调 /system/storage-blob/upload 取 blobId;保存时把该单当前全部 blobId 放进 blobIds 一起提交(全量替换语义,传空数组=清空,不传=不动)", "roles": ["报销人"]},
       {"stage": "保存", "action": "create 由 Redis 生成单号 BX+yyyyMMdd+6位流水(每日重置);明细先删后增全量覆盖且提交剥掉行 id;status=0 草稿", "roles": ["报销人"]},
       {"stage": "修改", "action": "update 仅放行 status=0 草稿 与 status=35 已驳回;delete 仅放行草稿(连带清理明细与发票附件)", "roles": ["报销人"]},
       {"stage": "审批", "action": "submit 传 id 与 processDefinitionKey(query 参数,请求体为空)→ status=15 审批中 并回写 processInstanceId;BPM 监听器按结果回写:通过→20 已通过、驳回→35 已驳回、取消→退回 0 草稿;流程 key 取 approve-process-list 实时列表不写死", "roles": ["报销人", "审批人"]},
       {"stage": "归档", "action": "archive 仅放行 status=20 已通过 → status=40 已归档(终态)并写入 archiveTime", "roles": ["财务"]}]},
    {"id": "ums", "name": "会员入会流程", "mermaid": "mermaid/13-ums-membership-flow.mmd", "color": "#d6955c",
     "summary": "独立会员模块(yudao-module-ums):入会申请单(个人/单位二型 + 申请等级 + 证明材料附件) 草稿 → 提交 BPM 两级审批(秘书处初审 → 理事会审议)→ 通过后自动创建系统登录账号与会员记录 → 会员进入列表与通讯录(按地区/行业检索)。驳回可改后重提,取消退回草稿。模块不与其他业务模块产生单据关联,仅复用 BPM 与 system 附件中心。",
     "steps": [
       {"stage": "填报", "action": "选会员类型 1个人 / 2单位(**非租户多类型,而是两套字段**):个人填 姓名/工作单位/职称;单位填 单位名称/法定代表人/统一社会信用代码;共有 研究方向/联系电话/邮箱/行业/地区编码/备注", "roles": ["入会申请人", "会员管理员"]},
       {"stage": "定级", "action": "选申请等级;等级字典随会员类型切换(个人 ums_personal_grade / 单位 ums_unit_grade),前端切换类型时清空已选等级", "roles": ["入会申请人"]},
       {"stage": "附件", "action": "证明材料先调 /system/storage-blob/upload 取 blobId,随保存请求的 blobIds 一起提交;绑定 recordType=ums_application(会员侧为 ums_member)", "roles": ["入会申请人"]},
       {"stage": "保存", "action": "create 由 Redis 生成申请编号 SQ+yyyyMMdd+6位流水(每日重置),后端生成不可自定义;status=0 草稿,表单不填状态", "roles": ["入会申请人"]},
       {"stage": "修改", "action": "update 仅放行 status=0 草稿 与 status=35 已驳回(驳回时提示带申请编号);delete 仅放行草稿,且连带清理该单附件", "roles": ["入会申请人", "会员管理员"]},
       {"stage": "审批", "action": "submit 传 id 与 processDefinitionKey(query 参数)→ status=15 审批中并回写 processInstanceId;BPM 监听器 UmsApplicationStatusListener 按结果回写:通过→20、驳回→35、取消→退回 0;流程列表由 approve-process-list 实时取(先校验 BPM 分类 ums_membership_approve 存在,再按 key 取未挂起的最高版本),不写死 key", "roles": ["入会申请人", "秘书处(初审)", "理事会(审议)"]},
       {"stage": "建档", "action": "审批通过钩子自动建会员:①生成会员编号 HY+yyyyMMdd+6位流水 ②AdminUserApi.createUser 建系统账号(用户名与初始密码=联系电话、昵称=申请人姓名、部门=学会会员)③会员落库(入会日期=当天、状态 1 正常、applicationId 回填来源申请单);手机号重复会明确报错", "roles": ["系统自动"]},
       {"stage": "管理与检索", "action": "会员列表支持手工新增/编辑/删除(等级与类型校验:单位会员不可选『理事长』);会员通讯录为只读页,按地区编码与行业检索", "roles": ["会员管理员", "全体可查通讯录"]}]},
    {"id": "research", "name": "课题管理流程", "mermaid": "mermaid/14-research-flow.mmd", "color": "#e070a8",
     "summary": "独立科研模块(yudao-module-research):学会发布课题指南(重点/一般分类)→ 会员在线提交课题申报书 → 提交 BPM 三级审批(秘书处形式审查 → 专家评审 → 学会立项审批),专家评分以结构化明细子表记录并由后端自动汇总加权均分、排名与推荐结论 → 审批通过生成立项编号并发布系统公告 → 按批次登记经费拨付(凭证必填),每条拨款记录自动生成并提交一张 1:1 的财务报销单(finance_reimbursement),走 BPM 两级审批后由监听器把结果回写台账。院级二期再补结项验收与成果归档。",
     "steps": [
       {"stage": "发布指南", "action": "新建指南选类别 1重点课题 / 2一般课题,填申报起止时间与指南正文;编号 GL+yyyyMMdd+6位流水(Redis 每日重置);status: 0草稿/20已发布/30已截止。仅草稿可改可删,发布后即冻结;已产生申报单的指南禁止删除(提示该指南下已有 N 张申报单)", "roles": ["学会秘书处", "课题管理员"]},
       {"stage": "在线申报", "action": "从已发布且在申报期内的指南新建申报书;选会员(ums_member)自动带出姓名/工作单位/职称/联系电话/邮箱,可改;填课题名称、课题组成员、申请经费、申报书正文;编号 SB+yyyyMMdd+6位流水;类别冗余自指南,不信任前端传值;status: 0草稿", "roles": ["课题申报人(会员)", "课题管理员"]},
       {"stage": "提交审批", "action": "submit 传 id 与 processDefinitionKey(query 参数)→ status=15 审批中并回写 processInstanceId。提交前逐条校验:① 指南必须已发布 ② 当前时间必须在申报起止期内——任一不满足即拒绝,提示带指南名与实际状态或起止时间;仅草稿与已驳回可改可重提,仅草稿可删(连带清附件)", "roles": ["课题申报人"]},
       {"stage": "专家评审", "action": "申报单处于审批中(15)时录入专家评分明细(一位专家一行:姓名/单位/职称 + 选题价值0.3/研究方案0.3/团队基础0.2/预期成果0.2 四项分值 + 结论 + 意见);后端每次增删改后重算 compositeScore 加权合计、按加权分降序重排 ranking,并回写主单 reviewAvgScore(全部专家均分)与 reviewPass(均分达标且多数结论为推荐立项);明细全部删除时汇总值清空为未汇总;四项分值均在 0~100 之外时提示指出具体维度与实际值", "roles": ["评审专家", "学会秘书处", "课题管理员"]},
       {"stage": "立项审批", "action": "BPM 流程 research_topic_approve 顺序走三节点:秘书处形式审查 → 专家评审 → 学会立项审批。审批方法与审批人在流程定义内配置,部署后由管理员在 BPM 流程设计器按学会实际组织调整;审批中心内的自定义表单只读回显申报单 + 评审明细 + 拨付记录。通过→20 已立项、驳回→35 已驳回、取消→退回 0 草稿", "roles": ["学会秘书处(形式审查)", "评审专家(评审节点)", "学会理事会(立项审批)"]},
       {"stage": "立项与通知", "action": "监听器 ResearchApplicationStatusListener 先回写审批状态,审批通过后再调 approveApplication:生成立项编号 LX+yyyyMMdd+6位流水 与 approveTime(已有编号则幂等跳过)。点「发布立项通知」生成 system_notice 公告(标题『关于公布立项课题的通知』,正文含课题名/立项编号/类别/申报人/单位/资助金额),写回 noticeId 防重复——重复点击按已发布拦截并告知上次发布时间", "roles": ["课题管理员", "学会秘书处"]},
       {"stage": "经费拨付", "action": "仅「已立项(20)」的申报单可登记拨款记录(批次名称/拨付金额/拨付日期/收款单位/备注/凭证附件),凭证必填。登记时一条龙完成:① 课题侧落 research_fund_record 台账(先置待拨付)② 同一批 blob 双绑到课题台账与财务报销单 ③ 经 yudao-module-finance-api 自动生成 1:1 的 finance_reimbursement 报销单(1 行明细:费用类型=其他、发票金额=拨付金额、发票日期=拨付日期、费用说明=「课题〔课题名〕第N期/第N次经费拨款」)④ 立即提交 BPM 报销审批 ⑤ 台账回写报销单号并置「报销审批中(15)」。金额校验:已登记金额 + 本次金额 不得超出该单申请经费,超出时提示报出本次、已登记、合计、预算四项数值。状态由系统驱动:0待拨付/15报销审批中/20已拨付/30已驳回;只有「待拨付」可删(删除联动清理财务侧草稿报销单),「待拨付」「已驳回」可改并重新发起报销(已驳回改后清空驳回原因与时间);取消流程则退回「待拨付(0)」(发起人撤回只把实例退回发起节点、流程未结束,台账保持「报销审批中(15)」,需重新提交后才继续走审批)。财务审批分类下没有已发布流程时直接报错且不建单,不留孤儿草稿报销单", "roles": ["学会财务", "课题管理员", "财务审核人", "财务主管"]}]},
    {"id": "ai", "name": "AI 业务流程", "mermaid": "mermaid/11-ai-business-flow.mmd", "color": "#56c8d8",
     "summary": "统一模式:业务侧收集上下文(必要时调 MDM/SRM/CRM API 取名称)→组装 Prompt→chat/chatWithImage→解析 JSON→前端预填或建议;无 Function Calling;RAG 知识库已建成但暂无业务消费。",
     "steps": [
       {"stage": "录入提效", "action": "发票 OCR(图片多模态/PDF文本)、报价单 OCR → 结构化预填", "roles": ["采购员", "销售"]},
       {"stage": "辅助判定", "action": "质检单+指标+缺陷 → 建议合格/特采/退货/报废+理由", "roles": ["质检员"]},
       {"stage": "决策预测", "action": "缺料/生产时长/生产风险/交付四类预测供计划排产参考", "roles": ["计划员"]},
       {"stage": "模板识别", "action": "上传既有检验报告文件(扫描件/照片、电子版 PDF、Word/Excel) → 分通道抽取(有文本层走文本、扫描版逐页转图) → 大模型按前端上传的组件清单产出扁平组件草稿 → 人工在设计器确认后保存为模板版本", "roles": ["质检员", "质量工程师"]},
       {"stage": "知识检索", "action": "文档→切片→Milvus 向量→检索接口(预留)", "roles": ["—"]}]}
  ]
}</script>
<script type="application/json" id="data-roles">{
  "note": "实际 system_role 仅 4 个有效角色(超级管理员/普通角色/CRM管理员/租户管理员)+1 测试角色;业务分工主要通过『菜单权限 + 数据权限(负责人制)』实现,而非细分角色。以下业务角色由各模块功能与数据权限模型推导。",
  "systemRoles": [
    {"name": "超级管理员", "code": "super_admin", "desc": "代码直返全部菜单"},
    {"name": "普通角色", "code": "common", "desc": "依赖 system_role_menu 配权"},
    {"name": "CRM 管理员", "code": "crm_admin", "desc": "CRM 查看全部数据"},
    {"name": "租户管理员", "code": "tenant_admin", "desc": "多租户已关闭,遗留"}
  ],
  "businessRoles": [
    {"role": "销售内勤/销售", "modules": ["CRM", "ERP"], "operations": ["线索客户商机维护", "报价与合同", "销售订单", "跟进记录"], "data": ["crm_*", "erp_sale_order"], "scope": "负责人(owner)+数据权限三级:只读/读写/负责人;公海池可领取;交接人应可通过 expandUserIds 看到离职人数据(接口已备、待接入)"},
    {"role": "销售经理", "modules": ["CRM"], "operations": ["商机/合同审批参与", "销售目标制定", "业绩排名漏斗分析"], "data": ["团队数据(下属用户)"], "scope": "AdminUserApi 下级用户扩展"},
    {"role": "采购员", "modules": ["ERP", "SRM"], "operations": ["采购申请/订单", "确认收货", "来票与付款发起"], "data": ["erp_purchase_*", "srm_*"], "scope": "requestUserId/supplierOwner 归属"},
    {"role": "采购开发/供应商管理员", "modules": ["SRM"], "operations": ["准入审批流转", "招标比价组织", "绩效评价"], "data": ["srm_supplier_apply", "srm_tender_*"], "scope": "BPM 多部门会签(采购/质量/财务)"},
    {"role": "计划员", "modules": ["MES"], "operations": ["MPS 与工单", "工艺路线维护", "AI 预测参考"], "data": ["mes_pro_*"], "scope": "车间/部门数据范围"},
    {"role": "操作工/班组长", "modules": ["MES"], "operations": ["任务接收", "报工提交(受工序顺序约束)", "安灯"], "data": ["mes_pro_feedback", "mes_pro_task"], "scope": "任务指派人可见"},
    {"role": "仓管员", "modules": ["MES-WM"], "operations": ["15 类出入库单据执行", "调拨", "盘点", "条码装箱"], "data": ["mes_wm_*"], "scope": "仓库维度;WMS 可按仓库配置出入库/调拨/盘点是否走 BPM"},
    {"role": "质检员", "modules": ["MES-QC"], "operations": ["IQC/IPQC/OQC/RQC 检验", "NCR 评审处置"], "data": ["mes_qc_*"], "scope": "inspectorUserId 归属"},
    {"role": "设备员/维修工", "modules": ["MES-DV"], "operations": ["点检保养", "维修工单", "工装领用"], "data": ["mes_dv_*", "mes_tm_*"], "scope": "chargeUserId 归属"},
    {"role": "客服/售后工程师", "modules": ["售后"], "operations": ["工单受理派工", "维修", "退货申请"], "data": ["after_sale_*"], "scope": "行级数据权限四视图:我负责/我参与/下属负责/全部(aftersales:view-all)"},
    {"role": "财务", "modules": ["ERP", "CRM"], "operations": ["收款单", "付款单", "开票审核", "结算账户"], "data": ["erp_finance_*", "crm_invoice", "crm_receivable"], "scope": "financeUserId 归属"},
    {"role": "HR", "modules": ["HRM"], "operations": ["员工档案入转调离", "考勤规则排班", "薪酬核算发放", "离职交接"], "data": ["hrm_*"], "scope": "部门范围 dataScope"},
    {"role": "管理层", "modules": ["BI"], "operations": ["查看采购销售/仓库/生产设备/质量/人力五大主题大屏"], "data": ["bi_dashboard"], "scope": "只读"},
    {"role": "入会申请人/会员管理员", "modules": ["UMS"], "operations": ["填报与修改入会申请(个人/单位)", "上传证明材料附件", "提交 BPM 审批", "会员列表维护与会员通讯录检索"], "data": ["ums_application", "ums_member"], "scope": "权限码 ums:application:create/update/delete/submit · ums:member:create/update/delete/query · ums:member:directory;无数据权限隔离(全员可查通讯录,维护类按钮另凭权限码)"},
    {"role": "秘书处/理事会(审批人)", "modules": ["UMS", "BPM"], "operations": ["入会申请两级审批:秘书处初审 → 理事会审议"], "data": ["ums_application(状态回写)", "bpm_task"], "scope": "审批人由 BPM 流程定义配置,后端不写死;通过后系统自动建会员与登录账号"},
    {"role": "课题申报人/课题管理员", "modules": ["RES"], "operations": ["课题指南发布与截止(课题管理员)", "新建与修改课题申报书(选会员带出申报人信息、上传申报附件)", "提交 BPM 审批、驳回后修改重提", "每月/每批次登记经费拨付记录与凭证", "发布立项通知"], "data": ["research_guideline", "research_application", "research_topic_review", "research_fund_record"], "scope": "权限码 research:guideline:create/update/delete/publish · research:application:create/update/delete/submit/notice · research:review:create/update/delete · research:fund:create/update/delete;无数据权限隔离(列表不按申请人过滤),按钮凭权限码控制"},
    {"role": "评审专家/学会秘书处·理事会(课题审批人)", "modules": ["RES", "BPM"], "operations": ["在审批中(15)录入本人的专家评分明细(四项分值 + 结论 + 意见)", "秘书处形式审查 → 专家评审 → 学会立项审批 三节点顺序审批", "在审批中心通过自定义表单只读查看申报书正文、评审明细与拨付记录"], "data": ["research_topic_review", "research_application(状态回写)", "bpm_task"], "scope": "评分录入要求申报单处于审批中(15)·由课题管理员或专家依据 research:review:* 权限码执行;审批人与审批方法在 BPM 流程定义 research_topic_approve 内配置(默认依次审批·角色兜底),后端不写死审批人,部署后由管理员在流程设计器按学会实际组织调整"}
  ]
}</script>
<pre style="display:none" class="mmd-src" data-mmd="01-business-mindmap">
mindmap
  root((MOM 工厂数字化平台))
    基础主数据 MDM
      物料与SKU
      物料分类
      计量单位与品牌
      仓库主数据
    销售管理 CRM
      线索
      客户与联系人
      商机与阶段
      报价单
      销售合同
      开票与回款
      销售目标
      数据权限与公海
    供应商管理 SRM
      供应商档案
      准入审批
      资质证书
      招标与比价
      绩效评价
    进销存与财务 ERP
      采购申请
      采购订单
      采购来票
      销售订单
      付款单
      收款单
      结算账户
    财务报销 FIN 独立
      报销单主表
      报销明细行
      发票附件上传
      提交审批与驳回重提
      归档终态
    会员管理 UMS 独立
      入会申请单
      个人与单位二型字段
      会员等级个人与单位两套
      秘书处理事会两级审批
      审批通过自动建会员
      自动创建系统登录账号
      会员列表与通讯录检索
    课题管理 RES 独立
      课题指南发布
      重点与一般课题分类
      指导申报期与截止
      在线课题申报书
      从会员带出申报人信息
      专家评审明细与加权排名
      秘书处理事会三级审批
      立项编号与立项通知公告
      经费拨付记录与凭证
    生产管理 MES
      主生产计划 MPS
      生产工单
      生产任务
      生产报工
      工艺路线与工序
      工单BOM
      研发设计项目
      安灯
    仓储管理 MES-WM
      仓库库区库位
      到货通知
      采购入库
      生产领料与退料
      生产入库
      销售出库与退货
      转移调拨
      盘点
      库存台账与事务
      批次SN条码装箱
    质量管理 MES-QC
      IQC 来料检验
      IPQC 过程检验
      OQC 出货检验
      RQC 退货检验
      NCR 不合格品
      质检方案与指标缺陷
    智能质检报告 QC-REPORT
      报告模板与多版本
      可视化设计器
      组件绑定与判定规则
      HTML 渲染引擎
      报告出件与编号
      数据快照冻结
    设备与排班
      设备台账
      点检与保养
      维修工单
      工装夹具
      班组班次与排班
    售后服务
      售后工单
      维修记录
      退货申请
      数据权限
    人力资源 HRM
      员工档案
      请假加班调岗离职
      离职交接映射
      考勤排班汇总
      薪酬核算与发放
    协同审批 BPM
      Flowable 引擎
      业务状态回调监听
    AI 智能
      发票OCR识别
      报价单OCR识别
      质检结果判定
      物料短缺预测
      生产时长与风险预测
      交付预测
      RAG 知识库
    BI 与管理驾驶舱
      SQL 配置式大屏
      采购销售库存生产质量人力主题
    系统底座
      认证与RBAC
      数据权限
      字典
      附件中心
      定时任务与消息
</pre>
<pre style="display:none" class="mmd-src" data-mmd="02-sales-flow">
flowchart TD
  A[线索 crm_clue] -->|转化·仅一次| B[客户 crm_customer]
  B --> C[联系人 crm_contact]
  B --> D[商机 crm_business]
  D -->|自定义阶段推进| E{商机结束}
  E -->|赢单| F[报价单 QT·草稿0→已转合同10]
  E -->|输单或无效| X[结束]
  F -->|转合同·带入客户产品折扣| G[合同 HT·草稿0→审批中10]
  D -->|直接建合同| G
  G -->|BPM审批通过20| H[生成 ERP 销售订单 XSDD]
  H -->|提交BPM| I[销售订单审批·审批中15→已审核20]
  I -->|明细需生产·自动创建| J[MES 主生产计划 MPS]
  I --> K[发货通知单]
  K -->|审核通过| L[销售出库单·OQC→拣货→发运→出库完成]
  L -->|回写出库数量与状态| M[销售订单 部分出库2/全部出库3]
  G --> N[回款计划·按期数分期]
  G -->|提交BPM| O[开票 KP·审批中10→通过20·累计不超合同额]
  O -->|回款前必须先开票| P[回款 HK·创建即审批中10→手动审核20]
  P -->|校验合同剩余额与开票剩余额| N
</pre>
<pre style="display:none" class="mmd-src" data-mmd="03-purchase-flow">
flowchart TD
  S0[SRM 准入申请 AP·待提交0→审批中10→已通过40] --> S1[供应商档案 srm_supplier]
  S1 --> S2[资质证书管理]
  S1 --> S3[绩效评价·质量交付价格服务四维]
  S4[SRM 招标比价·发布→投标→开标→评标→定标] -->|定标审批通过生成订单| C3
  C1[采购申请 CGSQ·草稿0→审批中10] -->|提交BPM| C2{审批}
  C2 -->|驳回30→可重置回草稿| C1
  C2 -->|通过20且有供应商·自动生成| C3[采购订单 CGDD·审批中15→已审核20]
  C2 -->|通过20无供应商| C4[手动指定供应商生成订单]
  C4 --> C3
  C3 -->|确认收货| C5[到货通知单·草稿0→待质检2→待入库3→完成4]
  C5 -->|物料需检验·自动创建| C6[IQC 来料检验]
  C6 -->|合格1或特采2 可入库| C7[采购入库单·草稿0→待上架2→待执行3→完成4]
  C6 -->|不合格退货3| C8[供应商退货·待拣货→待执行→完成·库存扣减]
  C6 -->|不合格报废4| C9[NCR 不合格品处置]
  C7 -->|回写 inCount 与 inStatus 未入库0部分1全部2| C3
  C7 -->|回写申请明细 inCount| C1
  C3 -->|订单总价控制| C10[采购来票 FPD·累计金额≤订单总价]
  C10 -->|付款单必须关联来票| C11[付款单 FKD·审批中15→已审核20·累计付款≤来票金额]
</pre>
<pre style="display:none" class="mmd-src" data-mmd="04-production-flow">
flowchart TD
  P0[销售订单明细 needProduction=1] -->|审批通过自动创建| P1[MPS 主生产计划·草稿0→已下发1]
  P1 -->|下发或合并下发| P2[生产工单·草稿0→已确认1]
  P2 -->|按工艺路线工序自动生成| P3[生产任务×N·预备0→生产中1→部分完成2→完成4·可暂停9]
  P3 --> P4[生产领料 或 线边虚拟库备料]
  P4 --> P5[报工提交·草稿0]
  P5 -->|校验:上一道工序未报满则拦截| P5
  P5 -->|启用BPM则提交审批| P6{报工审批}
  P6 -->|驳回| P5
  P6 -->|通过且倒冲工序| P7[按工单BOM自动生成物料消耗·扣线边虚拟库FIFO允许负库存]
  P6 -->|通过且质检工序| P8[产出行待检→IPQC 过程检验]
  P6 -->|通过且非质检关键工序| P9[产品产出直接完工入库]
  P8 -->|检验合格| P10[拆合格与不合格行入库]
  P8 -->|不合格| P11[NCR 不合格品]
  P9 --> P12[进度回写·仅最后一道工序报工才更新工单已生产数量]
  P10 --> P12
  P12 -->|已生产数量≥计划数量·自动| P13[工单完成2]
  P12 -->|累计| P14[MPS 回写已生产数量并判断完成2]
  P14 -->|全部完成| P15[发布 ProductionFinishedEvent→回写销售订单生产状态]
</pre>
<pre style="display:none" class="mmd-src" data-mmd="05-quality-flow">
flowchart TD
  T1[到货通知单 或 外协入库单] --> A1[IQC 来料检验·草稿0→完成4]
  T2[报工质检工序通过后产出行] --> A2[IPQC 过程检验·草稿0→完成4]
  T3[销售出库单提交] --> A3[OQC 出货检验·草稿0→完成4]
  T4[生产退料单 或 销售退货单 或 售后退货] --> A4[RQC 退货检验·草稿0→完成4]
  A1 --> R{检验判定 MesQcCheckResultEnum}
  A2 --> R
  A3 --> R
  A4 --> R
  R -->|1 合格| Q1[允许入库·回写来源单据推进状态]
  R -->|2 特采| Q2[允许入库·并生成NCR留痕]
  R -->|3 不合格退货| Q3[生成NCR·走供应商退货或销售退货]
  R -->|4 不合格报废| Q4[生成NCR·走报废出库]
  A2 -->|合格数量| Q5[报工转完成·产出拆行入库]
  A3 -->|完成| Q6[销售出库单推进待拣货]
  A4 -->|完成| Q7[退货单推进上架或入库·售后回调结论]
  N1[NCR 待评审0] -->|评审| N2[已评审6]
  N2 -->|处置方式:退货1报废2返工3让步接收4降级使用5| N3[已处置7]
  N3 -->|关闭| N4[已关闭8]
  Q8[支撑数据:质检方案·检验指标·缺陷库·指标结果] --- A1
  Q8H["检验指标的层级可在质检指标页维护·条目类型 所属分组 公式 排序号·只允许两层·删分组级联删子项"] --- Q8
  A1 -->|出件 generate-from-qc| GATE{出件六道校验·按业务优先级只报第一处}
  A2 -->|出件 generate-from-qc| GATE
  A3 -->|出件 generate-from-qc| GATE
  A4 -->|出件 generate-from-qc| GATE
  GATE -->|000 类型非法| GE0[105_000 qcType 不在 1~4]
  GATE -->|001 单据不存在| GE1[105_001]
  GATE -->|002 未完成检验| GE2[105_002·带当前状态中文名]
  GATE -->|003 未填判定| GE3[105_003·列出四个可选判定]
  GATE -->|005 模板类型不符| GE5[105_005·两边类型中文名都给·前端按 reportType 预过滤]
  GATE -->|006 无可用检验项| GE6[105_006]
  GATE -->|通过·只允许同类型已发布模板| RP[智能质检报告平台 qcreport]
  RP --> RPT[报告模板 qc_report_template·启用0停用1]
  RPT --> RPTV[模板版本 qc_report_template_version·草稿0已发布1已停用2·当前版本不可停用 101_010]
  RPTVSAVE[设计器保存 create 或 update] -->|写入侧闸门·规则101_006 与画布安全101_009 不合格一律不落库| RPTV
  RPTVSAVE -.->|只卡新写入·存量脏画布不回溯核查| CVNOTE[画布校验覆盖三处注入面·tagName 属性名 整段CSS]
  RPTV -->|按已发布版本渲染| RPI[报告实例 qc_report_instance·编号 QR+yyyyMMdd+4位流水·按天独立]
  RPI --> SNAP[冻结 data_snapshot·判定后数据含 PASS FAIL 与合格率]
  RPI --> RHTML[render_html 完整 HTML 文档·出站已剥离事件属性与危险协议]
  RPI --> RREGEN[重新生成 regenerate·冻结快照+同一模板版本重渲·数据与编号不变·并作废已归档PDF]
  RPI --> RPDF[导出PDF 后端调Chromium打印已存HTML·归档 system_storage_attachment·recordType=qc_report_instance]
  RPI --> RUI[报告实例页 /qc/report-instance·菜单挂质量管理 parent_id=5500]
  RUI -->|权限码| RPERM["qc-report:instance:query 查询·:export 导出PDF·:delete 删除·:create 通用出件·:generate 质检单出件(menu 1075416)"]
  RUI -->|反查 businessType=mes_qc_iqc 等表名 + businessId| RBACK[同一张质检单可出多份报告·历史留痕需要·不做重复拦截]
  RUI --> RPREVIEW[详情弹窗·iframe sandbox空属性渲染 render_html·数据快照页签展示冻结 data_snapshot]
  A1 -.->|前端入口·行操作「出报告」仅 status=已完成可见| UIREP[出报告弹窗·IQC IPQC OQC RQC 四页共用]
  A2 -.->|同上| UIREP
  A3 -.->|同上| UIREP
  A4 -.->|同上| UIREP
  UIREP -->|只列 reportType 与本单一致·启用·当前版本已发布且画布非空·避免选中必被拒的组合| UIPICK[用户选模板]
  UIPICK -->|POST generate-from-qc| GATE
  UIREP -->|结果就地展示 reportNo itemCount| UIRES["undecidableCount>0 蓝色信息条(多为过程参数无判定规则·黄色会几乎每单误报)·warnings 与 errors 逐条展示·不落库事后看不到"]
  UIRES -->|查看报告列表| RUI
  RUI -->|路由 query 带入 businessType+businessId 并预填搜索| RBACK
  GATE -->|MesQcReportApi.getQcReportData| MESMAP[MES 四层取数归一·单头 / 判定依据行 max min threshold / 样品 indicator_result / 实测值 result_detail]
  MESMAP --> RCTX[报告上下文 report+inspectionItems·由后端映射器产出·前端不再自己拼]
  RCTX --> RCITEM[检验项=样品×指标交叉展开·全指标进不过滤·多样品时样品号折进 remark]
  RCTX --> RCQC[携带单据原判定 qcResult 数·qcResultText 中文·与引擎判定并存互不覆盖]
  MESMAP -->|分组项 item_type=2 与它的子项按 parent_id 还原两层·归属不看 type 也不看行序| RCTXG
  RCTXG["分组还原·组头行背组名与 formula_text 公式·组内第一行背 span=该组总行数·同组其余行 span=1 且 hidden 让位·合并的是模板 columns 里带 # 的那一列(默认列即检验项目)·合并行数是本单该组实际行数而非主数据子项数·该组本单无子项行则不合并"] --- RCTX
  MESMAP -.->|分组项本单没有属于它的子项 / 组头本单没有行由后端补一行| RCWARN
  MESMAP -.->|指标已删 / 未录实测值(组头锚点行按定义无值·不报) / 样品超 3 个·进 warnings| RCWARN
  RCWARN[warnings 软提示·不落库·事后看不到需自行留痕] --- RCTX
  RCITEM --- RCTX
  RCQC --- RCTX
  RCTX --> RPI
  RCTX --> RUNDEC["undecidableCount 没有判定结论的项数=快照里 result 为空串的项·含无判定规则与待判定两种·不计入合格率分母与报告结论·生成时弹提示不回填"]
  RPT -->|进入设计器并带 templateId·权限 qc-report:template:ai-import| AIIMP[AI导入 qc-report/ai-import/draft]
  AISRC["源文件 扫描件照片·电子版PDF·Word(.docx) Excel(.xlsx)·txt csv"] -->|上传走 system Storage API 取 blobId·绑为 qc_report_template 附件| AIIMP
  AISRC -.->|同一模板重复导入按替换处理·先删旧附件再绑新的·连同 blob 行与磁盘文件一起清| AIIMP
  AISRC -.->|docx 的 Word 页眉页脚读取并以 页眉 页脚 标记并入识别内容·页眉在正文前页脚在正文后·同时进 warnings·扫描件照片不受影响| AIIMP
  AIIMP -->|识别·只产草稿不落库| AIRD[模板草稿 扁平组件+属性清单]
  AIHINT[积木清单每项带 hint 选型说明·来自前端注册表 aiHint·组件选型的唯一真相来源] -.->|选型以 hint 为准·有专门组件就不退用通用组件凑数| AIIMP
  AIIMP -->|表格末行的落款签署行属落款不属数据行·与原件页脚合并且人名日期留空手填·不得自造绑定键| AIRD
  AIIMP -.->|原件列比默认多或表头分两层或样品字段不同时·写进组件的 columns headerSpans fields 三个文本属性覆盖·不换组件| AIRD
  AIKEY[报告可绑定键白名单·由后端 ReportFields 反射生成·不手写] -.->|只禁编造不给可选集合模型只能猜·故全量枚举| AIIMP
  AIRD -->|人工在设计器确认·装配器编译进画布后保存| RPTV
  AIRD -.->|画布上的 ReportHeader ReportFooter 是正文里的抬头落款·与 PDF 每页重复的版式页眉页脚不是一回事| RPTV
  AIIMP -.->|表头某列自己既是列名又是上格·原件里是一格纵向合并跨住上下两层、下层那格在提取文本里写着↑同上| AIRD
  AIIMP -.->|该段标题与列名一字不差才认成纵向合并格、出 rowspan=2 一格且下层跳过该列·不一致就被当成另一层分组标题、结论二字上下各印一遍| AIRD
  RPTVSAVE -.->|设计器画布不跑绑定·组名格的 hidden 占位符会被浏览器当成真 hidden 让整行左移、末列挤成竖排细条| CVNOTE
  RPTVSAVE -.->|自定列不带宽度声明·列宽只能由最小内容宽度倒推·而值是占位符这类不含空格的长串·整表会被撑出纸张、末列挤成竖条| CVWIDTH
  CVWIDTH["故自定列的单元格带 overflow-wrap:anywhere 允许长串折行·默认列有手工宽度声明不加·实测加了反而把检验项目子项挤到 30 余像素、行高翻几倍"] --- RPTVSAVE
</pre>
<pre style="display:none" class="mmd-src" data-mmd="06-warehouse-flow">
flowchart LR
  subgraph INB [入库·事务IN·库存加]
    I1[采购入库]
    I2[生产入库单]
    I3[报工产出自动入库]
    I4[销售退货入库]
    I5[外协入库]
    I6[杂项入库]
  end
  subgraph OUTB [出库·事务OUT·库存减]
    O1[销售出库]
    O2[生产领料]
    O3[物料消耗倒冲·允许负库存]
    O4[外协发料]
    O5[生产退料回仓]
    O6[供应商退货]
    O7[杂项出库·调整或报废]
  end
  W[(库存台账 mes_wm_material_stock 物料×仓库×库区×库位×批次×供应商)]
  INB -->|getOrCreate 加数量| W
  OUTB -->|校验可用量后扣减| W
  W -->|占用预留 reservedQuantity| RES[库存占用 销售出库·领料·外协·杂项·调拨]
  MV[转移调拨·内部或外部] -->|成对事务 MOVE_OUT+MOVE_IN 在途| W
  ST[盘点 方案→任务→明细行→结果] -->|盘盈 ADJUST_IN·盘亏 ADJUST_OUT·可冻结库存| W
  W --> TX[事务流水 mes_wm_transaction 只读全量留痕]
  BT[批次 mes_wm_batch·SN码·条码·装箱嵌套] --- W
  WIP[[线边虚拟库 WIP_VIRTUAL_WAREHOUSE·懒初始化]] --- O3
  FR[四级冻结:仓库·库区·库位·库存记录] --- W
</pre>
<pre style="display:none" class="mmd-src" data-mmd="07-after-sales-flow">
flowchart TD
  F1[客户反馈·后台人工录入工单·关联客户联系人销售订单合同] --> K1[售后工单·草稿0]
  K1 -->|assignTicket 分配处理人| K2[处理中10·问题判定:一般1维修2退货3·严重度0-3]
  K2 -->|维修类| R1[维修记录·待处理0→维修中10→已完成20→已关闭30·现场返厂远程]
  R1 -->|completeRepair| K4[已完结50]
  K2 -->|退货类创建退货申请| T1[退货申请·草稿0]
  T1 -->|approveReturn 审核通过| T2[已审核20·自动创建MES销售退货单]
  T2 --> T3[MES 销售退货·草稿→待检验RQC→待上架→待入库→完成]
  T3 -->|质检结论回调 onReturnQualityFinished 合格特采退货报废| T4[已质检40]
  T3 -->|入库完成回调 onReturnInboundFinished| T5[已入库50]
  T5 -->|退款完成回调 onRefundCompleted 记录实际退款额| T6[已退款60]
  T6 --> T7[已关闭70]
  T2 -.->|状态流转中| T8[审核中10·预留未启用]
  K2 -->|退货链路联动工单| K3[待入库30→待退款40]
  K3 --> K4
  K2 -->|resolveTicket| K4
  K4 -->|closeTicket| K5[已关闭60]
  K1 -->|cancelTicket| K9[已取消99]
  K2 -->|cancelTicket| K9
  PM[数据权限 bizType+bizId+userId+级别 负责人只读读写·我负责我参与下属全部四视图] --- K1
</pre>
<pre style="display:none" class="mmd-src" data-mmd="08-module-relation">
flowchart LR
  MDM[MDM 主数据 物料单位仓库品牌分类] --> MES[MES 生产·仓储·质量·设备]
  MDM --> ERP[ERP 采购·销售·财务]
  MDM --> CRM
  CRM[CRM 线索客户商机报价合同回款] -->|合同生成销售订单| ERP
  SRM[SRM 供应商准入资质证书招标绩效] -->|定标生成采购订单| ERP
  ERP -->|确认收货生成到货通知| MES
  ERP -->|销售订单审批生成MPS| MES
  MES -->|入库出库回写单据进度| ERP
  MES -->|ProductionFinishedEvent| ERP
  MES -->|退货质检入库回调| AS[售后工单维修退货]
  CRM -->|客户主数据| AS
  HRM[HRM 员工入转调离交接考勤薪酬] -.->|expandUserIds 数据可见性扩展·接口已就绪待各模块接入| CRM
  BPM[BPM Flowable 审批引擎] -.->|状态事件回调·15个监听器| ERP
  BPM -.-> CRM
  BPM -.-> SRM
  BPM -.-> MES
  BPM -.-> HRM
  AI[AI 平台 通义千问+Milvus] -.->|发票OCR| ERP
  AI -.->|报价OCR| CRM
  AI -.->|质检判定与生产预测| MES
  MES -.->|质检单上下文·本轮由调用方传入 待 MesQcReportApi 打通| QCR[智能质检报告 qcreport 模板版本实例快照]
  QCR -.->|PDF 归档·后续轮次| STG
  MES --> BI[BI 管理驾驶舱 SQL配置大屏]
  ERP --> BI
  CRM --> BI
  HRM --> BI
  SYS[系统底座 认证·RBAC·字典·附件中心·定时任务] --- MDM
  SYS --- MES
  STG[附件中心 storage-blob+attachment 全模块统一挂附件] --- ERP
  FIN[财务报销 独立模块 yudao-module-finance 报销单 明细 发票附件] -.->|复用 BPM 审批与状态回调| BPM
  FIN -.->|发票走附件中心 recordType=finance_reimbursement| STG
  FIN -.->|"对外仅提供 yudao-module-finance-api(FinanceReimbursementApi)供他模块建单"| ERP
  UMS[会员管理 独立模块 yudao-module-ums 入会申请 会员 通讯录] -.->|复用 BPM 两级审批与状态回调| BPM
  UMS -.->|证明材料走附件中心 recordType=ums_application / ums_member| STG
  UMS -.->|审批通过经 AdminUserApi.createUser 建系统登录账号| SYS
  UMS -.->|独立模块·不与其它业务模块产生单据或数据关联| ERP
  RES[课题管理 独立模块 yudao-module-research 指南 申报书 专家评审 经费拨付] -.->|复用 BPM 三级审批与状态回调·流程 key=research_topic_approve| BPM
  RES -.->|附件走附件中心 recordType=research_guideline / research_application / research_fund_record| STG
  RES -->|选会员取申报人姓名 工作单位 职称 联系电话| UMS
  RES -->|立项通知写 system_notice 公告| SYS
  RES -->|经费拨付 1:1 自动生成 finance_reimbursement 报销单并提交审批·走新建的 yudao-module-finance-api| FIN
  FIN -.->|报销审批结果经 BPM 事件回调 ResearchFundStatusListener 回写课题拨款台账| RES
</pre>
<pre style="display:none" class="mmd-src" data-mmd="09-data-flow">
flowchart LR
  subgraph S1 [销售前端 CRM]
    CL[线索] --> CU[客户]
    CU --> BU[商机]
    BU --> QU[报价单]
    QU --> CT[合同]
    CT --> IV[开票]
    IV --> RK[回款]
    CT --> PL[回款计划]
  end
  subgraph S2 [执行链 ERP+MES]
    SO[销售订单] --> MP[MPS]
    MP --> WO[工单]
    WO --> TK[任务]
    TK --> FB[报工]
    FB --> PP[产品产出]
    PR[采购申请] --> PO[采购订单]
    PO --> AN[到货通知]
    AN --> IR[采购入库]
  end
  subgraph S3 [仓储与财务]
    ST[库存台账]
    PS[销售出库]
    RC[收款单]
    PI[采购来票] --> PA[付款单]
  end
  CT -->|generateSaleOrder 带入客户产品折扣定金·回写orderId| SO
  SO -->|审批通过带入 saleOrderId 行ID 数量 客户 交期| MP
  PO -->|确认收货带入订单行物料供应商数量| AN
  AN -->|IQC完成后生成·带入合格行| IR
  FB -->|BOM倒冲扣减| ST
  PP -->|IN事务入库| ST
  IR -->|IN事务入库·回写inCount| PO
  PS -->|OUT事务出库·回写outCount| SO
  FB -->|最后一道工序回写已生产数量| WO
  FB -->|ProductionFinishedEvent 回写生产状态| SO
  IV -->|回款强制先开票| RK
  RC -->|关联CRM客户校验| CU
  PA -->|关联来票与供应商| PI
  subgraph S4 [智能质检报告 qcreport]
    RPT[报告模板] --> RPTV[模板版本]
    RPTVSAVE[设计器保存 create 或 update] -->|写入侧闸门 规则101_006 画布安全101_009 不合格不落库| RPTV
    RPTVSAVE -.->|只卡新写入 存量脏画布不回溯核查| CVNOTE[画布校验覆盖 tagName 属性名 整段CSS 三处注入面]
    RPTV --> RPI[报告实例]
    RPI --> SNAP[数据快照冻结]
    RPI --> RHTML[HTML 渲染产物]
    RPI --> RPDF[PDF 产物]
    AISRC[源文件 扫描件照片·电子版PDF·Word Excel·txt csv] -->|上传走 system Storage API 取 blobId 并绑为模板附件| AIRD[AI 识别草稿]
    AIRD -->|人工在设计器确认后保存| RPTV
  end
  QCDOC[质检单 IQC IPQC OQC RQC] -->|generate-from-qc·带 qcType qcId templateId| QCGATE{五道校验 类型 存在 已完成 已判定 模板类型 有检验项}
  QCGATE -->|任一不过·按业务优先级只报第一处| QCERR[105_000~105_006]
  QCGATE -->|通过| MESQCAPI[MesQcReportApi 四层取数·单头 判定依据行 样品 实测值]
  MESQCAPI -->|纯函数映射器归一 含原判定 qcResult qcResultText| RPI
  MESQCAPI -.->|分组标题跳过 指标已删 未录实测值 样品超3| QCWARN[warnings 软提示·不落库]
  QCWARN -.-> RPI
  RPI -.->|undecidableCount=快照 result 为空串的项·生成时弹提示| QCFE
  RPI -.->|businessType=mes_qc_iqc 等表名 + businessId 反查·一单可出多份| QCFE
  RPT -.->|report_type 数字字符串须等于 qcType·前端据此过滤模板列表| QCGATE
  RPTV -->|模板停用后不再出新件·历史实例不受影响·当前版本不可停用 101_010| RPI
  RPT -->|进入设计器带 templateId 调 AI 导入| AIRD
  RPI -->|列表 详情 预览 打印| QCFE["前端报告页面 /qc/report-instance 已落地·iframe 渲染 render_html + 快照页签 + 导出PDF"]
  RPDF -->|归档 附件中心 recordType=qc_report_instance| PDFARCH[system_storage_attachment]
  AISRC -.->|归属 附件中心 recordType=qc_report_template| PDFARCH
  AISRC -.->|同一模板重复导入按替换处理·先删旧附件·连同 blob 行与磁盘文件一起清| PDFARCH
  QCFE -.->|权限码 qc-report:instance:query/export/delete/create 已入 system_menu| SYS1[system_menu]
  QCGATE -.->|权限码 qc-report:instance:generate 已入 system_menu id=1075416| SYS1[system_menu]
  QCDOC -.->|前端入口 行操作「出报告」仅 status=已完成可见·四页共用同一弹窗| UIREP[出报告弹窗·只列同类型 启用 且有生效版本的模板]
  UIREP -->|选模板后 POST generate-from-qc| QCGATE
  UIREP -.->|结果就地展示 reportNo itemCount 与 undecidableCount 告警| QCFE
  QCFE -.->|进入时带 businessType+businessId 路由 query·预填搜索表单的「业务单据」字段| UIREP
  RPT -.->|权限码 qc-report:template:ai-import 已入 system_menu| SYS1
  subgraph S5 [独立财务报销 finance]
    RBM[报销单 finance_reimbursement] --> RBI[报销明细 finance_reimbursement_item]
    RBM --> RBA[发票附件 recordType=finance_reimbursement]
    RBM --> RBS[状态 草稿0 审批中15 已通过20 已驳回35 已归档40]
  end
  RBM -->|create 带主表与 items 数组与 blobIds 一次性提交| RBI
  RBI -.->|invoiceAmount 求和回填 totalAmount·用户可手改·后端不校验一致性| RBM
  RBM -->|submit 带 id 与 processDefinitionKey 走 query| RBS
  RBS -.->|BPM 监听器结果回写 通过20 驳回35 取消退回0| RBM
  RBA -.->|上传与保存分离·禁在业务表存文件地址| PDFARCH
  FINNOTE[独立模块·销售采购链条任意单据均不生成报销单·报销单亦不回写任何外部单据·唯一例外是课题经费拨付] -.-> RBM
  subgraph S6 [独立课题管理 research]
    RGL[课题指南 research_guideline] --> RAP[课题申报书 research_application]
    RAP --> RRV[专家评审明细 research_topic_review]
    RAP --> RFR[经费拨付记录 research_fund_record]
    RAP --> RST[状态 草稿0 审批中15 已立项20 已驳回35]
  end
  UMSM[会员 ums_member] -->|"选会员带出 applicantName workUnit jobTitle contactPhone contactEmail·软关联无外键"| RAP
  RGL -->|"申报时引用·要求 publishStatus=已发布20 且 现在在 applyStartTime 至 applyEndTime 内"| RAP
  RAP -->|"category 冗余自指南·不信任前端传值"| RAP
  RAP -->|"submit 带 id 与 processDefinitionKey 走 query·回写 processInstanceId"| RST
  RST -.->|"BPM 监听器 ResearchApplicationStatusListener 回写 通过20 驳回35 取消退回0"| RAP
  RAP -->|"审批通过后 approveApplication 生成 researchNo LX 与 approveTime·已有编号幂等跳过"| RAP
  RRV -->|"recalculate: compositeScore 加权 0.3/0.3/0.2/0.2 → ranking 降序重排 → 主单 reviewAvgScore 与 reviewPass"| RAP
  RRV -.->|"明细删空时主单汇总清为 null"| RAP
  RAP -->|"publishNotice 生成 system_notice 公告并回写 noticeId·重复发布拦截"| SYS2[system_notice 系统公告]
  RFR -->|"金额校验 已登记 + 本次 不得超出 budgetAmount 申请经费"| RAP
  RFR -.->|"凭证走附件中心 recordType=research_fund_record·同一批 blob 双绑 finance_reimbursement·表内不存文件地址"| PDFARCH
  RAP -.->|"申报附件 recordType=research_application"| PDFARCH
  RGL -.->|"指南附件 recordType=research_guideline"| PDFARCH
  RFR -->|"1:1 生成报销单·经 FinanceReimbursementApi 本地 Bean 建单并立即 submit·回写 reimbursement_id/reimbursement_no·台账置 15 报销审批中"| RBM
  RBM -->|"报销单进入同一条 BPM 流程 finance_reimbursement_approve(财务审核 → 财务主管审批)·businessKey=报销单 ID"| RBS
  RBS -.->|"BPM 监听器 ResearchFundStatusListener 回写课题台账 通过20 驳回30(+驳回原因/时间) 撤回0"| RFR
  RESNOTE[一期已对接财务·课题台账为拨款台账·报销单为财务出账凭证·两边以 reimbursement_id 软关联无外键·财务侧直接删单会留下悬空 ID 由业务报错兜底] -.-> RFR
</pre>
<pre style="display:none" class="mmd-src" data-mmd="10-business-object">
erDiagram
  crm_customer ||--o{ crm_business : "拥有商机"
  crm_business ||--o{ crm_sale_quotation : "形成报价"
  crm_sale_quotation |o--o| crm_contract : "转为合同"
  crm_contract ||--o| erp_sale_order : "生成销售订单"
  crm_contract ||--o{ crm_receivable_plan : "分期计划"
  crm_contract ||--o{ crm_invoice : "开票"
  crm_invoice ||--o{ crm_receivable : "凭票回款"
  erp_sale_order ||--o{ mes_pro_mps : "需生产行触发"
  mes_pro_mps ||--o{ mes_pro_work_order : "下发工单"
  mes_pro_work_order ||--o{ mes_pro_task : "按工序拆任务"
  mes_pro_task ||--o{ mes_pro_feedback : "报工"
  mes_pro_work_order ||--o{ mes_pro_work_order_bom : "工序投料BOM"
  erp_purchase_request ||--o{ erp_purchase_order : "生成采购订单"
  erp_purchase_order ||--o| mes_wm_arrival_notice : "确认收货"
  mes_wm_arrival_notice ||--o| mes_qc_iqc : "触发IQC"
  mes_wm_arrival_notice ||--o{ mes_wm_item_receipt : "生成采购入库"
  erp_sale_order ||--o{ mes_wm_product_sales : "销售出库"
  mes_wm_product_sales ||--o| mes_qc_oqc : "触发OQC"
  mes_pro_feedback ||--o| mes_qc_ipqc : "质检工序触发IPQC"
  mes_pro_feedback ||--o{ mes_wm_product_produce : "产出入库"
  mes_pro_feedback ||--o{ mes_wm_item_consume : "BOM倒冲消耗"
  mes_qc_iqc ||--o| mes_qc_ncr : "不合格生成NCR"
  mes_wm_item_receipt ||--o{ mes_wm_material_stock : "库存增加"
  mes_wm_material_stock ||--o{ mes_wm_transaction : "事务流水"
  mes_wm_material_stock }o--|| mes_wm_batch : "按批次"
  after_sale_ticket ||--o{ after_sale_return : "退货申请"
  after_sale_return ||--o| mes_wm_return_sales : "生成MES退货单"
  mdm_item ||--o{ erp_sale_order_items : "销售明细"
  mdm_item ||--o{ erp_purchase_order_items : "采购明细"
  mdm_item ||--o{ mes_wm_material_stock : "库存物料"
  srm_supplier ||--o{ erp_purchase_order : "供货"
  srm_supplier ||--o{ mes_wm_arrival_notice : "到货来源"
  erp_purchase_order ||--o{ erp_purchase_invoice : "来票"
  erp_purchase_invoice ||--o{ erp_finance_payment : "付款"
  ums_application ||--o| ums_member : "审批通过自动建档"
  hrm_employee ||--o| hrm_user_handover : "离职交接映射"
  qc_report_template ||--o{ qc_report_template_version : "多版本"
  qc_report_template_version ||--o{ qc_report_instance : "冻结出件版本"
  finance_reimbursement ||--o{ finance_reimbursement_item : "费用明细"
  research_guideline ||--o{ research_application : "被申报引用"
  research_application ||--o{ research_topic_review : "专家评分明细"
  research_application ||--o{ research_fund_record : "经费拨付记录"
  research_fund_record ||--|| finance_reimbursement : "1:1 生成出账报销单(软关联 reimbursement_id)"
  ums_member ||--o{ research_application : "申报人(软关联无外键)"
</pre>
<pre style="display:none" class="mmd-src" data-mmd="11-ai-business-flow">
flowchart TD
  U1[采购员上传发票文件 blobId] --> O1{文件类型}
  O1 -->|图片 png/jpg/gif/bmp| O2[chatWithImage 多模态视觉模型]
  O1 -->|PDF| O3[PDFBox 抽取文本 → chat]
  O1 -->|txt/csv| O4[直读文本 → chat]
  O2 --> O5[JSON 发票号 抬头 供应商 金额 税率 日期]
  O3 --> O5
  O4 --> O5
  O5 --> O6[预填采购来票表单·人工确认]
  Q1[销售上传报价单 PDF/Word/Excel/图片] --> Q2[POI/PDFBox/视觉识别]
  Q2 --> Q3[JSON 报价头+产品明细行] --> Q4[预填报价单表单]
  J1[质检单头+指标结果+缺陷记录] --> J2[业务侧组装Prompt → chat]
  J2 --> J3[MdmItemApi 物料名·SrmSupplierApi 供应商名·CrmCustomerApi 客户名 拼入上下文]
  J3 --> J4[建议结果:合格/特采/退货/报废+理由+要点]
  J4 --> J5[质检员参考判定·AI不可用时提示人工判定]
  R1[工单BOM+库存可用量+已投料] --> R2[物料短缺预测·风险等级与缺口清单]
  R3[工序标准工时+设备每小时产量+传送倍率] --> R4[生产时长预测]
  R5[IPQC缺陷率+设备保养点检+物料可用率+关键工序] --> R6[生产风险预测]
  R7[进度+剩余天数+任务完成状态+客户交期] --> R8[交付预测·能否按时+置信度+延误因素]
  W1[质检员上传既有检验报告文件 blobId] --> W2{扩展名与内容判定}
  W2 -->|png/jpg/bmp/gif/webp| W3[原字节走 chatWithImage]
  W2 -->|PDF 有文本层| W4[PDFBox 抽取文本 → chat]
  W2 -->|PDF 扫描版| W5[PDFRenderer 逐页转PNG → 每页 chatWithImage]
  W2 -->|docx/xlsx| W6["POI 抽取·表格归一成稳定网格(gridSpan 补空占位 / vMerge 续格标 ↑同上)"]
  W2 -->|txt/csv| W7[直读文本 → chat]
  W2 -->|doc/xls| W8[拒绝·提示另存为 docx/xlsx]
  W3 --> W9[多页逐页识别]
  W4 --> W9
  W5 --> W9
  W6 --> W9
  W7 --> W9
  W9 --> W10["按(type,props)去重合并→扁平组件草稿+summary+warnings"]
  W10 --> W11[弹窗预览·人工确认后装进设计器画布]
  W11 --> W12[人工调整→保存为模板版本·这是唯一落库点]
  WCL[组件积木清单由前端从活注册表生成并随请求上传] --- W9
  WSAFE[只产草稿不落库·未注册type在装配阶段被跳过·永远产不出任意HTML] --- W10
  KB1[知识库文档上传URL] --> KB2[POI/Tika提取文本] --> KB3[切片约800tokens→向量化→Milvus]
  KB3 --> KB4[分段检索接口 topK+相似度阈值]
  KB4 -.-> KB5[暂未接入业务问答流程·待确认]
  MODE[模型层:通义千问为主·预留DeepSeek/OpenAI/Ollama未实现] --- O2
  NOTE[全项目无Function Calling·AI只输出结构化JSON由业务侧解析] --- J2
</pre>
<pre style="display:none" class="mmd-src" data-mmd="12-finance-flow">
flowchart TD
  U1["报销人填报报销单·主表: 报销单号(选填) 报销标题 报销总金额 申请日期 备注"] --> U2["录入报销明细 1..n 行: 费用类型 发票号码 发票金额 发票日期 开票方 费用说明"]
  U2 --> U3[发票附件: 图片或 PDF 先传 system storage-blob upload 取 blobId]
  U3 --> S1[保存 create·主表与明细与全部 blobIds 一次性提交]
  S1 --> G1["报销单号:可自定义;留空(含纯空格)则由后端生成 BX + yyyyMMdd + 6位流水(Redis·每日重置)"]
  G1 --> B1["明细先删后增全量覆盖·提交剥掉行 id(软删后重插避免主键冲突)"]
  B1 --> A1["附件绑定 recordType = finance_reimbursement·按该单全部 blobId 全量替换"]
  A1 --> K1[草稿 0]
  K1 -->|update 修改主表 明细 附件| K1
  K1 -->|submit 选审批流程 processDefinitionKey| K2[审批中 15·回写 processInstanceId]
  K2 -->|BPM 审批通过| K3[已通过 20]
  K2 -->|BPM 审批驳回| K4[已驳回 35]
  K2 -->|BPM 审批取消| K1
  K4 -->|改发票后重新 submit| K2
  K3 -->|archive 归档| K5[已归档 40·终态·写入 archiveTime]
  D1["仅草稿 0 可 delete·连带清理明细与发票附件"] -.-> K1
  E1["仅草稿 0 与已驳回 35 可 update·其余状态只读"] -.-> K4
  N1["独立模块·与销售采购链条任何单据均无关联;唯一例外是被课题经费拨付 1:1 自动生成(经 yudao-module-finance-api)"] --- K1
</pre>
<pre style="display:none" class="mmd-src" data-mmd="13-ums-membership-flow">
flowchart TD
  A1[填报入会申请·选会员类型 1个人 / 2单位] --> A2[个人分支: 姓名 联系电话 邮箱 工作单位 职称 研究方向 行业 地区]
  A1 --> A3[单位分支: 单位名称 法定代表人 统一社会信用代码 研究方向 行业 地区]
  A2 --> A4[选申请等级·按会员类型切换字典 ums_personal_grade / ums_unit_grade]
  A3 --> A4
  A4 --> A5[证明材料附件: 先传 system storage-blob upload 取 blobId]
  A5 --> S1[保存 create·附件按该单全部 blobIds 绑定]
  S1 --> G1["申请编号 SQ + yyyyMMdd + 6位流水(Redis·每日重置)·后端生成不可改"]
  G1 --> K1[草稿 0]
  K1 -->|update 修改申请| K1
  K1 -->|submit 选审批流程 processDefinitionKey| K2[审批中 15·回写 processInstanceId]
  K2 -->|BPM 审批通过| K3[已通过 20]
  K2 -->|BPM 审批驳回| K4[已驳回 35]
  K2 -->|BPM 审批取消| K1
  K4 -->|修改后重新 submit| K2
  K3 --> M1["审批通过钩子: 自动创建会员 ums_member (内部先查状态确为已通过才执行)"]
  M1 --> M2["会员编号 HY + yyyyMMdd + 6位流水(Redis)"]
  M2 --> M3["创建系统登录账号 AdminUserApi.createUser·用户名与初始密码=联系电话·昵称=申请人姓名·部门=学会会员"]
  M3 --> M4[会员落库·入会日期=当天·状态 1正常·来源申请单 applicationId 回填]
  M4 --> D2[会员列表·可手工 create / update / delete]
  D2 --> V1["等级类型校验: 单位会员(memberType=2) 不可选『理事长』(memberGrade=5) 直接拒绝"]
  M4 --> D1[会员通讯录·只读·按地区 regionCode 与行业 industry 检索]
  ATT1["附件 recordType = ums_application"] -.-> K1
  ATT2["附件 recordType = ums_member"] -.-> M4
  RES1["仅草稿 0 可 delete·连带清理附件"] -.-> K1
  RES2["仅草稿 0 与已驳回 35 可 update·其余状态只读"] -.-> K4
  ROL["审批链: BPM 流程定义 ums_membership_approve(归属分类 ums_membership_approve) 秘书处初审 → 理事会审议·两节点审批人=角色策略(超管角色兜底)·后端按分类实时取流程、不写死 key"] -.-> K2
  F1["审批中心看单: 流程定义表单类型=自定义表单·查看路径 /ums/application/detail/approve/index.vue·按 businessKey 回显申请单"] -.-> K2
  N1["独立模块·不与其他业务模块产生单据关联·仅复用 BPM 审批与 system 附件中心"] --- K1
</pre>
 
<pre style="display:none" class="mmd-src" data-mmd="14-research-flow">
flowchart TD
  subgraph P1 [课题指南发布]
    G0[新建指南·选类别 1重点课题 / 2一般课题] --> G1[填指南名称 申报起止时间 指南正文 附件]
    G1 --> G2["指南编号 GL + yyyyMMdd + 6位流水(Redis·每日重置)"]
    G2 --> G3[草稿 0·仅草稿可改可删]
    G3 -->|已产生申报单则禁止删除| G3B["提示该指南下已有 N 张申报单·需先处理申报单"]
    G3 -->|publish 发布| G4["已发布 20·回写 publishTime"]
    G4 -->|close 截止| G5[已截止 30]
  end
  G4 -->|"指南已发布才可作为申报前提"| A1
  subgraph P2 [在线申报]
    A1[选指南·要求已发布 且在申报期内] --> A2[选会员 ums_member 自动带出 姓名 工作单位 职称 电话 邮箱]
    A2 --> A3[填课题名称 课题组成员 申请经费 申报书正文 申报附件]
    A3 --> A4["申报编号 SB + yyyyMMdd + 6位流水·类别冗余自指南不信任前端传值"]
    A4 --> K1[草稿 0]
  end
  K1 -->|update 修改·仅草稿 0 与已驳回 35 放行| K1
  K1 -->|delete 删除·仅草稿 0 放行·连带清理附件| K1
  K1 -->|submit 选审批流程 processDefinitionKey| K2["审批中 15·回写 processInstanceId"]
  K1 -->|"submit 前逐条校验: ① 指南必须已发布 ② 当前时间必须在申报起止期内"| KERR["任一不满足即拒绝提交·提示带指南名与实际状态或起止时间"]
  K2 -->|"仅审批中可增删改专家评分"| RK[专家评审明细 research_topic_review·一位专家一行]
  RK -->|"后端逐行重算 compositeScore = 选题0.3 + 方案0.3 + 团队0.2 + 成果0.2"| RC[按加权分降序重排名次 ranking]
  RC -->|"回写主单: reviewAvgScore=全部专家均分 · reviewPass=均分达标 且 多数结论为推荐立项"| SUM[申报单评审汇总·删除最后一条明细时清空汇总]
  SUM -->|"秘书处汇总申报书与评审结果"| K3
  K2 --> K3
  subgraph P3 [立项审批 BPM research_topic_approve]
    K3["三节点顺序·审批方法与审批人在流程定义内配置"] --> KA[Activity_secretariat_review 秘书处形式审查]
    KA -->|通过| KB[Activity_expert_review 专家评审]
    KB -->|通过| KC[Activity_council_approve 学会立项审批]
  end
  KC -->|BPM 审批通过| K4["已立项 20·监听器生成 立项编号 LX + yyyyMMdd + 6位流水 与 approveTime·已有编号则幂等跳过"]
  KC -->|BPM 审批驳回| K5[已驳回 35]
  KC -->|BPM 审批取消| K1
  KA -->|驳回| K5
  K5 -->|修改后重新 submit| K2
  K4 -->|publishNotice 发布立项通知| N1["system_notice 公告·标题『关于公布立项课题的通知』·正文含课题名 立项编号 类别 申报人 单位 资助金额·回写 noticeId·重复发布被拦截"]
  K4 -->|登记拨款记录 research_fund_record| F1[填批次名称 拨付金额 拨付日期 收款单位 备注·凭证附件必填]
  F1 -->|"金额校验: 已登记金额 + 本次金额 不得超出申报单申请经费·超出时报出本次 已登记 合计 预算四项数值"| FGEN["自动生成 1:1 财务报销单 finance_reimbursement(1 行明细: 费用类型=其他 发票金额=拨付金额 发票日期=拨付日期 费用说明=课题名+批次)·附同一批凭证·立即提交 BPM·回写报销单号"]
  FGEN -->|回写台账状态| F3[报销审批中 15]
  F3 -->|"财务审核 Activity_finance_review"| FAPP[财务主管审批 Activity_finance_approve]
  FAPP -->|BPM 审批通过| F4[已拨付 20]
  FAPP -->|BPM 审批驳回| F5["已驳回 30·回写驳回原因与驳回时间"]
  F5 -->|"修改金额或凭证后重新发起报销"| F3
  FAPP -->|"发起人/管理员取消流程"| F2["待拨付 0·可编辑可删除"]
  F2 -->|"仅待拨付 0 可改可删·删除时联动清理财务侧草稿报销单"| F2
  F2 -->|"修改后重新发起报销"| F3
  ATT1["附件 recordType = research_guideline"] -.-> G3
  ATT2["附件 recordType = research_application"] -.-> K1
  ATT3["附件 recordType = research_fund_record·同一批 blob 双绑 finance_reimbursement(按 recordType+recordId 隔离互不影响)"] -.-> F1
  ROL["审批中心看单: 流程定义表单类型=自定义表单·创建路径 /research/application/index · 查看路径 /research/application/detail/approve/index.vue·按 businessKey 回显申报单"] -.-> K3
  ROL2["审批人取值: 三节点在流程定义内配好审批方法(依次审批)与审批人策略·部署后由管理员在 BPM 流程设计器按学会实际组织调整·提交时前端只让选流程不选审批人"] -.-> K3
  LIS2["监听器 ResearchFundStatusListener(流程 key=finance_reimbursement_approve·businessKey=报销单 ID): 通过→台账已拨付 20·驳回→已驳回 30+原因与时间·取消流程→退回待拨付 0(发起人撤回仅退回发起节点、流程未结束,不触发回写)·与 finance 自己的监听器订阅同一 key 并行触发"] -.-> F3
  FINAPI["跨模块走新建的 yudao-module-finance-api(FinanceReimbursementApi·本地 Bean 非 HTTP): 无已发布报销审批流程时先解析即报错·不建单·避免留下孤儿草稿报销单"] -.-> FGEN
  RES1["一期已对接财务: 拨款必须走 finance_reimbursement 报销单出账·课题侧台账=拨款台账·财务报销单=出账凭证·两边以 reimbursement_id 软关联(无外键)·财务审批结果自动回写本台账"] -.-> F1
  RES2["二期范围(本图未覆盖): 结项验收 结项证书 成果归档知识库 采纳情况与社会效益标注"] -.-> K4
  BOUND["独立模块 yudao-module-research·对 ums_member 为软关联(无外键)·对财务模块走 -api 子模块本地调用·仅复用 BPM 审批、system 公告与附件中心"] --- K1
</pre>
 
<script type="module">
const DATA = {};
async function loadJson(key){
  try{
    const r = await fetch('data/' + (key==='business'?'business':key) + '.json', {cache:'no-store'});
    if(r.ok) return await r.json();
  }catch(e){}
  return JSON.parse(document.getElementById('data-'+key).textContent);
}
function esc(s){return String(s ?? '').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));}
const mermaid = (await import('https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs')).default;
mermaid.initialize({startOnLoad:false, theme:'dark', securityLevel:'loose',
  themeVariables:{fontFamily:'"Segoe UI","Microsoft YaHei",sans-serif',fontSize:'13px'},
  flowchart:{useMaxWidth:true, htmlLabels:true}});
 
const mmdSrc = {};
document.querySelectorAll('pre.mmd-src').forEach(p=>{ mmdSrc[p.dataset.mmd]=p.textContent.trim(); });
 
const flowColor = {sales:'var(--c-sale)',purchase:'var(--c-erp)',production:'var(--c-prod)',quality:'var(--c-qc)',warehouse:'var(--c-wm)',aftersales:'var(--c-as)',finance:'var(--c-fin)',ums:'var(--c-ums)',research:'var(--c-res)',ai:'var(--c-ai)'};
 
async function boot(){
  DATA.business = await loadJson('business');
  DATA.modules  = await loadJson('modules');
  DATA.objects  = await loadJson('objects');
  DATA.flows    = await loadJson('flows');
  DATA.roles    = await loadJson('roles');
  const b = DATA.business.project, s = DATA.business.stats;
  document.getElementById('hProject').textContent = b.name + ' · 业务全景';
  document.getElementById('hSub').textContent = b.type + ' | ' + b.mainSystem;
  document.getElementById('footer').innerHTML = DATA.business.analysisBasis + ' · 生成于 ' + DATA.business.generatedAt + ' · 维护请走技能 project-business-analysis';
  renderOverview(); renderFlows(); renderObjects(); renderRoles(); renderAI(); renderPending();
  bindNav(); bindSearch(); bindFigToolbar();
  showPage('overview');
}
 
function renderOverview(){
  const b = DATA.business.project, s = DATA.business.stats;
  const answers = DATA.business.businessAnswers;
  let html = `
  <h2 class="pt">${esc(b.name)}</h2>
  <p class="desc">${esc(b.description)}</p>
  <div class="banner">
    <div class="small muted" style="margin-bottom:4px">核心业务主线</div>
    <div class="chain"><b>主线</b> ${esc(b.coreChain)}</div>
  </div>
  <div class="grid stats">
    <div class="stat"><b>${s.businessModules}</b><span>业务模块</span></div>
    <div class="stat"><b>${s.coreObjects}</b><span>核心业务对象</span></div>
    <div class="stat"><b>${s.documentFlows}</b><span>端到端业务流程</span></div>
    <div class="stat"><b>${s.approvalListeners}</b><span>BPM 审批监听器</span></div>
    <div class="stat"><b>${s.aiCapabilities}</b><span>AI 能力</span></div>
    <div class="stat"><b>${s.wmBillTypes}</b><span>仓储出入库单据类型</span></div>
  </div>
  <h3 class="sec">业务模块</h3>
  <div class="grid cards">`;
  const targets = {crm:'flow-sales',erp:'flow-purchase',pro:'flow-production',wm:'flow-warehouse',qc:'flow-quality',aftersales:'flow-aftersales',fin:'flow-finance',ums:'flow-ums',res:'flow-research',ai:'ai',srm:'flow-purchase',mdm:'objects',dv:'objects',hrm:'objects',bpm:'relation'};
  const accs = {mdm:'var(--c-base)',crm:'var(--c-sale)',srm:'var(--c-sup)',erp:'var(--c-erp)',fin:'var(--c-fin)',ums:'var(--c-ums)',res:'var(--c-res)',pro:'var(--c-prod)',wm:'var(--c-wm)',qc:'var(--c-qc)',dv:'var(--c-dv)',aftersales:'var(--c-as)',hrm:'var(--c-hrm)',bpm:'var(--c-bpm)',ai:'var(--c-ai)'};
  DATA.modules.modules.forEach(m=>{
    html += `<div class="card" style="--acc:${accs[m.key]||'#2c5cff'}" data-goto="${targets[m.key]||'panorama'}">
      <h3>${esc(m.name)}<span class="tag">${esc(m.tag)}</span></h3>
      <p>${esc(m.positioning)} — ${esc(m.problem)}</p>
      <div class="objs">核心对象:${esc(m.objects.slice(0,6).join('、'))}${m.objects.length>6?' 等':''}</div>
      <div class="objs">上游 ${esc(m.upstream.join('、')||'—')} → 下游 ${esc(m.downstream.join('、')||'—')}</div>
    </div>`;
  });
  html += `</div><h3 class="sec">业务十问(准确性自检)</h3><div class="grid cards">`;
  answers.forEach(([q,a])=>{ html += `<div class="card" style="cursor:default"><h3 style="font-size:13.5px">${esc(q)}</h3><p style="color:#b9c8dc">${esc(a)}</p></div>`; });
  html += `</div>`;
  document.getElementById('overview').innerHTML = html;
  document.querySelectorAll('#overview .card[data-goto]').forEach(c=>c.addEventListener('click',()=>showPage(c.dataset.goto)));
}
 
function flowFigHtml(f, name){
  return `<div class="fig" style="--acc:${flowColor[f.id]||'#2c5cff'}">
      <div class="bar"><span class="name">${esc(name||f.name)} · ${esc(f.mermaid)}</span>
        <div class="btns"><button data-z="-">-</button><button data-z="+">+</button><button data-z="0">重置</button><button data-fs>全屏</button></div></div>
      <div class="canvas"><div class="stage mermaid-wrap"><pre class="mermaid" data-mmd="${f.mermaid.replace(/^mermaid\//,'').replace(/\.mmd$/,'')}"></pre></div></div>
    </div>`;
}
function flowStepsHtml(f){
  return `<h3 class="sec">步骤与角色</h3>
    <table class="steps" style="--acc:${flowColor[f.id]||'#7ea6e8'}"><tr><th>阶段</th><th>动作(含真实状态流转)</th><th>角色</th></tr>
    ${f.steps.map(st=>`<tr><td class="stage">${esc(st.stage)}</td><td>${esc(st.action)}</td><td class="role">${esc(st.roles.join('、'))}</td></tr>`).join('')}
    </table>`;
}
function renderFlows(){
  DATA.flows.flows.forEach(f=>{
    if(f.id==='ai') return;
    const sec = document.getElementById('flow-'+f.id);
    if(!sec) return;
    sec.innerHTML = `<h2 class="pt">${esc(f.name)}</h2><p class="desc">${esc(f.summary)}</p>${flowFigHtml(f)}${flowStepsHtml(f)}`;
  });
}
 
function renderObjects(){
  const groups = {};
  DATA.objects.objects.forEach(o=>{ (groups[o.module]=groups[o.module]||[]).push(o); });
  let html = `<h3 class="sec">对象清单(${DATA.objects.objects.length})</h3>
  <table class="data"><tr><th style="width:120px">对象</th><th style="width:110px">所属模块</th><th>作用</th><th style="width:190px">表名</th></tr>`;
  DATA.objects.objects.forEach((o,i)=>{
    html += `<tr data-obj="${i}"><td><b style="color:#fff">${esc(o.name)}</b></td><td class="muted">${esc(o.module)}</td><td>${esc(o.doc)}</td><td class="small muted">${esc(o.table||'—')}</td></tr>`;
  });
  html += `</table>`;
  document.getElementById('objGrid').innerHTML = html;
  document.querySelectorAll('#objGrid tr[data-obj]').forEach(tr=>tr.addEventListener('click',()=>{
    const o = DATA.objects.objects[+tr.dataset.obj];
    document.getElementById('objDetail').innerHTML = `
      <h4>${esc(o.name)} <span class="muted small">· ${esc(o.module)}${o.table?' · 表 '+esc(o.table):''}</span></h4>
      <div class="kv"><b>作用</b>${esc(o.doc)}</div>
      <div class="kv"><b>上游</b>${esc((o.upstream&&o.upstream.length)?o.upstream.join('、'):'—')}</div>
      <div class="kv"><b>下游</b>${esc((o.downstream&&o.downstream.length)?o.downstream.join('、'):'—')}</div>`;
    document.getElementById('objDetail').scrollIntoView({behavior:'smooth',block:'nearest'});
  }));
}
 
function renderRoles(){
  const rs = DATA.roles;
  let html = `<h2 class="pt">角色与权限</h2><p class="desc">${esc(rs.note)}</p>
  <h3 class="sec">业务角色 → 可操作业务 → 产生数据</h3>
  <table class="data"><tr><th style="width:150px">角色</th><th style="width:130px">模块</th><th>操作</th><th style="width:180px">数据范围/机制</th></tr>
  ${rs.businessRoles.map(r=>`<tr><td><b style="color:#fff">${esc(r.role)}</b></td><td class="muted">${esc(r.modules.join('、'))}</td><td>${esc(r.operations.join(';'))}</td><td class="small">${esc(r.scope)}</td></tr>`).join('')}
  </table>
  <h3 class="sec">底座能力</h3>
  <div class="grid cards">${DATA.modules.platform.capabilities.map(c=>`<div class="card" style="cursor:default"><p style="color:#b9c8dc">${esc(c)}</p></div>`).join('')}</div>
  <p class="small muted" style="margin-top:10px">未启用模块:${esc(DATA.modules.platform.unused.join(';'))}</p>`;
  document.getElementById('roles').innerHTML = html;
}
 
function renderAI(){
  const aiFlow = DATA.flows.flows.find(f=>f.id==='ai');
  document.getElementById('ai').insertAdjacentHTML('afterbegin',
    (aiFlow? flowFigHtml(aiFlow) + flowStepsHtml(aiFlow) : '') );
  document.getElementById('aiGrid').innerHTML = DATA.business.aiCapabilities.map(a=>`
    <div class="card" style="--acc:var(--c-ai);cursor:default">
      <h3>${esc(a.name)}<span class="tag">AI</span></h3>
      <p>${esc(a.flow)}</p>
      ${a.callsApis&&a.callsApis.length?`<div class="objs">调用:${esc(a.callsApis.join(' · '))}</div>`:''}
    </div>`).join('');
}
 
function renderPending(){
  document.getElementById('pendingList').innerHTML = DATA.business.pendingConfirm.map(p=>`<li>${esc(p)}</li>`).join('');
}
 
// 渲染排队:同一时刻只跑一个 mermaid.run,避免快速切换导航时图表在隐藏容器里布局(产生 NaN transform)
let renderQueue = Promise.resolve();
function showPage(id){
  document.querySelectorAll('nav a').forEach(a=>a.classList.toggle('active', a.dataset.t===id));
  document.querySelectorAll('section.page').forEach(s=>s.classList.toggle('on', s.id===id));
  const sec = document.getElementById(id);
  const todo = sec ? [...sec.querySelectorAll('pre.mermaid[data-mmd]')].filter(p=>!p.dataset.done) : [];
  if(todo.length){
    renderQueue = renderQueue.then(async()=>{
      if(!sec.classList.contains('on')) return; // 用户已切走,下次进入再渲染
      todo.forEach(p=>{ if(mmdSrc[p.dataset.mmd]) p.textContent = mmdSrc[p.dataset.mmd]; p.removeAttribute('data-processed'); });
      try{ await mermaid.run({nodes: todo}); }
      catch(e){ todo.forEach(p=>p.textContent = 'Mermaid 渲染失败:'+e.message); return; }
      if(sec.classList.contains('on')) todo.forEach(p=>p.dataset.done='1');
    });
  }
  window.scrollTo(0,0);
}
function bindNav(){
  document.querySelectorAll('nav a').forEach(a=>a.addEventListener('click',()=>showPage(a.dataset.t)));
  document.addEventListener('click',e=>{
    const card=e.target.closest('.card[data-goto]');
    if(card) showPage(card.dataset.goto);
  });
}
function bindFigToolbar(){
  document.addEventListener('click',e=>{
    const btn=e.target.closest('.fig button');
    if(!btn) return;
    const fig=btn.closest('.fig'), stage=fig.querySelector('.stage');
    let z=parseFloat(stage.dataset.z||'1');
    if(btn.dataset.z==='+'||btn.dataset.z==='-') z=Math.min(3,Math.max(.4, z + (btn.dataset.z==='+'?.2:-.2)));
    else if(btn.dataset.z==='0') z=1;
    stage.dataset.z=z; stage.style.transform='scale('+z+')';
    if(btn.hasAttribute('data-fs')){
      if(document.fullscreenElement) document.exitFullscreen();
      else fig.requestFullscreen();
    }
  });
}
function bindSearch(){
  const input=document.getElementById('globalSearch'), box=document.getElementById('searchResult');
  const items=[];
  DATA.objects.objects.forEach(o=>items.push({type:'业务对象',name:o.name,meta:o.module+' · '+o.doc,go:()=>{showPage('objects');const idx=DATA.objects.objects.indexOf(o);const tr=document.querySelector('#objGrid tr[data-obj="'+idx+'"]');if(tr)tr.click();}}));
  DATA.flows.flows.forEach(f=>items.push({type:'业务流程',name:f.name,meta:f.summary.slice(0,60)+'…',go:()=>showPage('flow-'+f.id)}));
  DATA.modules.modules.forEach(m=>items.push({type:'业务模块',name:m.name,meta:m.positioning,go:()=>showPage('overview')}));
  DATA.roles.businessRoles.forEach(r=>items.push({type:'角色',name:r.role,meta:r.modules.join('、'),go:()=>showPage('roles')}));
  input.addEventListener('input',()=>{
    const q=input.value.trim().toLowerCase();
    if(!q){box.style.display='none';return;}
    const hit=items.filter(i=>(i.name+i.meta).toLowerCase().includes(q)).slice(0,15);
    box.innerHTML = hit.length? hit.map((h,i)=>`<div class="sr-item" data-i="${i}"><div>${esc(h.name)} <span class="sr-type">${esc(h.type)}</span></div><div class="sr-meta">${esc(h.meta)}</div></div>`).join('') : '<div class="sr-item"><span class="muted">无匹配结果</span></div>';
    box.style.display='block';
    box.querySelectorAll('.sr-item[data-i]').forEach(el=>el.addEventListener('click',()=>{hit[+el.dataset.i].go();box.style.display='none';input.value='';}));
  });
  document.addEventListener('click',e=>{ if(!e.target.closest('#globalSearchWrap')) box.style.display='none'; });
}
await boot();
</script>
</body>
</html>