p2_ready_check.py
341 KB
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
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
存量 Controller「就绪度」盘点:老门户的每个 Controller,它依赖的 `clouds.api` 服务在新栈**齐不齐**。
它回答什么(这是挑下一批迁移目标前的**第一步**,见 AGENTS.md §3.1 第 0 步):
一个老 Controller 里的每个 `import com.zteits.clouds.api.service.*`,是不是
(a) 接口在 `parking-module-api` 里存在,且
(b) 有一个 `class XxxImpl implements Xxx` 的实现类。
两条都满足才算"就绪"。**任一不满足 = 这个 Controller 搬过来就是 500**,
不管它的端点看上去多简单、多像"只读",所以**不要**靠读代码去猜,跑这个脚本。
全仓当前结果(2026-10-13 盘点,迁移进度同步到 **2026-10-26 第二百八十切片**,`zteits-bcp-portal` 一个 portal):老门户 **232 个 Controller**,
其中 **227 个有映射 / 5 个是死代码**(整类被注释掉,本脚本已跳过)。
有映射的 227 个里:**223 个 / 1180 端点就绪**(依赖服务齐全),**4 个 / 38 端点有缺口**。
就绪的 223 个中已迁 **223 个 / 1180 端点** → 就绪且未迁 **0 个 / 0 端点**;
**再过坑 22 一刀:✔ 0 个 / 0 端点,⚠ 0 个 / 0 端点(⚠A 0/0 + ⚠B 0/0)。**
⛔ **2026-10-20 第一百六十八切片:`OrderExamineController` 判定不可迁(半合并壳)**
四个生产分支都只有 Controller + API 接口,**没有实现、没有 Dubbo provider、`examinePlNos` 全仓无赋值**;
实现只在未合入的 `origin/chifeng_dev_high_video_examine` ⇒ 机械缺口仍报,但单列
`gapAdjudicated*` / `gapOpen*` 两组数,让「可迁缺口」归零而「机械缺口」仍说真话。
详见代码里的 `GAP_ADJUDICATED` 表。
★ **2026-10-20 第一百六十九切片:P3 ③ APP 端登录(`UserLoginAppController` 5 端点)**
**本片不在本脚本口径内**:`DEFAULT_LEGACY` 只扫 `zteits-bcp-portal/.../web`,
`zteits-pay-portal/.../web/app` 从来不在扫描根里(app 端 Controller 不消费 portal 的
`clouds.api` 契约)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**(已判 1/2、可迁 1/9)、5 死代码**全部不变**。这里只记录口径,不改判定。
★ **2026-10-20 第一百七十切片:P3 ④ 登录桥补 `loginCode`(零端点)**
只改框架层(`LoginUser.info` 多一个 `username`)+ `LoginUserBridge`,**不新增也不迁移任何端点**
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
★ **2026-10-20 第一百七十一切片:APP 端只读小接口(5 类 / 7 端点)**
搬的是 `zteits-pay-portal/.../web/app/{Activity,AppNews,CommonProblem,CustRule,AppCheck}`,
同 §169 一样**不在 `DEFAULT_LEGACY` 里** ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。这里只记录口径,不改判定。
★ **2026-10-20 第一百七十二切片:APP 端码表/组织/反馈建议(3 类 / 8 端点)**
搬的是 `zteits-pay-portal/.../web/app/`
`{SysCodeValueQueryController, AppOrgController, SuggestController(只读 4/5)}`,
同样**不在 `DEFAULT_LEGACY` 里**(这 3 个类只依赖 `AppOrgService`/`TdCCustPersonService`/
`TdCCustSuggestService`/`TdCCustSuggestDetailQueryService` 与缓存工具,不消费 portal 契约)
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。这里只记录口径,不改判定。
★ **2026-10-20 第一百七十三切片:APP 端对外车场信息/停车记录/ETC(3 类 / 5 端点)**
搬的是 `zteits-pay-portal/.../web/app/`
`{outinterface/ParkInfoController, outinterface/ParkRecordController,
newversion/ETCStateController}`,同样**不在 `DEFAULT_LEGACY` 里**
(这 3 个类只依赖 `ParkFreeBerthQueryService`/`ParkingLotQueryService`/`BerthManageService`/
`ParkOrderQueryService`/`TdCCustPersonService`/`SysConfigCodeOrgIdService` 与 `AppTokenUtil`,
不消费 portal 契约)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。这里只记录口径,不改判定。
★ **2026-10-20 第一百七十四切片:APP 端接口平台/积分/时域卡/模板缓存/现金找零(5 类 / 9 端点)**
搬的是 `zteits-pay-portal/.../web/app/`
`{ApiController, CustScoreController, newversion/VipTimeCardRuleController,
MessageTempletCacheRefreshController, cashchange/KeyTopCashChangeController}`,
同样**不在 `DEFAULT_LEGACY` 里**(这 5 个类只依赖 `ApiService`/`TdCCustScoreDetailService`/
`TdCFileService`/`TVipAreaCardService`/`TdCMsgTemplateService` + `MessageTempletCacheUtil`,
不消费 portal 契约)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。这里只记录口径,不改判定。
★ **2026-10-20 第一百七十五切片:APP 端初始化/无感支付/特殊车牌(3 类 / 4 端点)**
搬的是 `zteits-pay-portal/.../web/app/`
`{pingkang/InitController, newversion/NonSensePaymentController,
specialcar/SpecialCarnumberBillingController}`,同样**不在 `DEFAULT_LEGACY` 里**
(只依赖 `SysCommonService`/`TdCAcctNoSecretService`/`SpecialCarnumberBillingService`/
`TdCAcctPersonService` + `RedisCacheUtil`/`ParkFreeBerthsCacheUtil`,不消费 portal 契约)
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。这里只记录口径,不改判定。
★ **2026-10-20 第一百七十六切片:APP 端用户签到/免密支付(2 类 / 9 端点)**
搬的是 `zteits-pay-portal/.../web/app/`
`{UserSignController, AcctNoSecretController}`,同样**不在 `DEFAULT_LEGACY` 里**
(只依赖 `TdCCustSignService`/`AcctNoSecretService`/`TdCCustPersonService`,
不消费 portal 契约、不新增缓存)
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。这里只记录口径,不改判定。
★ **2026-10-20 第一百七十七切片:APP 端年底活动/卡劵(2 类 / 8 端点)**
搬的是 `zteits-pay-portal/.../web/app/`
`{YearActivityController, CouponRuleController}`,同样**不在 `DEFAULT_LEGACY` 里**
(只依赖 `TdCCouponRuleService`/`TdCCouponPersonService`/`TdCCustScoreDetailService`/
`TdCCustPersonService`/`AppOrgService`,不消费 portal 契约)
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。这里只记录口径,不改判定。
★ **2026-10-20 第一百七十八切片:APP 端车主车牌(1 类 / 3 端点)**
搬的是 `zteits-pay-portal/.../web/app/UserCarController`,同样**不在 `DEFAULT_LEGACY` 里**
(只依赖 `TdCCarService`/`TdCCarBrandService`/`TdCCarCheckRecordQueryService`,不消费 portal 契约)
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。这里只记录口径,不改判定。
★ 注意:同批原定的 `CustScoreController`(`/score/*` 4 条)新栈早已迁过
(`controller/app/score/CustScoreAppController`)⇒ 立项先 `grep` 路由,别只 grep 类名。
★ **2026-10-20 第一百七十九切片:APP 端停车场信息查询(1 类 / 7 端点)**
搬的是 `zteits-pay-portal/.../web/app/TabParkingLotController`(老 `/park`),同样
**不在 `DEFAULT_LEGACY` 里**(只依赖 `ParkingLotQueryService`/`TBillingDescriptionQueryService`/
`ParkFreeBerthQueryService`/`TpPBerthsQueryService`/`BerthManageService`/`AppOrgService`,
全部是新栈既有 Bean;6 个门户 VO 落到 `parking-legacy-common/.../vo/app/parklot`)
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。这里只记录口径,不改判定。
★ 注意:本片 7 条里只有 `query-park-berth-msg-res` 验签;带对象体的 5 条沿用老 `@Valid`,
所以 `{}` 会被 `AppBaseReq` 的 `@NotBlank` 挡在绑定层 400(不是 500)。
★ **2026-10-20 第一百八十切片:APP 端小额免密支付(1 类 / 6 端点,4 条真写)**
搬的是 `zteits-pay-portal/.../web/app/PettyPaymentController`(老 `/pettyPayment`,新 `/pay/petty-payment`),
同样**不在 `DEFAULT_LEGACY` 里**(只依赖 `TdCCustPersonService`;2 个门户 VO 落在
`parking-legacy-common/.../vo/app/pettypayment`)⇒ 就绪 **225/1199**、已迁 **221/1195**、
未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。这里只记录口径,不改判定。
★ 注意:本类**一次都没调** `validateSigin`/`checkParam`,`@NoAuth`/`⚠A` 那套启发式对它不适用;
取用户靠 `AppTokenUtil.getUserInfo` ⇒ 假 token 是 [存量错误码 -10000],不是 [4014]。
★ 注意:`IRainParkingController`(9)整个类已注释掉 ⇒ 死代码,没有迁移目标。
★ **2026-10-20 第一百八十一切片:反馈建议第 5 条 `saveFeedbackAndSuggest`(1 端点,真写)**
补的是第一百七十二切片留下的那条写端点,直接加进既有 `SuggestAppController`
(老 `/suggest/saveFeedbackAndSuggest` → 新 `/pay/suggest/save-feedback-and-suggest`)
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。这里只记录口径,不改判定。
★ 注意:`orgId` 非数字时 `Long.parseLong` 抛 NFE,位置在 token 校验**之前** ⇒ 500「系统异常」。
★ 注意:新增分支的 `insertTdCCustSuggest` 返回值被丢弃(存量缺陷,照搬)。
★ **2026-10-20 第一百八十二切片:APP 端个人卡券 `CouponPersonController`(1 类 / 8 端点,4 条真写)**
同样**不在 `DEFAULT_LEGACY` 里**(`zteits-pay-portal/.../web/app` 不在盘点范围)。依赖全是新栈既有 Bean:
`TdCCouponPersonService`/`TdCCouponRuleService`/`TdCCustScoreDetailService`/`TdCAcctPersonService`/
`WhitelistService`/`AppOrgService`/`DiscountWrapper`/`TActivityThirdDiscountService`/`PayOrderQueryService`;
5 个门户 VO 落到 `parking-legacy-common/.../vo/app/coupon` + `.../vo/app/billquery`
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
这里只记录口径,不改判定。
★ 注意:同类里**三种老鉴权档位并存** —— `AppRsesultUtil.checkParam(String)`(回信封)与老
`AppBaseController#checkParam(AppBaseReq)`(抛 `BizException`)的 `{}` 都是 [4014];
裸 `AppTokenUtil.getUserCustId` 是 [-10000];`deleteCouponPerson` 的 1003 文案校验在鉴权**之前**。
★ 注意:本片把 `DiscountWrapper` 从 `parking-module-order` 移到 `parking-legacy-common`
(**同 FQN `com.zteits.ipay.util`**),order 侧 import 不变、单进程仍只有一个 Bean。
★ **2026-10-20 第一百八十三切片:APP 端账号信息 `UserController`(7 端点,4 条动库)**
默认盘点口径同样**不含** `zteits-pay-portal/.../web/app`。老类共 25 条 POST,其中 6 条**早已搬过**
(`sendverificode`/`checkverificode`/`login`/`alias`/`logout` → §169 `/pay/user-login/*`;
`userCarsInfoEdit` → §178 `/pay/user-car/*`)⇒ 本片只搬 `queryUserInfo`/`shareInfo`/
`getUserPhoneByToken`/`updateCustPersonBaseInfo`/`queryCustPersonBaseInfo`/`updateUserChangeOrgId`/
`queryUserCars` 七条到 `/app-api/pay/user-info/*`。依赖全是既有 Bean;老门户的 `AppOrgCommonService`
复制进 `parking-legacy-common/.../pay/portal/util`(同 FQN)
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
这里只记录口径,不改判定。
★ 注意:`updateUserChangeOrgId` **没有任何签名校验**,`checkAppToken` 失败就 `success(true)` 早退
(回成功信封但一行不写);`queryUserInfo`/`queryCustPersonBaseInfo` 在读接口里 `updateByUserPhone` 写库。
★ **2026-10-20 第一百八十四切片:APP 端注册/登录/改密一族 `UserController` 最后 10 端点(9 条动库)**
默认盘点口径同样**不含** `zteits-pay-portal/.../web/app`;且工具是**按类**判已迁,`UserController`
早在 §169 就计入 221/1195 ⇒ 本片把老类剩下的 `userRegistration`/`passwordLogin`/`messCodeBeforeCheck`/
`verificationCodeLogin`/`userRecoverPasswordCheck`/`changePassword`/`setPassword`/`clearAccount`/
`oneClickLogin`/`uploadHeadPic` 十条搬到 `/app-api/pay/user-account/*` 后,计数**一字不动**
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
这里只记录口径,不改判定。
★ 注意:**multipart 端点缺参是 500 不是 400** —— `upload-head-pic`(`@RequestParam picFile + token`)
空 body 被全局兜底成「系统异常」⇒ 进不了 `LEGACY_WRITES` 的 400 断言,单钉在冒烟 §2fu (c)。
★ 注意:`oneClickLogin` **不验签**,第一道是 `operatorToken` 非空(空 ⇒ [1003]),非空值会调**外部运营商接口**
⇒ 冒烟只发空串;其余 8 条走老私有校验(先必填、再验签),假签名一律 [4014]。
★ 注意:`FastDFSClientWrapper` 在 **pay 模块**是活的 `@Component`(`com.zteits.pay.util`),
order 模块那份整类被注释 ⇒ `uploadHeadPic` 直接注入即可,无需复制。
★ **2026-10-20 第一百八十五切片:APP 端会员卡办理 / 区域卡规则 / 时域卡使用(3 类 / 9 端点,全只读)**
默认盘点口径同样**不含** `zteits-pay-portal/.../web/app`:`ApplyVipCardController`(2)→
`/app-api/pay/apply-vip-card/*`、`VipAreaCardRuleController`(6)→ `/app-api/pay/vip-area-card-rule/*`、
`VipTimeCardUseController`(1)→ `/app-api/pay/vip-time-card-use/*`。依赖全是既有 Bean
⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
这里只记录口径,不改判定。
★ 注意:**老基类 `com.zteits.pay.portal.web.app.AppBaseController` 本片首次搬进 `parking-legacy-common`(同 FQN)**
⇒ 后续 APP 切片直接 `extends`,别再内联副本;同时搬了 `com.clouds.common.constants.ParkConstant`
(parking-cloud 原先只有 `com.zteits.park.constant.ParkConstant`,pay 模块不依赖 park 模块)。
★ 注意:`{}` 落点分两档 —— 带 `@Valid` 的 3 条 400「请求参数不正确」,`VipAreaCardRule` 的 6 条
**无 `@Valid`** ⇒ 进方法体被 `checkParam` 拦成 500 `[4014]`。
★★ 注意:`@Valid` 多约束并存时校验报文**不稳定**(`@NotNull carNumber` 与 `@NotBlank` 同时命中,
遍历顺序随 HashMap 变)⇒ 冒烟只锁 400 + 「请求参数不正确」前缀。
★ **2026-10-20 第一百八十六切片:APP 端充电桩充电(1 类 / 4 端点,3 读 + 1 写)**
口径同样**不含** `zteits-pay-portal/.../web/app/battery/BatteryChargeController`:
`queryEquipChargeStatus`→`/app-api/pay/battery-charge/query-equip-charge-status`、
`queryBatteryChargeInfo`→`/query-battery-charge-init-info`、
`queryAccountDetailsForPage`→`/query-order-charge-info-for-page`、`stopCharge`→`/stop-charge`。
依赖全是既有 Bean ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。这里只记录口径,不改判定。
★ 注意:APP 端老控制器**还有第二种鉴权形态** —— 这 4 条**都没有 `checkParam`**,只靠裸
`AppTokenUtil.getUserCustId(token)` ⇒ 无 token / 假签名的落点是 500 `[存量错误码 -10000]`,
不是 4014;`query-order-charge-info-for-page` **无 `@Valid`** ⇒ `{}` 也到 [-10000]。
★ 注意:`stop-charge` 是**真写 + 出站**(`queryStopCharge` 下发停机 + `updateOrderChargeStatusStop`),
冒烟只发空 body;`query-battery-charge-init-info` 会做出站设备认证,探针只用必然查不到的假 `connectorCode`。
★ 注意:`ChargeStateInfoDTO` 本片**补回老 api 的 3 个字段**(`feeInfoList`/`stationLng`/`stationLat`)——
parking-cloud 早先裁剪过,纯加字段、不改 `BizResult` 与接口签名。
★ **2026-10-20 第一百八十七切片:APP 端停车场会员卡(1 类 / 12 端点,11 读 + 1 写)**
口径同样**不含** `zteits-pay-portal/.../web/app/ParkVipCardController`(老 `/parkvip`)⇒
就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
这里只记录口径,不改判定。
★ 注意:本类是「三种鉴权形态并存」样本 —— 2 条 `@Valid` + `validateSigin`、2 条 `checkParam`、
1 条 `AppRsesultUtil.validateSigin(JSONObject)`、其余 7 条**零校验** ⇒ `{}` 落点分
400 / 500 [4014] / 老信封 1003 / 老信封 0 四档,写断言前必须逐条 grep。
★ 注意:`query-parking-vip-card` 本地恒 500「系统异常」—— bill 的
`TVipCardParkSaleRelSmapper.querySaleVIPCardRuleForPage` 是存量 `GROUP BY` 写法,
本地 MySQL 8 `only_full_group_by` 直接拒 ⇒ 非回归、红线⑥不修、冒烟不断言。
★ 注意:`VipCardAuthPersonBjxcService` 只有**嵌套包**(`...order.VipCardAuthPersonBjxc.`)那个接口
有实现;用扁平包注入会启动失败。⛔ `change-car-number-for-vip-card` 是真写,冒烟只发空 body。
★ **2026-10-20 第一百八十八切片:APP 端发票申领(1 类 / 21 端点,11 读 + 10 写)**
口径同样**不含** `zteits-pay-portal/.../web/app/InvoiceHelperController`(老 `/invoice`)⇒
就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
这里只记录口径,不改判定。
★ 注意:**`@Valid` 能否把 `{}` 挡成 400 取决于 VO 的父类** ——
`ResendInvoiceMailReq extends AppBaseReq`(app 公共五件套 `@NotBlank`)⇒ 400;
`QueryInvoiceHeaderReq extends AppBaseRequest`(无约束)⇒ `{}` 直接成功;
17 条 `@RequestBody String` 与 1 条**无 `@Valid`** 的 `query-rnt-pay-order-for-invoice`
⇒ `{}` 落到方法体里的 `checkParam`,500 `[4014]`。
★ 注意:`query-invoic-header` / `query-invoic-order-date` 老代码把 `checkParam` **注释掉**
⇒ 无鉴权公开可读(照搬)。`query-invoic-order-date` 读 `SysCodeValueCacheUtil(APP_INVOICE_QUERY_DATE)`,
本机未配 ⇒ 空列表,别据此判"端点死了"。
⛔ `open-ticket-for-cust-invoice-his-info`(真插开票历史 + 航信开票队列出站)与
`resend-invoice-mail`(真实发邮件)是真写,冒烟只发空 body;同片另有 8 条纯 CRUD 写端点。
★ **2026-10-20 第一百八十九切片:APP 端账户充值(1 类 / 19 端点,15 读 + 4 写)**
口径同样**不含** `zteits-pay-portal/.../web/app/AppAcctRechargeController`(老 `/appAcctRecharge`)⇒
就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
这里只记录口径,不改判定。(`zteits-minipay-portal` 里同 FQN 的另一份不搬。)
★ 注意:同一类里**三种鉴权形态混排** —— 7 条 `@Valid` + VO 继承 `AppBaseReq` ⇒ `{}` 400;
9 条无 `@Valid` 的 VO + 3 条 `@RequestBody String` ⇒ `{}` 落 `checkParam`,500 `[4014]`;
`get-account-recharge-amount` **无入参、无鉴权**(公开可读码表,本机恒 `[{"code":"0","name":"0"}]`)。
★ 注意:`get-park-card-by-park-card` / `account-recharge-by-park-card` 是**死端点**,恒抛
`[存量错误码 3017]`「该版本已经失效」(照搬,别当回归)。
⛔ 真写 4 条:`get-accout-balance`(`checkParam` 后 `updateByUserPhone` 刷客户操作时间)、
`open-account`、`acct-deposit-refund`(建退款单)、`account-recharge-by-recharge-card`
(`aliWxPayService` 建充值支付单);冒烟只发空 body。
★ 负向探针必须**带 token**:不带 token 时安全层先回 401「账号未登录」,看不到 404。
★ **2026-10-20 第一百九十切片:APP 端会员卡退款(1 类 / 2 端点,1 读 + 1 写)**
口径同样**不含** `zteits-pay-portal/.../web/app/ParkVipCardRefundController`(老 `/parkviprefund`)⇒
就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
依赖只有既有 Bean `RefundOrderOperService`;新增 2 个门户 VO(`vo/app/vipcard/VipCardRefundReq` +
`QueryVipCardRefundReq`)。
★ 注意:两条都 `@Valid` + VO 继承 `AppBaseReq` ⇒ `{}` 统一 400;假签名统一 500 `[4014]`。
⛔ `/refund` 是真写(建退款单,且先查重),冒烟只发空 body。
★ **2026-10-20 第一百九十一切片:APP 端消息推送开关(1 类 / 1 端点,1 写)**
口径同样**不含** `zteits-pay-portal/.../web/app/MsgPushController`(老 `/msgPush`)⇒
就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
依赖只有既有 Bean `TdCCustPersonService.openMsgPush`,零新增 VO/DTO。
★★ 注意:该类**不继承 `AppBaseController`**、没有 `checkParam`/`validateSigin`;token 从 body JSON 取,
直接 `AppTokenUtil.getUserInfo` ⇒ 无效 token 是 **`-10000`「登录失效」**,不是 `4014`。
⛔ `/open-or-close` 改客户推送状态,冒烟只发空 body。
★ **2026-10-20 第一百九十二切片:APP 端极光消息推送(1 类 / 6 端点,4 读 + 2 写)**
口径同样**不含** `zteits-pay-portal/.../web/app/JPushController`(老 `/push`)⇒
就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
依赖全是既有 Bean(`JPushService`/`ParkingLotQueryService`/`TdCMsgTemplateService`/`TdCCustPersonService`);
新增 3 个门户 VO(`vo/app/messagepush/*`)。
★★ 注意:**同一类三种鉴权落点** —— 4 条 `String`+`checkParam`/`validateSigin` ⇒ `{}`/假签名 500 `[4014]`;
`new-message-push-homepage` 假 token 抛 `[2003]`;`un-reads-total` 假 token **回 code "0" 全 0 成功**
(存量契约,别当缺陷)。逐条看方法体,别整类套模板。
⛔ 真写 2 条:`delete-pushed-message-details`(按 msgType 删)、`new-message-push-homepage`(读完置已读);
冒烟只发空 body。
★ **2026-10-20 第一百九十三切片:APP 端与华软 ETC 结算对接(1 类 / 2 端点,2 写)+ 补回 QR 进出场链**
口径同样**不含** `zteits-pay-portal/.../web/app/etcinterface/ElectronicTollCollectionController`
(老 `/etc/pay/`)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。
本片补整条链:13 个 VO(etcinterface/qrcodeinout/billquery/ETCBaseReq)、
`portal/util/{ParkLotCommonService,DateUtils}`、`portal/config/TimedCacheFactory`、
`portal/web/app/qrcodeinoutparking/`(接口/路由/适配器/基类 + 4 个 impl + 科拓费用查询 InnerService)。
★★ 注意:**返回值不是 `AppBaseRes` 信封** —— 签名失败回 `{"code":"5","message":"验证签名失败"}`
(`QueryFeeVO`/`PayNoticeVO`),断言按 code 字符串比,别套 500/[4014] 模板。
⛔ 两条都真写:`/query-fee` 改支付单 + 写 Redis 防重;`/notice` 走 `etcPayService.payNotify` 真结算。
冒烟只发空 body(两条也都进 `LEGACY_WRITES`)。
★ `KeyTopQRcodeInOutParkController` 仍留(下一片搬;当时误以为它「真正使用 `BillQueryCommonService`」)。
★ **2026-10-20 第一百九十四切片:科拓道闸扫码进出场(1 类 / 2 端点,2 写)**
口径同样**不含** `zteits-pay-portal/.../web/app/qrcodeinoutparking/KeyTopQRcodeInOutParkController`
(老 `/keyTopQrCodeInOutPark`)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。零新增 VO(`UseCouponQueryFeeReq/Res` 侦察阶段已补)。
★★ 纠正上一片的结论:`BillQueryCommonService` 在本类**是死字段**(注入后两个方法体从未引用),
删掉即可,**不必**搬 `BillQueryService`/`BillQueryRouteService` 整条链。
★★ 注意:老类 `// validateSigin(request);` **是注释掉的**,全类无签名校验 ⇒ `{}`/假字段都是
**400「请求参数不正确」**,不是 500/[4014]。别按签名失败模板写断言。
⛔ 两条都真写:`QrCodeInPark`/`QrCodeOutPark`(补录进场、建/改待支付单)、
`updateWaitPayOrderFeeByPayOrderId`(改金额);冒烟只发空 body / `{}` / 假字段。
★ **2026-10-21 第一百九十五切片:共享车位收益提现(1 类 / 2 端点,1 读 + 1 写)**
搬的是 `zteits-pay-portal/.../web/app/sharedparking/SharedParkingTransferController`
(老 `/sharedparking/transfer`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,
就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。新增 3 个门户 VO:
`vo/app/sharedparking/{OperTransferReq,QueryTransferReq,QueryTransferLogRes}`。
★★ 同一控制器里的**两个端点假签名码不同**:`oper-transfer` 的 `OperTransferReq` 自带
5 个 `@NotNull/@NotBlank`,`@Valid` **先于** `checkParam` 失败 ⇒ **400**;
`query-transfer` 的 `QueryTransferReq` 无自有注解 ⇒ **500 [存量错误码 4014]**。别套模板。
⛔⛔ `/oper-transfer` 是全仓最危险的写端点:`aliWxPayService.transfers` **真实发起
支付宝/微信提现**,微信分支还先调 `sns/oauth2/access_token` 换 openid。
冒烟**只发无 body**(进 `LEGACY_WRITES`),真机联调必须沙箱/测试账号。
★ 存量缺陷照搬:`query-transfer` 按月**循环查库(N+1)**;`queryTransferLogResList` 在循环外创建
⇒ 第 n 个月响应重复带上前 n-1 个月记录;`totalFee` 无 `@NotNull`,`getTotalFee()<=0` 会 NPE。
★ **2026-10-21 第一百九十六切片:共享车位·我的共享车位(1 类 / 6 端点,全部读)**
搬的是 `zteits-pay-portal/.../web/app/sharedparking/SharedParkingHomeController`
(老 `sharedparking/sharedBerthHome`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,
就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。新增 10 个门户 VO(`vo/app/sharedparking/` 9 个 +
`vo/app/billquery/QuerySharedParkFeeRes`)。
★★ 与 §194 相反:`BillQueryCommonService` 在这里**不是死依赖**(3 处 `queryBillingForShared` 调用)。
新栈在 `com.zteits.pay.portal.util` 补回**只含两个重载**的版本(不需要 `BillQueryService`/
`BillQueryRouteService` 整条链)。
★★ `{}` 在同一个类里有**三种落点**:`query-shared-page` 无 `checkParam` ⇒ `code="0"`(**字符串**);
两个走 `@Valid` 的 ⇒ 400「请求参数不正确」;其余三个 ⇒ 500 `[4014]`。
★★ 400 文案**不稳定**(`不能为空` 与 `不能为null` 随机,取决于 Hibernate Validator 遍历顺序)
⇒ 断言只钉 `请求参数不正确` 前缀。
★ 假签名三种:`query-shared-page` ⇒ 500 `[-10000]`(`AppTokenUtil` 认不出 token);
`query-shared-by-berth-no` ⇒ 400(VO 自带 `@NotNull`);其余四个 ⇒ 500 `[4014]`。
★ 6 条**全是读** ⇒ 零 `LEGACY_WRITES` 增量、零 `@OperateLog`。存量缺陷:分页里 N+1 查订单、
`isAllowDownLock` 查不到订单直接 NPE、`getPreActFeeForShareBerth` 的允许取消时长可能为负。
★ **2026-10-21 第一百九十七切片:共享车位·预订(1 类 / 6 端点,2 读 + 4 写)**
搬的是 `zteits-pay-portal/.../web/app/sharedparking/SharedParkingReservationController`
(老 `/sharedparking/reservation`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,
就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。新增 10 个门户 VO(全部继承 `QuerySharedByPlNoAndBerthNoReq`)。
★★ 本类两个口径最干净的对照:6 个 VO 全**继承带 `@NotNull` 的基类** ⇒ `{}` 与假签名
**一律 400「请求参数不正确」**,`@Valid` 先于 `validateSigin`/`checkParam`,**零 500**。
⇒ 判 `{}` 落点的唯一判据就是"VO 有没有自带注解",别按控制器套模板。
★ 4 条真写(`reservation-submit`/`cancel-pre-order`/`in-parking`/`out-parking`)进 `LEGACY_WRITES`,
只发无 body;老类零 `@SysOpLog` ⇒ 新类零 `@OperateLog`。
★ 存量缺陷:`queryPreInTimes` 第一个时间点 `psPreFee` 恒 0(后 3 个才取担保费);
`inParking` 在"现在已过预约入场时间"时把进场时间回退成预约时间;`outParking` 在
`psInTime == null` 时 NPE;`reservationSubmit` 的 `Long.valueOf(preTotalFee)` 无 null 守卫。
★★ **2026-10-21 第一百九十八切片:共享车位·我的共享车位(Rnt)(1 类 / 6 端点,4 读 + 2 写)**
搬的是 `zteits-pay-portal/.../web/app/sharedparking/MySharedBerthController`
(老 `sharedparking/mySharedBerth`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,
就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、
5 死代码**全部不变**。零新 VO(全走 `JSONObject.parseObject`)。
★★ 本类**不继承 `AppBaseController`**,手写 `AppRsesultUtil.checkParam` ⇒ 响应
`{code, message}`(不是 `msg`)且 `code` 是**字符串**;`save-parking-berth-share`
把 5 个业务字段校验放在 `checkParam` 之前 ⇒ `{}` 直接回 `code="1003"`
「共享车位详细地址为空!」,其余 5 条才是 500 `[4014]`。
★ 2 条真写(`save-parking-berth-share`/`update-park-share-berth-by-berth-no-and-cust-id`)
进 `LEGACY_WRITES`,只发无 body;老类零 `@SysOpLog` ⇒ 新类零 `@OperateLog`。
★ 存量缺陷:`saveParkingBerthShare` 客户号为 null 时 NPE;两个费用接口逐条调计费(N+1);
`updateParkShareBerth...` 不校验车位归属;6 条全 `catch(Exception)` 吞成业务码。
★★ **2026-10-21 第一百九十九切片:自助停车(1 类 / 2 端点,1 读 + 1 写)**
搬的是 `zteits-pay-portal/.../web/app/AppSelfParkingController`(老 `/appSelfParking`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。新增 2 个门户 VO;补回 `SelfParkingCommonService`。
★★ `@Valid` 才是 `{}` 落点的开关:本类继承 `AppBaseController`、`BerthParklotReq` 也有
`@NotNull berthNo`,但方法参数没加 `@Valid` ⇒ `{}` 与假签名**一律 500 `[4014]`**
(落 `validateSigin`),与 §197 的 400 正好对照。
★ `in-park-self` 是 `private` 方法但仍是真端点(Spring 反射调用);它是本片唯一真写
(白名单 → Redis 防重 → `insertParkOrder` 建单 + 推 PDA 消息)⇒ 只进 `LEGACY_WRITES`、只发无 body。
★ 存量缺陷:`getIsSelfParking() == 1` 自动拆箱 NPE;`nightChargeDesc` 误取 `dayChargeDesc`;
`regionAreaDTO`/`parkLotByPlNo` 无 null 守卫;`Integer.valueOf(carNumberColor)` 无守卫;
`listBizResult.getData()` 前不判 `isSuccess`。
★★ **2026-10-26 第二百八十一切片:`IncomeBerthReportController` +1(停车位日报 URL 型导出补齐 ⇒ 该类 3/3 全迁)**
同样补在**已迁类**里 ⇒ 本脚本看见的仍是 **+0 类 / +0 端点**;重测全部不变:
就绪 **223/1180**、已迁 **223/1180**、**就绪且未迁 0/0**、⚠A **0/0**、⚠B **0/0**、
机械缺口 **4/38**(已判 **1/2** ⇒ 机械口径下可迁 **3/36**)、死代码 **5**、导出 **97**。
★ 这是本脚本的**口径边界**:它按**类**计数,"同一个类里再补端点"永远显示 +0;
端点级差集靠 `api_smoke.py`(§2i27 等分块断言)与 `p2_audit_*` 兜住,别拿这里说"没变化 = 没干活"。
★ 顺带销账一条**旧判定的错因**:`billing/UploadController.uploadPic` 早在第二百一十六切片就迁完了
(`POST /admin-api/pay/upload/upload-pic`,`pay:upload:create`);旧笔记里说的
「`FileStorageServiceImpl.uploadFile` 整段注释、`return null`」指的是 `com.zteits.pay.biz` 的 **Dubbo 服务**,
与本端点无关。⇒ 当前"上传类"队列只剩 4 条(`insertParkingBerthShare` / `upload-employee-head-pic` /
`recharge-card-generate-pic` / `uploadParkLotPic`),本机都只验负向、正向走离线桩。
★★ **2026-10-26 第二百八十切片:两条 URL 型导出补迁(`ParkInOutRecordController` +1 / `IncomeChargerCheckTaskController` +1)**
两条同样补在**已迁类**里 ⇒ 本脚本看见的仍是 **+0 类 / +0 端点**;重测:
就绪 **223/1180**、已迁 **223/1180**、**就绪且未迁 0/0**、⚠A **0/0**、⚠B **0/0**、
机械缺口 **4/38**(已判 **1/2** ⇒ 机械口径下可迁 **3/36** = `ParkingRecordController` 15 +
`InvoiceOperController` 12 + `ParkingAssistantController` 9,后一个 ⛔ 红线不动)、死代码 **5**。
⚠️ **225/1199 → 223/1180 不是回归**:用户 2026-10-26 的三个提交(10:56 / 11:27 / 14:14)在老仓
`zteits-api` 新增 `order.parkorder.oper.ParkRecordCashService`、`pay.invoice.InvoiceRedFlushService`、
`pay.invoice.InvoiceFaceAuthService`,而 `parking-module-api` **都没有** ⇒ `ParkingRecordController`(15)
与 `InvoiceOperController`(12) 从「就绪」掉进「缺口」。缺口 4 类 = 15 + 12 + `ParkingAssistantController` 9(⛔)
+ `OrderExamineController` 2(已判不可迁)。
★ 这两条**返回 URL 字符串而不是响应流** ⇒ 按既定口径保持 `POST`,不进「导出 97」的统计;
改 `GET /export-excel` 是契约变更(红线⑥)。
★ 本脚本不受影响的真原因:它只比「老类依赖的 service 有没有实现」,URL 型导出的差别在 Controller 形状。
★★ **2026-10-26 第二百七十九切片:`ArrearageStaticController` +3(欠费统计的 ajax 导出补齐)**
老类是「4 读 + 1 响应流导出已迁、3 条模板型 ajax 导出挂起」,本片补上后**该老类 8 端点全迁**。
3 条同样**补在已迁类里** ⇒ 本脚本看见的仍是 **+0 类 / +0 端点**;就绪口径
**225/1199、就绪且未迁 0/0、⚠A 0/0、⚠B 0/0、机械缺口 2/11(已判 1/2)、死代码 5** 全部不变。
★ 本片顺带把「模板类导出」的挂起理由也销账了一条:卡点一直是**模板文件没随迁**,
不是存储、也不是依赖 ⇒ 打法 = 模板复制到模块 `resources` + `ClassPathResource` 同路径读 + 需要时加 `easyexcel`。
★ 下一批只剩 `ParkInOutRecordController` −1、`IncomeChargerCheckTaskController` −1(两条模板导出),
见 AGENTS.md EOF §279 ⑤。
★★ **2026-10-26 第二百七十八切片:`AppVersionCheckController` +2 / `InspectionLogController` +1(multipart 上传补迁)**
**本片搬的 3 个端点全补在「已迁类」里** ⇒ 按**类**计数的本脚本看见的是 **+0 类 / +0 端点**
(它只数老类与端点映射,不追"某个已迁类又补了几个方法")。
实测(⚠️ 本片在 **`beijing_master`** 上跑;用户 2026-10-26 从 `fangshan_master` 切过来 ⇒
**口径整体变了、不是回归**):就绪 **225/1199**、已迁 **225/1199**、**就绪且未迁 0/0**、
⚠A **0/0**、⚠B **0/0**、机械缺口 **2/11**(已判 **1/2** ⇒ 可迁 **0/9**)、死代码 **5**。
⚠️ 别再拿 §277 记的 **235/1267 / 死代码 6** 跟现在比:`beijing_master` 的老仓端点总数少 68 条、
死代码少 1 个(`fangshan_master` 口径)。换分支后**先重跑**再报数。
★ 本片的价值不在本脚本:它是把「等文件存储抽象」这条挂起理由**彻底销账**——
抽象 `OssClientBaseWrapper` 一直都在,真卡点是①强制 FastDFS 无 `fdfs.*` 配置②本机 `ali` 是真 bucket。
★ 下一批:就绪池仍 **0/0**;剩 `ArrearageStaticController` −3(本片已备好 `uploadFileWithName` 的 wrapper)
与几条模板类导出(见 AGENTS.md EOF §278 ⑤)。
★★ **2026-10-23 第二百七十七切片:`TdCCustBusinessParkController` 老类 3 端点全迁(商户-车场关联)★ 就绪池清零**
老路由 `/tdCCustBusinessParkManage`(`business/`),新路由
`/admin-api/business/td-c-cust-business-park/*`(1 读 + 2 写,无导出)。
实测:就绪 **235/1267**、已迁 **235/1267**、**就绪且未迁 0/0**、⚠A **0/0**、⚠B **0/0**、
缺口的 2 类 / 7 端点**已判不可迁** ⇒ 真正可迁 **0/0**、死代码 **6**。
★ 3 条全抛 `AUTH_TOKEN_NOT_EXISTS`(照搬)且全 `@PreAuthorize`;老类**无** `@SysOpLog` ⇒ 新类**无** `@OperateLog`。
★ 失败形状与 §276 相反:缺表 `td_c_cust_business_park` 结尾是 `park`(无下划线)⇒ 不命中 `park_` ⇒ **500**
(§276 的 `…park_rel` 命中 ⇒ 501)。501/500 只取决于表名里的子串。
★ 分页回填 `plName`:`isSuccess()` → `ParkingLotCacheUtil` 查缓存 → 按 `plNo` 双循环,照搬。
⛔ `save` 服务层**先物理删除**该 businessId 下全部关联再逐车场插入(`{}` 会真删真插)。
★ 零写基线仍 **+248/轮**(实测 238550 → 238798,变化表 1)。
★ 下一批:**就绪池无待办**;只剩已判不可迁的 2 类 / 7 端点与端点级差集(见 AGENTS.md EOF §277 ⑤)。
★★ **2026-10-23 第二百七十六切片:`TdCBusinessCouponRuleParkRelController` 老类 4 端点全迁(商户卡券规则-车场关联)**
老路由 `/tdCBusinessCouponRuleParkRelManage`(`business/`),新路由
`/admin-api/business/td-c-business-coupon-rule-park-rel/*`(1 读 + 3 写,无导出)。
实测:就绪 **235/1267**(不变)、已迁 **234/1264**(+1 类 / +4 端点)、就绪未迁 **1/3**、
缺口 **2/7 已判不可迁**、真正可迁 **0/0**、死代码 **6**。
★ 4 条全抛 `AUTH_TOKEN_NOT_EXISTS`(照搬)且全 `@PreAuthorize`;老类**无** `@SysOpLog` ⇒ 新类**无** `@OperateLog`。
★★ 501 提示被**子串匹配**成「停车场模块」:缺的表名 `td_c_business_coupon_rule_park_rel` 含 `park_`,
纯属表名撞词(§275 的 `td_c_business_acct` 不含 ⇒ 同一批里它是 500)。
★★ 同一 `update` 端点两种形状:只带 `id` ⇒ `<set>` 空 ⇒ SQL 语法错 **500**;带宽真实列 ⇒ **501**。
⛔ `save` 无校验且 `parks` 非空时**逐车场各插一行**(`{}` 会真插);`update`/`bath-delete` 的 1003 闸门照搬。
★ 零写基线仍 **+248/轮**(实测 236814 → 237062,变化表 1)。
★ 下一批只剩 1 个类 / 3 端点(`TdCCustBusinessParkController`,清单见 AGENTS.md EOF §276 ⑤)。
★★ **2026-10-23 第二百七十五切片:`TdCBusinessAcctController` 老类 4 端点全迁(商户账户)**
老路由 `/tdCBusinessAcctManage`(`business/`),新路由
`/admin-api/business/td-c-business-acct/*`(1 读 + 3 写,无导出)。
实测:就绪 **235/1267**(不变)、已迁 **233/1260**(+1 类 / +4 端点)、就绪未迁 **2/7**、
缺口 **2/7 已判不可迁**、真正可迁 **0/0**、死代码 **6**。
★ 4 条全抛 `AUTH_TOKEN_NOT_EXISTS`(照搬)且全 `@PreAuthorize`;老类**无** `@SysOpLog` ⇒ 新类**无** `@OperateLog`。
★★ **与 §273/§274 不同**:分页服务是**真实现**(`PageHelper` + `PageBeanUtil`),本机缺表 ⇒ **500**,不是恒空页。
★ 本 DAO **没有**表存在性守卫 ⇒ `bath-delete {死主键}` 也是 **500**(§274 同形状端点回 501)——各自照搬。
⛔ `save` 服务层无校验(`{}` 真插一行);`update`/`bath-delete` 的 1003 闸门、`bath` 拼写照搬。
★ 零写基线仍 **+248/轮**(实测 235326 → 235574,变化表 1)。
★ 下一批剩 2 个类 / 7 端点(清单见 AGENTS.md EOF §275 ⑤)。
★★ **2026-10-23 第二百七十四切片:`OrderBusinessCouponController` 老类 4 端点全迁(商户购券主单)+ 就绪工具修正**
老路由 `/orderBusinessCouponManage`(`business/`),新路由
`/admin-api/business/order-business-coupon/*`(1 读 + 3 写,无导出)。
★★★ **本片先修工具再搬类**:原先 `impls` 扫描只做 `"implements" in src` 的文本预过滤,
`CustPersonCreditServiceImpl.java`(整文件注释)因此被判「有实现」,让 `CustPersonCreditController`
长期挂在「就绪且未迁」;改为 `strip_comments` 后再套 `IMPL_PAT`。
⇒ 该 Controller 落回缺口并登记 `GAP_ADJUDICATED` 判 **⛔ 不可迁**;就绪基数 **236/1272 → 235/1267**。
实测(修正后口径):就绪 **235/1267**、已迁 **232/1256**(+1 类 / +4 端点)、就绪未迁 **3/11**、
缺口 **2/7 已判不可迁**(OrderExamine 2 + CustPersonCredit 5)、真正可迁 **0/0**、死代码 **6**。
★ 4 条全抛 `AUTH_TOKEN_NOT_EXISTS`(照搬)且全 `@PreAuthorize`;老类**无** `@SysOpLog` ⇒ 新类**无** `@OperateLog`。
★★ `query-order-business-coupon-for-page` 服务实现体**整段被注释** ⇒ **恒返回空页、一次库都不查**。
★ 本机缺表:`update` 死主键 **500**(MBG 直接抛)、`bath-delete` 死主键 **501**(DAO 守卫)——**不是回归**。
⛔ `save` 服务层无校验(`{}` 真插一行);`update`/`bath-delete` 的 1003 闸门、`bath` 拼写照搬。
★ 零写基线仍 **+248/轮**(实测 234086 → 234334,变化表 1)。
★ 下一批剩 3 个类 / 11 端点(清单见 AGENTS.md EOF §274 ⑤)。
★★ **2026-10-23 第二百七十三切片:`OrderBusinessCouponSubController` 老类 5 端点全迁(商户购券子单)**
老路由 `/orderBusinessCouponSubManage`(`business/`),新路由
`/admin-api/business/order-business-coupon-sub/*`(2 读 + 3 写,无导出)。
实测:就绪 **236/1272**(不变)、已迁 **231/1252**(+1 类 / +5 端点)、就绪未迁 **5/20**、
机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。
★ 5 条全抛 `AUTH_TOKEN_NOT_EXISTS`(照搬)且全 `@PreAuthorize`;老类**无** `@SysOpLog` ⇒ 新类**无** `@OperateLog`。
★★ `query-order-business-coupon-sub-for-page` 服务实现体**整段被注释** ⇒ **恒返回空页、一次库都不查**。
★ `query-order-business-coupon-list-for-page` 本机缺表 ⇒ **501「表结构未导入」**(DAO 有前置守卫,不是 500)。
★ 零写基线仍 **+248/轮**(本片零 `@OperateLog`,实测 232598 → 232846,变化表 1)。
⛔ `save` 服务层无校验(`{}` 真插一行);`update`/`bath-delete` 的 1003 闸门、`bath` 拼写照搬。
★ 下一批剩 5 个类 / 20 端点(清单见 AGENTS.md EOF §273 ⑤)。
★★ **2026-10-23 第二百七十二切片:`InspectionLogController` 老类 5 端点迁 4(巡检日志)**
老路由 `/inspectionlog`(`operationsmanage/inspectionlog`),新路由
`/admin-api/order/inspection-log/*`(1 读 + 3 写;第 5 个 multipart+FastDFS 上传**挂起不迁**)。
实测:就绪 **236/1272**(不变)、已迁 **230/1247**(+1 类 / +5 端点)、就绪未迁 **6/25**、
机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。
★ 老类**没有一条**抛 `AUTH_TOKEN_NOT_EXISTS`(靠门户全局拦截器)⇒ 新栈 4 条全 `@PreAuthorize`。
★ `@SysOpLog` 三条 → `@OperateLog(module="巡检日志")`;`query` 老代码没有 ⇒ 新代码也没有。
★★ **零写基线从 +244 变 +248**:`OperateLogAspect` 在 `finally` 里落库、**业务 500 也记**;
§2i18 里 `update`/`delete` 各两次穿透控制器 ⇒ 多 4 行(变化表仍是 1)。
★ 坑 24 补充:全局 `spring.jackson.date-format: yyyy-MM-dd HH:mm:ss` ⇒ **请求体 `Date` 只认带时分秒**,
`yyyy-MM-dd` 被 Jackson 拒 → 400「请求参数类型错误」。
⚠️ 本机缺表 `inspection_log` ⇒ 读/改/删恒 500;⛔ `save` 服务层无校验(`{}` 真插一行)。
★ 下一批剩 6 个类 / 25 端点(清单见 AGENTS.md EOF §272 ⑤)。
★★ **2026-10-23 第二百七十一切片:`business/TdCBusinessCouponRuleController` 整类 6 端点首迁(卡券规则)**
老路由 `/tdCBusinessCouponRuleManage`,新路由 `/admin-api/business/business-coupon-rule/*`
(2 读 + 1 导出 + 3 写)。
实测:就绪 **236/1272**(不变)、已迁 **229/1242**(+1 类 / +6 端点)、就绪未迁 **7/30**、
机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。
★ 老类**零** `@SysOpLog` ⇒ 新类零 `@OperateLog`(零写基线不变,+244/轮);导出数 **96 → 97**。
★ 新增 `com.zteits.business.util.SnowFlakeUtil`(business 模块第 5 份同源副本,生成 `cardRuleNo`);
⚠️ **别**换 `com.clouds.common.utils.SnowflakeIdWorker`(twepoch/datacenter 位数不同)。
⚠️ 老导出是 `@RequestMapping`(**不限方法**)+ `@NoAuth` **且不判登录态**,新栈收紧为 `:export`
且**只收 GET**;`orgId`/`sysCode` 是**必填 query 参数**(缺 → 400)。
⚠️ **导出不分页** ⇒ `BaseInfo` 默认 0/10 ⇒ 最多 10 行;**DAO 根本不按 orgId 过滤**(全组织同一份)。
⚠️ 本机缺表 `td_c_business_coupon_rule` ⇒ `page`/`list`/导出/死主键写全为 500「系统异常」。
★ 三个写端点的闸门**都在落库前**(save 卡名称/类型/优惠值、update 卡 id+三项、delete 卡 id);
`delete` 是逻辑删除(`data_state=0`)且 `opName` 恒 null。
★ 下一批剩 7 个类 / 30 端点(清单见 AGENTS.md EOF §271 ⑤)。
★★ **2026-10-23 第二百七十切片:`business/TdCBusinessCouponController` 整类 6 端点首迁(商户卡券)**
老路由 `/tdCBusinessCouponManage`,新路由 `/admin-api/business/business-coupon/*`
(2 读 + 1 导出 + 3 写)。
实测:就绪 **236/1272**(不变)、已迁 **228/1236**(+1 类 / +6 端点)、就绪未迁 **8/36**、
机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。
★ 老类**零** `@SysOpLog` ⇒ 新类零 `@OperateLog`(零写基线不变,+244/轮);导出数 **95 → 96**。
⚠️ 老导出是 `@RequestMapping`(**不限方法**)+ `@NoAuth`,新栈收紧为 `:export` 且**只收 GET**
(POST → 405);⚠️ **导出不带 `orgId`**(跨组织全量,存量缺陷照搬)。
⚠️ 本机缺表 `td_c_business_coupon` ⇒ `page`/`sum-sta`/导出/`update {死主键}` 全为 500「系统异常」。
⚠️ 分页回填 `BusCardTypeEnum.getValue(int).desc()`:`cardType` null 拆箱 NPE、未知类型也 NPE。
⛔ `save` 服务层无任何校验(`{}` 真插一行)⇒ 只发无 body。
★ 下一批剩 8 个类 / 36 端点(清单见 AGENTS.md EOF §270 ⑤)。
★★ **2026-10-23 第二百六十九切片:`share/CouponController` 整类 7 端点首迁(对外卡券接口)**
老路由 `share/coupon`(最江阴下发优惠券),新路由 `/admin-api/pay/share-coupon/*`
(2 读 + 5 写,**无导出**)。
实测:就绪 **236/1272**(不变)、已迁 **227/1230**(+1 类 / +7 端点)、就绪未迁 **9/42**、
机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。
★ 老类 `@SysOpLog` **全是注释掉的** ⇒ 新类零 `@OperateLog`(零写基线不变,+244/轮)。
★ 两条读端点查不到用户时走 `setErrorInfo(COMMON_ERROR)` ⇒
**500 `[存量错误码 1002] 账户不存在`**(`selectByUserPhone` 返回非 null 空 DTO,不是 NPE)。
⚠️ 五条写端点的 `{}` 全是**只失败不落库**:`save`→flg NPE、`batch-insert`→`@Valid` 400、
`delete-coupon-rule`→MBG「Value for cardNo cannot be null」、
`user-registration`→1003「手机号不能为空」、`delete-coupon-info`→`Long.valueOf(null)` NFE。
★ 下一批剩 9 个类 / 42 端点(清单见 AGENTS.md EOF §269 ⑤)。
★★ **2026-10-23 第二百六十八切片:`TdCBusinessAcctPayoutController` 整类 7 端点首迁**
老路由 `/tdCBusinessAcctPayoutManage`(`business` 模块第三个),新路由
`/admin-api/business/business-acct-payout/*`(3 读 + 1 导出 + 3 写)。
实测:就绪 **236/1272**(不变)、已迁 **226/1223**(+1 类 / +7 端点)、就绪未迁 **10/49**、
机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。
★ 老类**零 `@SysOpLog`** ⇒ 新类零 `@OperateLog`(零写基线不变,+244/轮)。
★ 导出老 `@NoAuth` + 手工 `x-auth-token` ⇒ 按 §3.3 第 1 类收紧为
`business:business-acct-payout:export`,`GET /export-excel`,不再收 `token` 参数。
⚠️ **坑 24**:`@RequestParam Date` 只认 JS `Date.toString()`,`yyyy-MM-dd` → 400。
⚠️ **导出不带 `orgId` 过滤**(存量缺陷,跨组织全量,照搬)。
⚠️ `td-c-business-acct-payout-detailfor-card` 的服务实现是**空壳 `return null`**
⇒ 控制器 `getErrCode()` NPE ⇒ **任何环境都 500**(老栈同样,照搬别补实现)。
⚠️ **本机缺表 `td_c_business_acct_payout`** ⇒ `page`/`static`/导出/`update {死主键}`/
`bath-delete {ids:[死主键]}` 恒 500「系统异常」,冒烟断言按双收写 ——
**这是本机 schema 缺口,不是迁移缺陷**。
⛔ `save` 服务层无任何校验(`{}` 会真插一行)⇒ 冒烟只发无 body;
⚠️ `bathDelete` 写的 `opName` 服务读的是 `BaseRequest.sOpName`(**永为 null**,存量缺陷照搬)。
★ 下一批剩 10 个类 / 49 端点(清单见 AGENTS.md EOF §268 ⑤)。
★★ **2026-10-23 第二百六十七切片:`TdCBusinessAcctSourceController` 整类 7 端点首迁**
老路由 `/tdCBusinessAcctSourceManage`(`business` 模块第二个),新路由
`/admin-api/business/business-acct-source/*`(2 读 + 1 导出 + 4 写)。
实测:就绪 **236/1272**(不变)、已迁 **225/1216**(+1 类 / +7 端点)、就绪未迁 **11/56**、
机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。
★ 老类**零 `@SysOpLog`** ⇒ 新类零 `@OperateLog`(零写基线不变,+244/轮)。
★ 导出老 `@NoAuth` + 手工 `x-auth-token` ⇒ 按 §3.3 第 1 类收紧为 `business:business-acct-source:export`,
`GET /export-excel`,不再收 `token` 参数。⚠️ **坑 24**:`@RequestParam Date` 只认
JS `Date.toString()`,`yyyy-MM-dd` → 400。⚠️ **导出不带 `orgId` 过滤**(存量缺陷,跨组织全量,照搬)。
⚠️ **本机缺表 `td_c_business_acct_source`** ⇒ `page`/`static`/两个导出/`update {死主键}`/
`bath-delete {ids:[死主键]}`/`examine` 恒 500「系统异常」,冒烟断言按双收写 ——
**这是本机 schema 缺口,不是迁移缺陷**。
⛔ `save` 服务层无任何校验(`{}` 会真插一行)⇒ 冒烟只发无 body;
⚠️ `td-c-business-acct-source-examine` 审批通过会**真加余额 + 建支付单**。
★ 下一批剩 11 个类 / 56 端点(清单见 AGENTS.md EOF §267 ⑤)。
★★ **2026-10-23 第二百六十六切片:`TdCBusinessUserController` 整类 7 端点首迁**
老路由 `/tdCBusinessUserManage`(`business` 模块第一个),新路由 `/admin-api/business/business-user/*`。
实测:就绪 **236/1272**(不变)、已迁 **224/1209**(+1 类 / +7 端点)、就绪未迁 **12/63**、
机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。
★ 坑 22 安全(只写登录态 `orgId`/`sOpId`/操作人)。⚠️ **本机缺表 `td_c_business_user`**
⇒ `page`/`get-business-user-name-list`/`batch-delete {ids:[死主键]}` 恒 500「系统异常」,
冒烟断言按「500 或 0」双收 —— **这是本机 schema 缺口,不是迁移缺陷**。
★ 下一批剩 12 个类 / 63 端点(清单见 AGENTS.md EOF §266 ⑤)。
★★ **2026-10-23 第二百六十五切片(P3 桥回收第十二组):`BlackListRuleController` 补 `parkTree`**
§94 只落 6/7(`parkTree` 读登录态 `plNos` 当树根,P3 搁置);P3 ① 落地后补迁 ⇒ **7/7**。
★ **口径提示**:本片运行时本机已从 `beijing_master` 检出到 **`fangshan_master`**
(2026-10-23 09:29;会话开始时是 `beijing_master`),老门户树不同 ⇒ 本脚本实测
**就绪 236/1272**、已迁 **223/1202**、就绪未迁 **13/70**、机械缺口 **1/2**
(已判 1/2、可迁 0/0)、死代码 **6**。历史行记的 **225/1199 是 `beijing_master` 口径**,
**两者不可直接比**;换回 `beijing_master` 后应重跑再记录。
★ **本次重跑暴露的 13 个就绪且未迁类(70 端点,全部 ⚠B)** —— 下一批从这里取:
`TdCBusinessAcctSourceController`(7)、`TdCBusinessAcctPayoutController`(7)、
`TdCBusinessUserController`(7)、`CouponController`(7)、`TdCBusinessCouponController`(6)、
`TdCBusinessCouponRuleController`(6)、`InspectionLogController`(5)、
`OrderBusinessCouponSubController`(5)、`CustPersonCreditController`(5)、
`OrderBusinessCouponController`(4)、`TdCBusinessAcctController`(4)、
`TdCBusinessCouponRuleParkRelController`(4)、`TdCCustBusinessParkController`(3)。
抽样两个(`TdCBusinessUserController` / `TdCBusinessAcctSourceController`)只读登录态 `orgId`
⇒ 坑 22 安全候选(逐个还应读源码再判)。
★★ **2026-10-22 第二百六十四切片(P3 桥回收第十一组):`AnomalyStatisticsController` 补齐 2 条**
第一百四十八切片只落 2/4(`querySealedParkingReportForPage` /
`querySealedParkingReportForLineChart` 在 `parkIds` 为空时回落 `userInfo.getPlNos()`,
且只留 `plType == 2`);P3 ① 落地后补迁 2 条,交付口径 2/4 → **4/4**。
★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、
机械缺口 **2/11**、5 死代码不变;本工具按**类**判已迁 —— §148 起它就一直算「全迁」。
★ 本机 `plNos=[]` ⇒ 回落得到空列表,两个存量 SQL 都是 `IN <foreach>` 且**没有空集合守卫**
⇒ 拼出 `IN ()` ⇒ Druid wall 判语法错误 ⇒ **500**(老门户同样 500,照搬)。
★ 折线图**先快照时间串**:存量 `ParkOrderQueryServiceImpl#statisticOrderFeeByDay` 会就地
把入参拼成 `"… 00:00:00 00:00:00"`;老门户跨 JVM 远程调用不受影响,本栈 injvm 传引用
⇒ 不快照就 `DateTimeParseException` ⇒ 500。`Period.getDays()` 只取日分量(存量缺陷)。
★ 另记两个**假缺口**(不必再写代码):`GroundLockManageController` −3 已由
`GroundLockAppController` 落、`RealTimeMonitorController` −2 已由
`RealTimeMonitorAppController` 落(§3.3 的 `@NoAuth` 外部端点拆到 `controller/app/`)。
★★ **2026-10-22 第二百六十三切片(P3 桥回收第十组):`VipCardRuleController` 补齐 3 条**
第九十五切片只落 4/7(`queryVIPCardRuleForPage` / `queryVIPCardRuleForPageNew` /
`vipCardSelectParkTree` 都读登录态 `userInfo.getPlNos()`);P3 ① 落地后补迁 3 条,
交付口径 4/7 → **7/7**。
★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、
机械缺口 **2/11**、5 死代码不变;本工具按**类**判已迁 —— §95 起它就一直算「全迁」。
★ 本机 `plNos=[]` ⇒ 两个分页回落成空 `plNos`,存量 SQL **对空 plNos 不加过滤** ⇒
`{}` 回全量 364 条;`tree` 回空树 `[]`。★ 冒烟只发不存在的 `cardNo`:`{}` 会让
`queryParkName` 逐条回填时把「本地 DB 快照里没有的车场」的空 DTO 写进 Redis。
★★ **2026-10-22 第二百六十二切片(P3 桥回收第九组):`IncomeCalculationNewController` 补齐 4 条**
第九十八切片只落 5/9(`getSummationIncomeInfoforPlTypeByTime` /
`getAllIncomeCalculationDataOfAreaByTime` / `getSummationIncomeByTimeOfArea` /
`getSummationIncomeByTimeOfPl` 都是「`req.plNos` 为空就回落 `userInfo.getPlNos()`」);
P3 ① 落地后补迁 4 条,交付口径 5/9 → **9/9**。
★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、
5 死代码不变;本工具按**类**判已迁 —— §98 起它就一直算「全迁」。
★ `plNos=[]` ⇒ 不带 `plNos` 的 4 条全在**服务层**抛 1003(500);缓存内车场
`A150402000` 时「按类型拆三次」的两条问 i=1 空子集 ⇒ 同样 500 + 1003,
另两条回 `code=0` + `[]`;缓存外车场 ⇒ `getParkTypePlNos` 对 null 元素 NPE。
★★ **2026-10-22 第二百六十一切片(P3 桥回收第八组):`ParkLotManageController` 补齐 2 条**
第九十七切片只落 14/18(`getParkLotNamesByUserId` 读登录 `plNos` 后逐车场查缓存/服务;
`queryAreaTreeByOpIdAndOrgId` 最终用 `plNos` 过滤车场节点);P3 ① 落地后补迁
`get-park-lot-names-by-user-id` / `get-areatree-by-op-id`,交付口径 14/18 → **16/18**。
★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、
5 死代码不变;本工具按**类**判已迁 —— §97 起它就一直算「全迁」。
★ `refreshParkLotCache`(把 `plNos` 写回 `HttpSession`,JWT 栈无对应语义)与
`uploadParkLotPic`(FastDFS/OSS 挂起)仍不迁,故是 16/18 而不是 18/18。
★ `plNos=[]` ⇒ 第一条回空数组(老门户「登录人无车场」同口径);第二条本机**恒 500**:
存量服务查到 92 个块、块→区县 0 条 ⇒ 地市查询空 id 列表 ⇒ `and id in and area_type=?`
被 Druid WallFilter 判语法错(红线⑥照搬,不是本片引入)。
★★ **2026-10-22 第二百六十切片(P3 桥回收第七组):`SelectItemController` 补齐后 6 条**
第一百四十七切片只落 2/8(另 6 条读登录 `plNos`:5 条经 `GetParkLotNameListByUserId()`
查 `ParkingLotCacheUtil`,1 条直接喂 `queryVipCardParkSaleRelForListByPlNo`);
P3 ① 落地后补迁 6 条,交付口径 2/8 → **8/8**。
★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、
5 死代码不变;本工具按**类**判已迁 —— §147 起它就一直算「全迁」。
★ `plNos=[]` ⇒ 前 5 条回空集(只剩「全部」);第 6 条 DAO 空集不过滤 ⇒ 105 张在售卡
(+ALL = 106,ALL 写 `value=ALL`/`value4=全部`、`label=null`);前 5 条缺 `noAll` 仍 NPE 500。
★★ **2026-10-22 第二百五十九切片(P3 桥回收第六组):`SpecialCarManageController` 补齐后 2 条**
第八十八切片只落 6/8(`querySpecialCar`/`initSpecialCarTree` 读登录 `plNos`、
后者另固定 `sourceTypes=[17,9]`);P3 ① 落地后补迁 `query-special-car`/`init-special-car-tree`,
交付口径 6/8 → **8/8**。
★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、
5 死代码不变;本工具按**类**判已迁 —— §88 起它就一直算「全迁」。
★ `plNos=[]` ⇒ DAO 不加车场过滤(老门户「登录人无车场」同口径);两条本机**恒 500**:
`query-special-car` 存量 `GROUP BY` 撞 MySQL 8 `only_full_group_by`;
`init-special-car-tree` 区县查空 ⇒ `and id in ()` 非法 SQL(存量无空集守卫)。
★★ **2026-10-22 第二百五十八切片(P3 桥回收第五组):`CloseCarParkController` 补齐前 2 条**
第八十六切片只落 17/19(`QueryParkingList`/`queryParkingParkList` 读登录 `plNos` +
固定 `sourceType=[17,9]`);P3 ① 落地后补迁 `query-parking-list`/`query-parking-park-list`,
交付口径 17/19 → **19/19**。
★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、
5 死代码不变;本工具按**类**判已迁 —— §86 起它就一直算「全迁」。
★ `plNos=[]` ⇒ DAO 不加 `pl_no` 过滤(本机 `total=2`);不分页那条 DAO 无 null 守卫,
`{}` 让 `getPlProvinceId() != 0` 拆箱 **NPE ⇒ 500**(存量照搬,省市区显式 0 才正常)。
★★ **2026-10-22 第二百五十七切片(P3 桥回收第四组):`TdCCustInvoiceSaleController` 补齐 `/relParkTree`**
第八十四切片只落 11/12(`/relParkTree` 用登录 `plNos` 裁掉已绑定车场);P3 ① 落地后补迁
`POST /rel-park-tree`,交付口径 11/12 → **12/12**。
★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、
5 死代码不变;本工具按**类**判已迁 —— §84 起它就一直算「全迁」。
★ 桥实测 `orgIds=[10003] userAreas=0 plNos=[]`(**空集合,不是 null**)⇒ `removeAll` 正常执行、
回空树;`{}` 不给 `saleTaxids` 时服务层 `saleTaxids.get(0)` **NPE ⇒ 500**(存量照搬)。
★★ **2026-10-22 第二百五十六切片(P3 桥回收第三组):`KeyTopServiceController` 补齐 `/parkTree`**
第一百八十五切片只落 7/8(`/parkTree` 读登录 `plNos` 构树);P3 ① 落地后补迁
`POST /park-tree`(**无入参**、无 `@RequestBody`),交付口径 7/8 → **8/8**。
★ 本类**在扫描根内** ⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、5 死代码不变;
但本工具按**类**判已迁 —— §185 起它就一直算「全迁」。
★ 本机 admin 经桥换算 `orgIds=[10003]`、但本地 `sys_org_area` 无该组织行 ⇒ `plNos=[]`(空集合,不是 null)⇒ 恒回空树 `[]`(老门户「无车场」同口径)。
★★ **2026-10-22 第二百五十五切片(P3 桥回收第二组):`BalanceManagementController` 补齐 2 个分页**
第一百五十切片只落 3/5(另 2 个在 `plNos` 空/`ALL` 时回落 `userInfo.getPlNos()`);P3 ① 落地后
补齐 `query-bill-fill-total-for-pay-type-new`/`query-blancacfor-day-detail-page`,交付口径 3/5 → **5/5**。
★ 本类**在扫描根内** ⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、5 死代码不变;
但本工具按**类**判已迁 —— §150 起它就一直算「全迁」。
★ 服务层 `BillReconciliationForQueryServiceImpl` 的 DAO 调用**全被注释掉**(存量遗留)
⇒ 两条端点恒 `{total:0,list:[]}`;无 `@Valid` ⇒ `{}` 不 400。
★★ **2026-10-22 第二百五十四切片(P3 桥回收第一组):`ParkSourceController` 补齐 3 个 GET**
第一百四十九切片只落 3/6(另 3 个读 `userInfo.getPlNos()`);第一百五十九切片的 P3 ① 给
`LoginUserBridge` 补了懒推导 `plNos`/`userAreas` ⇒ 本片补齐 `query-park-source-statistics`/
`query-park-source-employee-total`/`query-park-source-device-total`,该类交付口径 3/6 → **6/6**。
★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、
5 死代码全部不变。★ 但本工具按**类**判已迁 —— §149 起它就一直算「全迁」,
本片只是把当初的交付口径补成名副其实。
★ `sysCode` 是死参数(只进日志,真正给服务的是 `BasicEnum.SYS_CODE`)但老签名
`@RequestParam` 必填 ⇒ 缺它 **400「请求参数缺失:sysCode」**(照搬)。
★ 本机 admin 经桥换算 `orgIds=[10003]`、但本地 `sys_org_area` 无该组织行 ⇒ `plNos=[]`(空集合,不是 null)⇒ 三条都走空值早退(统计全 null、两个 total 回 0),
与老门户「登录人无车场」同口径。
★★ **2026-10-22 第二百五十三切片(批 7 第二十七切片):PDA 收费员登录 9 端点**
`PdaUserAppController`:`/app-api/pda/user/*` 9 个,**写 4 个**(login/sign-out/modify-pass/sign-in)
全进 `LEGACY_WRITES`(`modify-pass` 还会写 `td_b_api_log`)。
★★ 通用口径:`PDABaseReq` 的 6 个公共参数是 `@NotBlank`,但 `data` **没有 `@Valid` 级联**
⇒ `{}`/仅 token 一律 400「不能为空」;齐参无 data 一律 500「系统异常」;
齐参 + `data={}` 落 500「系统异常」/ 500「-10001」/ 400 三档。
★ 新增 4 个 VO(`ChargerInfoRes`/`ChargerLoginRes`/`SignInReq`/`account/ChargeAccountIsRechargeRes`)。
★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百五十二切片(批 7 第二十六切片):PDA 图片上传 2 端点**
`UploadPictureAppController`:`upload-pic`、`sf-pda-upload-pic`,**都是 multipart + OSS 真写**
⇒ 不进 `LEGACY_WRITES`(那张表要求无 body 时 400),零写守卫写在 §2i10。
★ `validateToken` 在取文件名 / 加水印 / 上传之前 ⇒ 零字节 `picFile` + 假 token 必落 500「-10001」,
绝无 OSS 写;JSON 侧(无 `@RequestBody`)一律 500「系统异常」。
★ 落地映射:老 `uploadCarPic`/`uploadXCPic`/`uploadFile(file,plNo)` 新栈都没有 ⇒ 统一
`uploadFile(in,size,ext)`/`uploadFile(file)`;字体用 `reportpdf/Microsoft Yahei.ttf`。
★ 本片**零新增 VO**。本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百五十一切片(批 7 第二十五切片):PDA 停车记录 5 端点**
`ParkRecordAppController`:`query-berth-info`/`query-park-record`/`query-park-record-by-order-id`
只读;`modify-order-info`/`out-park-nullify` **写** ⇒ 进 `LEGACY_WRITES`。
★ 同类两种 `data={}` 落点:`query-berth-info` 的 data 是 `?` ⇒ 齐参 + `data={}` 落 **500「-10001」**;
其余 4 个 data 类带 `@NotBlank` ⇒ 落 **400「不能为空」**。
★ 启动坑复现:`@Resource private CommonService` 会撞 park 模块同名 bean ⇒ 必须
`@Resource(name = "pdaCommonService")`(§238 已记)。
★ 新增 5 个 VO + `pda.portal.constant.BerthConstant`(抄自老门户)。
本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百五十切片(批 7 第二十四切片):PDA 排班服务 2 端点**
`DutyAppController`:`query-latest-week-duty`、`query-latest-week-duty-by-charge-code`,**都只读**
⇒ 不进 `LEGACY_WRITES`。
★ 两套校验并存:前者 `@Valid` + `checkParam` ⇒ 齐参 + `data={}` 落 **500「-10001」**;
后者 **`@RequestBody` 上没有 `@Valid`**、也不调 `checkParam`,只调
`BeanValidatorsUtils.validateWithException(request.getData())` ⇒ **`{}` 落 500「系统异常」**
(data 为 null 时 NPE)、齐参 + `data={}` 落 **400「不能为空」**。
⚠️ 后者**完全不校验 token**(假 token + 真 `chargerCode` 直接 8888 成功),存量缺陷照搬(红线⑥)。
★ 新增 1 个 VO:`QueryWeekDutysReq`(抄自老门户 `vo/pda`)。本工具只扫 `bcp-portal`
⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百四十九切片(批 7 第二十三切片):收费员考核任务 1 端点**
`ChargerCheckTaskAppController`:`query-charger-check-task`,**只读** ⇒ 不进 `LEGACY_WRITES`。
★★ **新样板:没有 `checkParam` 的端点,`data={}` 不会落 -10001** —— 只 `@Valid` 校验公共参数 +
直接 `PdaTokenCacheUtil.getPDAInfoByToken` ⇒ 假 token 得 null ⇒ `.getChargerInfo()` NPE
⇒ **齐参缺 data 与齐参 + `data={}` 都是 500「系统异常」**,两者无差别。
★ 存量缺陷照搬:两处 `setSysCode("sysCode")` 是**字面量字符串**。
★ 本片**零新增 VO**。本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百四十八切片(批 7 第二十二切片):PDA收费员充值/支出记录 2 端点**
`ChargerAccountAppController`:`query-acct-recharge-for-page`、`query-acct-pay-out-for-page`,
**两个都只读** ⇒ 不进 `LEGACY_WRITES`;`custId` 取自 PDA token,不从入参取。
★ 类名带 `App`:`admin/chargeraccount/ChargerAccountController` 已占 bean 名。
★ 落点第三档:`PDABaseReq<BaseInfo>` 而 `BaseInfo` **无任何约束** ⇒ 齐参 + `data={}` 落 **500「-10001」**。
★ 本片**零新增 VO**。本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百四十七切片(批 7 第二十一切片):车牌识别激活码 2 端点**
`CarNumberIdentifyAppController`:`get-register-code`(只读 `tp_p_pda`)、
`report-register-result`(⚠️ 真写 `tp_p_pda`:`updatePDAByPdaNo`)⇒ 只有后者进 `LEGACY_WRITES`。
★ 两端点都是 `@Valid` + `checkParam`、data 类都有约束 ⇒ 齐参 + `data={}` 一律 400;
`ReportRegisterResultRequest` 的 `@NotBlank` + `@NotNull` 并存 ⇒ 报文**不稳定** ⇒ 断言只认 400 + 二者之一。
★ 老方法名 `queryFreeBerths` 是复制粘贴残留(实际是「获取激活码」),新栈按语义改名 `getRegisterCode`。
★ 本片**零新增 VO**。本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百四十六切片(批 7 第二十切片):违章车辆上报 2 端点**
`PdaCarBreakRuleAppController`(★ 类名带 `Pda`:`controller.app.carbreak.CarBreakRuleAppController`
已占 bean 名 `carBreakRuleAppController`,同名会 `ConflictingBeanDefinitionException` 启动即死):
`upload-pic`(multipart,OSS + `td_c_file`)、`report-car-break-rule`(真插 `tp_c_car_break_rule`)。
★ 只有 `report-car-break-rule` 进 `LEGACY_WRITES`;`upload-pic` 无 body 是 500「系统异常」⇒ 守卫写在 §2i4。
★★ **multipart 零写冒烟规矩**:缺 `picFile` part ⇒ 500;**零字节空 part + 假 token ⇒ 500「-10001」**
(`validateToken` 在 `getSize()`/`uploadParkFile` 之前 ⇒ 既不传 OSS 也不写 `td_c_file`);
缺 `token` part ⇒ 400「请求参数缺失:token」。⛔ 永不发真字节 + 有效 token。
★ `upload-pic` 的 JSON 侧(空 body / `{}` / 坏 JSON)**一律 500「系统异常」**,不是 bug。
★ 本片新增 1 个 VO(`CarBreakRuleReq`)。本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百四十五切片(批 7 第十九切片):车辆出场 8 端点**
`CarOutParkAppController`:4 条真写进 `LEGACY_WRITES`(两个现金出场 `outParkPayedByCash`、
`batch-out-park` 推 MQ、`set-park-order-recosting` 真重算),其余 4 个只读。
★ 落点三档:两个无约束 data 类 ⇒ 500「-10001」;`PayOutFeeByCashReq` ⇒ 400;
其余 4 个 ⇒ 400「不能为空」;缺 data ⇒ 八个一致 500「系统异常」。
★★ `PayOutFeeByCashReq` 的 400 报文在「不能为空」/「不能为null」之间**不稳定**
(`ConstraintViolation` 是 HashSet)⇒ 断言只认 code=400 + 二者之一。
★ 本片新增 8 个 VO(`QueryHaveOrderBerthsRes`/`CarOutParkReq`/`QueryOutParkInfoRes`/
`PayOutFeeByCashReq`/`BatchOutParkReq`/`VehicleLeavingReq`/`RecostingByOrderIdReq`/
`VehicleLeavingResForTicket`)。
★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百四十四切片(批 7 第十八切片):车辆入场 6 端点**
`CarInParkAppController`:`in-park`(`insertParkOrder` 真建停车单)与 `pay-pre-fee-by-cash`
(真现金预付)进 `LEGACY_WRITES`,其余 4 个只读。
★★ 本片**三种 `data={}` 落点并存**:`query-free-berths`(`CarInOutParkReq` 无约束)⇒ 500「-10001」;
`get-vehicle-entering-data`(**`checkParam` 被注释** ⇒ 进业务,服务端 `@NotNull`)⇒ 400「不能为null」;
其余 4 个 ⇒ 400「不能为空」。缺 data ⇒ 六个一致 500「系统异常」。
★ 本片新增 8 个 VO(`QueryFreeBerthsRes`/`CarInOutParkReq`/`CarInParkRes`/`CarInParkReq`/
`QueryPreInParkFeeRes`/`QueryPreInParkFeeReq`/`PayPreFeeByCashReq`/`VehicleEnteringReq`)。
★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百四十三切片(批 7 第十七切片):欠费补交 6 端点**
`ArrearageAppController`:`pay-arrearage-by-cash` 是**真现金补缴**(`LEGACY_WRITES`),其余 5 个只读。
★ 六个端点都 `@Valid` + `checkParam`,data 类都有 `@NotBlank` ⇒ `data={}` 一律 400;缺 data ⇒ 500。
★ 存量缺陷照搬:一键补缴取 orderIds 用 `getRecordInfos()` 而非局部 `recordInfos`。
★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百四十二切片(批 7 第十六切片):个人账户支付 1 端点**
`PersonAccountPayAppController`:`pre-or-out-pay-by-account` 是**真扣个人账户余额**(`LEGACY_WRITES`)。
★ `@Valid` + `checkParam` + data 类 `@NotBlank/@NotNull` ⇒ `data={}` 一律 400;缺 data ⇒ 500。
★ `saveCallInterfaceLog` 在 `checkParam` 之后 ⇒ `data={}` 时**不写 `td_b_api_log`**。
★ 方法名沿老代码误写 `preOrOutPayByBarcode`(红线⑥不改)。
★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百四十一切片(批 7 第十五切片):退款 2 端点**
`RefundAppController`:`pre-fee-refund` 是**真退款**(`LEGACY_WRITES`);`query-allow-refund-order` 只读。
★ 两个端点都 `@Valid` 且方法体都走 `checkParam`,两个 data 类都有 `@NotBlank`
⇒ `data={}` 一律 400「不能为空」(PDA 口径里最简单的情形);缺 data ⇒ 500「系统异常」。
★ `saveCallInterfaceLog` 在 `checkParam` 之后 ⇒ `data={}` 时**不写 `td_b_api_log`**。
★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百四十切片(批 7 第十四切片):条码支付 4 端点 + 基类 PDABassController**
`BarcodePayAppController`:**3 个真扣款/充值**(`LEGACY_WRITES`),只发空 body / `{}` / `data={}`。
★★ 修正 §238 的过度概括:`@Valid` 打在 `PDABaseReq<T>` 上,`data` 字段没有 `@Valid`
⇒ **只校验公共参数,不下钻 data**;`data={}` 的落点取决于方法体走不走 `checkParam`。
★ 本片三种落点:checkParam ⇒ 400;只 validateSigin + 查库 ⇒ 500;只 validateSigin + 服务
校验失败 ⇒ 8888 信封 + 4000(account-recharge,未扣款)。
★ 随迁基类 `PDABassController` 到 `com.zteits.pay.portal.web.pda`(扫描包内)。
★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百三十九切片(批 7 第十三切片):停车公共查询 13 端点**
`ParkSourceQueryAppController`:**13 个全是纯读**(零写,`LEGACY_WRITES` 无新增)。
★ 都没有 `@Valid`、都走 `checkParam` ⇒ `{}`/缺 data 都是 500(HV000116);
`data={}` 按 data 类有无约束分两组:无约束 ⇒ 500(齐参时 -10001),有约束 ⇒ 400。
★ 存量照搬:`getEqpIsOnline() == "0"` 字符串引用比较(onlineEqpNum 恒 0)、
`queryNearbyParkLotInfo` 的循环内查收费员(N+1),均已注释、不批量化。
★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。
★★ **2026-10-22 第二百三十八切片(批 7 第十二切片):巡查 登录/签退/改密 4 端点**
`PatrolUserAppController`:**4 个全是真写**(Redis token / 改密码),`LEGACY_WRITES`,只发空 body。
★ 四个都有 `@Valid`、`data` 类也都有约束 ⇒ 空 body/`{}`/`data={}` 全是 400,**够不到写服务**。
★ 坑:`@Resource private CommonService commonService` 撞 park 模块同名 bean
⇒ 必须 `@Resource(name = "pdaCommonService")`。
★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。
★★ **2026-10-22 第二百三十七切片(批 7 第十一切片):巡查任务 6 端点**
`PatrolTaskAppController`:4 读 + `update-task-state`/`apply-help`(⚠️写,`LEGACY_WRITES`,只发空 body)。
★★ 总口径:`PDARsesultUtil.checkParam` 第一句就是 HV 校验 `data`
⇒ `data={}` 是 400 还是 500 **取决于 `data` 类上有没有 Bean Validation 约束**,与 `@Valid` 无关。
★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。
★★ **2026-10-21 第二百三十六切片(批 7 第十切片):巡查 设备巡查 4 端点**
`EqpInspectAppController`:3 读 + `save-inspect-record`(⚠️写,`LEGACY_WRITES`,只发空 body)。
★★ 与 §235「5 端点整齐一致」相反:同类的 4 个端点 `data={}` 落点各不相同(设备类型 200+`"8888"`、设备属性/异常上报 500、工单明细 500+存量码 1003)。
★ 无 `@Valid` + 只 `validateSigin` ⇒ 除空 body/坏 JSON 外没有 400。
★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。
★★ **2026-10-21 第二百三十五切片(批 7 第九切片):巡查 设备维护 5 端点**
`EqpMaintenanceAppController`:4 读 + `update-record-detail`(⚠️写,`LEGACY_WRITES`,只发空 body)。
★★ **参数上没有 `@Valid`** ⇒ `{}` 不是 400 而是 500「系统异常」(进方法体 `validateSigin` 里 NPE)。
★ 只 `validateSigin`(不校验 token),随后 `getPDAInfoByToken` ⇒ 假 token 也是 500 而**不是** `-10001`。
★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。
★★ **2026-10-21 第二百三十四切片(批 7 第八切片):巡查 情况上报 3 端点**
`PatrolInspectAppController`:`save-inspect-situation`(⚠️写)+ `get-service-rule-list`(读)+ `to-save-service-record`(⚠️写)。
★ 三端点落点完全一致(`checkParam` **不吞异常**)⇒ 假 token 一律 500「-10001」;与同目录的巡查心跳(异常全吞)相反。
★ 老方法名 `updateTaskState` 与语义不符,是复制粘贴残留 ⇒ 照搬不改。
★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。
★★ **2026-10-21 第二百三十三切片(批 7 第七切片):巡查 版本检测 + 心跳 2 端点**
`PatrolCheckAppController`(纯读)+ `PatrolHeartBeatAppController`(⚠️ 真写,`LEGACY_WRITES`,只发空 body)。
★★ 同名端点、**不同老版本**:PDA 心跳给 signId 加了 loginRecordId 兜底并设 signDetailid,巡查心跳两样都没有 ⇒ 各搬各的。
★ 巡查版本检测同 PDA 口径:**不校验 token**,`data` 非空即真查版本表,`appType=5`。
★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。
★★ **2026-10-21 第二百三十二切片(批 7 第六切片):PDA 推送消息 1 端点**
`JPushMsgAppController`(老 `pda-portal` `web/pda/JPushMsgController`),纯读。
★ 老类 `extends PDABassController`(`@Component`,带 MQ/HttpServletRequest),但本端点没调用任何继承方法 ⇒ 新类不继承。
★ 鉴权同 §2hm:`checkParam`(签名 + token,不吞)⇒ 假 token 500「-10001」。
★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。
★★ **2026-10-21 第二百三十一切片(批 7 第五切片):PDA 版本检测 1 端点**
`PDACheckAppController`(老 `pda-portal` `web/pda/PDACheckController`),纯读。
★★ **PDA 鉴权强度不一致**:本端点只调 `validateSigin`、**不校验 token** ⇒ `data` 非空就真查版本表、返回真数据。
★ 返回老 `PDABaseRes` 信封,`code` 是**字符串 `"8888"`**(断言按字符串比)。
★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。
★★ **2026-10-21 第二百三十切片(批 7 第四切片):PDA 自助停车车牌上报 1 端点**
`CarSelfParkingAppController`(老 `pda-portal` `web/pda/CarSelfParkingController`),**真写**(文件表)⇒ 已进 `LEGACY_WRITES`。
★ 三种分界:`{}`/`data={}` ⇒ 400(`ConstraintViolationException` 走 400 分支);`data` **整个缺失** ⇒ 500 系统异常;假 token ⇒ 500 -10001。
★ 老类硬编码 `sysCode="ZTEITS-ORDER"`;hutool3 `CollectionUtil` → Spring `CollectionUtils`。
★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。
★★ **2026-10-21 第二百二十九切片(批 7 第三切片):PDA 码表查询 1 端点**
`SysMsgQueryAppController`(老 `pda-portal` `web/common/SysMsgQueryController`),纯读、走本地码表缓存。
★ 新坑:老方法**没有 `@Valid`** ⇒ `{}` 不落 400 而落 500「系统异常」(`validateSigin` NPE)。
★ 同一个 `checkParam`:带 `@Valid` 时 `{}`=400(§2hm),不带时 `{}`=500(本片)。
★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。
★★ **2026-10-21 第二百二十八切片(批 7 第二切片):PDA 今日统计 1 端点 + 门户 CommonService**
`PdaTodayStatisticsAppController`(老 `pda-portal` `web/pda/TodayStatisticsController`),纯读。
★ 伴迁 `web/pda/CommonService`(543 行)→ `com.zteits.pay.portal.web.pda`(**必须改包名**:basePackages 没扫 `com.zteits.pda`)。
★ 新坑:bean 名/字段名都必须是 `pdaCommonService`,否则撞 `park...inoutpark.CommonService`(@Resource 先按名再按类型)。
★ 同基座不同形态:本端点不吞异常 ⇒ 假 token 500「-10001 登录失效」;`data` 缺失 ⇒ NPE 500「系统异常」。
★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。
★★ **2026-10-21 第二百二十七切片(批 7 第一切片):PDA 门户基座 + 心跳 1 端点**
`PdaHeartBeatAppController`(老 `pda-portal` `web/pda/HeartBeatController`):收费员实时位置上报告。
★ 顺带把 PDA 门户基座搬进 `parking-legacy-common`(逐字):`com.zteits.pda.portal.common.PDARsesultUtil`、
`vo/base/{PDABaseReq,PDABaseRes}`、`vo/pda/GpsResportReq`。
★ PDA 鉴权 = 自己的签名(`PDARsesultUtil.checkParam`)+ `PdaTokenCacheUtil` token ⇒ 全 `@PermitAll`。
★ 返回值是**裸 `Boolean`**(响应体 `true`),冒烟要 `raw=True`;老方法**吞掉所有异常**并 return true。
★ 新坑:`@Valid` 多约束同时失败时报哪条是随机的(「不能为空」/「不能为null」)⇒ 断言锁 code、文案两种都收。
★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。
★★ **2026-10-21 第二百二十六切片(P5 第十批):违章车辆上报 3 端点**
`CarBreakRuleAppController`(老 `app/carbreak/CarBreakRuleController`):违章照片上传(multipart+水印)、
违章车辆上报、违章道路分页。搬了两个门户 VO(`CarBreakRuleReq`、`RoadNameReq`,都逐字)。
★ 三条都靠**老 APP token**(`checkAppToken` / `checkParam`)⇒ 全 `@PermitAll`;三条都 `@PostMapping` ⇒ GET 401/405。
⚠️ **`/upload-pic` 是 multipart + OSS 直连 ali 的真写**:空 body 落 500「系统异常」(不是 400)
⇒ **不能进 `LEGACY_WRITES`**(同 `/app-api/pay/file-upload/upload-pic`);冒烟只发不带文件分片 / 0 字节假分片。
★ `/query-...-for-page` 的 `{}` 落 500「[存量错误码 4014] appId不存在」(验签在方法体里抛)。
★ 水印字体类路径 `MicrosoftYahei.ttf` → `reportpdf/Microsoft Yahei.ttf`(同 md5)。
★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。
★★ **2026-10-21 第二百二十五切片(P5 第九批):微信公众号 4 端点**
`AliWeixinPublicAppController`(老 `weixinpublic/AliWeixinPublicController`):公众号换 openId /
小程序 code2session / 小程序手机号登录 / JS-SDK 扫码签名。搬了两个门户 VO(`GetPhoneNumberReq`、
`WxChatScanReq`,都逐字)。
★ 四个方法都没验签 ⇒ 全 `@PermitAll`;四条都 `@PostMapping` ⇒ GET 401/405。
⚠️ ⭐ **四条都会出站调微信,URL 里带解密后的 appSecret** ⇒ 冒烟只用**不存在的 appId**(落 1001、不出站)。
★ `{}` 落点不一致:前三个老信封 1003;`/get-wechat-scan` 服务层抛 ⇒ 500(存量照搬)。
★ `/small-routine-login` 是**真写**(建 `td_c_cust_person` + Redis 写 token)⇒ 只发空 body。
★ 老类 `@Value("${local.base.url}")` 字段源码零引用 ⇒ 不搬(坑 16)。
★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。
★★ **2026-10-21 第二百二十四切片(P5 第八批):农行支付 5 端点**
`ABCBankAppController`(老 `bank/ABCBankController`):钱包充值下单 / app 内嵌下单 / H5 下单 /
银行异步通知 / 银行订单状态查询。搬了门户 `XMLDocument`(逐字)。
★ 五条老代码都写了 `method = RequestMethod.POST` ⇒ 只收 POST(GET 401/405),
与上一片「未写 method」口径**相反**。
★ ★ **三个端点在老代码里是 private**(`bankH5Pay`/`bankPayForH5`/`queryBankOrderState`):
Spring 反射注册 handler 不过滤可见性 ⇒ 真能通,迁移照搬 private。
⚠️ 风险最高的一批:3 条真下单(`appPay`)+ 1 条真结算(`payNotify`)⇒ 只测绑定层/第一道闸。
★ `/pay-notify-url` 没有 `@RequestBody` ⇒ 空 body 是 500,**不进** `LEGACY_WRITES`。
★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。
★★ **2026-10-21 第二百二十三切片(P5 第七批):北京西城用户认证 3 端点**
`BJXCUserAuthAppController`(老 `wxpublic4bjxc/BJXCUserAuthController`):认证状态 / 可购卡列表 /
线下转线上专用。搬了两个门户 VO(`UserAuthRes` 逐字、`AvailableVipCardInfoVO` 仅换 import)。
★ 三个方法都没验签 ⇒ `@PermitAll`;★ **老 mapping 未写 method** ⇒ 任意方法可进,
GET 无 token 也落 400(不是 401/405)。
★ `/auth-user` 是读接口带写(`buy_state` 10→20)⇒ 进 `LEGACY_WRITES`。
★ 本地认证表缺列 `examine_state` ⇒ 三个端点的 `{}` 恒 500(环境漂移)。
★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。
★★ **2026-10-21 第二百二十二切片(P5 第六批):北京西城会员卡 3 端点**
`BJXCVipCardAppController`(老 `wxpublic4bjxc/BJXCVipCardController`):可办车场 / 新增认证 / 下单。
★ 三个端点全部匿名(`save` 令牌失效只降级成「未登录支付」,不拦)⇒ `@PermitAll`。
★★ 坑:`...service.order` 下有两个同名 `VipCardAuthPersonBjxcService`,只有**子包**那份有实现;
导错包 ⇒ 启动 `required a bean ... could not be found`。
★ 本地无 `VIP_OPEN_PARKING` 字典 ⇒ `query` 的 `{}` 恒 500(NPE);认证表缺列 `examine_state` ⇒
`save` 落库路径 500。两者老栈同样复现 ⇒ 环境漂移,不是迁移缺陷。
★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。
★★ **2026-10-21 第二百二十一切片(P5 第五批):北京西城会员卡开票 2 端点**
`BJXCVipCardInvoiceAppController`(老 `wxpublic4bjxc/BJXCVipCardInvoiceController`)。
搬了两个门户 VO(`UserAuthReq` 逐字、`HasBuyVipCardInfoVO` 仅换 `StringUtils` import)。
★ 老类继承 `AppBaseController` 但**两个方法都没验签** ⇒ 新类 `@PermitAll`。
★ 本地表 `vip_card_auth_person_bjxc` 缺列 `examine_state` ⇒ 业务路径在本地恒 500,
**老栈同样会 500** ⇒ 环境漂移,不是迁移缺陷。
★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。
★★ **2026-10-21 第二百二十切片(P5 第四批):公众号用户 2 端点**
`WXPublicUserAppController`(老 `weixinpublic/WXPublicUserController`):openId 换 App token、
openId 绑手机号。**整片无出站**,依赖全在新栈(无需搬类)。
★ 响应是第三套协议:`AppRsesultUtil` 的 `{"code":"1003","message":...}`
—— `code` 字符串、键是 `message`,别按 `CommonResult` 的 `msg` 写断言。
★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。
★★ **2026-10-21 第二百一十九切片(P5 第三批):电池(充电)微信下单 1 端点**
`BatteryWeixinPayAppController#unifiedOrder`(老 `weixin/BatteryWeixinPayController`)。
老门户 VO `BatteryAliWxPayRequest` 本片才搬进 `parking-legacy-common`
—— 注意:**「包目录已存在」≠「类已存在」**,按类名 `find`。
★ 仍是本工具盲区(`pay-portal` 不在 `DEFAULT_LEGACY` 口径内)⇒ 225/1199 不变。
★★ **2026-10-21 第二百一十八切片(P5 第二批):4 个真下单端点,两个类补齐 3/3**
`AliPayAppController` 的 `/app-pay`、`/ali-h5-pay` 与 `WeixinPayAppController` 的
`/unified-order`、`/public-unified-order`。
★ 订正:上一片写的"下单端点依赖未迁的门户本地类"**是误判** ——
`BillQueryCommonService`(pay 模块) / `EmployeeChargerValidateUtil` / `BillQueryRes` /
`QueryFeePublicReq`(legacy-common) 早就在新栈。**判定缺口前先 `find` 一遍新栈。**
★ 仍是本工具盲区:`pay-portal` 不在 `DEFAULT_LEGACY` 口径内 ⇒ 225/1199 不会变。
★★ **2026-10-21 第二百一十七切片(P5 第一批):第三方支付回调 2 端点 —— 本工具的盲区**
搬的是 `zteits-pay-portal` 的 `alipay/AliPayController#aliPayNotify` 与
`weixin/WeixinPayController#payNotifyUrl`(各 1/3 端点;**下单端点不是缺口**,
第二百一十八切片已补齐 3/3 —— 当时写的"依赖未迁的门户本地类"是误判)。
★★ **P5 的进度本工具完全看不见**:`DEFAULT_LEGACY` 写死 `zteits-bcp-portal/.../web`,
`pay-portal` / `minipay-portal` 全在口径外 ⇒ 搬完 P5 后 225/1199、未迁 0/0 **都不会变**。
要统计 P5,必须传 `--legacy-root` 或另写工具;**别拿本工具的数字当全站进度**。
★ 回调是 `@PermitAll` + 协议原文返回(支付宝 `success`、微信 XML),不是 `CommonResult`;
冒烟绝不发 `result_code=SUCCESS`(会走真结算)。
★★ **2026-10-21 第二百一十六切片:老门户最后 4 个管理后台 Controller(4 类 / 4 端点)**
搬的是 `zteits-bcp-portal` 的 `HomeController`、`aliyunecs/AliyuncsEcsController`、
`billing/UploadController`、`personpc/PersonOrderBackWxPayController` ⇒
★★ **就绪与已迁首次对齐:就绪 225/1199、已迁 225/1199、未迁 0/0**(此前长期 4/4)。
机械缺口 2/11 不变(`OrderExamineController` 2 已判不可迁 + `ParkingAssistantController` 9 ⛔ 不碰),
5 个死代码不变。
★ `HomeController` **必须**落在 `parking-module-*` 的 `controller` 包(`controller.admin` 的同级):
本工具只扫 `parking-module-*`,放 `parking-server` 会让本类重新变回「未迁」(本片实测踩过)。
★ `aliyunecs` 端点带 token 会**真出站调阿里云**(实现在无配置时回落到写死的 key)⇒ 冒烟不带 token,
只验「匿名 401 / 带 token GET 405 / 老路由 404」;`upload` 老 `@NoAuth` 按 §3.3 第 1 类收敛为
`pay:upload:create`(缺文件是 500,不进 `LEGACY_WRITES`);`wxpay` 老 `@SysOpLog` → `@OperateLog`。
★★ **2026-10-21 第二百一十五切片:时域月卡购买/续费(1 类 / 2 端点,全写,老类已弃用)**
搬的是 `zteits-pay-portal/.../web/app/viparea/TimeVipCardController`(老 `/timeVipCard`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ 老类注释已标**弃用**,但映射仍在门户 ⇒ 照搬;2 条全真写,进 `LEGACY_WRITES`、只发空 body。
★ 与 §214 的差别:**没有** `isJudgeArreage` 前置,`checkParam` 是第一道业务闸,结构完整+appId 假直接 `[4014]`。
★ 复用 §214 补搬的两个 VO,零新 VO、零新依赖;存量缺陷:`createVipTimeCardOrder` 调 `createVipAreaCardOrder`。
★★ **2026-10-21 第二百一十四切片:区域月卡购买/续费(1 类 / 2 端点,全写)**
搬的是 `zteits-pay-portal/.../web/app/viparea/AreaVipCardController`(老 `/areaVipCard`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ 2 条全是真写(建卡 + `order_vipcard` + 明细 / 续费同四步)⇒ 进 `LEGACY_WRITES`、只发空 body。
★ ⚠️ **`checkParam` 不是第一道闸**:先跑 `isJudgeArreage(carNumber, effDate, cardNo)`(只读,查不到卡 return false),
所以要证明 `checkParam` 在链路上得给"结构完整 + appId 假 + cardNo 不存在"的 body(落 `[4014]`);
只给 `AppBaseReq` 5 个字段的假签名会先被 `@Valid` 业务必填挡成 400。日期只认 `yyyy-MM-dd HH:mm:ss`。
★ 存量缺陷:`getIsJudgeArreargeVipcard()`/`getType()` 拆箱 NPE;`Long.valueOf(orderFee)`;两分支逐行重复。
★★ **2026-10-21 第二百一十三切片:错峰共享信息验证(1 类 / 3 端点,1 读 + 2 写)**
搬的是 `zteits-pay-portal/.../web/app/newversion/SharedAuthenticationController`(老 `/sharedAuthentication`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ 1 读 + 2 写(`driver-license-review`/`idcard-review` 改 `td_c_car`)⇒ 两条写进 `LEGACY_WRITES`、只发空 body。
★ 3 条都 `@Valid` ⇒ `{}`/空 body 都是 400;假签名**两种落点并存**:读走 `checkParam` ⇒ `[4014]`,
两条写没有 `checkParam`、第一道闸是 `AppTokenUtil.getUserCustId` ⇒ `[-10000]`。
★ 存量缺陷:逐车 N+1 查审核记录;`ExamineStateEnum.of(Integer)` 拆箱 NPE;日志占位符与字符串拼接混用。
★★ **2026-10-21 第二百一十二切片:新版 APP 区域卡(1 类 / 3 端点,全读)**
搬的是 `zteits-pay-portal/.../web/app/newversion/VipAreaCardController`(老 `/vipAreaCard`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ 3 条全读;⚠️ `valid-vip-area-card-buy-or-renew` 在 `checkParam` **之前** `getType() == 1` 拆箱
⇒ `{}`/假签名是 500「系统异常」,带 `type` 才落 `[4014]`。复用 §211 补搬的两个 VO。
★★ **2026-10-21 第二百一十一切片:新版 APP 时域卡(1 类 / 3 端点,全读)**
搬的是 `zteits-pay-portal/.../web/app/newversion/VipTimeCardController`(老 `/vipTimeCard`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ `query-vip-time-card-info` 带 `@Valid`(`AppBaseReq` 5 个 `@NotBlank`)⇒ `{}` 400;
另两条老代码已标弃用、无 `@Valid` ⇒ `{}`/假签名都 500 `[4014]`。
★ `validVipTimeCardBuyOrRenew` **只查不写**(次数/时长校验)⇒ 3 条全读、不进 `LEGACY_WRITES`。
★ 补搬 `vo.app.vipareacard.QueryVipAreaCardRequest`、`web.app.viparea.query.TVipAreaCardValidRequest`。
★★ **2026-10-21 第二百一十切片:ETC 德亚追缴通知(1 类 / 1 端点,真写)**
搬的是 `zteits-pay-portal/.../web/app/etcinterface/ETCDeYaController`(老 `/etc/deya`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ 第三方回调不吃老 token;裸 VO 入参(无 `@RequestBody`)⇒ 任何空 payload 都 500「系统异常」
(`bizParam`=null 的 NPE 在 try 之外)。★ 真写:`arrearsPayNotify` 欠费追缴结算。
⚠️ 空 body 是 500 ⇒ **不能进 `LEGACY_WRITES`**(那张表要求 400)。
★★ **2026-10-21 第二百零九切片:第三方车场查询(1 类 / 1 端点,纯缓存读)**
搬的是 `zteits-pay-portal/.../web/app/thirdservice/ParkingLotForThirdController`(老 `/thirdParkQuery`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ 不吃老 token:`@Valid @RequestBody String` 取 `appId` 查 `td_c_third_part_app_authorization`。
空 body ⇒ 400;`{}`/假签名 ⇒ 500「系统异常」(appId=null 存量路径);空白串 ⇒ 200 + 空列表。
⚠️ 存量缺陷:该 Service 查不到也返回**空壳 DTO** ⇒ `THIRDPART_APPID_NOT_EXISTS` 不可达。
★ `sysCode` 传进了 `GetAuthorizationByAppIdRequest` 但实现类**只按 appId 查** ⇒ 不必另立独立键。
★★ **2026-10-21 第二百零八切片:丹阳公共自行车(1 类 / 4 端点,纯出站代理)**
搬的是 `zteits-pay-portal/.../web/app/danyang/DanYangBikeQueryController`(老 `/danYangBike`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ 4 条全是 `@RequestParam` 代理到 danyangbike.com:缺必填参数 ⇒ 400「请求参数缺失:<name>」。
⛔ `/baseinfo/get-batch` 参数全 optional,空参数也出站 ⇒ 冒烟只验 GET 405/401,绝不 POST。
★ 零落库、零写 ⇒ 不进 `LEGACY_WRITES`。
★★ **2026-10-21 第二百零七切片:商户信息(1 类 / 4 端点,2 写 + 2 读)**
搬的是 `zteits-pay-portal/.../web/app/business/CustBusinessController`(老 `/custBusiness`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ `@Valid` 先于 `checkParam`:两条写 `{}` ⇒ 400「请求参数不正确」;假签名带 `token` ⇒ 500 `[4014]`。
两条读无 `@Valid` ⇒ `{}`/假签名都 500 `[4014]`。
★ 2 条写(落 `td_c_cust_business`、改商户并重置审核状态)进 `LEGACY_WRITES`,只发无 body。
★★ **2026-10-21 第二百零六切片:车位预约(1 类 / 6 端点,2 写 + 4 读)**
搬的是 `zteits-pay-portal/.../web/app/appointment/AppointMentBerthController`(老 `/appointmentBerth`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ 6 条全 `@RequestBody`(无 `@Valid`):空 body 400;`{}`/假签名**三落点** ——
`checkParam` ×4 ⇒ 500 `[4014]`;手写 `checkAppToken` ×1 ⇒ 500 `[-10000]`;
不验签 ×1 ⇒ 500「系统异常」。
★ 2 条写(建 `order_appointment`、改预约日期)进 `LEGACY_WRITES`,只发无 body。
★★ **2026-10-21 第二百零五切片:提现(1 类 / 1 端点,存量空壳)**
搬的是 `zteits-pay-portal/.../web/app/TransferController`(老 `/parkInfo`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ `@Valid @RequestBody String`:空 body 400;非空 body 直接 **`return null`** ⇒ HTTP 200 + 空体。
★ 零副作用(`orderTransferService` 从未调用)⇒ **不进 `LEGACY_WRITES`**。
★ 至此老 `web/app/` 顶层类全部处理完(`IRainParkingController` 整类注释掉、无需迁)。
★★ **2026-10-21 第二百零四切片:APP 图片上传(1 类 / 1 端点,写)**
搬的是 `zteits-pay-portal/.../web/app/SysFileUploadController`(老 `/pic`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ 入参是 **multipart/form**(`@RequestParam MultipartFile picFile + token + fileType`):
空 body/`{}` 因缺必填参数被兜底成 **500「系统异常」**(不是 400)
⇒ **不能进 `LEGACY_WRITES`**,零写入守卫单独钉在 §2go。
★ 会落 FastDFS/OSS + `td_c_file` ⇒ 冒烟**永不发真文件**。
★★ **2026-10-21 第二百零三切片:会员卡购买(1 类 / 1 端点,写)**
搬的是 `zteits-pay-portal/.../web/app/VipCardController`(老 `/vip`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ `@RequestBody String` + 手写 `checkParam` ⇒ `{}`/假签名 500 `[4014]`。
★ 真写 `order_vipcard` + `order_vipcard_detail` ⇒ `LEGACY_WRITES` 只发无 body。
★★ **2026-10-21 第二百零二切片:我的首页(1 类 / 2 端点,全读)**
搬的是 `zteits-pay-portal/.../web/app/MyHomeController`(老 `/home`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ 两种落点:`home-page`(`@RequestBody String` + `validateSigin`)⇒ 500 `[4014]`;
`query-arrearage-is-exist` **不验签名** ⇒ 成功信封(`code` 字符串 `"0"`、`isLogin=0`)。
★ 全读,`LEGACY_WRITES` 不加。
★★ **2026-10-21 第二百零一切片:停车订单查询(1 类 / 13 端点,12 读 + 1 写)**
搬的是 `zteits-pay-portal/.../web/app/ParkOrderQueryNewController`(老 `/queryParkOrder`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★ 补回 8 个老 VO(`parklot/` 5 个 + `billquery/` 3 个)到 legacy-common;
jfinal `JsonUtils.toJson` 唯一一处(日志)换 fastjson `JSON.toJSONString`。
★ 鉴权有**三类落点**:8 条无 `@Valid` ⇒ 500 `[4014]`;4 条 `@Valid` ⇒ 400;
`query-arrearage-sta-for-wx-publc` 因 `getOrgId() == 10003` 拆箱 null ⇒ 500「系统异常」。
★ 唯一写 `appDeleteParkOrder`(`delType=0` 按 custId 全部车牌批量软删)⇒ `LEGACY_WRITES` 只发无 body;
`openOrClose` 名字像写、实际只读。
★★ **2026-10-21 第二百切片:个人账户支付(1 类 / 4 端点,全写)+ 补回 billquery 路由链**
搬的是 `zteits-pay-portal/.../web/app/AppAccoutPayController`(老 `/appAccountPay`)⇒
口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、
机械缺口 **2/11**、5 死代码**全部不变**。
★★ 补链路是本片主体:整条 `com.zteits.pay.portal.billquery`(11 文件)+ `HttpClientTutorial`
+ 5 个 billquery VO + `ZeroPayReq` + `EmployeeChargerValidateUtil`,并把 §196 精简版
`BillQueryCommonService` 换成 964 行全量版。
⚠️ 这些类放在 **`parking-module-pay`**:`KeyTopBillQueryServiceImpl` 依赖 module-pay 的
`KeyTopBillQueryInnerService`,而 `parking-legacy-common` 不能反向依赖模块。
`DiscountWrapper` 复用 `com.zteits.ipay.util.DiscountWrapper`(同签名)。
★ 同一控制器两种鉴权:`account-pay` 手写 `checkParam` ⇒ `{}` 500 `[4014]`;
其余 3 条 `@Valid` + `ZeroPayReq` ⇒ `{}` 400(文案随机)。
★ 4 条全是钱/账 ⇒ 全进 `LEGACY_WRITES`、只发无 body。
★★ **2026-10-20 第一百六十七切片:补回月卡合同整条链路 + 搬完车场运营·月卡合同管理 6 端点**
(`VipCardContractController`,路由 `/admin-api/order/vip-card-contract`)
★ 缺口原因:`VipCardContractService` 整条链路都不在新栈(接口 + 4 个 DTO/入参 + 域对象
+ 两套 Mapper/XML + Word/Excel 模板)。本次按老仓逐字搬回,再补 3 个编排服务
(归档 / Word 渲染 / 四川营台账导出)。
⇒ 就绪 **224/1193 → 225/1199**、已迁 **220/1189 → 221/1195**、未迁 **4/4**、
缺口 **3/17 → 2/11**(剩 `OrderExamineController` 2、`ParkingAssistantController` 9[不碰])、
死代码 5 不变。
★ 老类零 `@NoAuth`、零 `@SysOpLog` ⇒ 6 条全 `@PreAuthorize`、新类零 `@OperateLog`。
★ ⛔ `archive-now` 是真归档 + 真传 OSS:冒烟只允许**空 body**(绑定层 400),⛔ 不发 `{}`。
每日 03:23 的 `VipCardContractTask` 定时任务**未搬**(会自行上传真实 OSS)。
★ 冒烟 34 条(+22 §2ff + 6 §5b + 6 `LEGACY_WRITES`),6262 → 6296;零写 `system_operate_log`
**+244/轮**(§166 的 5 条 `@OperateLog`,本片零新增),`td_b_vip_card_contract` 恒 2 行。
★ POI 4.1.2:`Cell.CELL_TYPE_NUMERIC` 已删,台账导出用 `CellType.NUMERIC`。
★★ **2026-10-20 第一百六十六切片:补回 `BjxcPushService` + 搬完停车订单管理 14 端点**
(`ParkingRecordController`,路由 `/admin-api/order/park-record`)
★ 缺口原因只有一条:12 个服务依赖都在,**只差 `BjxcPushService`**(接口 + 2 DTO + 太极响应 DTO
+ 实现类都不在新栈)。本次按老仓逐行搬回 `BjxcPushServiceImpl`,
并给 `ParkOrderDTO` 补 `isUploadThird`(老 DTO 有、新栈缺,导出侧 `getParkInfo` 要回填)。
⇒ 就绪 **223/1179 → 224/1193**、已迁 **219/1175 → 220/1189**、未迁 **4/4**、
缺口 **4/31 → 3/17**(剩 `ParkingAssistantController` 9[不碰]、`VipCardContractController` 6、
`OrderExamineController` 2)、死代码 5 不变。
★ `@NoAuth` 两条:导出收紧为 `GET /export-excel` + `:export`;`queryParkOrderInOutImgNew`
是上一方法的逐字副本、老前端 0 调用 ⇒ 收紧为 `:query`。两条都不进 `@PermitAll` 白名单。
★ 冒烟 46 条(+26 §2fe + 14 §5b + 6 `LEGACY_WRITES`);零写 `system_operate_log` **+244/轮**
(本片 5 条 `@OperateLog`),其余 52 张表 0 变化。
★ 编译踩坑:`OperateLog`/`OperateTypeEnum` 在 `com.zteits.parking.framework.operatelog.core`。
★ `ParkOrderDTO` 加字段 ⇒ §2u 的 RSN 全键断言 46 → **47**,`RSN_KEYS` 已同步。
★★ **2026-10-19 第一百六十五切片:修第二类实现误判 + 搬完进出场上报 3 端点**
(`InOutParkStatisticController` 2 读 + 1 导出,路由 `/admin-api/park/in-out-park-statistic`)
★★ **工具修正**:实现扫描此前只 glob `*Impl.java` / `*Ipml.java`,漏掉**不叫 Impl 的实现类**
(典型:桥接空实现 `NoopDfGroundLockMsgPushService implements DfGroundLockMsgPushService`)
⇒ 已迁的 `GroundLockManageController` 6 端点一直被误挂在缺口档。现改为扫全部 `*.java`
(先 `"implements" in src` 预过滤,再套 `IMPL_PAT`),彻底不依赖命名约定
⇒ 就绪 **221/1170 → 223/1179**、缺口 **6/40 → 4/31**。
★ `InOutParkStatisticService` 接口一直在 `parking-module-api`,但**无实现**(存量实现整类被注释、
Dubbo provider 早已注释);本次按 `git show 550cba464e^` 搬回实现,
并补回它依赖、同样被删干净的 `TdACardCouponsService`(接口 + `queryCardCouponsByCarNumbers`
单方法 + pay 模块实现;DB 访问留在 pay 内,不在 park 跨库直连)。
⇒ 已迁 **217/1166 → 219/1175**、未迁 **4/4**、✔ **3/3**、⚠A **0/0**、⚠B **1/1**、死代码 5 不变。
★ 冒烟 17 条(+3 §5b):两个读端点空 body 停在 1003;chart 跨天横轴 19 格、同一天横轴
**25 格(00:00 … 24:00,存量 `j <= hour + 1` 多排一格)**;导出缺参停在绑定层 400。
★ 本机 `parking_cloud` **没有 `inpark_log` / `outpark_log`** ⇒ list/export 的 SQL 抛
`Table … doesn't exist`,被兜底成 501「表结构未导入」;断言写成 `0 或 501`,表导入后自然变 0。
★ 零写一轮:`system_operate_log` 两次计数均 101348(本片零 `@OperateLog`),其余表 0 变化。
★★ **2026-10-19 第一百六十四切片:修 DTO 误判 + 搬完会员卡购买 15 端点**
(`TVipTimeCardCustInfoController` 6 + `TVipAreaCardCustInfoController` 9)
★★ **工具修正**:`RenewVipAreaCardOperReq` 位于 `api.service.pay.viparea.oper`,是**入参 DTO 类**、
不是服务契约。旧版只看 `import com.zteits.clouds.api.service.*` 就判"接口不在新栈",
把这两个 Controller 误挂进缺口档。新增 `API_CLASS_PAT` 收集 `api.service.*` 下的类,
`scan_legacy` 命中即跳过 ⇒ 就绪 **219/1155 → 221/1170**、缺口 **8/55 → 6/40**。
★ 15 个端点全 `@PreAuthorize('bill:vip-{time,area}-card-cust-info:*')`、全 `@PostMapping`;
7 个写端点挂 `@OperateLog`(时域卡 3 + 区域卡 4),其余读/校验端点零注解。
⇒ 已迁 **215/1151 → 217/1166**、未迁 **4/4**、✔ **3/3**、⚠A **0/0**、⚠B **1/1**、死代码 5 不变。
★ 冒烟 15 条全发空 body:create 停在 1003(时域卡编码/区域卡编码)、renew 停在存量
`Value for cardNo cannot be null`(500「系统异常」,红线⑥照搬)、delete 停在 `@Valid`、
update/delete-list 停在控制器 1003、push 停在 `@NotNull id`、statistic 停在参数校验。
★ 零写一轮:仅 `system_operate_log` +239(7 个 `@OperateLog` 里 delete 两条在绑定层就 400、
到不了切面,其余 5 条真正写入;另 234 条来自全脚本既有端点);新增监控表
`t_vip_area_card_cust_info` / `t_vip_area_card_cust_info_rel` 均 0 变化。
★★ **2026-10-19 第一百六十三切片落地 P3 ②(操作员 id 换算表)**
(`LoginUserBridge` + `LegacyOpIdMappingConfig`,配置键 `parking.legacy.op-id-mapping`)
⇒ 本片**不搬 Controller、不加端点**,上面所有计数**逐位不变**:
**215/1151**、未迁 **4/4**、✔ **3/3**、⚠A **0/0**、⚠B **1/1**、缺口 **8/55**、死代码 5。
★ `opId` 现在与 `orgId` 同一手法:`新system_users.id:老sys_op.id` 查表换算;
**默认空表 = 恒等映射**(不配与改造前完全一致),非法格式启动即失败。
★ 本机 `sys_op` 0 行 ⇒ 代码里**零猜测映射**;本地 yaml 的 `1:10001` 仅为验证链路。
★ 端到端实测:`save-black-list-rule` 写入 `create_empid=10001`(探查行已清理);
本片零写,基线仍 **+234/轮**。
★★ **2026-10-19 第一百六十二切片补全两个既有类的 session 端点**
(`PersonPCIndexController` +3/3:首页卡片 / 停车记录 / 欠费记录;
`PersonPCCarNumController` +1/1:车牌读取)
⇒ 两个类此前已计入"已迁",本片只是补齐端点 ⇒ 就绪/已迁数**不变**:
**215/1151**、未迁 **4/4**、✔ **3/3**、⚠A **0/0**、⚠B **1/1**、缺口 **8/55**、死代码 5。
★ 4 条都经私有 `getOwnerCarNumbers()` 或直接读 `getUserInfoForPersonPC()`,
带 token 无 session 时依次撞存量 1003(`custId为空!` / `组织id为空!` /
`获取客户信息失败`,顺序见 §162 ⑥)。
★ 新增 4 条 `§5b` 无 token 断言;本片零写,基线仍 **+234/轮**。
★★ **2026-10-19 第一百六十一切片借 §160 登录桥搬走 ⚠B 的 `PersonPCCouponController` 1/1**
(`/admin-api/pay/person-pc-coupon/get-coupon-person`,1 读、零导出、零 `@OperateLog`)
⇒ 已迁 **214/1150 → 215/1151**、未迁 **5/5 → 4/4**、✔ **3/3** 不变、
⚠B **2/2 → 1/1**(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。
★ 唯一闸门是 `getUserInfoForPersonPC()`:session `USER_INFO.custId` 为空 ⇒ 存量 1003
「获取客户信息失败」;带 token 但无个人客户 session 时冒烟就停在这里。
★ 查询条件照搬:`cardTypes=[1,2,3]`、`day=15`、`dataState` 有效;回填 `orgName` 的
N+1 与 `getData()` 空时 NPE 是存量缺陷,照搬。
★ ⚠️ 本地 `td_c_coupon_person` **缺 `car_num` 列**(parking-cloud 存量 mapper 多列漂移,
老 `zteits-pay` 同名 SQL 并不查它)⇒ 正样本探针临时加列、跑完删列;
冒烟只走 1003,不执行 SQL,不受影响。⛔ 别为此改 mapper/schema。
★ 本片零写:基线仍 **+234/轮**;`td_c_coupon_person` / `td_c_coupon_rule` 已在监控内。
★★ **2026-10-19 第一百六十切片落地 P3 ③(个人客户登录桥)并搬走 `PersonPCAuthController` 4/4**
(`/app-api/pay/person-pc-auth`,4 条 `@PermitAll`:发码 / 登录 / 取用户信息 / 登出)
⇒ 已迁 **213/1146 → 214/1150**、未迁 **6/9 → 5/5**、✔ **4/7 → 3/3**、
⚠B **2/2** 不变(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。
★ 桥的原理:登录端点把 `LOGIN_USER_TOKEN` / `USER_INFO` 写进 **HTTP session**,
于是 `SessionCommUtil#getUserInfoForPersonPC()` 在新栈恢复可用
⇒ `PersonPCCouponController` 已按原样搬走,其余「只差 session」的类仍待补。
★ ⛔ 冒烟**绝不**用已注册手机号打 `send-validator-code`(会真实发短信);
正样本用 Redis 种 `person-pc:validator_code_<phone>` 单独验证(见 §160 ⑥)。
★★ **2026-10-19 第一百五十九切片落地 P3 ①(数据权限桥)并搬走 ⚠B 的 `ParkLotStatisticController` 1/1**
⇒ 已迁 **212/1145 → 213/1146**、未迁 **7/10 → 6/9**、✔ **4/7** 不变、
⚠B **3/3 → 2/2**(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。
★ `LoginUserBridge` 现在按老登录口径**懒推导** `userAreas`/`plNos`:
`queryAreaListByOrgIds` → `sys_org_area`;`sys_code=1001` 取 `areaType=60` 走
`queryParkInfoByAreaBlockIds`,`sys_code=1002` 取 `areaType=40` 走 `queryParkListByPark`;
没有区域时 `plNos` 为空集合(不回退「空即可见」);`resIds`/`roleIds` 仍 null。
★ 该类唯一端点只读登录 `plNos`,为空抛 `AUTH_TOKEN_NOT_EXISTS`(存量 2003),
本机 org 10003 无区域 ⇒ 冒烟断言 500/2003;正样本另用 `LEGACY_ORG_ID_MAPPING=1:10038`
验证(恰好 2 个车场,与 SQL 一致)。
★ 本片零写:基线仍 **+234/轮**;新增只读表 `sys_org_area`、`sys_org` 已进零写监控。
★★ **2026-10-19 第一百五十八切片搬走 ⚠B 的 1 个类 / 1 端点**
(`RemoteControlDzController` 1/1)
⇒ 已迁 **211/1144 → 212/1145**、未迁 **8/11 → 7/10**、✔ **4/7** 不变、
⚠B **4/4 → 3/3**(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。
★ 同片判定:`PersonPCCouponController` 读 `getUserInfoForPersonPC().getCustId()`
⇒ 个人客户登录桥(P3 ③)未落地,**不迁**;`AliyuncsEcsController` 虽只读 `orgId`,
但会带存量 AK/SK **真实外呼阿里云**(且 service 会把配置对象打进 info 日志)
⇒ 继续挂起,不做正测。
★★ **2026-10-19 第一百五十七切片搬走 ⚠B 的 2 个类 / 8 端点**
(`StoreCarManageController` 7 / `AppointmentManageController` 1)
⇒ 已迁 **209/1136 → 211/1144**、未迁 **10/19 → 8/11**、✔ **4/7** 不变、
⚠B **6/12 → 4/4**(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。
★ 同片判定:`ParkLotStatisticController` 读登录 `plNos`(**不是** `orgId`)⇒ P3 未落地前不可搬。
★★ **2026-10-19 第一百五十六切片搬走 ⚠B 的 2 个类 / 12 端点**
(`OrderRefundController` 5 / `AccountRechargeStatisController` 7)
⇒ 已迁 **207/1124 → 209/1136**、未迁 **12/31 → 10/19**、✔ **4/7** 不变、
⚠B **8/24 → 6/12**(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。
★ **2026-10-19 第一百五十四切片修正实现识别**(`extends Base implements X` 与 `RefundServiceIpml.java`)
⇒ 就绪 211/1118 → **219/1155**、缺口 16/92 → **8/55**、未迁 8/11 → **15/42**、
✔ 4/7 → **6/15**、⚠ 4/4 → **⚠A 1/3 + ⚠B 8/24**。总数 232/227/5 不变。
★ **2026-10-19 第一百六十五切片修正实现识别(第二刀)**:上一条只解决了「文件名拼错」,
仍按 `*Impl.java`/`*Ipml.java` 命名约定找实现 ⇒ **不叫 Impl 的实现类**一律漏判。
实证:`NoopDfGroundLockMsgPushService implements DfGroundLockMsgPushService`(桥接空实现)
被当成「无实现」,让**早已迁完**的 `GroundLockManageController` 6 端点一直挂在缺口队列。
现改为 glob 全部 `*.java`(`"implements" in src` 预过滤后再套 `IMPL_PAT`),与命名约定解耦
⇒ 就绪 **221/1170 → 223/1179**、缺口 **6/40 → 4/31**、已迁 **217/1166 → 219/1175**。
★★ **2026-10-19 第一百五十五切片把上片新进 ✔ 池/⚠A 池的 3 个类一次搬完**
(`PersonPCAcctController` 3 / `UserRechargeRefundController` 5 / `AppUserAccountController` 3)
⇒ 已迁 **204/1113 → 207/1124**、未迁 **15/42 → 12/31**、✔ **6/15 → 4/7**、
⚠A **1/3 → 0/0(再次清零)**、⚠B **8/24** 不变、缺口 **8/55**、死代码 5。
★ **2026-10-16 第三十四切片:✔ 那 5 个已逐个读源码判定**(人工结论见下面 ADJUDICATED 表,脚本只做标注、不改机械判定)
⇒ ⏸ 4 个 / 10 端点(待定:共享密钥来源 1 个、个人客户登录桥 2 个、文件存储 1 个)、
⛔ 1 个 / 1 端点不适用(HomeController 不是 JSON 接口) ⇒ **✔ 池当前 0 个可直接开搬,别按 4/7 排期。**
★ 2026-10-17 第四十六切片:`RefreshCacheController` 已按 (B) 配置化方案搬走 ⇒ ✔ 池从 5/11 降到 **4/7**。
★★ **2026-10-16 第三十五切片:⚠ 那 124 个再切一刀**(`LEGACY_ID_PAT` 不知道该 `get*()` 打在谁身上,
把前端**请求体**一起抓了)⇒ **⚠A 12 个 / 61 端点**整类**没有一处**读登录态 = **纯机械误报**、
**⚠B 113 个 / 700 端点**才是真读了登录态的那批。A 组逐类核完命中行(全落在
`request.getPlNos()` / 局部入参 `req.getOrgId()` / 下游 DTO 上),结论见 `IDSPACE_FALSE_POSITIVE` 表;
其中 `EqpIncomeStatisticController`(3 端点)本片已搬 ⇒ **⚠A 余 11 个 / 58 端点**。
★★ **2026-10-16 第三十六切片:⚠A 里再搬 2 个**(`BerthStatisticController` 2 端点、
`WxcpMessageController` 2 端点)⇒ **⚠A 余 9 个 / 54 端点**。本片的价值在于把 §3.3
「老 `@NoAuth` 分两类」**在同一批次里两侧都钉住**:berth 落 `/admin-api` + `@PreAuthorize`
(无 token **401**),wxcp 落 `/app-api` + `@PermitAll` + 白名单登记(无 token **进业务**)。
★★ **2026-10-16 第三十七切片:⚠A 里的 `IndexParkSituationController`(6 端点)搬走**
⇒ **⚠A 余 8 个 / 48 端点**。它一搬走,⚠A 的 6 端点整档就**自动从「未迁」里消失**
(下面的 85/359 与 ⚠A 8/48 是**同一次移动的两个投影**,别当两件事重复计数)。
★ 本片是「云平台首页」两个 tab 的**收尾**:兄弟类 `IndexParkIncomeController` 早在
批 6 第三十X 切片就搬了,本片把 `IndexParkSituationController` 补上 ⇒ 该页面在
`/admin-api` 下已无遗留。★ 坑 22 侧:老类 5 处命中**全是请求体**(3 处
`CollectionUtils.isEmpty(request.getPlNos())` + 2 处 `setPlNos(request.getPlNos())` 转投下游),
全类 **0 处** `getUserInfo(` / `SessionCommUtil` ⇒ ⚠A 机械误报,逐行核销见 `IDSPACE_FALSE_POSITIVE`。
★★ **2026-10-16 第三十八切片:⚠A 里的 `IncomeCalculationController`(5 端点,4 读 + 1 导出)搬走**
⇒ **⚠A 余 7 个 / 43 端点**(`86/364` 与 `7/43` 同一次移动的两个投影)。
★ 导出的 `@NoAuth` 按 §3.3 **第 1 类**(管理页面)收紧;★★ **本片是「坑 25」的第二处修点**——
两条走 `PageHelper.startPage` 的 select 原是 `statementType="STATEMENT"`,老 PageHelper 5.0.0
拼字面量 `LIMIT 3` 能跑、新栈 6.1.1 拼 `LIMIT ?` 绑不上 ⇒ 真分页必 500;修法与回归守卫
见 §2ah 抬头 ① 与 `TIncomeCalculationStaticNumSMapper.xml` 的 `⚠️ 手改` 注释。
★★ **2026-10-16 第三十九切片:⚠A 里的 `VehicleFlowController`(5 端点,3 读 + 2 导出)搬走**
⇒ **⚠A 余 6 个 / 38 端点**(`87/369` 与 `6/38` 同一次移动的两个投影)。
★ 两条老 `@NoAuth` 都按 §3.3 **第 1 类**收紧(一条 `GET /export-excel`、一条 `GET /export-pdf`,
老 `carFlowPrint` 的 `@RequestMapping` **没写 method**);★ 本片**新建了 1 个老门户独有 VO**
(`com.zteits.bcp.portal.vo.report.runtime.VehicleFlowVO`,只喂 PDF、不进响应体)——
⚠️ 上两片那条"先看返回类型在不在新栈"的省力规律**要连 VO 一起数**,本片就漏了一个。
★★ **2026-10-16 第四十切片:⚠A 里的 `IncomeAllStatisticController`(5 端点,5 读)搬走**
⇒ **⚠A 余 5 个 / 33 端点**(`88/374` 与 `5/33` 同一次移动的两个投影)。
★ 本片**又新建了 3 个老门户独有 VO**(`com.zteits.bcp.portal.vo.report.income` 下
`IncomeStatisticTrentReportVO` / `IncomeStatistic2MonthFeeVO` / `IncomeStatisticDayFeeVO`)——
第三十九切片留下那条"返回类型要连 VO 一起数"的教训本片照做了,**先数再开搬**。
⚠️ **本片 5 个端点里 4 个在本机恒 500**,原因**不在迁移**:老 SQL(`IncomeFinanceBillSMapper.xml`
3 处**在本片路径上** + 同族的 `IncomeStatisticDaySMapper.xml` 1 处)写的是 `GROUP BY x DESC`,**这个语法在 MySQL 8.0.13 已被移除**,
本机 MySQL 是 8.x ⇒ 老栈同样 500。按红线⑥(存量缺陷照搬不修)**不动 SQL**,改为**离线桩测**证明Controller 逻辑等价
(剥 fat jar + `Proxy` 桩 + 反射注入,见 AGENTS.md 第四十切片 ④)。⇒ 这 4 条端点的"可交付"以
**目标库若不是 MySQL 8 才成立**为准,是**非阻塞决策项**,已上报。
★★ **2026-10-16 第四十一切片:⚠A 里的 `OrderParkStatisticController`(4 端点,4 读)搬走**
⇒ **⚠A 余 4 个 / 29 端点**(`89/378` 与 `4/29` 同一次移动的两个投影)。
★ 本片**一个新类都没新建**(4 个返回类型全在新栈)——"返回类型要连 VO 一起数"这条第 3 次生效。
★ 老类 **3 条端点是大屏用 `@NoAuth`** ⇒ 按 §3.3 **第 1 类**(管理页面)收紧成
`/admin-api` + `@PreAuthorize('park:order-park-statistic:query')` ⇒ 4 条无 token 全 **401**。
⚠️ **本片两条端点在本机恒空、但都不是迁移回归**:EP2 走的 SQL 打 `order_park.modfiy_date`
(该表 `max = 2018-10-16`、`> yesterday` 的行 0 条);EP3/EP4 的表 `parking_running_statistic`
全量 500 行停在 `2026-01-31`,而窗口由代码**按今天算** ⇒ 恒空。
★ 判定手法:**同一条 SQL 直连、手动换窗口**(`2026-01-24~01-31` 回 1 组 15188/15073)
⇒ 证明 SQL 活着,"空"只来自窗口。**先怀疑样本、再怀疑代码**。
★★ **2026-10-16 第四十二切片:⚠A 里的 `ReconciliationManagementController`(7 端点,5 读 + 2 导出)搬走**
⇒ **⚠A 余 3 个 / 22 端点**(`90/385` 与 `3/22` 同一次移动的两个投影)。
★ 本片**一个新类都没新建**(7 个返回类型全在新栈)——"返回类型要连 VO 一起数"第 4 次生效。
★ 老类 `extends ReconciliationBaseController`(140 行、只有一个 `getPayOrderTypeName(Integer,Integer)`、自己零映射)⇒ 它躺在本脚本的"5 个死代码"清单里,但**它是活基类,不是死代码**;新栈没有基类可继承 ⇒ 那段 switch **原地抄进新控制器**。
★ 两条导出老 `@NoAuth` 按 §3.3 **第 1 类**(管理页面)收紧:一个类两条导出 ⇒ 主导出占 `GET /export-excel`、另一条落 `GET /export-detail-excel`(§3.3 第 3 条)。
⚠️ **本片两条源表样本都停在过去、且采到的列组正好错开**:`td_b_pay_blncac_result` 的 `report_date` ∈ 2018-01-01~01-16 而 `pl_no` **500 行全是空串**(⇒ 传真 plNo 恒空);`pay_order` 全表停在 `pay_finish_time = 2017-09-25` 且 `pay_type = 6`(⇒ 写死 `pay_type IN (1,2,4,5)` 的 EP2 **恒全 0**)。**又一条「先怀疑样本」**。
⚠️ 导出端点的日期是 `@RequestParam Date` ⇒ **只吃 JS `Date.toString()`**(`yyyy-MM-dd HH:mm:ss` → 400)——**第 4 种日期契约**,⛔ 别顺手统一。
★★ **2026-10-16 第四十三切片:⚠A 里的 `BerthsStatisticController`(9 端点,5 读 + 4 导出)搬走**
⇒ **⚠A 余 2 个 / 13 端点**(`91/394` 与 `2/13` 同一次移动的两个投影)。
⚠️ **别与 `BerthStatisticController`(第三十六切片已搬)混** —— 只差一个 `s`,
那个在新路由 `/park/berth-statistic`,本片这个在 `/park/berths-statistic`。
★ 本片**一个新类都没新建**(9 个返回类型全在新栈)——"返回类型要连 VO 一起数"第 5 次生效。
★ 4 条导出**全是老 `@NoAuth`**(老前端 `unit.js:877~890` 逐条有入口)⇒ 按 §3.3 **第 1 类**收紧
⇒ 9 条端点无 token 全 **401**;一个类 4 条导出 ⇒ 第 3 条命名:主导出占 `GET /export-excel`,
另三条落 `-detail` / `-month` / `-month-detail` 后缀。
★★ **本片新增一条样本判据**:`parking_lot_use_statistic` 500 行**只有一行车场**
(`A150402003`,且它**不在** `parking_lot` 里)⇒ 多车场断言全废;要验 N+1 回填车场名,
得另找 `parking_lot` 里有、统计表里没有的 `B1504020C7`。挑正样本前先
`select count(distinct <业务键>)`。
★★ **两条"全小写连写"的老路由值 `getstatisticsummary` / `getstatisticlist` 逐字保留**——
kebab 只对驼峰切词,对全小写连写是恒等映射,加了连字符就回推不出来。
★★ **2026-10-17 第四十四切片:⚠A 里的 `RealTimeMonitorController`(10 端点,8 读 + 1 导出 + 2 免认证读)搬走**
⇒ **⚠A 余 1 个 / 3 端点**(`92/404` 与 `1/3` 同一次移动的两个投影)。
★★ **本片第一次把「一个老类 = 两个新类」落成事实**:本栈 URL 前缀由包路径决定
(`**.controller.admin.**` → `/admin-api`、`**.controller.app.**` → `/app-api`),
一个类不能两栖 ⇒ 管理端 8 条进 `park/real-time-monitor`(全 `@PreAuthorize`),
外部网站 2 条进 `/app-api/park/real-time-monitor` + `@PermitAll`(先例 `AppVersionCheck*`)。
★ 老类 3 条 `@NoAuth` **不是同一类**:导出是第 1 类(老前端三处 `unit.js` 调用)、
两条按组织查车场是第 2 类(`git grep` 两个前端分支**都搜不到** + 老注释写明"黄石官网")
⇒ **口径更正**:第四十三切片 ⑫ 里"只有黄石那条才是第 2 类"的预判是错的,两条都是。
★ 本片**零新建 VO**(10 个返回类型全在新栈)——"返回类型要连 VO 一起数"第 6 次生效。
★ 第五种日期契约:导出是 `@RequestParam String` + hutool `DateUtil.parseDateTime`(受检异常)。
★★ **本片新增一条缓存判据**:`getParkLotByPlNo` 会把"查不到"的空 DTO 写进 Redis hash
`zteits-park:parking_lot:plNo_parkLotInfo`,之后 `QueryParkingLotByPkNos` 在
`BerthManageServiceImpl:123` 对 null `plNo` **NPE** ⇒ 500。本地 `tp_p_berths` 的 6 个 plNo
**全部**在 `query-park-berth-occupancy-rate` 的 `{}` 返回里 ⇒ 冒烟里 ep5 只发带 `plNo` 的过滤调用,
ep3 断言写成"缓存干净/被污染"两支(红线⑥ 不修)。
★★ **2026-10-17 第四十五切片:⚠A 最后一个类 `VipCardQueryController`(3 端点)搬走 ⇒ ⚠A 清零(0 个 / 0 端点)**
(`93/407` 与 `0/0` 同一次移动的两个投影)。★ 本片推翻了两条预测:
① 第四十三切片 ⑫ 说「§3.2 要另建 2 个老门户独有 VO」——**不用**:`VipCardStatisticIndexDTO`
/ `AuthCarNumberDTO` 本来就已在 `parking-module-api`,`VipCardStatisticFeeVO` 也早已随
`parking-legacy-common` 整包搬入(只是用不上);
② 更要紧的是**第 3 条端点的签名在说谎**:声明 `BizResultVO<VipCardStatisticFeeVO>`(6 字段),
方法体却是裸 `new BizResultVO(incomeStatisticFeeVipCardDTOBizResult)` ⇒ 实际线格式是
**21 字段**的 `IncomeStatisticFeeVipCardDTO`,那个 VO **一次都没被 new 过**。
⇒ 新契约按实际线格式声明;照签名搬会**静默丢 15 个字段**(编译期不报)。
**新判据:§3.2 数 VO 之前先看方法体 new 的是谁,别只看泛型声明。**
★ 本片**零新建 VO/DTO**("返回类型要连 VO 一起数"第 7 次生效,且这次连"要不要建"都省了)。
★ 老类 3 条端点**都没有** `@NoAuth`、没有写端点、没有 `@SysOpLog` ⇒ `LEGACY_WRITES` 与
`system_operate_log` 的 +16 常量都不动。
⚠️ 第 2 条端点老 `@RequestMapping` **没写 method**(老栈 GET/POST 都进),新栈吃 `@RequestBody`
一律 `@PostMapping` ⇒ 带 token GET 是 405(§2ao (d) 钉住)。
★★ **2026-10-17 第四十六切片:✔ 池里的 `RefreshCacheController`(4 端点)搬走 ⇒ ✔ 池 5/11 → 4/7**
(`94/411` 与 `4/7` 同一次移动的两个投影)。★ 本片按第三十四切片 ④ 的 **(B) 配置化**方案落地:
老类 4 条 `@NoAuth` + **写死的 32 位共享密钥**不搬(红线 11),改成 `parking.refresh-cache.token`
配置项(本地 profile 给测试值,生产不配即 fail-closed);调用方不登录 ⇒ §3.3 **第 2 类**:
包 `**.controller.app.**` → `/app-api` + `@PermitAll` + 注解自动进白名单。
★ 一个**新行为**:`refresh-sys-cache?type=1` 会把 `sys_code_value` 缓存**热起来**(跨轮次生效),
于是 §2ak ep1 的首条图片会从 null 变成 `LZ_DZSP_*` 配置的 URL ⇒ 那条断言已改成「只有首条可能
非 null、值只能是 null 或 http URL」,两种缓存态都合法。
★ 零新建 VO(4 个返回类型全是 `CommonResult<String>`);`LEGACY_WRITES` 不加(4 条只写 Redis、
不写 DB,零写复查扫 51 张表只有 `system_operate_log` +16)。
★★ **2026-10-17 第四十七切片:⚠B 里的 `ApiCallLogController`(1 端点)搬走,同时落地 P3 组织 id 换算表**
⇒ **⚠B 113/700 → 112/699**(`95/412` 与 `112/699` 同一次移动的两个投影)。
★ 本片**不再「等 P3」**:`LoginUserBridge` 新增 `parking.legacy.org-id-mapping`
(`新id:老id,...`,**默认空 = 恒等映射**、行为不变;本地 profile 配 `1:10003`),
把登录人的新 `system_org.id` 换算成存量业务表认的老 `sys_org.id`。
★ 判据是**数据量而不是形状**:本接口在本地从 `total=0` 变成 `total=498`
(`td_b_api_log` 里 org=10003 的行数);同时 `AreaController` 的区域树从 494 行变成
586 行(省市区 494 + 区块 92)——后者是**已迁端点**,说明这张表是全局生效的,
不是给单个 Controller 打补丁。
⚠️ 仍然没打通的是 `plNos` / `userAreas` / `resIds`(桥依旧留 null),所以 ⚠B 里
**按这些字段过滤的类还不能直接开搬**;本片只解锁了「只读 orgId/orgIds」的那批。
★★ **2026-10-17 第四十八切片:⚠B 里的 `SmsTemplateConfigController`(5 端点)搬走**
⇒ **⚠B 112/699 → 111/694、已迁 95/412 → 96/417、未迁 116/706 → 115/701**。
★ 它是换算表落地后的**第二个**消费者(第一个是第四十七切片的 `ApiCallLogController`),
证明「只读 orgId/orgIds」这条解禁路径可复制:本类 5 条端点只读 `userInfo.getOrgId()`,
没有任何 `plNos`/`userAreas`/`resIds` 读取。
★ 判据依旧是**数据量**:`/page {}` 在本地 `sms_template_config` 上回 **total=1**
(org=10003 且 `data_status=1` 的正好 1 行);映射不生效时是 `org_id = 1` ⇒ total=0(不报错)。
⚠️ 本类还有 4 个写端点,其中 `/delete-list` 的老 DAO 是 `return 0;` **桩**(点了从没删掉过任何行)
——红线⑥照搬,别说它「坏了」。
★★ **2026-10-17 第四十九切片:⚠B 里的 `ParkGroupController`(6 端点)搬走**
⇒ **⚠B 111/694 → 110/688、已迁 96/417 → 97/423、未迁 115/701 → 114/695**。
★ 它是换算表落地后的**第三个**消费者。`/page`、`/list` 只读 `userInfo.getOrgId()`,
4 个写端点只读 `userInfo.getOpId()`(写 `create_empid`/`modfiy_empid`),
没有任何 `plNos`/`userAreas`/`resIds` 读取。
★ 判据依旧是**数据量**:`/page {}` 在本地 `parking_lot_group` 上回 **total=13**
(org=10003 且 `data_status=1`、`park_num>0` 的正好 13 行);映射不生效时是 `org_id = 1` ⇒ total=0。
⚠️ 老 smapper 的 `plGroupName` 分支**少一个 `and`** ⇒ 一传过滤条件就 SQL 语法错、恒 500
——红线⑥照搬,别说它「坏了」。
★★ **2026-10-17 第五十切片:⚠B 里的 `DiscountController`(7 端点)搬走**
⇒ **⚠B 110/688 → 109/681、已迁 97/423 → 98/430、未迁 114/695 → 113/688**。
★ 它是换算表落地后的**第四个**消费者,也是**最新的一类解禁形态**:
**整类不读 `orgId`**(按请求体 `plNos` 过滤),三个写端点只拿 `userInfo.getOpId()`/`getName()`
写审计列 ⇒ 连组织换算都不涉及。别再认为"⚠B 命中 = 必须等 P3 orgId 换算"。
★ 判据是**过滤后的数据量**:`/page {}` 在本地 `t_activity_discount` 上回 **total=21**
(表里 500 行,smapper 硬编码 `data_state=1` 只剩 21 行)——拿表行数当期望值会一跑就红。
⚠️ 老 SQL 的 `disc_name like '%${discName}%'` 是**拼串**(注入面)、`queryParkName` 是
**N+1 且不判空**、`/create` 的规则列表**不判空就是 NPE** —— 红线⑥全部照搬。
★★ **2026-10-17 第五十一切片:⚠B 里的 `TActivityAbcbankDiscountController`(5 端点)搬走**
⇒ **⚠B 109/681 → 108/676、已迁 98/430 → 99/435、未迁 113/688 → 112/683**。
★ 它是换算表落地后的**第五个**消费者,也是最"轻"的一类:`/page`、`/query` 只读
`userInfo.getOrgId()`,三个写端点只拿 `userInfo.getOpId()`/`getName()` 写审计列,
没有 `plNos`/`userAreas`/`resIds` 读取。
★ 判据同样是**过滤后的数据量**:`/page {}` 在本地 `t_activity_abcbank_discount` 上回 **total=1**
(表里 15 行,`data_state=1 AND org_id=10003` 只剩 1 行 id=68);映射不生效是 `org_id=1` ⇒ 0 行。
⚠️⚠️ 本片最危险的一条:`/create` 在调服务层**之前**先调 `setTActivityAbcbankDiscountFalse`,
那条 SQL **不带 `org_id`** ⇒ 会把**全库所有组织**的生效中配置(`disc_state in (0,1)`)
一起下架。冒烟**只能发空 body**(绑定层 400),⛔ 绝不能发 `{}`(会先执行这条 update、之后才 NPE)。
⚠️ 另三处存量缺陷照搬:`disc_name like ?` **绑定不过滤通配符** ⇒ `discName=农行` 查不到
`农行充值`(用户得自己传 `%`)、`/query` 查不到时**服务层 NPE**、`/delete-list` **逐 id 先查后删**
(N+1)且 id 不存在时先 NPE。
★★ **2026-10-17 第五十二切片:⚠B 里的 `TActivityThirdDiscountController`(5 端点)搬走**
⇒ **⚠B 108/676 → 107/671、已迁 99/435 → 100/440、未迁 112/683 → 111/678**。
★ 它与第五十一切片是同一包(`discount/`)的孪生类,也是最"轻"的一类:`/page`、`/query`
只读 `orgId`,`/update` **连 `orgId` 都不 set**,三个写端点只拿 `opId`/`getName()` 写审计列。
★ 判据同样是**过滤后的数据量**:`/page {}` 在本地 `t_activity_third_discount` 上回 **total=3**
(表里 9 行,`data_state=1 AND org_id=10003` 正好 3 行 id=7/8/9);映射不生效是 `org_id=1` ⇒ 0 行。
★ 与孪生类**行为不同的两处**(别按上个类抄断言):`/query` 查不到回 **code=0、data=null**
(服务层把 null 装进 BizResult,`getDataOrThrow` 允许透传,**不 NPE**)、`/delete-list`
的 id 不存在回 **data=false**(逻辑删 0 行,**不 NPE**)。
⚠️ `/create` 的 `{}` 会**真插一行**(`disc_no` 由 `SequenceUtil` 生成;本类**没有**全库下架那一步)
⇒ 冒烟只能发空 body(绑定层 400),⛔ 绝不能发 `{}`。
⚠️ 与孪生类相同的坑:`andDiscNameLike` **不补 `%`**(`农行` → 0 行、`农行%` → 2 行)、
`PageUtil.pageSizeValidate` 校验 1..1000。
★★ **2026-10-17 第五十三切片:⚠B 里的 `TpPNetworkCardController`(11 端点)搬走**
⇒ **⚠B 107/671 → 106/660、已迁 100/440 → 101/451、未迁 111/678 → 110/667**。
★ 本类落在 `park` 域(`POST /admin-api/park/network-card/*`),7 读 + 4 写、**无导出**;
读端点只读 `orgId` **且带超管豁免**(超管不覆盖请求体 `orgId`),写端点只拿 `opId`/`getName()`。
`/list` **完全不读登录态**(老 DAO 是 `//TODO` + `return null` ⇒ 恒空)。
★ 判据:`/page {}` 在本地 `tp_p_network_card` 上回 **total=500**
(表里 500 行,全部 `data_state=1 AND org_id=10003`);映射不生效是 `org_id=1` ⇒ 0 行。
★ **本类没有 `PageUtil.pageSizeValidate`** ⇒ `pageSize=0` 不分页(回全量 500 行)、`1001` 照过 ——
别把折扣类的「0 → 1002 / 1001 → 500」断言抄过来。
★ 存量缺陷照搬:`/delete` 与 `/delete-list` **都不置 `data_state=0`**(只改审计列);
`/statistic` 查不到回 **code=0、data=null**;`/query-eqp-for-page {}` 与
`/query-pda-for-page {eqpType:…}` 都因 tk `and*EqualTo(null)` 抛异常 ⇒ **500**。
⚠️ `/create` 的 `{}` 会**真插一行**(`insertSelective`,`create_date` 由 DAO 补 now)
⇒ 冒烟只能发空 body(绑定层 400),⛔ 绝不能发 `{}`。
★★ **2026-10-17 第五十四切片:⚠B 里的 `ParkingBindingChargingController`(10 端点)搬走**
⇒ **⚠B 106/660 → 105/650、已迁 101/451 → 102/461、未迁 110/667 → 109/657**。
★ 落在 `bill` 域(`POST /admin-api/bill/parking-binding-charging/*`),7 读 + 3 写、**无导出**、
**无 `@NoAuth`、无 `@SysOpLog`** ⇒ 新类没有 `@OperateLog`。
★ 整类只有 2 处读登录态(`queryParkingBindingChargingRules`、`initCloseParkingBindTree`),
都是**无条件覆写 `orgId`、没有超管豁免**(与第五十三切片网络卡类**相反**);
`orgId` 经 `parking.legacy.org-id-mapping` 换算 ⇒ `areas {plNo=P15040214Z}` 回 1 行即映射生效。
★ **本类两个分页端点都有 `PageUtil.pageSizeValidate`**(min=1/max=1000)⇒ `pageSize=0`/`1001` 都 500。
★ 本地恒 500 的四条端点**不是回归**:`query-parking-binding-charging-rules` 与
`query-binding-rules-list` 撞 MySQL 8 `only_full_group_by`(老 SQL `GROUP BY r.pl_no` 却 select 非聚合列);
`areas {}` 撞 Druid wall `where 1=1`;`areas {plNo=不存在}` 空结果拼出 `in ()` 撞 wall。
★ 空结果语义两分:`areas {plNo=NOPE}` **500**、`query-binding-area-rules-list {plNo=NOPE}` 回 **`[]`**。
★ `initCloseParkingBindTree` 必须调 **park 模块**的同名服务(bill 服务接口上那个实现 `return null`)。
⚠️ 3 个写端点:`/save` 的 `{}` NPE、`/update` 缺 `areaId`、`/delete` 缺 `plNo` 都在 DAO 前 500;
**`/delete` 是物理删除**(`deleteByExample`)、`/update` 不存在则 `insertSelective` ⇒
⛔ 冒烟只能发空 body(400),绝不能发带真实 `areaId`/`plNo`/`parkDtoList` 的 body。
★★ **2026-10-17 第五十五切片:⚠B 里的 `TbAreaController`(2 端点)搬走**
⇒ **⚠B 105/650 → 104/648、已迁 102/461 → 103/463、未迁 109/657 → 108/655**。
★ 落在 `park` 域(`POST /admin-api/park/tb-area/*`),**全只读**、无导出、无 `@NoAuth`、
无 `@SysOpLog` ⇒ 新类没有 `@OperateLog`。同目录 `AreaManageController`/`RegionAreaController`
早已迁完,本类是「区域管理」页的**最后一块**。
★ `/query-next-lower-lever-by-id` **不查库**:数据源是 Redis 字典
(`GOV_CLOUDS_LBS_AREA_` + cityId,`SysCodeValueCacheUtil`),`cityId` 为空先抛
`PARAMM_NULL「区域ID」`;字典里没有该城市 ⇒ **code=0 + `[]`**(不是 500)。
★ `/get-berth-area-tree` 读 `orgId` 且**无条件覆写、没有超管豁免**;`orgId` 经
`parking.legacy.org-id-mapping` 换算 ⇒ 本地回 588 行(32 省 + 342 市 + 120 区 +
**92 区块** + 2 停车场),92 个区块即映射生效的判据。本地这 2 个车场没有泊位区间样本
⇒ **无 areaType=1000 行**(数据事实,不是代码丢段)。
★ `TpPRegionAreaService#queryTpPRegionAreaForPage` 接口声明 `throws Exception`
⇒ 控制器方法必须保留 `throws Exception`(同 `RegionAreaController`)。
★★ **2026-10-17 第五十六切片:⚠B 里的 `CarBreakRuleController`(3 端点)搬走**
⇒ **⚠B 104/648 → 103/645、已迁 103/463 → 104/466、未迁 108/655 → 107/652**。
★ 落在 `park` 域(`POST /admin-api/park/car-break-rule/*`),1 读 + 2 写、**无导出**;
读端点**无条件覆写 `orgId`、没有超管豁免**(同第五十四/五十五切片),写端点只拿
`opId`/`name` 写审计列;两个写端点老类都有 `@SysOpLog` ⇒ 新类有 `@OperateLog`。
★ 读端点 **不带 `plNos` 时老 DAO 走 `andPlNoIsNull()`** ⇒ `{}` 只回「未挂车场」的记录
(本地 `org_id=10003` 有效行 52、其中 `pl_no IS NULL` **35** 行;显式 `plNos` 才查得到另 17 行)。
★ 两条写端点在控制器校验关口就 500(「主键信息」/「审核状态」),**服务层都碰不到**;
⛔ 冒烟只能发空 body 或这些炸关口样本,真 `id`/`ids` 会插审核记录或把 `upload_type` 标成 1。
★★ **2026-10-17 第五十七切片:⚠B 里的 `TBillingDescriptionController`(3 端点)搬走**
⇒ **⚠B 103/645 → 102/642、已迁 104/466 → 105/469、未迁 107/652 → 106/649**。
★ 落在 `bill` 域(`POST /admin-api/bill/billing-description/*`),2 读 + 1 写、**无导出**;
两个读端点**完全不读登录态**,写端点只拿 `opId`/`name`/`orgId` 写审计/组织列、不参与过滤;
只有写端点老类有 `@SysOpLog` ⇒ 新类只在该端点带 `@OperateLog`。
★ 按计费集合查不到时返回**空 DTO 且 code=0**(不是 null、不是 500)。
★ 按车场查在本机**恒 500「系统异常」**:存量 join SQL 撞两表 **collation 不一致**
(`t_billing_rule_collect` = utf8mb4_unicode_ci,另两张 = utf8mb4_general_ci),
MySQL 回 **1267**;直连同一条 SQL 同样报错 ⇒ 非迁移回归,按红线⑥不改 SQL/库。
★ ⛔ `/save-or-update` 只能发空 body 或炸服务层校验关口的样本(缺 `ruleCollectNo` / 缺 `descText`);
真发两者会把同集合旧描述置失效、插新描述,并清空两个 Redis key。
★★ **2026-10-17 第五十八切片:⚠B 里的 `DutyRosterGroupController`(4 端点)搬走**
⇒ **⚠B 102/642 → 101/638、已迁 105/469 → 106/473、未迁 106/649 → 105/645**。
★ 落在 `park` 域(`POST /admin-api/park/duty-roster-group/*`),1 读 + 3 写、**无导出**;
查询/修改/删除三个端点**完全不读登录态**(`orgId`/`sOpId` 都来自请求体),
只有保存端点读 `opId`(审计列)与 `orgId`(新行组织列 + 按计划物理删的过滤条件)。
★ 保存端点的顺序是「校验列表非空 → 按 `orgId+planId+dutyType` **物理删** → 逐条插」;
列表为空时 1003 在删除**之前**抛出 ⇒ 冒烟只发列表为空的样本。
★ 修改 `{}` 返回 `code=0 + false`(`where id = null` 命中 0 行);
⛔ 本库**真有一行 `id=0`**,冒烟绝不能发 `id=0`(会改它的 `modify_time`)。
★ 删除是逻辑删(`data_status=0`),`ids` 为空 → 1003「值班班次ids」。
★★ **2026-10-18 第五十九切片:⚠B 里的 `ChargerAssessTargetController`(4 端点)搬走**
⇒ **⚠B 101/638 → 100/634、已迁 106/473 → 107/477、未迁 105/645 → 104/641**。
★ 落在 `park` 域(`POST /admin-api/park/charger-assess-target/*`),1 读 + 3 写、**无导出**;
**四个端点全部读登录态**:查询把登录 `orgId` **无条件**覆盖请求体并作为 SQL 过滤条件
(`andOrgIdEqualTo`,**无超管豁免**),保存/修改/删除只拿 `opId`/`name` 写审计列。
★ ★ 删除端点的老拼写是 **`bath-`**(少一个 c),路由照抄,别"修正"成 `batch-`。
★ `targetNo` 的自动生成在 DAO 里是**注释掉的**(「考核指标不生成,在码表里配置」);
修改缺 id → 控制器 1003「id为空!」;删除 `ids` 为空 → 1003「id为空!」。
★ ⛔ 冒烟写样本只用「空白 targetNo / 不存在 id / 空 ids」;非空 targetNo 会真插行。
★★ **2026-10-18 第六十切片:⚠B 里的 `ChargerAssessRuleController`(4 端点)搬走**
⇒ **⚠B 100/634 → 99/630、已迁 107/477 → 108/481、未迁 104/641 → 103/637**。
★ 落在 `park` 域(`POST /admin-api/park/charger-assess-rule/*`),1 读 + 3 写、**无导出**;
查询把登录 `orgId` **无条件**覆盖请求体并作为 SQL 过滤条件(**无超管豁免**),
保存/修改/删除只拿 `opId`/`name` 写审计列。
★ 与兄弟类 `ChargerAssessTargetController` 的三处不同:查询**有 `order by id asc`**;
请求体是**列表包装类**(`chargerAssessRuleRequests`,空/null 被 `ListCopyUtil` 安全吞掉 ⇒ `false`);
修改的 id 判空在**服务层循环内**(`{}`→false、`[{}]`→1003)。
★ `ruleNo` 由 DAO `queryNextRuleNoByOrgId` **自动生成**(默认 `R1000001`,存量 N+1);
删除端点老拼写是 **`bath-`**(单数 rule);删除是逻辑删(`data_state=0`)。
★ ⛔ 冒烟写样本只用「空列表 / 缺 id / 不存在 id / 空 ids」;非空列表会真插行。
★★ **2026-10-18 第六十一切片:⚠B 里的 `ChargerAssessTemplateController`(5 端点)搬走**
⇒ **⚠B 99/630 → 98/625、已迁 108/481 → 109/486、未迁 103/637 → 102/632**。
★ 落在 `park` 域(`POST /admin-api/park/charger-assess-template/*`),2 读 + 3 写、**无导出**;
老类里 `/saveChargerAssessTemplateRel` **整段被注释** ⇒ 只迁 5 条。
★ 两个读端点把登录 `orgId` 用于主表 WHERE 与配置过滤(**无超管豁免**);
`by-id` **不**按组织/`data_state` 过滤主表,查不到回全 null DTO(不是 null、不是 500)。
★ 保存先插主表再校验配置列表(空列表 1003;`@Transactional` 实测回滚);
修改 id 判空在**控制器**;不存在的 id 回 1002「保存模板信息错误!」。
★ 删除的 `modify_emp_name` 因控制器 `setOpName` 与服务层 `getsOpName` **字段错位恒为 null** —— 存量缺陷照搬。
★ ⛔ 冒烟写样本只用「空 body / `{}`(缺 id)/ 不存在 id / 空 ids」;非空配置列表会真插行。
★★ **2026-10-18 第六十二切片:⚠B 里的 `ChargerAssessController`(5 端点)搬走**
⇒ **⚠B 98/625 → 97/620、已迁 109/486 → 110/491、未迁 102/632 → 101/627**。
★ 落在 `park` 域(`POST /admin-api/park/charger-assess/*`),**5 端点全部只读**、无导出;
老类无 `@NoAuth`、无 `@SysOpLog` ⇒ 新类**不挂** `@OperateLog`,冒烟跑一轮审计仍只 +59。
★ 五个端点都把登录 `orgId` **无条件覆盖**进请求体并用于 WHERE,**无超管豁免**
(请求体传 `orgId=10007`,结果仍是登录组织 `10003` 的)。
★ 结果分页的必填项:`beginTime`/`endTime` 缺 → 1003;`templateNo` 缺 →
tk.mybatis Example 抛 `Value for templateNo cannot be null` → 500「系统异常」;
`chargerCodes` 缺 → 服务层 for 循环 **NPE** → 500「系统异常」(存量缺陷照搬)。
★ 模板配置查询**只看配置表自身 `data_state=1`**,不看模板主表 `data_state`
(主表 `data_state=0` 的 `M100000001` 仍出 1 条配置)。
★ ⚠ 本机 MySQL 8 上结果分页**恒 500**:存量聚合 SQL `GROUP BY charger_code,org_id`
却 select `charger_name`/`person_type`,撞 `only_full_group_by`;**老栈同 SQL 同样 500**
⇒ 不是迁移回归,按红线⑥不改。
★★ **2026-10-18 第六十三切片:⚠B 里的 `ChargerAssessResultController`(7 端点)搬走**
⇒ **⚠B 97/620 → 96/613、已迁 110/491 → 111/498、未迁 101/627 → 100/620**。
★ 落在 `park` 域(`POST /admin-api/park/charger-assess-result/*` + `GET /export-excel`),
**2 读 + 4 写 + 1 导出**;老类 6 条 POST 都有 `@SysOpLog` ⇒ 4 个写端点带 `@OperateLog`。
★ 两个读端点把登录 `orgId` **无条件覆盖**进请求体并用于 WHERE,**无超管豁免**
(传 `orgId=10007` 仍回登录组织 `10003` 的 18 / 206 条)。
★★ **导出是唯一例外**:老 `@NoAuth`,`orgId` 由前端 query 参数传入并原样使用,
**不覆盖**;缺 `orgId` → 500「系统异常」(Example 拒 null),`orgId=10007` → 1002,
证明真按传入值查。按 §3.3 第 1 类收敛为需登录 + `park:charger-assess-result:export`。
★ 明细分页 `recordNo` 必填(服务层 1003「记录编码」),`order_num` 升序;
结果分页 `id desc`、`pageSize=0` 回全部(18 行)。
★ 写端点校验顺序:`save` 先插主表再校验(缺开始时间 1003、`@Transactional` 回滚);
`update` 控制器先判 id(1003「id为空!」)、服务层判审核状态/记录编码后**先插日志再改主表**;
`bathDelete` 不存在的 id 回 **`code=0 + data=false`**(不是错误码);
`updateDetail` 不存在 id 回 1002「…**为**查询到相关记录」(错别字照搬)。
★ ⚠ 本机没有 `charger_assess_result_log` 表 ⇒ `update` 恒 500「系统异常」
(本地 schema 漂移、老栈同依赖);`save` 的聚合仍撞 MySQL 8 `only_full_group_by`。
★ ⛔ 冒烟写样本只用「空 body / `{}`(缺必填)/ 不存在 id / 空 ids」。
★★ **2026-10-18 第六十四切片:⚠B 里的 `TdCCustSuggestDetailController`(7 端点)搬走**
⇒ **⚠B 96/613 → 95/606、已迁 111/498 → 112/505、未迁 100/620 → 99/613**。
★ 落在 `pay` 域(`POST /admin-api/pay/cust-suggest-detail/*`),**3 读 + 4 写**、无导出;
老类 4 条写都有 `@SysOpLog` ⇒ 新类带 `@OperateLog`,3 条读不带。
★ **只有建议分页读登录组织,且有超管豁免**(`SUPER_ORG_ID` 时**不**覆盖、看全部);
两个回复读端点**完全不读登录态**。本机 `td_c_cust_suggest.org_id` 全 NULL +
登录组织覆盖 ⇒ 建议分页 6 种入参都 `total=0`。
★ **回复分页是存量死读**(服务层直接返回空 `PageBean`、DAO 实现整段注释)⇒ 恒 `total=0`,照搬。
★ `save` 先插回复再置主表 `is_answer=1` 再推极光;`suggestId` 空的 1003 在写审计字段**之后**,
且 `@Transactional` 内抛 `BizException` ⇒ `@OperateLog` **不记账**。
★ `update` 只写 `sOpId`,空 SET 段被 druid 判语法错 ⇒ 空 body/id 不存在都 500。
★ `bath-delete` 两条 500:`{}` → `Value for id cannot be null`、`{ids:[]}` → `id in ()` 语法错;
`{ids:[不存在]}` → `code=0 + data=false`。路由拼写是 **`bath-`**(照抄)。
★ ⛔ 冒烟写样本只用「空 body / `{}`(缺必填)/ 不存在 id / 空 ids」。
★★ **2026-10-18 第六十五切片:⚠B 里的 `PdaManageMentController`(12 端点)搬走**
⇒ **⚠B 95/606 → 94/594、已迁 112/505 → 113/517、未迁 99/613 → 98/601**。
★ 落在 `park` 域(`POST /admin-api/park/pda/*` + 3 条 `GET ...excel`),
**4 读 + 5 写 + 3 导出**;3 条导出与 1 条导入老类 `@NoAuth`。
★ 老类 4 条写有 `@SysOpLog` ⇒ 新类带 `@OperateLog`;`import` 不带。
★ 两个分页读端点的登录 `orgId` **无条件覆盖**进请求体并用于 WHERE、**无超管豁免**;
本机按 `orgId` 过滤不缩小结果(229 → 229)。统计端点登录 null 时回退 `orgId=99999`。
★ 存量缺陷照搬:`/export-berth-info-excel` 判的是刚 `new` 出来的空 `ArrayList`
⇒ **恒空文件**;`/query-passedca4pabe` 在本地因 `NID`(32 位 hex)映射 `Integer`
抛异常被 `catch` 吞掉 ⇒ `data=null`;`/import-pda` 的 `list` 初值为 null 后直接 `for` ⇒ NPE。
★ `insert-pda` 的 `{}` 会**真插一行**(全列可空)⇒ 冒烟只能用已存在 `pdaNo`;
`update {}` → 1007 / 空 SET、`delete {}`/`{ids:[]}`、`batch-delete {}`/`{padList:[]}` 均 500。
★ `updateByPrimaryKeySelective` 空 SET 段、`andIdIn(null)`、空 `IN ()` 都是存量行为,不修。
★ ⛔ 冒烟写样本只用「已存在 `pdaNo`(回 3003)/ 空 body / 不存在 id / 空 ids」。
★★ **2026-10-18 第六十六切片:⚠B 里的 `ChargerManageController`(32 端点)搬走**
⇒ **⚠B 94/594 → 93/561、已迁 113/517 → 114/550、未迁 98/601 → 97/568**。
★ 落在 `park` 域(`POST /admin-api/park/charger/*` + 3 条 `GET ...export...`),
**16 读 + 13 写 + 3 导出**;3 条导出老类 `@NoAuth` 全按 §3.3 第 1 类收紧。
★ 老类 33 个端点,本片只迁 32:`upload-employee-head-pic` 走 FastDFS/存储后端,
等 `FileClient` 抽象拍板后再补。脚本按整类计 ⇒ 机械计数 **+33** 而实际迁 **32**。
★ 11 个写端点老类有 `@SysOpLog` ⇒ 新类带 `@OperateLog`;
`update-bind-regions`、`get-employee-tail` 不带。
★ 分页/签到类读端点的登录 `orgId` **无条件覆盖**进请求体并用于 WHERE、**无超管豁免**;
`queryEmployerByPlNo`/`queryInspectSituationForPage`/`querySignRecordByChargerCode`/
3 导出/`getEmployeeTail` **完全不读登录态**。
★ 存量缺陷照搬:老 GPS 查询/导出**恒 500**(存量 SQL 末尾 `WHERE 1=1` 撞新栈 druid
`wall`;老 portal 无 wall);`/query-lot-region-tree` 无「事」节点时
`rootDTO.getChildren()` 回 **null** ⇒ `data=null` 不是 `[]`;`resetpwd {}` 与
`acct-status {不存在编码}` 均回 `true`(0 行也算成功);`getEmployeeTail` 只回最后一次
循环的 `BizResult`。
★ `insert {}`/`update {}` 是 **400**(`@Valid`),不写审计;其余写端点的 `{}` 多落在
服务层 NPE / tk.mapper null 守卫 ⇒ 500。
★ ⛔ 冒烟写样本只用「空 body / `{}`(缺必填)/ 不存在编码 / 空列表」。
★★ **2026-10-18 第六十七切片:⚠B 里的 `DutyRosterController`(25 端点)搬走**
⇒ **⚠B 93/561 → 92/536、已迁 114/550 → 115/575、未迁 97/568 → 96/543**。
★ 落在 `park` 域(`POST /admin-api/park/duty-roster/*` + 1 条 `GET ...-list`),
**12 读 + 12 写 + 1 导出**;导出老类 `@NoAuth` 按 §3.3 第 1 类收紧。
★ 12 个读端点把登录 `orgId` **无条件覆盖**进请求体并用于 WHERE、**无超管豁免**;
`queryDutyRosterPlanForPage` 覆盖的是 `List<Long> orgId`。
`updateDutyRoster`/`exchangeDutyRoster`/两个验证码端点/导出**完全不读登录态**。
★ 4 个写端点老类有 `@SysOpLog` ⇒ 新类带 `@OperateLog`
(保存调休记录 / 删除排班计划 / 正式提交排班计划 / 调班)。
★ 存量缺陷照搬:`queryNoDutyRosterParkTree` 老实现只 set `orgId/sysCode/plType`、
**从不 set `plNos`**,而底层 `queryParkLotByOrgIdAndPlNos(orgId, plNos, plType)`
三参版要求 `plNos` 非空 ⇒ **恒 1003「停车场编号」**(老栈同样炸);
`deleteParkByPlanIdAndPlNos {}` 未判空,直落服务层 ⇒ **500「系统异常」**。
★ `queryDutyPersonNumAndRecessNum` 老类 `@Validated` ⇒ 新类 `@Valid`,`{}` 是 **400**。
★ `generatorDutyRosterPlanId` 不收 body(空 body 回 code=0),**不能**进 `LEGACY_WRITES`。
★ ⛔ 冒烟写样本只用「空 body / `{}`(缺必填)/ 假验证码」:**绝不发真实手机号**
(`sendVerifyCodeByPhoneNumber` 会真发短信)。
★ POI 4.1.2 已移除 `XSSFCellStyle.ALIGN_CENTER`/`XSSFCell.CELL_TYPE_STRING` 等 int 常量,
照搬存量导出必须先换 `HorizontalAlignment`/`VerticalAlignment`/`BorderStyle`/`CellType`。
★★ **2026-10-18 第六十八切片:⚠B 里的 `TemporarySettlementController`(18 端点)搬走**
⇒ **⚠B 92/536 → 91/518、已迁 115/575 → 116/593、未迁 96/543 → 95/525**。
★ 落在 `order` 域(`POST /admin-api/order/temporary-settlement/*` + 2 条 `GET …excel`),
**12 读 + 4 写 + 2 导出**;老路由 `/TemporarySettlement`,
老类在 `financialmanagement/clearsettlement/`。
★ 登录态只在 8 处把**当前登录人组织**写进 `fromOrgId`/`toOrgIds`(打款方/收款方),
**无 `plNos`/`userAreas`/`resIds`、无超管豁免**;`querySettlementRecordByStatusForOrgId`/
`querySettlementRecordListBySettleNO`/`querySettlementInfoByNo` 与 2 条导出**完全不读登录态**。
★ 3 个写端点老类有 `@SysOpLog` ⇒ `@OperateLog`(`confirmReceiveMoney` 没有);
第 4 处 `@SysOpLog` 挂在**读端点** `querySettlementRecordSumforOrgId` 上,
按第六十三切片口径新类**不挂**。
★ 两条导出老 `@NoAuth` 按 §3.3 第 1 类收紧为 `GET` + `order:temporary-settlement:export`。
★ 存量缺陷照搬:`orgIds` 为空时把 `new ArrayList<>()` 塞回请求体(**不是**加 orgId,恒查空集合);
`Boolean flag` 直接拆箱 ⇒ 缺 `flag` 是 **500 NPE**(`fee-summary`/`list-page`);
导出 `getData().getDataList()` 无判空;`fenChangeToyuan` 用 `Double.toString()`
(`100`→`"1.0"`);主导出 `new BaseInfo(1, 0)` 吃全局 `pagehelper.page-size-zero: true`。
★ ⛔ `update-settlement-record-check-status-by-org-id-and-time` 只要过校验就**必插一行
`td_b_settlement_serial`**(0 行可改也插)⇒ 冒烟只发 `{}` 与空 body。
★ 导出日期走 `com.clouds.common.utils.DateUtil.strDate`(严格 `yyyy-MM-dd HH:mm:ss`),
只给 `yyyy-MM-dd` 是 **500「系统异常」**,不是 400。
★★ **2026-10-18 第六十九切片:⚠B 里的 `ChargerAuditController`(13 端点)搬走**
⇒ **⚠B 91/518 → 90/505、已迁 116/593 → 117/606、未迁 95/525 → 94/512**。
★ 落在 `park` 域(`POST /admin-api/park/charger-audit/*` + 4 条 `GET …excel`),
**9 读 + 0 写 + 4 导出**;老路由 `/pda/input`,
老类在 `operationsmanage/chargemanage/`。**零写**、无 `@SysOpLog` ⇒ 无 `@OperateLog`。
★ 登录态只出现在 4 个收入报表端点:把**当前登录人组织** `orgId` **无条件覆盖**进请求体
(无超管豁免);前 5 个稽核读端点与 4 条导出**完全不读登录态**。
★ 4 条导出老 `@NoAuth`(任何人可下载全部收费员/收益数据)按 §3.3 第 1 类收紧为
需登录 + `park:charger-audit:export`;一个 Controller 四个导出 ⇒ 主导出
`/export-excel`(两张 sheet:收费员收益稽核 + 停车场收益稽核),其余
`/export-input-nums-excel`、`/export-report-time-excel`、`/export-dy-excel`。
★ 5 个稽核读端点在 DAO 层**无条件拆箱** `inOutState`(`queryEqpAndPdaTimesForLine`
另拆 `timeType`)⇒ 带时间但缺这两个字段是 **500「系统异常」**;冒烟正样本都带上。
★ 存量缺陷照搬:收入报表 3 条列表 + 主导出/大元导出在本机撞
**MySQL 8 `only_full_group_by`**(smapper `GROUP BY charger_code, pl_no, ...`
却 SELECT `charger_name`),**两栈同坏、不改 SQL**(红线⑥),冒烟断言 500;
`queryChargerIncomeStatisticTotal` 的 SQL 不分组 ⇒ 本机 code=0 可正向。
★ `getAreaName` 在停车场 sheet 里是**每行一次查询的 N+1**,按红线⑥照搬并加注释。
★★ **2026-10-18 第七十切片:⚠B 里的 `SubCardInfoManageController`(9 端点)搬走**
⇒ **⚠B 90/505 → 89/496、已迁 117/606 → 118/615、未迁 94/512 → 93/503**。
★ 落在 `bill` 域(`POST /admin-api/bill/sub-card-info-manage/*`),
**5 读 + 4 写 + 0 导出**;老路由 `/subCardInfoManage`,
老类在 `parkinglotcloudplatform/closecarpark/`。
**无 `@NoAuth`、无导出、无 `@SysOpLog`** ⇒ 新类无 `@OperateLog`。
★ 登录态只出现在 `query` / `save` / `check-sub-custmoer-bind` 三条:
「`userInfo != null && userInfo.getOrgId() != null` 才覆盖」、**无超管豁免**;
其余 6 条**完全不读登录态**(无 `plNos`/`userAreas`/`resIds`)。
★ 存量缺陷照搬(新栈 DAO 原本就这样):`{}` 打在 update/delete/check/view/
viewCar/flow 全是 **500「系统异常」**(tk.mybatis 条件值 null 抛
IllegalArgumentException / 查回 null 后继续解引用);只有 `query {}` 回空页;
`query` 的 `pageSize` 走 `PageUtil.pageSizeValidate` ⇒ 0/1001 是 500 存量错误码 1002。
★ ⛔ **两个写端点绝不能用 `{}`**:`save {}` 会在事务内**真插 3 张表**;
`save-sub-flow-record {}` **先插一行 `td_cl_sub_flow_record`**、再在
`chargeType` 拆箱处 NPE,而该 inner 方法**没有 `@Transactional`** ⇒ 不回滚。
冒烟只用**无 body**(绑定层 400),四张表行数已实测不变。
★★ **2026-10-18 第七十一切片:⚠B 里的 `VipCardAuthPersonBjxcController`(12 端点)搬走**
⇒ **⚠B 89/496 → 88/484、已迁 118/615 → 119/627、未迁 93/503 → 92/491**。
★ 落在 `order` 域(`POST /admin-api/order/vip-card-auth-person-bjxc/*` + 2 条 `GET …excel`),
**4 读/导入(3 读 + 1 导入)+ 6 写 + 2 导出**;老路由 `/vipCardAuthPersonBjxcManage`,
老类在 `vipCardAuthPersonBjxc/`(北京西城「一位多车卡」授权人)。
★ 登录态出现在 8 条端点(9 处调用,`batch-inset` 里两处),全部只覆盖 `orgId` 或写审计列
(`sOpId`/`createEmpid`/`modfiyEmpid`/`opId`/`opName`),
**无 `plNos`/`userAreas`/`resIds`**;另 4 条(`query-cust-invoice-info-by-id`、两个导出、导入)完全不读登录态
(`export-excel` 连组织过滤都不加,导出的是全部授权人)。
★ 三个老 `@NoAuth` 端点按 §3.3 收紧:两个导出 → `:export`(主 `GET /export-excel`、
次 `GET /export-invoice-excel`,26 列)、导入 → `:import`(multipart,
`POST /batch-import-bjxc-vipcard-info`,与 `OrderModifyController#read-excel` 同款)。
★ 唯一的 `@SysOpLog`(`batchInsetBjxcVIPcardList`「优惠月卡-批量保存」)
⇒ `@OperateLog(create, 优惠月卡, 批量保存)`。
★ **本地 schema 缺口(新旧两栈 mapper XML 逐字相同,不是迁移引入)**:
`VipCardAuthPersonBjxcMapper.xml` select 带 `examine_state`/`examine_date`/`picture_path`,
而本机 `vip_card_auth_person_bjxc` 只建到 `invoice_apply_time` ⇒ `selectByExample`/
`selectByPrimaryKey` 恒 `BadSqlGrammarException` ⇒ `query-pay-order-for-vip-card-invoicebjxc`、
`export-invoice-excel`、`examine` 三条**本机恒 500「系统异常」**;分页查询走另一条 SQL,正常。
★ 存量缺陷照搬:`save {}` 的 `vipAuthType` 拆箱 NPE;`examine {}` 的
`getExamineState()+""` 得到字符串 `"null"`(非空串)⇒ 校验放行后拆箱 NPE;
`batchInsetBjxcVIPcardList` 的 `list.size()` 在空判**之前**(`{}` NPE、`[]` 才是 1003「数据为空」);
`ExcelReadUtil.readUploadExl` 对假文件**不抛 IOException** 而是回空表 ⇒ 导入的
`BIZ_ERROR` 分支实际走不到(冒烟断言 `code=0 data=null`)。
★ ⛔ **写端点绝不能发业务形状**:`save {vipAuthType:1}` 且手机号不存在时会
**真插一条 `td_c_cust_person`**。冒烟只用**无 body**(绑定层 400)与上述炸关口样本,
`vip_card_auth_person_bjxc`、`td_c_cust_invoiceinfo` 行数已实测不变。
★★ **2026-10-18 第七十二切片:⚠B 里的 `GeomagneticManagementController`(10 端点)搬走**
⇒ **⚠B 88/484 → 87/474、已迁 119/627 → 120/637、未迁 92/491 → 91/481**。
★ 落在 `park` 域(`POST /admin-api/park/geomagnetic/*` + 1 条 `GET …export-excel`),
老路由 `/geomagnetic`(老类在 `devicemanagement/`,地磁设备管理);
**3 读 + 1 导入 + 1 导出 + 5 写**。
★ 登录态只在 4 条端点(`statistic` / `records` / `insertone` / `batch-inset`),
都是把当前登录人 `orgId` 写进查询条件或库,**无 `plNos`/`userAreas`/`resIds`**;
其余 6 条完全不读登录态。
★ 两个老 `@NoAuth` 端点按 §3.3 收紧:导出 → `:export`(`GET /export-excel`,11 列,
`sysCode` 硬编码 `"1001"`、`new BaseInfo(1, 10000)` 照搬,空结果也出文件);
导入 → `:import`(`POST /batch-import-eqp-info`,multipart `excelFile`)。
★ **本类 `@Valid` 是真生效的**(老类逐字相同):`DeleteEqpsRequest.eqps` @NotEmpty、
`NewEqpRequest` 的 eqpNo/createOp/usedType 必填、`EqpHostRequest.ids` @NotEmpty、
`QueryParkLotInfoByPkNoRequest.pklNo` @NotNull ⇒ 这些端点的 `{}` / 空集合是 **400**;
`QueryEqpRequest` 无约束 ⇒ `statistic` / `records` 的 `{}` 能到 service(空条件查询)。
★ 存量缺陷照搬:`insertone` 缺 `joinMethod` 在拆箱处 NPE(发生在 insert 之前,安全)、
`batch-inset-excle-list` 的 `list.size()` 在空判**之前**(`{}` NPE、`[]` 才是 1003「数据为空」)、
`export-excel` 的 `SimpleDateFormat.parse` 遇到坏日期抛 ParseException ⇒ 500,
`readUploadExl` 对假文件回空表而不抛 IOException ⇒ 导入断言 `code=0 data=null`。
★ ⛔ **`insertone` 带 `joinMethod=1`(设备直连)+ 不存在的 `eqpNo` 会真插一行 `tp_p_eqp`**;
`deleteall` 会逻辑删除 `eqps` 里列出的设备、`updateone` 改主数据、
`bind-eqp-host-by-ids` 改 `gateway_no`。冒烟一律用**无 body**(绑定层 400)
或带 `id:0` 的 0 行 no-op 样本,`tp_p_eqp` 行数已实测不变。
★★ **2026-10-18 第七十三切片:⚠B 里的 `TdCRechargeCardController`(10/11 端点)搬走**
⇒ **⚠B 87/474 → 86/463、已迁 120/637 → 121/648、未迁 91/481 → 90/470**。
★ 落在 `pay` 域(`POST /admin-api/pay/recharge-card/*` + 1 条 `GET …export-excel`),
老路由 `/tdCRechargeCardManage`(老类在 `appservice/`,充值卡管理);
**4 读 + 5 写 + 1 导出**。
★ **唯一未迁端点 `rechargeCardGeneratePic`**:本地 `./cardpic` 生成二维码 zip +
`FastDFSClientWrapper.uploadFile` + 落 `td_c_recharge_card_pic_zip`,
卡在「文件存储收敛到 `FileClient` 抽象」的独立决策(与 `/upload-employee-head-pic`、
停车场车位共享同款)。
★ 登录态在 7 条端点(page / zip-page / save / update / delete / bath-delete /
batch-update),全部只碰 `orgId` 或 `opId`/审计列,**无 `plNos`/`userAreas`/`resIds`**;
list / statistic / 导出完全不读登录态。
★ 导出老 `@NoAuth` 按 §3.3 收紧为 `:export`:13 列、`sysCode` 硬编码 `"1001"`、
入参全 `required=false`、`orgId` 取**请求参数**不取登录组织、空表也出文件。
★ 存量死读:`queryTdCRechargeCardList` 的 DAO 方法整段注释、`return null` ⇒ 恒 `data=[]`。
★ 存量缺陷照搬:`save {}` 的 `cardNum` 拆箱 NPE、`save cardNum=0` 空 `<foreach>` 坏 SQL、
`bath-delete {}` / `batch-update {}` 的 `andIdIn(null)` 坏 SQL(均 500「系统异常」);
`delete` 只写 `modfiy_empid`/`modfiy_date`、**不改 `data_state`**;
`zip-page` 的 `andCreateDateBetween(null,null)` 抛 RuntimeException ⇒ 两个日期是事实必填;
`export` 的 `getCardType()==1` / `getEndTime().getTime()` 拆箱,坏日期走 hutool `parse`。
★ `delete`/`bath-delete`/`batch-update` 里的 `setsOpId(...)` 是存量死赋值
(DTO 自己声明 `String opId` 并覆盖 `getOpId()`,服务读的是那个 String)。
★ ⛔ **`save` 带 `cardNum>0` 会真插 N 行 `td_c_recharge_card`**;冒烟只发 `{}` /
`{cardNum:0}` / `id:0` / `ids:[0]`,`td_c_recharge_card` 与
`td_c_recharge_card_pic_zip` 行数已实测不变。
★★ **2026-10-18 第七十四切片:⚠B 里的 `VipCardCustInfoController`(迁 14/15 端点)搬走**
⇒ **⚠B 86/463 → 85/448、已迁 121/648 → 122/663、未迁 90/470 → 89/455**。
★ 落在 `order` 域(`POST /admin-api/order/vip-card-cust-info/*` + 1 条
`GET …export-excel` + 1 条 multipart 导入),老路由 `/vipCardCustInfo`
(老类在 `operationsmanage/vipcardmanage/`,个人购买会员卡记录);
**6 读 + 7 写 + 1 导入 + 1 导出**。
★ **唯一未迁端点 `exportVipCardWordDoc`**:输出目录硬编码
`/Users/wangbiao/Downloads/四川营协议/`、模板用 `ClassPathResource.getFile()`
(打 jar 后不可用)、`XWPFTemplate`(deepoove poi-tl)新栈**没有依赖**
⇒ 与 `rechargeCardGeneratePic` 同归「文件存储/外部模板」决策。
★ 登录态只碰 `orgId`/`createEmpid`/`modifyEmpid`/`name`,
**无 `plNos`/`userAreas`/`resIds`**;读端点与导出完全不读登录态。
★ 老 `@NoAuth` **三处**,三种处置:multipart 导入 → `:import`;导出 → `:export`
(`GET /export-excel`);`checkPhoneAndCarNumber`(老前端只有登录后的
`cardrule.js`/`xcvipcard.js` 在调)→ `:query`,老 `@SysOpLog` 保留成
`@OperateLog(QUERY, …)`。
★ 存量缺陷照搬:`update-*-by-id` 服务层 `detailList.iterator()` NPE、
`update-*-by-cust-card-no` 空指针、`change-*-car-number`/`delete-*` 的 1003 判空、
`check {}` 的 `DateUtil.parse(null)` NPE;`check` 里「每个别人的车牌都覆盖一次结果」
的循环与 `getCustId().equals(...)` 裸调用一律保留。
★ 导出 `@RequestParam Date beginTime/endTime` 走 §3c 契约(坏日期 400,
与充值卡导出的 hutool 解析 500 不同);`pageSize=0` 靠全局 `page-size-zero`。
★ ⛔ **`save-vip-card-cust-info-and-person` 会建客户/车牌/订单/授权多表插入**;
`batch-save-vip-card-buy` 批量建卡订单;`check-phone-and-car-number` 在
「手机号存在、车牌不存在」时**真插一条 `td_c_car`**。冒烟只发**无 body** 与
`checkFlg=2` + 假车场样本,`t_vip_card_cust_info` 等表行数已实测不变。
★★ **2026-10-18 第七十五切片:⚠B 里的 `WorkOrderManageController`(迁 8/8 端点)搬走**
⇒ **⚠B 85/448 → 84/440、已迁 122/663 → 123/671、未迁 89/455 → 88/447**。
★ 落在 `order` 域(`POST /admin-api/order/work-order/*`),老路由 `/workorder`
(老类在 `operationsmanage/workordermanage/`,工单管理);**4 读 + 4 写、无导出**。
★ 登录态只在 `save-order-work` / `save-order-work-for-inspection` 两处读,
且**只取 `orgId`**(`userInfo != null && orgId != null` 才覆盖);
其余 6 条完全不读登录态,**无 `plNos`/`userAreas`/`resIds`**。
★ `query-order-work-track-by-work-id` 的 `workId` 为空抛 1003「入参为空」;
轨迹列表走老 `returnJqGridDataByList` 口径(`total = list.size()`)→
`toPageResultOfList`(同 `ParkingFlowController` 的写法)。
★ 存量错拼 `Histoty` 保留在路由里(`…-for-page-for-histoty`)。
★ 老 `@SysOpLog` 5 处 → `@OperateLog`(CREATE×3、UPDATE×1、DELETE×1);
`save-order-work-for-publish` 底层是 `updateOrderWorkAndOrderWorkTrack` ⇒ 记 UPDATE。
★ ⛔ **`save-order-work-for-inspection` 与 `save-order-work-for-publish` 传 `{}`
会真插 `order_work`/`order_work_track`**;冒烟只发无 body,两张表行数已实测不变。
★★ **2026-10-18 第七十六切片:⚠B 里的 `TdBPayProductController`(迁 7/7 端点)搬走**
⇒ **⚠B 84/440 → 83/433、已迁 123/671 → 124/678、未迁 88/447 → 87/440**。
★ 落在 `order` 域(`POST /admin-api/order/pay-product/*`),老路由
`/tdBPayProductManage`(支付产品管理);**4 读 + 3 写 + 1 批量删、无导出**。
★ 6 处读登录态但**只碰 `orgId`/`opId`/`name`**:page/save/appNameList 在
`userInfo == null || orgId == null` 时抛 `AUTH_TOKEN_NOT_EXISTS`,
非超管(`orgId != -1`)才用登录组织覆盖请求;`opId` 只作审计字段写库,
**无 `plNos`/`userAreas`/`resIds`**。
★ `bath` 是存量错拼,路由保留(`/bath-delete-td-b-pay-product`)。
★ 存量缺陷照搬:`delete-td-b-pay-product` 写 `request.setsOpId(...)`
(`BaseRequest` 字段),服务层却读 `getOpId()`(子类字段)⇒ 操作人恒 null;
`batchDelete` 才写 `setOpId`。`update` 的请求 DTO 没有 id,`{}` 直接撞空主键 500。
★ ⛔ **`save-td-b-pay-product` 传 `channelEnum` 会真插 `td_b_pay_product` +
渠道配置 + `sys_org_app_id`**;冒烟只发 `{}` 与无 body,三张表行数已实测不变。
★★ **2026-10-18 第七十八切片:⚠B 里的 `IncomeChargerCheckTaskSettingController`(迁 7/7 端点)搬走**
⇒ **⚠B 82/426 → 81/419、已迁 125/685 → 126/692、未迁 86/433 → 85/426**。
★ 落在 `order` 域(`POST /admin-api/order/charger-check-task-setting/*`),老路由
`/incomeChargerCheckTaskSettingManage`(收费员考核任务设置);
**2 读 + 1 导出 + 1 导入 + 3 写**;导出数 64 → 65。
★ 导出按 §3.3 第 1 类收紧:老 `@NoAuth` → 登录 + `:export`,路由固定
`GET /export-excel`;导出里 `orgId` 硬编码 10003、`plBusiType` 硬编码 `"1"`,照搬。
★ 5 处读登录态但**只碰 `orgId`/`sOpId`/`name`**;**无 `plNos`/`userAreas`/`resIds`**。
★ `bath` 是存量错拼,路由保留(`/bath-delete-income-charger-check-task-setting`)。
★ page/sumFee 的 `staticDate` 判空 + beginOfDay/beginOfMonth 是存量 Controller
层处理,照搬;`{}` 是 500「统计时间不能为空」。
★ `update {"id":-1}` 其余字段全 null ⇒ tk mapper 空 SET ⇒ Druid 报
`expect SET, actual WHERE` ⇒ 500(存量同样炸)。
★ 导入 multipart 先解析 `statisticDateStr`:缺月份是 400「请求参数缺失」,
只发月份缺文件才是 `MissingServletRequestPartException` ⇒ 500。
★ ⛔ **`save-income-charger-check-task-setting` 有 body 就会真插
`income_charger_check_task_setting`**(无任何必填校验);冒烟只发无 body。
★ ⛔ 导入**合法 Excel** 会走 `batchSaveChargerCheckTask`(按月份先删后插)——
冒烟只发缺参 multipart,服务层未被调用,该表行数已实测不变。
★★ **2026-10-18 第七十九切片:⚠B 里的 `CustomerManagementController`(迁 7/7 端点)搬走**
⇒ **⚠B 81/419 → 80/412、已迁 126/692 → 127/699、未迁 85/426 → 84/419**。
★ 落在 `pay` 域(`POST /admin-api/pay/customer-manage/*`),老路由
`customerManage`(★ 老 `@RequestMapping` **没有前导斜杠**);
**4 读 + 3 写、无导出**。
★ 3 处读登录态但**只碰 `orgId`/`orgName`**,老写法是宽松守卫
`if (userInfo != null && null != userInfo.getOrgId())`(不抛
`AUTH_TOKEN_NOT_EXISTS`);**无 `plNos`/`userAreas`/`resIds`**。
★ 老类无 `@SysOpLog` ⇒ 不加 `@OperateLog`。
★ 存量差异照搬:`nameByNo {}` 对 null 列表 for-each NPE ⇒ 500;
`view {}` 撞 `BeanUtils.copyProperties(null, dto)` ⇒ 500;
`update {}` 的 DAO 是 `updateByPrimaryKey`(非 selective)⇒ SET 全列、
`where id = null` 命中 0 行(code=0 false)。
★ ⛔ **`save-customer-manage-info` 带 `carInfoDTOList` 会真插
`td_cl_customer_management` + `td_cl_customer_car_info`**;
冒烟只发无 body 与 `update {}`,两张表行数已实测不变。
★★ **2026-10-18 第八十切片:⚠B 里的 `TdCPresentRuleController`(迁 7/7 端点)搬走**
⇒ **⚠B 80/412 → 79/405、已迁 127/699 → 128/706、未迁 84/419 → 83/412**。
★ 落在 `pay` 域(`POST /admin-api/pay/present-rule/*`),老路由
`/tdCPresentRuleManage`;**3 读 + 4 写、无导出**。
★ 类路由按「去掉表前缀 `TdC` 再 kebab」定为 `present-rule`
(先例 `TdCRechargeCardController` → `/pay/recharge-card`);
方法段 kebab 老 mapping 逐字,`bath` 存量错拼保留。
★ 5 处读登录态但**只碰 `orgId`/`sOpId`/`createEmpid`/操作人**;
**无 `plNos`/`userAreas`/`resIds`**。老类 4 处 `@SysOpLog` ⇒ 新类 4 处 `@OperateLog`。
★ 存量差异照搬:`save {}` 撞 `getPayType().contains(...)` 的 NPE ⇒ 500
(**在任何 DB 调用之前**);`update {}` 的全 null selective update 在 SQL 层炸 ⇒ 500;
`bath-delete {}` 对 null ids 直接 `.size()` NPE ⇒ 500;`delete {}` 命中 0 行 ⇒ code=0 false。
★ ⛔ **`save-td-c-present-rule` 只要 `payType` 有值就会真插 1~2 行
`td_c_present_rule`**(`3`/`1,3`/`2,3` 还会先查农行 5 折活动);
冒烟只发无 body 与炸关口样本,该表行数已实测不变。
★★ **2026-10-18 第九十六切片:⚠B 里的 `ArrearageStaticController`(迁 5/8 端点,3 个「模板 + FastDFS 回 URL」导出挂起)搬走**
⇒ **⚠B 64/281 → 63/273、已迁 143/830 → 144/838、未迁 68/288 → 67/280**。
★ 落在 `order` 域(`POST /admin-api/order/arrearage-static/*` + `GET /export-excel`),
老路由 `/arrearageStatic`(欠费统计);**4 读 + 1 响应流导出 + 3 挂起**。
★ 坑 22 安全:4 读 + 导出的登录态**只读 `orgId`**(并无条件覆盖进请求体),
请求体 `plNos` 是前端下发的,**不读** `userInfo.getPlNos()`/`userAreas`/`resIds`。
★ 老类 8 个端点**一条 `@SysOpLog` 都没有** ⇒ 新类无 `@OperateLog`。
★ 老 `@NoAuth` 的响应流导出按 §3.3 第 1 类**收紧**为
`order:arrearage-static:export`(无 token 401)。
★ ⏸ 3 个 `...ForAjax[New]`/`...ForParkAjax` 导出挂起:模板
`ArrearageReportTemplate.xlsx` / `ArrearageReportTemplate4Park.xlsx` 未随迁,
形状是「写临时文件 → FastDFS 上传 → 回 URL」,属 §4.3 待定端点;
**别硬搬成响应流**(会改掉老契约)。
★ 存量语义照搬:两个列表端点(含 New 的非 vip/非来源分支)走
`queryArrearListForParkOrCarNumber`,其 SQL 撞本地 MySQL 8
`only_full_group_by` ⇒ **500「系统异常」**(老栈同 SQL 同样 500,红线⑥不修);
New 的 vip/parkSource 分支走 `...WithVipCard`,本地活着(窗口内 0 行)。
★ ⛔ 全类只读/导出,无写端点;`system_operate_log` 零写基线不变。
★★ **2026-10-19 第一百三十二切片:⚠B 里的 `TVipTimeCardController`(3 端点整类迁完)搬走**
⇒ **⚠B 28/102 → 27/99、已迁 179/1009 → 180/1012、未迁 32/109 → 31/106**。
★ 落在 `bill` 域(`/admin-api/bill/vip-time-card/*`),老类在 `portal.web.viptime` 包、
老路由 `/tVipTimeCardManage`;**2 读 + 1 写**、无导出、无 `@NoAuth`。
★ 与已迁的 `/bill/vip-area-card` 共用 `TVipAreaCardService`,本类强制 `type=2`。
★ 坑 22 安全:只写 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。
★ 真写风险:`create` 服务层无校验、`{}` 会真插一行 `t_vip_area_card`
⇒ 冒烟只发无 body(400)。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百五十五切片:上片新进池的 3 个类一次搬完(11 端点)**
⇒ **已迁 204/1113 → 207/1124、未迁 15/42 → 12/31、✔ 6/15 → 4/7、
⚠A 1/3 → 0/0(再次清零)、⚠B 8/24 不变、缺口 8/55、死代码 5。**
★ `PersonPCAcctController` 3/3(`/admin-api/pay/person-pc-acct/*`):
⚠A **机械误报核销** —— 第 93 行 `acctRequest.setOrgId(request.getOrgId())`
打在**请求体**上,全类不读登录态;新类不注入 `SessionCommUtil`。
★ `AppUserAccountController` 3/3(`/admin-api/pay/app-user-account/*`):
1 读 + 2 写;老 `@SysOpLog` ×2 ⇒ `@OperateLog` ×2。`delectUserCar`(老拼写)
→ `delete-user-car`,按主键软删;`updateAccountFreezeState` 的冻结流水插入
**无条件**(不看 update 结果)⇒ 冒烟只能发零字节/坏 JSON/GET,**不能发 `{}`**。
★ `UserRechargeRefundController` 5/5(`/admin-api/order/user-recharge-refund/*`):
3 读 + 1 写 + 1 导出;导出老方法名误称 `excleImport…`、老 `@NoAuth` 按 §3.3
第 1 类收紧为 `:export`;`sysCode` 硬编码 `"1001"`、`pageSize=0`、
空对象兜底、provider 空 data 会 NPE 等存量缺陷照搬。
★ 坑 22:读端点/导出零登录态;写端点只拿 `opId`/`name` 写审计列。
★ 零写基线上移 **+224/轮 → +226/轮**(新增 2 条 `@OperateLog` 留痕);
新增监控表 `order_refund` / `td_c_cust_person_freeze`(均一行不动)。
★★ **2026-10-19 第一百五十六切片:⚠B 的 2 个类一次搬完(12 端点)**
⇒ **已迁 207/1124 → 209/1136、未迁 12/31 → 10/19、✔ 4/7 不变、
⚠B 8/24 → 6/12(⚠A 仍 0/0)、缺口 8/55、死代码 5。**
★ `OrderRefundController` 5/5(`/admin-api/order/order-refund/*`):
3 读 + 1 写 + 1 导出;两个读端点把登录 `orgId` 写进查询(null 抛
`AUTH_TOKEN_NOT_EXISTS`),`plNos` 只出现在导出 query 参数里(不是登录态);
导出老 `@NoAuth` 按 §3.3 第 1 类收紧为 `:export`。
存量缺陷照搬:补缴分支 `split("-")[1]` AIOOBE、`payOrderid == null` 时
`.toString()` NPE;`{}` 打写端点先炸 1003。
★ `AccountRechargeStatisController` 7/7(`/admin-api/pay/account-recharge-statis/*`):
6 读 + 1 导出、**零写**、老类零 `@SysOpLog`;前两个读端点 `null != userInfo`
才设 `orgId`(null 不抛错,与 OrderRefund 相反);导出老 `@NoAuth` 收紧。
本机两条端点恒 500 = 存量 `group by ... desc` 撞 MySQL 8(老栈同坏)。
★ 坑 22:两类命中都只读登录 `orgId`,**不读** `plNos`/`userAreas`/`resIds`。
★ 零写基线上移 **+226/轮 → +228/轮**(新增 1 个写端点 `@OperateLog` ×2 条留痕);
本片没有新增被写表。
★★ **2026-10-19 第一百五十七切片:⚠B 的 2 个类一次搬完(8 端点)**
⇒ **已迁 209/1136 → 211/1144、未迁 10/19 → 8/11、✔ 4/7 不变、
⚠B 6/12 → 4/4(⚠A 仍 0/0)、缺口 8/55、死代码 5。**
★ `StoreCarManageController` 7/7(`/admin-api/pay/store-car-manage/*`):
4 读 + 3 写(开通/销户/现金充值)、零导出;老类 3 处 `@SysOpLog` ⇒ 3 条
`@OperateLog`(CREATE / DELETE / UPDATE)。
读端点 `null == userInfo` 抛 `AUTH_TOKEN_NOT_EXISTS`;开通/销户/充值只写
`orgId`/`sOpId`/`sOpName` 等审计列。
★ 真写风险:`open-store-car-acctount {}` 会**真插**个人账户(custId=null)⇒
探针/冒烟只发无 body/坏 JSON/GET;`close-store-car-account` 只发不存在的
`custId`;`account-store-car-recharge-by-xj {}` 在取值处 NPE,早于任何写入。
★ 本机 `query-acct-person-for-page` 恒 500 = 存量
`TdCAcctPersonSMapper.queryAcctPersonStoreCarForPage` 的 `GROUP BY a.cust_id`
却 SELECT 非聚合列 `b.acct_id` 等,撞 MySQL 8 `only_full_group_by`(老栈同坏,
照红线⑥不修)⇒ 冒烟断言 500 并留注释。
★ `AppointmentManageController` 1/1(`/admin-api/order/appointment-manage/*`):
1 读、零写零导出;老方法名 `queryAcctPersonForPage`(复制残留)、mapping 才是
`queryOrderAppointmentForBcpPage` ⇒ 新方法名按 mapping 恢复。
★ 坑 22:两类只读登录 `orgId`,**不读** `plNos`/`userAreas`/`resIds`。
★ 同片判定 `ParkLotStatisticController` **不可搬**:它读登录 `plNos` 且为空抛
`AUTH_TOKEN_NOT_EXISTS`,属 P3 未落地 ⇒ 继续挂起。
★ 零写基线 **+228/轮 → +231/轮**(新增 3 个写端点埋点,`system_operate_log`
每轮各多 1 行);本片没有新增被写表。
★★ **2026-10-19 第一百五十八切片:⚠B 的 `RemoteControlDzController` 1/1 搬走**
⇒ **已迁 211/1144 → 212/1145、未迁 8/11 → 7/10、✔ 4/7 不变、
⚠B 4/4 → 3/3(⚠A 仍 0/0)、缺口 8/55、死代码 5。**
★ 老类 `dzcontroller/RemoteControlDzController`(老路由 `/dzcontroller`),
新路由 `/admin-api/park/remote-control-dz/*`;老类 1 处 `@SysOpLog`
⇒ 新类 1 条 `@OperateLog`(UPDATE)。
★ 坑 22:只读登录 `orgId`/`loginCode`/`opId` 写 `api_log_request`,
**不读** `plNos`/`userAreas`/`resIds`。
★ ⛔ 真副作用:`sourceType == 7`(红门)会**真实开闸 + 插 API 调用日志**;
安全样本 `P15040214Z` 在本机 Redis 缓存里 `sourceType=17` ⇒ 走 else,
在**外呼与写库之前**抛「该车场暂不支持远程开闸」(存量错误码 1007)。
★ 存量 NPE 照搬:`getParkLotByPlNo(null/不存在)` 返回 null 后直接
`parkLot.getSourceType()` ⇒ `{}` / 假 plNo 都是 500「系统异常」(红线⑥)。
★ HTTP 工具改用 park 模块已有的 `com.zteits.park.util.HttpClientTutorial`
(与老门户同名同签名);两份 portal VO 原样搬进
`controller/admin/remotecontroldz/vo/`。
★ 零写基线 **+231/轮 → +234/轮**(3 条带 token 的 §2ex 调用触发 `@OperateLog`);
本片没有新增被写表(红门分支会写 `td_b_api_log`,该表历批已在监控内,本片不可测)。
★★ **2026-10-19 第一百五十四切片:先修实现识别,再搬 `ChargerAccountController` 6 端点**
⇒ **已迁 203/1107 → 204/1113、未迁 8/11 → 15/42、✔ 4/7 → 6/15、
⚠A 0/0 → 1/3、⚠B 4/4 → 8/24、缺口 16/92 → 8/55、就绪 211/1118 → 219/1155。**
★ 本片**先修工具再搬类**:`IMPL_PAT` 只认 `class X implements Y`、漏掉
`class X extends Base implements Y`;新版实现类只 glob `*Impl.java`、漏掉
`RefundServiceIpml.java`(存量把 Impl 拼成 Ipml)⇒ 8 个 Controller / 37 端点
被误判成「无实现」挡在缺口档:StoreCarManage(7)、AccountRechargeStatis(7)、
ChargerAccount(6)、UserRechargeRefund(5)、OrderRefund(5)、PersonPCAcct(3)、
AppUserAccount(3)、AppointmentManage(1)。总数 232/227/5 不变。
★ `ChargerAccountController` 落在 `pay` 域(`/admin-api/pay/charger-account/*`),
老类在 `operationsmanage/chargemanage` 包、老路由 `/chargerAccount`;
**4 读 + 2 导出**,零写、零 `@SysOpLog`。
★ 坑 22 安全:6 个端点**零登录态**(`custIds` 由前端下发);新类不注入 `SessionCommUtil`。
★ 两条导出老 `@NoAuth` 按 §3.3 第 1 类收紧为 `:export`;一个类两个导出 ⇒
`/export-excel` + `/export-pay-out-excel`。
★ 照搬:`acctTypeList` 无条件覆写、双重循环回填收费员名且不判空、
支出回填两次且退费单命中时 `berthNo=""`、停车查询用 `0001_SYS` 而退费/收费员用请求 `sysCode`、
导出 `BaseInfo(1, MAX)` + `DateUtil.to_date` + `rechargeType` 1/2/5/6/未知 + `/100` ROUND_HALF_UP。
★ 本机样本:`acct_type=6` 充值 2 行(统计 110000/2/0/0)、支出 0 行、
`tp_e_employee_charge` 0 行 ⇒ `custName` 恒 null;支出两次回填无正样本。
★ 零写库;零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百五十三切片:⚠B 里的 `RealTimeInfoController`(3 端点只落 2 个)搬走**
⇒ **⚠B 5/7 → 4/4、已迁 202/1104 → 203/1107、未迁 9/14 → 8/11**。
★ 落在 `park` 域(`/admin-api/park/real-time-info/*`),老类在
`parkinglotcloudplatform/monitor` 包、老路由 `/rtinfo`;**2 读**、零导出、零 `@SysOpLog`。
★ 未迁 `berthinfo`:真读 `userInfo.getUserAreas()` 做数据权限过滤(P3)。
★ 已迁两条**零登录态**:`escapePaymentInfo` 把请求原样透传;`warninginfo` 里的
`//userInfo.getUserAreas()` 在存量源码里就是注释掉的(「权宜之策构造」按 `cityId` 现造
`UserArea`)⇒ 不算登录态读取,别"顺手恢复"。
★ `escape-payment-info` 本机**恒 500**:`RealtimeInfoSMapper.xml` 的 namespace 写成
`com.zteits.park.mapper.smapper...`、接口在 `com.zteits.pay.mapper.smapper...`
⇒ `Invalid bound statement`;**老仓同一份 XML 同样写错**(两栈同坏、红线⑥照搬不修),
`p2_audit_mapper_bind.py` 归 B 类「存量就断」。
★ `warning-info` 本机 `free_berth=0` 与 `parking_lot_geo(geo_type=1)` 的连接结果为 0
⇒ 恒返回空数组(样本缺口,不是过滤错)。
★ 零写库;零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百五十二切片:⚠B 里的 `ChargerTicketController` 3 端点全迁**
⇒ **⚠B 6/10 → 5/7、已迁 201/1101 → 202/1104、未迁 10/17 → 9/14**。
★ 落在 `pay` 域(`/admin-api/pay/charger-ticket/*`),老类在 `web.ticket` 包、
老路由 `/chargerTicket`;**1 读 + 2 写**、零导出、零 `@SysOpLog`。
★ 坑 22 安全:只写 `orgId`/操作人,不读 `plNos`/`userAreas`/`resIds`;
老代码三处**都不判** `userInfo == null`,新栈由安全过滤器先挡(照搬不补判空)。
★ 两处预览都用**写死的样例 DTO**(收费员 0050001/张三、京A88888 等),
渲染后 `replaceAll("null","-")` 并把 `&&&&` 切成 `ticketTextUp/Down` + `hasPic`,
但正文**仍保留 `&&&&`**(只切不删)。
★ 照搬顺序:`save` 先雪花号(内存)再五项判空;`open` 先按条件把旧模板
`ticket_state=0`、再启用当前模板(返回第二步结果)。
★ 真写风险:`save` 带齐 `configRequests` 会真插配置+预览模板;
`open` 带真实 `ticketNo` 会真改两批行(本地 `charger_ticket_template` 81 行)。
冒烟只发无 body / `{}` / 标题-only / 不存在模板号;零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百五十一切片:⚠B 里的 `ArrearageCallConfigController` 4 端点全迁**
⇒ **⚠B 7/14 → 6/10、已迁 200/1097 → 201/1101、未迁 11/21 → 10/17**。
★ 落在 `order` 域(`/admin-api/order/arrearage-call-config/*`),老类在
`web.arrearangecall` 包、老路由 `/arrearageCallConfig`;
**1 读 + 3 写**、零导出、零 `@SysOpLog`。
★ 坑 22 安全:分页条件用的是**请求体** `plNos`(`sql.andPlNoIn(request.getPlNos())`),
控制器写的 `orgId` 在 DAO 里**根本没被使用**;save/update 只写操作人,delete 老代码不读登录态。
★ 存量形状:`plNos=null` ⇒ Example `addCriterion` 抛 RuntimeException;
`plNos=[]` ⇒ `and pl_no in ()` SQL 语法错 ⇒ 两条都是 500「系统异常」。
分页**必须显式带非空 plNos**,别顺手改成"不传就查全部"。
★ 其它照搬:`callMaxValue` 条件错用 `getCallMinValue`;update 先写操作人/时间再判 id;
delete 是**物理删除**(不是 `data_state=0`)。
★ 真写风险:`save` 带非空 `parkLontDTOs` 会真插行(本地 239 行)
⇒ 冒烟只发无 body / `{}` / `{"parkLontDTOs":[]}`;零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百五十切片:⚠B 里的 `BalanceManagementController`(5 端点只落 3 个)搬走**
⇒ **⚠B 8/19 → 7/14、已迁 199/1092 → 200/1097、未迁 12/26 → 11/21**。
★ 落在 `order` 域(`/admin-api/order/balance-management/*`),老类在
`web.financialmanagement.clearsettlement` 包、老路由 `/BalanceManagement`;
**1 写 + 2 导出**;另 2 个分页端点**有意不迁**
(`plNos` 空或 `"ALL"` 时回落 `userInfo.getPlNos()` = P3 数据权限)。
★ 坑 22:已迁 3 条**都不读登录态**(写只吃 body、导出只吃 7 个 query 参数)⇒ 可开搬。
★ 两条导出老 `@NoAuth`,按 §3.3 第 1 类收紧为需登录;
权限 `order:balance-management:update` / `order:balance-management:export`。
★ **本地服务层是空壳**(`BillReconciliationFor*ServiceImpl` 的 DAO 调用全注释)
⇒ 导出只有表头、写恒回 `false`;换环境接回 DAO 后冒烟 `dataRows == []` 会红,
那是功能恢复、不是回归。
★ 写端点冒烟只发空 body / 坏 JSON(绑定层 400),`@OperateLog` 不触发;
零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百四十九切片:⚠B 里的 `ParkSourceController`(6 端点只落 3 个)搬走**
⇒ **⚠B 9/25 → 8/19、已迁 198/1086 → 199/1092、未迁 13/32 → 12/26**。
★ 落在 `park` 域(`/admin-api/park/park-source/*`),老类在
`parkinglotcloudplatform.parklotmanage` 包、老路由 `parkSource`;
**3 读、零写、零导出、零 `@SysOpLog`**;另 3 个端点**有意不迁**
(统计 / 员工总数 / 设备总数都读 `userInfo.getPlNos()` = P3 数据权限)。
★ 坑 22:三个已迁端点**都不读登录态**(两条分页只吃请求体 `plNos`、
单车场只吃 `plNo`)⇒ 可开搬。
★ 三个端点统一挂 `park:park-source:query`;老类无 `@NoAuth`。
★ `plNos=null` 时存量 SQL 的 `<if>` 全不生效 ⇒ 车场扫全部 `data_status=1` 行
(本机 4 行);每行 `deviceList` 恒 3 项(PDA/地磁/地锁)、`deviceTotal` 走
`Integer` 拆箱;单车场端点查不到车场时服务层回**非 null 空 DTO**。
★ 单车场端点 `{}` → **500「系统异常」**:`plNo=null` 时缓存工具直接炸
(存量缺陷照搬);员工统计本机 `tp_e_employee_charge` 0 行 ⇒ 恒空页。
★ ⚠️ 盘点脚本按**类**判已迁 ⇒ 本片 6 个端点都记进"已迁"、⚠B 整类核销,
但**交付口径是 3/6**。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百四十八切片:⚠B 里的 `AnomalyStatisticsController`(4 端点只落 2 个)搬走**
⇒ **⚠B 10/29 → 9/25、已迁 197/1082 → 198/1086、未迁 14/36 → 13/32**。
★ 落在 `order` 域(`/admin-api/order/anomaly-statistics/*`),老类在
`portal.web.report.income` 包、老路由 `income/anomalyStatistics`;
**2 读/导出、零写、零 `@SysOpLog`**;另 2 个端点**有意不迁**
(`parkIds` 为空时回落 `userInfo.getPlNos()` = P3 数据权限)。
★ 坑 22:两个已迁端点**都不读登录态**(明细只吃请求体;导出只吃 4 个 `@RequestParam`)
⇒ 可开搬。
★ 老导出 `@NoAuth` 按 §3.3 第 1 类收紧为 `GET /export-excel` + `:export`;
明细分页 `POST /query-sealed-parking-detail-report-for-page` + `:query`。
★ 明细 `QuerySealedParkDetailReportRequest` 自带 `@NotBlank parkId` /
`@NotBlank paymentTime` / `@NotNull queryType` ⇒ `{}` 是绑定层 400。
★ 老导出整个方法体包在 `try/catch` → 只 `printStackTrace`(新栈 `log.error`):
`queryType=0` 撞本机 MySQL 8 `only_full_group_by` 时是 **HTTP 200 + 空 body**,
不是新栈 500;文件名/sheet/10 列表头逐字照搬,`sysCode="1001"`、
`BaseInfo(1,0)`、金额 ÷100、null → `""`、「封顶放行(次)」恒空串。
★ 月口径入参必须是 `yyyy-MM`(服务层补 `-01`);本机 `P1101020Z7` + `2018-02-01`
有 10 单、汇总 9400 分 ⇒ xlsx 行 `94.0/94.0`。
★ ⚠️ 盘点脚本按**类**判已迁 ⇒ 本片 4 个端点都记进"已迁"、⚠B 整类核销,
但**交付口径是 2/4**。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百四十七切片:⚠B 里的 `SelectItemController`(8 端点只落 2 个)搬走**
⇒ **⚠B 11/37 → 10/29、已迁 196/1074 → 197/1082、未迁 15/44 → 14/36**。
★ 落在 `bill` 域(`/admin-api/bill/select-item/*`),老类在
`parkinglotcloudplatform.datastatistic` 包、老路由 `/selectItem`;
**2 读、零写、零导出**;另 6 个端点**有意不迁**(都读 `userInfo.getPlNos()`)。
★ 坑 22:两个已迁端点**都不读登录态**(会员卡全量分页 + 前端参数 `noAll`)⇒ 可开搬。
★ 老接口是 `void` + `response.getOutputStream().write(JSONArray…)` 裸数组;
新栈统一 `CommonResult<List<SelectItemVO>>`,`SelectItemVO` 与老
`com.zteits.bcp.portal.common.SelectItem` 字段一一对应,`equals`/`hashCode`
只比 `code`/`label`/`userDefined`/`value`(老代码 `list.contains` 去重)。
★ `noAll` 缺失时照搬老代码 `noAll.equals("yes")` 的 **NPE → 500**;
`pageSize=0` 取全量靠 `page-size-zero: true`;`sysCode` 是字面量 `"1001"`。
★ 本机 `t_vip_card where data_state=1` **427 行**(card_type 1~7 全非空)⇒
`noAll=yes` 返回 8 条类型 / 428 张卡;`noAll=no` 返回 7 / 427。
★ ⚠️ 盘点脚本按**类**判已迁 ⇒ 本片 8 个端点都记进"已迁"、⚠B 整类核销,
但**交付口径是 2/8**。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百四十六切片:⚠B 里的 `PersonPCIndexController`(5 端点只落 2 个)搬走**
⇒ **⚠B 12/42 → 11/37、已迁 195/1069 → 196/1074、未迁 16/49 → 15/44**。
★ 落在 `pay` 域(`/admin-api/pay/person-pc-index/*`),老类在 `portal.web.personpc` 包、
老路由 `/personPc/index`;**2 读、零写、零导出**;另 3 个端点**有意不迁**。
★ 坑 22:两个已迁端点**都不读登录态**(`custId` 来自请求体、组织查询不读身份)⇒ 可开搬。
★ 不迁的 3 个(`getCustIndexBaseInfo` / `queryOwnerParkRecord` /
`pageQueryArrearRecordByCondition`)都走 `getUserInfoForPersonPC()` 读 HTTP session,
新栈不建 session ⇒ 恒 1003,记为「等个人客户登录桥」
(与 `PersonPCCarNumController` 同口径)。
★ `query-owner-pay-out-by-month`:控制器不校验 `custId`(服务层抛 1003);
`beginTime`/`endTime` 缺一则改「去年今天 ~ 本月月末」并补满 13 个月零值。
★ `select-all-app-org`:**无 `@RequestBody`**,DAO 过滤 `data_state=1`
(本机 14 行 → 12 行),服务层 Redis 缓存优先(读端点里的 Redis 写)。
★ ⚠️ 盘点脚本按**类**判已迁 ⇒ 本片 5 个端点都记进"已迁"、⚠B 整类核销,
但**交付口径是 2/5**。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百四十五切片:⚠B 里的 `IndexDeviceController`(3 端点整类迁完)搬走**
⇒ **⚠B 13/45 → 12/42、已迁 194/1066 → 195/1069、未迁 17/52 → 16/49**。
★ 落在 `park` 域(`/admin-api/park/index-device/*`),老类在 `portal.web.index` 包、
老路由 `/index/device`;**3 读、零写、零导出**。
★ 坑 22:**只有第 1 个端点**读登录态回填 `orgId`(`getUserInfo()` 为空抛
`AUTH_TOKEN_NOT_EXISTS`);第 2、3 个老代码不读登录态,照搬。
`plNos` 全来自前端,`userAreas`/`resIds` 不碰 ⇒ 可开搬。
★ 第 1 个 `eqpTypes=[ALL(-1)]` 由服务层展开成 GEO+PDA,每类型回 3 个 VO
(在线 0 / 离线 1 / 故障 2),`map` 空回**空列表**。
★ 第 2 个 `eqpType` **无校验**(`{}` ⇒ `eqpTypes=[null]` 空结果),4 个计数 `Integer` 拆箱累加。
★ 第 3 个 `@Valid`(`plNos` `@NotEmpty`、`eqpType` `@NotNull` ⇒ 400;两字段同违时
Bean Validation **不保证先报哪个**);`PageResult.total` = 地磁分页 total + PDA 分页 total,
`id` 左补零 10 位;两个分支各自 `pageBean==null` 提前返回空页(PDA 分支会丢弃已收集的地磁行)。
★ **本地样本**:`tp_p_eqp` 与 `parking_lot` **零 JOIN** ⇒ GEO 分支统计/分页恒空(样本缺口,
已直连 SQL 复核);PDA 正样本 `P150402254`(34 行),登录人会话 `orgId=10003`。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百四十四切片:⚠B 里的 `IncomeBerthReportController`(3 端点,2 迁 + 1 挂起)搬走**
⇒ **⚠B 14/48 → 13/45、已迁 193/1063 → 194/1066、未迁 18/55 → 17/52**。
★ 落在 `order` 域(`/admin-api/order/income-berth-report/*`),老类在
`portal.web.berthreport` 包、老路由 `/incomeBerthReport`;**2 读 + 1 导出**。
★ 坑 22 安全:两个读端点都照搬 `getUserInfo().getOrgId()` 且**不判空**
(新栈 `@PreAuthorize` 先挡无 token),`plNos` 全来自前端,`userAreas`/`resIds` 不碰。
★ 请求 DTO **无 `@Valid`** ⇒ 三条手工校验:空 `plNos` → 1003、开始/结束时间缺失 → 1003、
跨度 >31 天 → 1003;日期字段只吃 `yyyy-MM-dd HH:mm:ss`(`yyyy-MM-dd` 是 400)。
★ 分页老 `returnJqGridData` → `PageResult`;汇总 SQL 全列 `ifnull(sum(...),0)`
⇒ 假车场回**全 0 DTO**。
★ **DAO 除零风险照搬**:守卫只看 `parkDurationPay==0`,`0<parkDurationPay<3600`
时 `totalFee / (parkDurationPay/3600)` 除零 ⇒ 500(存量缺陷,红线⑥不修)。
★ ⏸ `exportIncomeBerthReport` **挂起**:EasyExcel 写临时文件 → FastDFS 上传 → 回 URL,
新栈运行期无存储后端;`handleData`/`getInitDTO`/`getExcelHeader`/`fen2yuan` 一并挂起。
★ **本地样本**:`income_berth_report` 500 行全部停在 `2020-07-07`,唯一非零行
`A320211000`(`berthNum=2800`、`totalFee=716200`)。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百四十三切片:⚠B 里的 `DurationParkController`(4 端点整类迁完)搬走**
⇒ **⚠B 15/52 → 14/48、已迁 192/1059 → 193/1063、未迁 19/59 → 18/55**。
★ 落在 `order` 域(`/admin-api/order/duration-park/*`),老类在
`portal.web.report.duration` 包、老路由 `/duration/durationPark`;
**3 读 + 1 PDF 导出、零写端点**。
★ 坑 22 安全:汇总读登录态**只读 `orgId`** 回填 `orgName`,且取不到登录态时
**静默跳过**(老代码没有 `else` 抛 401 —— 别照抄收费员收入那一类);
`plNos` 全来自前端,`userAreas`/`resIds` 不碰。
★ `@Valid` **不是空转**:`DurationParkReq.reportDate`/`reportTabTag` 有 `@NotNull`
⇒ `{}` 是 400;空 `plNos` 在汇总/分布是 1003,在分页是空 `in ()` 的 500。
★ 未知 tab:汇总返回**长度 1 的 orgName 空 Bean**(空结果被 `add`)、分布 `[]`、
分页空页;`tag=2`(周)仍查日表(存量注释掉周表分支)。
★ 老 `@NoAuth` 的 PDF 导出按 §3.3 收紧为 `GET /export-pdf` + `order:duration-park:export`;
方法体整个包在 try/catch ⇒ 日期错是 **200 + 0 字节**;假车场仍出 `%PDF-`;
`plNos=` 空列表的 1003 在 try 之外 ⇒ 500 JSON。
★ **本地样本**:日表 500 行最大 `2018-06-06`(`A150402003` 路侧 430 泊位 /
`P1101020EG` 封闭 335 泊位),月表 500 行最大 `2018-05`。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百四十二切片:⚠B 里的 `IncomeParkSourceController`(4 端点整类迁完)搬走**
⇒ **⚠B 16/56 → 15/52、已迁 191/1055 → 192/1059、未迁 20/63 → 19/59**。
★ 落在 `order` 域(`/admin-api/order/income-park-source/*`),老类在
`portal.web.report.income` 包、老路由 `/income/incomeParkSource`;
**2 读 + 2 导出(PDF + xlsx)、零写端点**。
★ 坑 22 安全:汇总端点读登录态**只读 `orgId`** 回填 `orgName`(P3 已解禁),
拿不到登录态抛 401(与 `IncomeParkAndVipController` 的"跳过"不同,照搬老代码);
分页与两条导出完全不读登录态,`plNos`/`sourceCodes`/`orgId`/`userName` 全来自前端。
★ 两条老 `@NoAuth` 导出按 §3.3 收紧:xlsx 占 `GET /export-excel`、
PDF 落 `GET /export-pdf`(先例 `IncomeStatisticController`)。
★ **同一类里两条导出的失败形态不同**:PDF 整个方法体 `try/catch(Exception)` 吞异常
⇒ 坏日期 = **200 + 0 字节 + `application/pdf`**;xlsx 无 try/catch ⇒ **500 JSON**。
★ 汇总端点返回的是**服务结果本身**(不是方法体里回填用的两元素 `list`);
Service 对空结果会塞 `plType=1/2` 两行 0 值 ⇒ `tag=1` 恒 2 行;未知 tab ⇒ `code=0 + []`。
★ **本地样本事实**:`income_statistic_park_source_day` 的 `pl_no` 与 `parking_lot`
**完全不重叠**(日表最大 `2018-06-04`)⇒ `A150402003` 验"有收入无车场"、
`B1504020C7` 验"有车场无收入",不能合并断言。
★ `reportTabTag=3` 传 `yyyy-MM-dd` 不报错(hutool3 容错),别写 500 断言;
`reportDateEnd` 日/周/月分支用不到但老签名必填(缺 ⇒ 400)。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百四十一切片:⚠B 里的 `IndexRealTimeController`(5 端点整类迁完)搬走**
⇒ **⚠B 17/61 → 16/56、已迁 190/1050 → 191/1055、未迁 21/68 → 20/63**。
★ 落在 `park` 域(`/admin-api/park/index-real-time/*`),老类在 `portal.web.index` 包、
老路由 `/index/new`;**4 读 + 1 导出、零写端点**。
★ 坑 22:只有 `query-employee-num-by-pl-no` / `query-parkinglot-basic-facts-by-pl-nos`
从登录态回填 `orgId`(**只读 orgId,P3 已解禁**);另两条老代码不读登录态;
`plNos` 全来自请求体 ⇒ 不碰 `plNos`/`userAreas`/`resIds`。
★ 实时汇总/列表空 `plNos` ⇒ 1003;本机缓存冷 ⇒ 汇总全 0 Bean(`plNo=null`)、列表 `[]`。
★★ **踩到跨切片环境坑**:列表端点传**真实车场**会异步写 `PARK_REAL_TIME_STATUS<plNo>`
到本机 Redis,**无 TTL、跨轮次常驻**,会把 §2ak「DTO 字段全 null」与
`park-detail-info-by-org-ids → 500` 两条老断言永久打红。冒烟只准用不存在的 plNo
(本片 `ZZZNOPE`,预热在 `getParkLotByPlNo` 返回 null 处 NPE 被 catch)。
★ 车场概况:`@Valid @NotEmpty` 先于老手写判空 ⇒ 空 body **400**(1003 不可达);
有效 ⇒ `total=plNos.size()`;不存在车场 ⇒ `total=0, list=[]`(老 `data=null` 口径)。
★ 老导出 `@NoAuth`(`/exportParkinglotBasicFactsByPlNos`,不限方法)按 §3.3 收紧为
`GET /export-excel` + `park:index-real-time:export`。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百四十切片:⚠B 里的 `InvoiceController`(5 端点整类迁完)搬走**
⇒ **⚠B 18/66 → 17/61、已迁 189/1045 → 190/1050、未迁 22/73 → 21/68**。
★ 落在 `pay` 域(`/admin-api/pay/invoice/*`),老类在 `portal.web.invoice` 包、老路由 `/invoice`;
**4 读 + 1 导出、零写端点**。
★ 坑 22 安全:4 读 + 导出只写 `orgId`(统计端点保留「非超管才覆盖」);
**不读** `plNos`/`userAreas`/`resIds`。
★ ★ `query-invioce-by-vo`(老 `queryInvioceByVo` 的 `Invioce` 是存量错拼,路由逐字 kebab)
与 `query-cust-invoice-his-for-page` 都是真分页:`data_state=1` + 登录 orgId=10003 ⇒ 本机 406 行;
`custName=赤峰` → 168。
★ ★ 分页**逐行 N+1 照搬**(`selectByCustId` + `queryCustInvoiceHisDetailList`):
本机 `td_c_cust_person` 0 行 ⇒ `recipientsPhone=""`;明细未挂 org=10003 且 `car_number` 全 NULL
⇒ `carNumbers=null`。别改批量。
★ 详情死主键 / `id=null` ⇒ `code=0 + data=null`。
★ 老导出 `@NoAuth`(`/exportCustInvoiceHisList`,不限方法)按 §3.3 收紧为
`GET /export-excel` + `pay:invoice:export`;坏日期 `ParseException` ⇒ 500(存量无包装)。
★ 老统计端点 `@NoAuth` 但方法内自校验登录 + 超管组织不覆盖 orgId;新栈外层加 `:query`,
保留老 bean 绑定(不加 `@RequestBody`);custName/saleName 空 ⇒ 1003;
两者都给 ⇒ 存量 `selectRateData` 撞 MySQL 8 `only_full_group_by` ⇒ 500(红线⑥不修)。
★ 补齐 `parking-module-api` 的 `TdCCustInvoiceHisQueryRequest`:老仓有 `carNumbers`/`custPhone`/
`custEmail` 三字段,新栈漏搬,本片补回(纯追加)。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百三十九切片:⚠B 里的 3 个类一次搬完**
(`TdBSmsRecordController` 4 + `OrderParkBjxcController` 4 + `OrderParkBjxcSuccessController` 4)
⇒ **⚠B 21/78 → 18/66、已迁 186/1033 → 189/1045、未迁 25/85 → 22/73**。
★ 全部落在 `order` 域:
`/admin-api/order/sms-record/*`(老 `/tdBSmsRecordManage`,类路由去 `TdB` 前缀,先例 `TdBPayApp`)、
`/admin-api/order/order-park-bjxc/*`(老 `/orderParkBjxcManage`)、
`/admin-api/order/order-park-bjxc-success/*`(老 `/orderParkBjxcSuccessManage`);
三个类都是 **1 读 + 3 写**、无导出、无 `@NoAuth`、**零** `@SysOpLog` ⇒ 零 `@OperateLog`。
★ 坑 22 安全:只写 `orgId`/`sOpId`/`createEmp*`/`modifyEmp*`/`opId`/`opName`,
**不读** `plNos`/`userAreas`/`resIds`。
★ `TdBSmsRecord` 分页**真查询**:DAO 按登录组织(admin 会话 orgId=10003)+ `data_state=1`
过滤 ⇒ 本机 total=500;`carNumber=蒙D7Y077` → 1、`sendType=10,sendState=20` → 466。
★ 北西城两个类的分页服务层把 DAO 调用整段注释 ⇒ **恒空页**(存量 TODO,别补 SQL);
本机也**没有** `order_park_bjxc_success` 表(delete → 501)。
★ ★ `OrderParkBjxc` 批量删除 DAO 的 `record.set*` 全被注释 ⇒ `updateByExampleSelective`
生成空 SET ⇒ **删除恒 500**;`OrderParkBjxcRequest` 无 `id` 字段 ⇒ 按主键改恒 `where id=null`。
`OrderParkBjxcSuccessRequest` 与 domain 只共享 `id` ⇒ update 恒空 SET(500)。均照搬。
★ 真写风险:三个 save 都无校验 ⇒ 冒烟只发无 body(400);其余只发手写 1003 / 0 行分支。
★ 零写基线 **+224/轮** 不变(`td_b_sms_record` 500、`order_park_bjxc` 0 实测不动)。
★★ **2026-10-19 第一百三十八切片:⚠B 里的 `TdCAcctPersonChangeController`(4 端点整类迁完)搬走**
⇒ **⚠B 22/82 → 21/78、已迁 185/1029 → 186/1033、未迁 26/89 → 25/85**。
★ 落在 `pay` 域(`/admin-api/pay/acct-person-change/*`),老类在 `portal.web.appservice` 包、
老路由 `/tdCAcctPersonChangeManage`;**1 读 + 3 写**、无导出、无 `@NoAuth`。
★ 坑 22 安全:只写 `orgId`/`sOpId`/`createEmp*`/`modifyEmp*`/`opId`/`opName`,
**不读** `plNos`/`userAreas`/`resIds`。
★ ★★ 分页**恒空页**:老 DAO 的 `queryTdCAcctPersonChangeForPage` 整段被注释
(`//TODO 编写smapper实现`),服务层直接 `return new PageBean<>()` ⇒
任何入参都是 `total=0` + `list=[]`,本机表里有 5 行也看不到(存量形状,别补 SQL)。
★ ★ `update` 入参 DTO 只有 `id/orgId/create*/modify*`;拷到 domain 后能进 `<set>` 的
只有 `orgId` ⇒ `{"id":999999999}` 生成空 SET(500 系统异常),
`{"id":999999999,"orgId":10003}` 才返回 `0 + false`。存量缺陷照搬。
★ 真写风险:`save` 无校验、`{}` 会走 insert(本机三列 NOT NULL 兜底)⇒ 冒烟只发无 body。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百三十七切片:⚠B 里的 `TdCStoreCarController`(4 端点整类迁完)搬走**
⇒ **⚠B 23/86 → 22/82、已迁 184/1025 → 185/1029、未迁 27/93 → 26/89**。
★ 落在 `pay` 域(`/admin-api/pay/store-car/*`),老类在 `portal.web.appservice` 包、
老路由 `/tdCStoreCarManage`;**1 读 + 3 写**、无导出、无 `@NoAuth`。
类路由按「去掉表前缀 `TdC` 再 kebab」定(先例 `TdCPresentRule` → `/pay/present-rule`)。
★ 坑 22 安全:只写 `orgId`/`sOpId`/`modfiyEmpid`/`opId`/`opName`,
**不读** `plNos`/`userAreas`/`resIds`。
★ ★ 分页 DAO **不按 `orgId` 过滤**(只认 `carNumber`/`custId`/`data_state`)——
控制器里的 `setOrgId` 不生效,本机 total=3、能看到 org=10005 的行;存量事实照搬。
★ 真写风险:`save` 无校验、`{}` 会真走 insert(本机 `car_number NOT NULL` 兜底)
⇒ 冒烟只发无 body;update/delete 只发 0 行 / 手写 1003 分支。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百三十六切片:⚠B 里的 `TbWorkOrderController`(4 端点整类迁完)搬走**
⇒ **⚠B 24/90 → 23/86、已迁 183/1021 → 184/1025、未迁 28/97 → 27/93**。
★ 落在 `system` 域(`/admin-api/system/tb-work-order/*`),老类在
`portal.web.workorder` 包、老路由 `/tbWorkOrderManage`;**1 读 + 3 写**
(分页 / 新增 / 按主键改 / 批量删),无导出、无 `@NoAuth`。
★ 坑 22 安全:只写登录态 `orgId`/`sOpId`/`createEmpId`/`modifyEmpId`/`opId`/`opName`,
**不读** `plNos`/`userAreas`/`resIds`。
★ ⛔ 真写风险:`saveTbWorkOrder` 服务层无任何校验 ⇒ `{}` 也真插一行 `tb_work_order`
⇒ 冒烟只发无 body(400)。update/delete 的 1003「id为空!」是 Controller 手写闸门 ⇒ 真零写。
★ 本机**没有** `tb_work_order` 表:走到 SQL 的分支都是 500「系统异常」
(表名不含 `order_` ⇒ 不吃 `tb_work_order_process` 的 501 兜底);冒烟断言写成 0/500/501
三选一,表导进来后自动转绿。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百三十五切片:⚠B 里的 `ReconciliationController`(3 端点整类迁完)搬走**
⇒ **⚠B 25/93 → 24/90、已迁 182/1018 → 183/1021、未迁 29/100 → 28/97**。
★ 落在 `order` 域(`/admin-api/order/reconciliation/*`),老类在
`portal.web.financialmanagement.reconciliation` 包、老路由 `/new/reconciliation`;
**2 读 + 1 导出(xlsx)**、无写、导出老 `@NoAuth` 已收紧为需登录 + `:export`。
★ 坑 22 安全:汇总/分页只判登录后写 `orgId`,超管再扩 `SUPER_OP_ID` 配置组织;
导出沿用老入参 `opId`/`orgId`/`sysCode`,**不读** `plNos`/`userAreas`/`resIds`。
基类 `ReconciliationBaseController` 仅 `getPayOrderTypeName`,整段照搬进新类。
★ 存量缺陷照搬:缺起止时间 ⇒ 200 + code=500 + `[存量错误码 1003]`;
只给时间不给 `dateType` ⇒ `request.getDateType()` 拆箱 NPE ⇒ 500;
导出不传 `opId` ⇒ `isSuperOpId` 内 `opId.longValue()` NPE ⇒ 500。
★ 导出是程序化 13 列 `SXSSFWorkbook`,无模板依赖;导出端点数 **79 → 80**。
日期参数保持存量 `@RequestParam Date`(坑 24),只吃 JS `Date.toString()` 格式。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百三十四切片:⚠B 里的 `EqpOnlineRateController`(3 端点整类迁完)搬走**
⇒ **⚠B 26/96 → 25/93、已迁 181/1015 → 182/1018、未迁 30/103 → 29/100**。
★ 落在 `park` 域(`/admin-api/park/eqp-online-rate/*`),老类在
`portal.web.report.equipment` 包、老路由 `/eqpOnlineRate`;
**2 读 + 1 导出(PDF)**、无写、导出老 `@NoAuth` 已收紧为需登录 + `:export`。
★ 坑 22 安全:分页/合计只判登录(合计另用 `sysOrg` 回填 `orgName`),
导出沿用老入参 `orgId`/`orgName`,**不读** `plNos`/`userAreas`/`resIds`。
★ 存量缺陷照搬:`pl_no in (<foreach>)` 在 `plNos` 为 null/空列表时拼成 `in ()`
⇒ MySQL 语法错 ⇒ 500;导出 `plNos=` 空串更早抛 `BizException(1003)`
⇒ 200 + code=500 + [存量错误码 1003](取参校验在 try/catch 外)。
★ 分页/合计按 `reportTabTag` 1/2/3 走日/周/月,未知 tab 回空页 / 数值全 null(只填 `orgName`)。
★ 导出是程序化 PDF(`PdfCreatTableUtil` 双子报表),无模板依赖;导出端点数 **78 → 79**。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百三十三切片:⚠B 里的 `IncomeOnlineBillController`(3 端点整类迁完)搬走**
⇒ **⚠B 27/99 → 26/96、已迁 180/1012 → 181/1015、未迁 31/106 → 30/103**。
★ 落在 `order` 域(`/admin-api/order/income-online-bill/*`),老类在
`portal.web.financialmanagement.clearsettlement` 包、老路由 `/incomeOnline`;
**2 读 + 1 导出**、无写、无 `@NoAuth`(导出老门户是 `@NoAuth`,已收紧为需登录 + `:export`)。
★ 坑 22 安全:两读判登录后写 `orgId`,导出沿用老入参 `orgId`/`sysCode`,
**不读** `plNos`/`userAreas`/`resIds`。
★ 存量 SQL 两种面貌:汇总带聚合却无 `GROUP BY`、又 select `DATE_FORMAT(check_date)`
⇒ 给时间窗口在 MySQL 8 恒 500(`only_full_group_by`,红线⑥不修),`{}` 命中空集合回全 0;
分页 `GROUP BY check_date, org_id` 正常(本机 total=121)。
★ 导出是程序化 15 列(`SXSSFWorkbook` + `Layouter` + `ExcleFillDateManager`),无模板依赖;
导出端点数 **77 → 78**。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百三十一切片:⚠B 里的 `TpPChargingPileController`(3 端点整类迁完)搬走**
⇒ **⚠B 29/105 → 28/102、已迁 178/1006 → 179/1009、未迁 33/112 → 32/109**。
★ 落在 `park` 域(`/admin-api/park/charging-pile/*`),老类在 `portal.web.chargingpile` 包、
老路由 `/chargingPile`;**1 读 + 2 写**、无导出、无 `@NoAuth`。
★ 坑 22 安全:三端点判登录后写 `orgId`/`opId`,**不读** `plNos`/`userAreas`/`resIds`。
★ 真写风险:保存是 tk `insert`(`charging_pile_no` NOT NULL)⇒ `{}` 500 不落行;
更新 `{}` 主键为空 500、`{id:999999999}` 0 行 false。
★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。
★★ **2026-10-19 第一百三十切片:⚠B 里的 `TdCCouponPersonController`(1 端点整类迁完)搬走**
⇒ **⚠B 30/106 → 29/105、已迁 177/1005 → 178/1006、未迁 34/113 → 33/112**。
★ 落在 `pay` 域(`POST /admin-api/pay/coupon-person/batch-insert-coupon-person`),
老类在 `portal.web.coupon` 包、老路由 `/couponPerson`;**1 写**、无导出、无 `@NoAuth`。
★ 坑 22 安全:判登录后写 `opId`/`orgId`,**不读** `plNos`/`userAreas`/`resIds`。
★ 真写风险:`custIds` 非空会逐条插 `td_c_coupon_person`;冒烟只发
「无 body / `{}` ⇒ 400」与「必填齐全 + `custIds` 缺省/`[]`/`[""]` ⇒ 零插入 false」。
★ 老类 1 处 `@SysOpLog` ⇒ 1 个 `@OperateLog` ⇒ 零写基线 **+221 → +224/轮**。
★★ **2026-10-19 第一百二十九切片:⚠B 里的 `OrderRechargerController`(2 端点整类迁完)搬走**
⇒ **⚠B 31/108 → 30/106、已迁 176/1003 → 177/1005、未迁 35/115 → 34/113**。
★ 落在 `order` 域(`POST /admin-api/order/order-recharger/...`),老类在
`portal.web.acctsource` 包、老路由 `/orderRecharger`;**1 读 + 1 写**、无导出、无 `@NoAuth`。
★ 坑 22 安全:读端点判登录并写 `orgId` + **字面量** `sysCode="10001"`;
写端点老代码不判登录(新栈过滤器兜住),只取 `opId`/`name`/`orgId`。
★ 存量语义照搬:读端点的 `...SMapper` 聚合 SQL `group by` 选非聚合列
⇒ 本地 MySQL 8 `only_full_group_by` **恒 500**(红线⑥不修);
写端点是**真充值**(充值单 + 支付单 + 加余额),查不到账户还会**自动建户**。
★ 冒烟只发零写入样本:无 body **400**、已存在 `custId` 缺 `acctGiveFee` **NPE 500**(落库前)。
★ 老类 1 处 `@SysOpLog` ⇒ 1 个 `@OperateLog` ⇒ 零写基线 **+220 → +221/轮**。
★★ **2026-10-19 第一百二十八切片:⚠B 里的 `StationInfoController`(1 个有效端点整类迁完)搬走**
⇒ **⚠B 32/109 → 31/108、已迁 175/1002 → 176/1003、未迁 36/116 → 35/115**。
★ 落在 `charge` 域(`POST /admin-api/charge/station-info/query-station-info-for-page`),
老类在 `portal.web.battery` 包、老路由 `/stationInfoManage`;
老类 4 个 mapping 里 save/update/bathDelete **整段被注释** ⇒ **1 读、零写**、无导出。
★ 坑 22 安全:page 无条件写登录 `orgId` + 注入 `${project.syscode}`;
**不读** `plNos`/`userAreas`/`resIds`。
★ 存量语义照搬:分页是 `station_info ⋈ operator_info` 两表内连接,
本地 `operator_info` **0 行** ⇒ 恒空页(`station_info` 3 行都不起作用)。
★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。
★★ **2026-10-19 第一百二十七切片:⚠B 里的 `EquipmentInfoController`(1 个有效端点整类迁完)搬走**
⇒ **⚠B 33/110 → 32/109、已迁 174/1001 → 175/1002、未迁 37/117 → 36/116**。
★ 落在 `charge` 域(`POST /admin-api/charge/equipment-info/query-equipment-info-for-page`),
老类在 `portal.web.battery` 包、老路由 `/equipmentInfoManage`;
老类 4 个 mapping 里 save/update/bathDelete **整段被注释** ⇒ **1 读、零写**、无导出。
★ 坑 22 安全:page 无条件写登录 `orgId` + 注入 `${project.syscode}`;
**不读** `plNos`/`userAreas`/`resIds`。
★ 存量语义照搬:分页是 `equipment_info ⋈ operator_info ⋈ station_info` 三表内连接,
本地 `operator_info` **0 行** ⇒ 恒空页(`equipment_info` 7 行、`station_info` 3 行都进不来)。
★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。
★★ **2026-10-19 第一百二十六切片:⚠B 里的 `ConnectorInfoController`(2 个有效端点整类迁完)搬走**
⇒ **⚠B 34/112 → 33/110、已迁 173/999 → 174/1001、未迁 38/119 → 37/117**。
★ 落在 `charge` 域(`POST /admin-api/charge/connector-info/*`),
老类在 `portal.web.battery` 包、老路由 `/connectorInfoManage`;
老类 5 个 mapping 里 save/update/bathDelete **整段被注释** ⇒ **1 读 + 1 写**、无导出。
★ 坑 22 安全:page 无条件写登录 `orgId` + 注入 `${project.syscode}`,
generator 写 `sysCode`/`orgId`;**不读** `plNos`/`userAreas`/`resIds`。
★ 存量语义照搬:分页是四表内连接,本地 `operator_info` **0 行** ⇒ 恒空页;
`queryConnectorInfoByPrimaryKey` 查不到 ⇒ 服务层 `copyProperties(null)` 500,
「设备id不存在」分支是死代码;本地 7 行都有 `qr_code_url` ⇒
`generator {id:1}` 停在 1002「太阳码已经存在」,不调微信、不写库。
★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。
★★ **2026-10-19 第一百二十五切片:⚠B 里的 `OperatorInfoController`(2 个有效端点整类迁完)搬走**
⇒ **⚠B 35/114 → 34/112、已迁 172/997 → 173/999、未迁 39/121 → 38/119**。
★ 落在 `charge` 域(`POST /admin-api/charge/operator-info/*`),
老类在 `portal.web.battery` 包、老路由 `/operatorInfoManage`;
老类 4 个 mapping 里 save/bathDelete **整段被注释** ⇒ **1 读 + 1 写**、无导出。
★ 坑 22 安全:page 无条件写登录 `orgId` + 注入 `${project.syscode}`,
update 写 `sOpId`/`sOpName`/`modifyEmpId`/`modifyEmpName`/`modifyTime`/`sysCode`;
**不读** `plNos`/`userAreas`/`resIds`。
★ 存量语义照搬:`PageBeanUtil` 只在 list 非空时 copy,本地 `operator_info` **0 行**
⇒ page 恒空页;update 走 selective,不存在的 id 命中 0 行回 `false`;
`sOpId`/`sOpName` 域对象没有对应字段 ⇒ 拷不过去。
★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。
★★ **2026-10-19 第一百二十四切片:⚠B 里的 `ChargeRecordController`(2 端点整类迁完)搬走**
⇒ **⚠B 36/116 → 35/114、已迁 171/995 → 172/997、未迁 40/123 → 39/121**。
★ 落在 `order` 域(`POST /admin-api/order/charge-record/*`),
老类在 `financialmanagement/chargerecord` 包、老路由 `/ChargeRecord`,类级 `@NoAuth`
⇒ 按 §3.3 第 1 类收紧为登录 + `order:charge-record:query`。
**2 端点 = 2 读、零写、无导出**,整类 2/2 迁完。
★ 坑 22 安全:page 判 `userInfo`/`orgId` 并有超管豁免(非超管强制覆盖 orgId);
pay-detail **完全不读登录态**(照搬);只碰 `orgId`。
★ 存量语义照搬:日期只收 `yyyy-MM-dd HH:mm:ss` / 毫秒时间戳,纯 `yyyy-MM-dd` 是 400;
`parkOutTime=null` ⇒ hutool `beginOfDay` NPE 500;`orderState` 拆箱 ⇒ `{}` 500;
`orderId` 为 null ⇒ tk mapper `Value for rltOrderId cannot be null` 500。
★ 本地 org 10003 `2017-09-25` 真读 **41 行**(首行 id=2048609 / state=50)、
`2017-09-28` **1 行**、9/2 归一末页首行 2048479;`orgId=10007` 被强制回 10003 仍 41 行。
★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。
★★ **2026-10-19 第一百二十三切片:⚠B 里的 `TdCCommonProblemController`(4 端点整类迁完)搬走**
⇒ **⚠B 37/120 → 36/116、已迁 170/991 → 171/995、未迁 41/127 → 40/123**。
★ 落在 `pay` 域(`POST /admin-api/pay/common-problem/*`),
老类在 `portal.web.commonproblem` 包、老路由 `/tdCCommonProblemManage`;
**1 读 + 3 写**,无导出,整类 4/4 迁完。
★ 老类 `@Controller` + 每方法 `@ResponseBody`、`extends BizController` ⇒ 新栈
`@RestController` + `CommonResult.success` + `BizResultAdapter`。
★ 坑 22 安全:4 个端点**全部**判登录;page/save 写 `orgId`、save 写
`sOpId`/`createEmpId`/`createEmpName`、update 写 `sOpId`/`modifyEmpId`/`modifyEmpName`、
batchDelete 写 `opId`/`opName`;**不读** `plNos`/`userAreas`/`resIds`。
★ 存量语义照搬:**分页服务层 `PageInfo` 整段被注释 ⇒ page 恒空页**(本地 13 行活数据也不读);
save 无校验、任何 body(含 `{}`)都真插一行;`update {}` ⇒ 1003「id为空!」、
`update id=不存在` 空 SET 被 Druid 拒 500;bathDelete 是软删(`data_state=0`)、
`{}`/`ids=[]` ⇒ 1003、不存在 id 回 false。
★ `TdCCommonProblemRequest` 无 `question`/`answer` 字段、`createEmpId` 与域对象
`createEmpid` 大小写不同 ⇒ 拷不过去,save 实际只落 `org_id`+`create_date`。
★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。
★★ **2026-10-19 第一百二十二切片:⚠B 里的 `TdCouponRuleController`(5 端点整类迁完)搬走**
⇒ **⚠B 38/125 → 37/120、已迁 169/986 → 170/991、未迁 42/132 → 41/127**。
★ 落在 `pay` 域(`POST /admin-api/pay/coupon-rule/*` + `GET /export-excel`),
老类在 `pay.controller.coupon` 包、老路由 `/tdCoupon`;**1 读 + 3 写 + 1 导出**,整类 5/5 迁完。
★ 老类 `@Controller` + 每方法 `@ResponseBody`、`extends BizController` ⇒ 新栈
`@RestController` + `CommonResult.success` + `BizResultAdapter`。
★ 坑 22 安全:save/query 读 `orgId`/`opId`/`name`,delete/deleteBatch **完全不读登录态**;
**不读** `plNos`/`userAreas`/`resIds`。
★ 存量语义照搬:save 由 `flg` 分支(`Integer` 拆箱 ⇒ `flg=null` NPE 500),
服务层另按 `cardType==2` 分叉;delete 只在 `id != null` 时软删 + 按 `card_no` 删关系表;
deleteBatch `andIdIn(ids)` 空 ⇒ tk mapper 500;导出 `BaseInfo(1, MAX)` 一次取全量,
`couponsType`/`isUesed` 拆箱、时间列判空缺失 ⇒ 存量缺陷。
★ 本地 org 10003 真读 **46 行**(首行 id=10169),`pageNum=9&pageSize=2`
被 reasonable 归一成末页 1 行(id=10132)。
★ 导出老 `@NoAuth` → 登录 + `:export`,10 列、参数全可选,真出 20KB+ xlsx。
★ 老类 3 个写端点带 `@SysOpLog` ⇒ 3 个 `@OperateLog`
⇒ 零写基线 **+214 → +220/轮**(冒烟发不落库样本,只多记 3 条日志)。
★★ **2026-10-19 第一百二十一切片:⚠B 里的 `ParkBerthAppointmentConfigRelController`(3 端点整类迁完)搬走**
⇒ **⚠B 39/128 → 38/125、已迁 168/983 → 169/986、未迁 43/135 → 42/132**。
★ 落在 `park` 域(`POST /admin-api/park/park-berth-appointment-config-rel/*`),
老类在 `parkberthappointmentconfig` 包、老路由 `/parkBerthAppointmentConfigRle`
(★ 存量拼写 `Rle`);**1 读 + 2 写**,整类 3/3 迁完。
★ 老类 `@Controller` + 每方法 `@ResponseBody`、`extends BizController` ⇒ 新栈
`@RestController` + `CommonResult.success` + `BizResultAdapter`。
★ 坑 22 安全:3 个端点**全部**判登录;page 写 `orgId`、save 写 `sOpId`/`createEmpId`/
`createEmpName`、batchDelete 什么都不写;**不读** `plNos`/`userAreas`/`resIds`。
★ 存量语义照搬:page 内联 `park_berth_appointment_config`、两边 `data_state=1`、
按 `b.org_id` 过滤、`order by a.id desc`;本地 org 10003 真读 **3 行**(首行 id=7),
plName 由 Dubbo 回填;不传 `baseRequest` 不炸(默认 10 行);
`pageNum=9&pageSize=2` 被 reasonable **归一成末页 1 行(id=2)**;
`appointmentConfigName` 的 like 过滤「医院」命中 2 行;
⛔ save **先按 `pl_noList` 物理删再逐条插**(空列表不删不插回 false);
⛔ batchDelete 物理删、`ids` 空 ⇒ 1003「id为空!」。
★ ⛔ 两个写端点:save 传真 `plNoList` 会真删真插、batchDelete 会真删,
冒烟只发无 body(绑定层 400)+ `plNoList=[]`;本地 3 行一行不动。
★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+214/轮)。
★★ **2026-10-19 第一百二十切片:⚠B 里的 `EtcSettlementController`(4 端点整类迁完)搬走**
⇒ **⚠B 40/132 → 39/128、已迁 167/979 → 168/983、未迁 44/139 → 43/135**。
★ 落在 `order` 域(`POST /admin-api/order/etc-settlement/*` + `GET /export-excel`),
老类在 `financialmanagement/clearsettlement` 包、老路由 `/EtcSettlement`;
**2 读 + 1 分页 + 1 导出**,整类 4/4 迁完。
★ 老类 `extends BizController` ⇒ 只借道 `BizResultVO`/`returnJqGridData`,
新栈改 `CommonResult.success` + `BizResultAdapter.getDataOrThrow/toPageResult`。
★ 坑 22 安全:3 个 POST 判 `userInfo == null`、写 `orgIds` = 登录 `orgId`、
`opId` 只用于 `isSuperOpId` 超管判定;导出**不读登录态**(老方法里的登录判定
整段被注释),读**请求参数** `optId`/`orgId`;**不读** `plNos`/`userAreas`/`resIds`。
★ 存量语义照搬:**`queryMchInfoList` 完全忽略入参**(服务层全表查 `td_b_etc_config`
+ 回填 appId/mchName),本地 0 行 ⇒ `code=0 + []`;`appIds` 的补齐在**服务层**
(空则按 `orgIds + payType=32` 查 `sys_org_app_id`,查不到 ⇒ `BIZ_ERROR "appId为空!"`
⇒ 500/1002);**`baseRequest` 不会 NPE**(字段初始化 + 懒 getter + `reasonable=true`)
⇒ 不传 `baseRequest` 实测 total 500 / 默认 10 行;导出 `getCheckResultType()==1`、
`getSettlementState()==0` 是 `Integer` 拆箱,`DateUtil.format` 不判空;
`isSuperOpId` 对 null map `forEach` ⇒ NPE。照搬。
★ 本地 org 10003 绑 ETC 商户 1504005 ⇒ `td_b_etc_pay_order_check_result` 500 行
(2020 年)是真读:2020 窗口 `totalFee=217134、totalCount=500、已结算 494、未结算 6`。
★ 导出老 `@NoAuth` → 登录 + `:export`,5 必填 / `appId` 可选;先查一次拿 pages
再把 pageSize 改成 **1000** 逐页取,18 列、起始行 `pageSize*(num-1)+2`。
★ ⛔ 全类只读/导出,无写端点;老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变。
★★ **2026-10-19 第一百一十九切片:⚠B 里的 `EtcReconciliationController`(5 端点整类迁完)搬走**
⇒ **⚠B 41/137 → 40/132、已迁 166/974 → 167/979、未迁 45/144 → 44/139**。
★ 落在 `order` 域(`POST /admin-api/order/etc-reconciliation/*` + `GET /export-excel`),
老类在 `financialmanagement/reconciliation` 包、老路由 `/etc/reconciliation`;
**2 对账读 + 2 异常流水动作 + 1 导出**,整类 5/5 迁完。
★ 老类 `extends ReconciliationBaseController`(基类只有一个 `getPayOrderTypeName`、
类上无 mapping)⇒ 新类**整段照搬**那一个方法,不继承、不抽公共类。
★ 坑 22 安全:total/page 读 `orgId`(进 `orgIds`)+ `opId`(只用于超管判定);
feedBack 读 `opId`/`name`(**不判空**);异常流水查询**完全不读登录态**;
导出读**请求参数** `optId`/`orgId`;**不读** `plNos`/`userAreas`/`resIds`。
★ 存量语义照搬:`dateType=null` 时 page 控制器拆箱 NPE、total 服务层 `.equals(2)` NPE,
都是 **500**;page **不传 `baseRequest` 不炸**(服务层 `PageBeanUtil.copyProperties`
补默认 BaseInfo ⇒ total 500 / 默认 10 行);异常流水 `{}` 500、假 appId 4014,
均停在外部调用/回写之前;导出老 `@NoAuth` → 登录 + `:export`,6 个必填 `@RequestParam`。
★ 本地 org 10003 绑 ETC 商户 1504005 ⇒ `td_b_etc_pay_order_check_result` 500 行
(2020 年)是真读:日口径汇总 217134、8 月月口径 35680/124 行。
★ ⛔ 两个异常流水端点**可能落库**(回写 `check_state` / 改平账),
冒烟只发炸关口样本;老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变。
★★ **2026-10-19 第一百一十八切片:⚠B 里的 `OrderChargeInfoController`(3 端点整类迁完)搬走**
⇒ **⚠B 42/140 → 41/137、已迁 165/971 → 166/974、未迁 46/147 → 45/144**。
★ 落在 `order` 域(`POST /admin-api/order/order-charge-info/*`),老类在 `battery` 包、
老路由 `/orderChargeInfoManage`;**2 读 + 1 退款动作**,整类 3/3 迁完。
★ 类级去 `Manage` 后 kebab;方法段按老 mapping 逐字 kebab。
★ 坑 22 安全:3 个端点**全部**判 `userInfo == null`;page 写 `orgId`(★ DAO 的
smapper **确实用它过滤**)与写死 `sysCode=10001`;退款/日志查询写 `sOpId`/`sOpName`
(日志查询其实没用这两列,照搬);**不读** `plNos`/`userAreas`/`resIds`。
★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+214/轮 不变**。
★ 存量语义照搬:page 固定 `data_state=1 and order_state > 30` + 登录人 `orgId`
⇒ 本地 org 10003 **0 行**(有数据时还会经 Dubbo 查充电站位置,走不到);
`platformManualRefund` 先 `queryByOrderId(operatorId, orderId)` ⇒ `{}` 因 null
条件在 tk-mapper 直接抛异常 **500**,给不存在编号 ⇒ **5010 没有找到充电订单**,
两条都在任何退款/外部调用之前;`queryOperatorLog` 的 DAO 直接
`andOrderIdEqualTo(null)` ⇒ `{}` **500「系统异常」**,条件齐了回 `[]`。
★ ⛔ `platformManualRefund` 带**真实已支付 orderId + operatorId** 会真发起退款并调
「停止充电」外部接口 ⇒ 冒烟只发 `{}`/不存在编号/无 body,业务表行数已实测不变。
★ 该三张表(`order_charge_info`/`order_charge_log`/`order_charge_operate_log`)已并入
零写计数脚本(`/tmp/zwcount66.py`)。
★★ **2026-10-19 第一百一十七切片:⚠B 里的 `InvoiceOperController`(5 端点整类迁完)搬走**
⇒ **⚠B 43/145 → 42/140、已迁 164/966 → 165/971、未迁 47/152 → 46/147**。
★ 落在 `pay` 域(`POST /admin-api/pay/invoice-oper/*`),老类在 `invoice` 包、
老路由 `/invoice/oper`;**1 改状态 + 3 开票/下载动作 + 1 个 `@NoAuth` 测试端点**,
整类 5/5 迁完。
★ 坑 22 安全:`updateInvoiceStateById` **不判空**直接取 `opId`/`name`(照搬);
`openInvoiceRecord` **完全不读登录态**;另两条写 `createEmpid`/`modifiyEmpid`
(`openInvoiceListByOrderIds` 另写 `orgId`,`downTheInvoice` **不写**);
**不读** `plNos`/`userAreas`/`resIds`。
★ 老类 3 处 `@SysOpLog` ⇒ 新类 3 处 `@OperateLog` ⇒ 零写基线 **+209 → +214/轮**。
★ 存量语义照搬:`updateInvoiceStateById` 的 `@Valid`(id/invoiceState)挡成 400;
给不存在 id ⇒ 0 行但**无条件回 true**;`openRecord {}` 在 `saleTaxid.substring`
处 NPE 500;`openList` 先判 orderIds(1003)再判 orgId,订单不存在 1002;
`downInvoice` **不设 orgId** ⇒ 销方查询 null ⇒ 1002。
★ `openInvoiceRecordTest` 老 `@NoAuth` 按 §3.3 收紧,且服务层会调**外部金税
WebService** ⇒ ⛔ 冒烟只发无 body。
★ ⛔ `openRecord` 带 `saleTaxid` 会真插历史+条目、`openList`/`downInvoice` 会调
外部发票服务并改状态 ⇒ 冒烟只发炸关口样本,业务表行数已实测不变。
★★ **2026-10-19 第一百一十六切片:⚠B 里的 `ParkingChargingRulesController`(5 端点整类迁完)搬走**
⇒ **⚠B 44/150 → 43/145、已迁 163/961 → 164/966、未迁 48/157 → 47/152**。
★ 落在 `bill` 域(`POST /admin-api/bill/parking-charging-rules/*`),老类在
`parkinglotcloudplatform/closecarpark` 包、老路由 `/parkingChargingRules`;
**1 读 + 4 写**,整类 5/5 迁完。
★ 类级与方法段均按老名字 kebab 逐字搬。
★ 坑 22 安全:query/save/update **判** `userInfo == null`;delete/view
**老代码根本不取登录态**(照搬,不补校验);只碰 `orgId`/`createBy`,
**不读** `plNos`/`userAreas`/`resIds`。
★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。
★ 存量语义照搬:**分页参数在 `baseRequest` 里**(直接传 pageNum/pageSize 被忽略,
默认 0/10);page 按 `orgId`+`data_flg='1'`+`fee_type=1` 过滤(本地 8 行);
`save` **先插主表再遍历 detailList** ⇒ `{}` 会先落一行再 NPE ⇒ ⛔ 绝不发;
`update` 的 `andRuleCollectIdEqualTo(null)` 直接抛异常 ⇒ **500,异常在写之前**;
`delete` 先 `checkBindPark`(空 ruleCollectId ⇒ 500),不存在 id ⇒ 0 行返回 0;
`view` 对结果 `.get(0)` ⇒ 查不到 **IndexOutOfBounds 500**。
★ ⛔ `save`/`update` 带 detailList 会真写 `td_cl_rule_collect`/`td_cl_rule`/
`td_cl_rule_detail` ⇒ 冒烟只发无 body 与炸关口样本,三表行数已实测不变。
★★ **2026-10-19 第一百一十五切片:⚠B 里的 `SpecialCarnumberBillingController`(5 端点整类迁完)搬走**
⇒ **⚠B 45/155 → 44/150、已迁 162/956 → 163/961、未迁 49/162 → 48/157**。
★ 落在 `pay` 域(`POST /admin-api/pay/special-carnumber-billing/*`),老类在 `specialcar` 包、
老路由 `/specialCarnumberBillingManage`;**1 读 + 4 写**,整类 5/5 迁完。
★ 类级去 `Manage` 后 kebab;方法段按老 mapping 逐字 kebab(`bath-delete` 保留拼写)。
★ 坑 22 安全:5 个端点都判 `userInfo == null`;page 只写 `orgId`(★ DAO 没用它),
save 另写 `createEmpId`/`createEmpName`,update 写 `modifyEmpId`/`modifyEmpName`,
batchDelete/del 写 `opId`/`opName`;**不读** `plNos`/`userAreas`/`resIds`。
★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。
★ 存量语义照搬:**page 不按组织过滤**(本地 220 行活数据全可见,默认 0/10 ⇒
`page {}` = total 220 / 10 条);`save` 手写判手机/车牌非空再查重;
`update`/`del` 先 `queryByPrimaryKey().get(0)` ⇒ 不存在 id **IndexOutOfBounds 500**
(异常在写之前);`del` 还会关闭 `acct_type=5` 的免密;`bathDelete` 逻辑删,
传 `getsOpName()` 的存量错位照搬。
★ ⛔ `save {phoneNum,carNumber}` 会真插主表并可能建客户/账户/车牌
⇒ 冒烟只发 `{}`(判空 1003)与无 body。
★★ **2026-10-19 第一百一十四切片:⚠B 里的 `TdCCustBusinessController`(5 端点整类迁完)搬走**
⇒ **⚠B 46/160 → 45/155、已迁 161/951 → 162/956、未迁 50/167 → 49/162**。
★ 落在 `pay` 域(`POST /admin-api/pay/cust-business/*`),老类在 `custperson` 包、
老路由 `/tdCCustBusinessManage`;**1 读 + 3 写 + 1 审核动作**,整类 5/5 迁完。
★ 类级与方法段都去 `TdC` 前缀后按 kebab 逐字搬;`examine` 审核动作复用 `:update` 权限。
★ 坑 22 安全:5 个端点都判 `userInfo == null`;page 只写 `orgId`,
save/update 另写 `opId`/`name`,batchDelete 写 `opId`/`opName`,examine 写
`createEmpid`/`createEmpname`/`createDate`/`examineDate`;**不读**
`plNos`/`userAreas`/`resIds`。
★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。
★ 存量语义照搬:本地三表全 **0 行** ⇒ page 恒 `total=0 / list=[]`;
`update {}` / `bathDelete {}` ⇒ 控制器 `PARAMM_NULL`;域对象无 `sOpId`
⇒ `update {id}` 空 SET 被 Druid 拒 ⇒ **500 且不改行**;`examine {}` 在
`getExamineState().equals(2)` 处 **NPE 500**(DAO 之前,不落库);
`bathDelete` 是逻辑删;`batchDelete` 传 `getsOpName()` 的存量错位照搬。
★ ⛔ `save {}` 会真插一行、⛔ `examine {examineState}` 会改主表 + 插审核记录
⇒ 冒烟只发无 body 与 `examine {}`(NPE)。
★★ **2026-10-19 第一百一十三切片:⚠B 里的 `TdCCustBusinessCheckTypeController`(4 端点整类迁完)搬走**
⇒ **⚠B 47/164 → 46/160、已迁 160/947 → 161/951、未迁 51/171 → 50/167**。
★ 落在 `pay` 域(`POST /admin-api/pay/cust-business-check-type/*`),
老类在 `custperson` 包、老路由 `/tdCCustBusinessCheckTypeManage`;
**1 读 + 3 写**,**整类 4/4 全部迁完,无 P3 尾巴**。
★ 类级与方法段都去 `TdC` 前缀后按 kebab 逐字搬(沿同包
`TdCCustBusinessCheckRecordController` → `/pay/cust-business-check-record` 的风格)。
★ 坑 22 安全:4 个端点都判 `userInfo == null`;page 只写 `orgId`,
save/update 另写 `opId`/`name`,batchDelete 写 `opId`/`opName`;**不读**
`plNos`/`userAreas`/`resIds`。
★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。
★ 存量语义照搬:**page 服务层整段被注释 ⇒ 恒 `total=0 / list=[]`**(DAO 也没有分页方法);
`queryTdCCustBusinessCheckType` 的 DAO 死方法 `return null`(老门户没调用);
Request 的 `createEmpId`/`modifyEmpId` 与域对象 `createEmpid`/`modfiyEmpid`
拼写不同 ⇒ `update {id}` 空 SET 被 Druid 拒 ⇒ **500「系统异常」且不改行**;
`bathDelete` 是**逻辑删除**(`data_state=0`,`where id in (?)`);`batchDelete`
传 `getsOpName()` 的存量错位照搬。
★ 本地 `td_c_cust_business_check_type` **0 行** ⇒ 读断言钉 `total=0 / []`;
⛔ `save {}` 会真插一行(DAO 补 `createDate`),冒烟只发无 body(绑定层 400)。
★★ **2026-10-19 第一百一十二切片:⚠B 里的 `ParkBerthAppointmentConfigController`(5 端点整类迁完)搬走**
⇒ **⚠B 48/169 → 47/164、已迁 159/942 → 160/947、未迁 52/176 → 51/171**。
★ 落在 `park` 域(`POST /admin-api/park/park-berth-appointment-config/*`),
老类在 `parkberthappointmentconfig` 包、老路由 `/parkBerthAppointmentConfigManage`;
**2 读 + 3 写**,**整类 5/5 全部迁完,无 P3 尾巴**。
★ 方法段按老 mapping 逐字 kebab(`/bath-delete-…` 保留存量拼写);类级按类名 kebab。
★ 坑 22 安全:5 个端点都判 `userInfo == null`;page/list 只写 `orgId`,
save/update 另写 `opId`/`name`,batchDelete 什么都不写;**不读**
`plNos`/`userAreas`/`resIds`。
★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。
★ 存量语义照搬:**page** 过滤 `data_state=1` + `org_id`;**list** 额外叠
`rule_state=1`;`update {}` / `bathDelete {}` ⇒ 控制器 `PARAMM_NULL`;
⛔ **`bathDelete` 是物理删除**(先删主表、再删 `park_berth_appointment_park_rel`,
影响行数相加),不是逻辑删。
★ ★ 本地两表都有活数据:`park_berth_appointment_config` **2 行**(id=2、3 / org 10003)、
`park_berth_appointment_park_rel` **3 行** ⇒ page/list 是**活读**,冒烟按真实数据钉
`total=2 / 2 条`;⛔ 绝不发真实 id、save 只发无 body。实测两表前后不变(2→2、3→3)。
★★ **2026-10-19 第一百一十一切片:⚠B 里的 `ArrearageSmsAutoSendConfigController`(5 端点整类迁完)搬走**
⇒ **⚠B 49/174 → 48/169、已迁 158/937 → 159/942、未迁 53/181 → 52/176**。
★ 落在 `order` 域(`POST /admin-api/order/arrearage-sms-auto-send-config/*`),
老类在 `arrautosendconfig` 包、老路由 `/arrearageSmsAutoSendConfigManage`;**2 读 + 3 写**,
**整类 5/5 全部迁完,无 P3 尾巴**。
★ 方法段按老 mapping 逐字 kebab(`/bath-delete-…` 保留存量拼写);类级按类名 kebab。
★ 坑 22 安全:5 个端点都判 `userInfo == null`,但只写 `orgId`/`opId`/`name`,
**不读** `plNos`/`userAreas`/`resIds`。
★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。
★ 存量语义照搬:`queryArrearageSmsAutoSendConfig` 只取 list `get(0)`;
`handelCronStr` 在判空之前执行(空 ⇒ "";不足 6 段 ⇒
`BIZ_ERROR「cron 表达式错误!」` ⇒ code 500);老 Request 的 `modifyEmpId` 与域对象
`modfiyEmpId` **拼写不同**、`BeanUtils.copyProperties` 拷不过去 ⇒ selective update
的 SET 只剩 `cron_str`;`bathDelete` 是逻辑删除(`data_state=0`)。
★ ★ 与最近几片不同:本地 `arrearage_sms_auto_send_config` **有 1 行活数据**
(id=1 / org 10003 / data_state=1,id=2 已删)⇒ page/query 是**活读**,
冒烟按真实数据钉 `total=1`、`id=1`;⛔ 别给 ids 传 1、别给 save 传 `{}`。
★ 实测冒烟前后该表 **2 → 2** 未变,零写差分只动 `system_operate_log`。
★★ **2026-10-19 第一百一十切片:⚠B 里的 `TdCCustBusinessCheckRecordController`(5 端点整类迁完)搬走**
⇒ **⚠B 50/179 → 49/174、已迁 157/932 → 158/937、未迁 54/186 → 53/181**。
★ 落在 `pay` 域(`POST /admin-api/pay/cust-business-check-record/*`),
老类在 `custperson` 包、老路由 `/tdCCustBusinessCheckRecordManage`;**2 读 + 3 写**,
**整类 5/5 全部迁完,无 P3 尾巴**。
★ 类级与方法段**都把 `TdC` 前缀去掉后按 kebab 逐字搬**(沿
`TdCCustSuggestDetailController` / `TdCCustBusinessPicController` 的既有风格)。
★ 坑 22 安全:`query-cust-business-check-record` **完全不读登录态**;其余 4 个端点
只写 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。
★ 老类 **没有 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。
★ 存量语义照搬:**page 服务层整段被注释** ⇒ 恒空页;list 的
`andBusinessIdEqualTo(null)` 抛 ⇒ **500**;`save {}` / `update id=1` 的 selective
SET 为空被 Druid 拒(`expect SET, actual WHERE`)⇒ **500 且不落库**;
`update {}` / `bathDelete {}` ⇒ 控制器 `PARAMM_NULL`;`bathDelete ids=[999999999]`
命中 0 行 ⇒ false(逻辑删 `data_state=0`)。
★ ⛔ `save` 仍只发无 body(绑定层 400);本地 `td_c_cust_business_check_record` 0 行,
实测前后不变。
★★ **2026-10-19 第一百零九切片:⚠B 里的 `SmsTemplateConfigRltParkingController`(5 端点整类迁完)搬走**
⇒ **⚠B 51/184 → 50/179、已迁 156/927 → 157/932、未迁 55/191 → 54/186**。
★ 落在 `park` 域(`POST /admin-api/park/sms-template-config-rlt-parking/*`),
老类在 `msg` 包、老路由 `/smsTemplateConfigRltParkingManage`;**2 读 + 3 写**,
**整类 5/5 全部迁完,无 P3 尾巴**。
★ 方法段按老 mapping 逐字 kebab(`/query-…-for-page`、`/query-…-for-list`、
`/save-…`、`/update-…-by-id`、`/bath-delete-…`);类级按类名 kebab。
★ 坑 22 安全:5 个端点**全部**读登录态判空,但只碰 `orgId`/`opId`/`name`,
**不读** `plNos`/`userAreas`/`resIds`;page/list 的 `orgId` **不判空直接覆盖**。
★ 老类 **没有 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。
★ 存量语义照搬:**page 服务层整段被注释** ⇒ 恒空页(`total=0 / list=[]`);
list 按登录 org=10003 查,本地 46 行全 `org_id=NULL` ⇒ 恒 `[]`;
`save` 缺 smsId / `update` 缺 id / `bathDelete` 缺 ids ⇒ 控制器抛 `PARAMM_NULL`
(HTTP 200 + `code=500` + 存量错误码 1003);
`update id=1` / `bathDelete ids=[1]` 因 DAO 是 TODO 空实现 ⇒ 恒 `false`。
★ ⛔ `save` 给**已存在的 smsId** 会先 `deleteByExample` **真删**该 smsId 下所有关联、
再按 `templateConfigRltParkList` 插新 ⇒ 冒烟只发无 body / `{}` / 空 smsId,
`sms_template_config_rlt_parking` 行数实测不变(46 → 46)。
★★ **2026-10-18 第一百零八切片:⚠B 里的 `CardRuleManageController`(6 端点整类迁完)搬走**
⇒ **⚠B 52/190 → 51/184、已迁 155/921 → 156/927、未迁 56/197 → 55/191**。
★ 落在 `bill` 域(`POST /admin-api/bill/card-rule-manage/*`),老类在 `closecarpark`
包、老路由 `/cardRuleManage`;**2 读 + 3 写 + 1 查看**,
**整类 6/6 全部迁完,无 P3 尾巴**。
★ 方法段按老 mapping 逐字 kebab;类级按类名 kebab,沿同目录
`SpecialCarManageController` / `SubCardInfoManageController` 的风格。
★ 坑 22 安全:只读 `orgId`,**不读** `plNos`/`userAreas`/`resIds`;
`query-card-rule-list-manage` / `update` / `delete` / `view` **完全不读登录态**。
★ 老类 **没有 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。
★ 存量语义照搬:本地 `td_cl_sub_card_rule` 0 行 ⇒ page total 0 / list [];
page 按 `data_status = 1 + org_id`,list **只按 data_status = 1、不按组织**(两条口径不同);
`view {}` / `view id=1` 都查不到 ⇒ `BeanUtils.copyProperties(null, dto)` 抛
`Source must not be null` ⇒ **500**;`update {}` / `delete {}` 主键 null ⇒ 更新 0 行、
回 false(delete 是逻辑删除 `data_status=0`)。
★ ⛔ `save` 服务层无校验、给 `{}` 会真插一行 `td_cl_sub_card_rule`
⇒ 冒烟只发无 body(绑定层 400);该表行数实测不变。
★★ **2026-10-18 第一百零七切片:⚠B 里的 `CompanyStoreAccountController`(6 端点整类迁完)搬走**
⇒ **⚠B 53/196 → 52/190、已迁 154/915 → 155/921、未迁 57/203 → 56/197**。
★ 落在 `pay` 域(`POST /admin-api/pay/company-store-account/*`),老类在
`closecarpark` 包、老路由 `/companyAccount`;**3 读 + 3 写**,
**整类 6/6 全部迁完,无 P3 尾巴**。
★ 方法段按老 mapping 逐字 kebab(`/query-company-account`、`/save-company-account-info`、
`/update-company-account-info`、`/view-company-account`、`/operate-company-account`、
`/query-company-account-flow`);类级按类名 kebab。
★ 坑 22 安全:登录态只读 `orgId`/`name`,**不读** `plNos`/`userAreas`/`resIds`;
`view` / `query-company-account-flow` **完全不读登录态**。
★ 老类 **没有 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。
★ 存量语义照搬:分页 SMapper 的 SQL 以 `where 1=1` 结尾,本地 Druid
`filters=stat,wall` 直接拒(`select alway true condition not allow`)⇒ **page 恒 500**;
老 `zteits-pay` 的 `druid.filters=stat,wall` 是同一份配置 ⇒ 老栈同样 500,不是迁移回归。
`view {}` 同一条 SQL ⇒ 500;`view accCode=不存在` 查询回 null、
`BeanUtils.copyProperties(null, dto)` 抛 `Source must not be null` ⇒ 500。
`save {}` 撞 `andCustomerNoEqualTo(null)` ⇒ 500;`update {}` 因空 `parkList`
的 `record.get(0)` 越界 ⇒ 500;flow 本地 0 行 ⇒ total 0 / list []。
★ ⛔ `operate` 给 `{}` 仍会调 `operateCompanyAccountFlow` **真插一行**
`td_cl_company_acc_flow` ⇒ 冒烟只发无 body(绑定层 400);三张 `td_cl_company_acc*`
表行数实测不变。
★★ **2026-10-18 第一百零六切片:⚠B 里的 `ParkingSegmentChargingController`(6 端点整类迁完)搬走**
⇒ **⚠B 54/202 → 53/196、已迁 153/909 → 154/915、未迁 58/209 → 57/203**。
★ 落在 `bill` 域(`POST /admin-api/bill/parking-segment-charging/*`),老类在
`closecarpark` 包、老路由 `/parkingSegmentCharging`(**无前导斜杠**);
**2 读 + 3 写 + 1 查看**,**整类 6/6 全部迁完,无 P3 尾巴**。
★ 方法段按老 mapping 逐字 kebab(`/query-parking-segment-charging`、
`/query-parking-charging-rules-list`、`/save-…`、`/update-…`、`/delete-…`、`/view-…`),
沿同目录 `SpecialCarManageController` / `SubCardInfoManageController` 的风格。
★ 坑 22 安全:登录态只读 `orgId`/`name`,**不读** `plNos`/`userAreas`/`resIds`;
`delete` / `view` **完全不读登录态**。
★ 登录 `orgId` 是「非 null 才覆盖」、**没有超管分支**(`-1` 原样写入)——与其它类不同,照搬。
★ 老类 **没有 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。
★ 两条读端点口径不同:分页 SQL 只查 `fee_type = 2 and data_flg = 1`(本地 6 行);
规则列表走 `selectByExample`,只按 org + `data_flg = 1`、**不过滤 fee_type**(本地 14 行)。
★ 存量语义照搬:`update {}` / `delete {}` 均因 Example criteria
`Value for ruleCollectId cannot be null` ⇒ **500**;`view {}` 的 id 是原始类型 `long`
默认 0 ⇒ 查不到回 null;`save` 返回**最后一条详情**的插入行数,无详情恒 `false`。
★ ⛔ `save` 给 `{}` 会真插一行 `td_cl_rule_collect`(列全可空、ID 雪花生成)却仍回 false
⇒ 冒烟只发无 body(绑定层 400);三张 `td_cl_*` 表行数实测不变。
★★ **2026-10-18 第一百零五切片:⚠B 里的 `TBlackRuleListController`(6 端点整类迁完)搬走**
⇒ **⚠B 55/208 → 54/202、已迁 152/903 → 153/909、未迁 59/215 → 58/209**。
★ 落在 `bill` 域(`POST /admin-api/bill/black-rule-list/*`),老路由 `/tBlackRuleListManage`
(黑名单规则列表管理);**2 读 + 4 写**,**整类 6/6 全部迁完,无 P3 尾巴**。
★ ★ 与已迁的 `BlackListRuleController`(`/bill/black-list-rule`,表 `t_black_list_rule`)
**不是同一个类**:本类操作 `t_black_rule_list`,路由/权限前缀都不同,别混。
★ CRUD 段沿同模块 `TVipAreaCardController` 的语义命名(`/list`、`/page`、`/create`、
`/update`、`/delete`、`/delete-list`);老方法名 `bathDelete…` 的错拼只留在方法名。
★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。
两个读端点老代码直接 `getUserInfo().getOrgId()`、**不判空**(无 token 先被过滤器拦);
save 判 `userInfo == null` 后写 `sOpId`/`sOpName`/`orgId`;其余三个只判 `userInfo == null`。
★ 老类 **4 处 `@SysOpLog`** ⇒ 4 个 `@OperateLog`(create/update/delete/delete-list);
冒烟每轮有 3 条带 `{}` 的写请求进控制器 ⇒ 零写基线 **+206 → +209/轮**。
★ 存量语义照搬:SMapper 真 SQL 是 `data_state = 1 and org_id = #{orgId}`(可加 carNum/
ruleType/ruleState/carNums),本地 org=10003 有 **3 行**;`update {}` 动态 SQL 无 SET 段
⇒ **500**;`delete-list {}` 因 `andIdIn(null)` ⇒ **500**;`delete {}` 主键 null ⇒ 更新 0 行、
回 false;两个删除端点写 `setsOpId`、服务读 `getOpId()` ⇒ 操作人 ID 恒 null(照搬)。
★ ⛔ `create` 服务层只强制 `auditorStatus=2`/`ruleState=1`、`ruleNo` 由 SequenceUtil 生成,
给合法 body 会真插一行 ⇒ 冒烟只发无 body(绑定层 400)。
★★ **2026-10-18 第一百零四切片:⚠B 里的 `TdBPayAppController`(6 端点整类迁完)搬走**
⇒ **⚠B 56/214 → 55/208、已迁 151/897 → 152/903、未迁 60/221 → 59/215**。
★ 落在 `order` 域(`POST /admin-api/order/pay-app/*`),老路由 `/tdBPayAppManage`
(支付APP应用配置管理);**2 读 + 4 写**,**整类 6/6 全部迁完,无 P3 尾巴**。
★ 路由风格照同模块同族 `TdBPayProductController`(`/order/pay-product`):
方法段逐字 kebab(`/query-td-b-pay-app-list` 等),不改成语义 CRUD 段。
★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。
`queryTdBPayAppList` **完全不读登录态**;page/save 判 `userInfo == null || orgId == null`,
非超管(`orgId != -1`)才覆盖 `orgId`;其余三个写端点只判 `userInfo == null`。
★ 老类 **4 处 `@SysOpLog`** ⇒ 4 个 `@OperateLog`(save/update/delete/bathDelete);
冒烟每轮有 3 条带 `{}` 的写请求进控制器 ⇒ 零写基线 **+203 → +206/轮**。
★ 存量语义照搬:列表 DAO 是 **TODO 空实现、恒 return null** ⇒ 端点**永远 `[]`**;
分页按登录 `orgId` + `data_state=1` 过滤(本地样本那行属 10079)⇒ 0 行;
`update {}` / `delete {}` 主键 null ⇒ 更新 0 行、回 `code=0 + data=false`;
`bath {}` 因 `andPayAppIdIn(null)` 抛 RuntimeException ⇒ **500**(老栈同);
两个删除端点写 `setsOpId`、服务读 `getOpId()` ⇒ 操作人 ID 恒 null;
DAO 的 `save` 第二处判空仍判 `createTime`(本意 `modifyTime`)——全部照搬。
★ ⛔ `save` 服务层无校验,给 body 会真插一行 `td_b_pay_app` ⇒ 冒烟只发无 body(绑定层 400)。
★★ **2026-10-18 第一百零三切片:⚠B 里的 `TdBPayChannelController`(6 端点整类迁完)搬走**
⇒ **⚠B 57/220 → 56/214、已迁 150/891 → 151/897、未迁 61/227 → 60/221**。
★ 落在 `order` 域(`POST /admin-api/order/pay-channel/*`),老路由 `/tdBPayChannelManage`
(支付渠道管理);**2 读 + 4 写**,**整类 6/6 全部迁完,无 P3 尾巴**。
★ 路由风格照同模块同族 `TdBPayProductController`(`/order/pay-product`):
方法段逐字 kebab(`/query-td-b-pay-channel-list` 等),不改成语义 CRUD 段。
★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。
`queryTdBPayChannelList` **完全不读登录态**;page/save 判 `userInfo == null || orgId == null`,
非超管(`orgId != -1`)才覆盖 `orgId`;其余三个写端点只判 `userInfo == null`。
★ 老类 **4 处 `@SysOpLog`** ⇒ 4 个 `@OperateLog`(save/update/delete/bathDelete);
冒烟每轮有 3 条带 `{}` 的写请求进控制器 ⇒ 零写基线 **+200 → +203/轮**。
★ 存量语义照搬:列表 DAO 是 **TODO 空实现、恒 return null** ⇒ 端点**永远 `[]`**;
分页只按登录 `orgId` 过滤;`update {}` 动态 SQL 无 SET 段 ⇒ **500**;
`bath {}` 的 `andChannelIdIn(null)` ⇒ **500**;`delete {}` 更新 0 行;
两个删除端点写 `setsOpId`、服务读 `getOpId()` ⇒ 操作人恒 null。
★ ⛔ 写样本红线:`saveTdBPayChannel` 服务层**无任何校验**、`{}` 会真插一行
`td_b_pay_channel` ⇒ 冒烟只发无 body(绑定层 400);该表已纳入零写计数表做守卫。
★★ **2026-10-18 第一百零二切片:⚠B 里的 `TdCPoliceCarInfoController`(6 端点整类迁完)搬走**
⇒ **⚠B 58/226 → 57/220、已迁 149/885 → 150/891、未迁 62/233 → 61/227**。
★ 落在 `pay` 域(`POST /admin-api/pay/police-car-info/*`),老路由
`/tdCPoliceCarInfoManage`(公安车辆信息);**2 读 + 4 写**(含 1 条 multipart Excel
解析),**整类 6/6 全部迁完,无 P3 尾巴**。
★ 类名以老表前缀 `TdC` 开头,按本仓既有做法(`TdCActivityController` → `/pay/activity`、
`TdCCarCheckRecordController` → `/pay/car-check-record`)**去掉 `TdC`** 落
`/pay/police-car-info`;CRUD 段语义命名,非 CRUD 保留原方法 kebab(`4` 原样保留)。
★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。
★ 老类**零 `@SysOpLog`** ⇒ 新类零 `@OperateLog`,零写基线 **+200/轮 不变**。
★ 存量语义照搬:两个 `parse` 端点**不判** `userInfo == null`(有 token 不受影响);
`parsePoliceCarInfoByCarNumber` 外部失败逐条吞掉仍 `code=0`,服务层拿到图片会往
`td_c_police_car_info` **插一行**(`@Async` 副作用);`parsePoliceCarInfo4Excel`
逐行落库(循环内 N+1 存量违例)、`sysCode` 写字面量、`>200` 行 500、
列数不足 500、车牌空/手机号非数字静默跳过;`update {}` / `delete-list {}` 停在
控制器 1003。
★ ⛔ 写样本红线:`saveTdCPoliceCarInfo` 服务层**无任何校验**、`{}` 会真插一行
`td_c_police_car_info` ⇒ 冒烟只发无 body(绑定层 400);该表已纳入零写计数表做守卫。
★★ **2026-10-18 第一百零一切片:⚠B 里的 `TVipAreaCardController`(6 端点整类迁完)搬走**
⇒ **⚠B 59/232 → 58/226、已迁 148/879 → 149/885、未迁 63/239 → 62/233**。
★ 落在 `bill` 域(`POST /admin-api/bill/vip-area-card/*`),老路由 `/tVipAreaCardManage`
(区域卡管理);**3 读 + 3 写**,**整类 6/6 全部迁完,无 P3 尾巴**。
★ 类名以老表前缀 `T` 开头,按本仓既有做法(`TBillingDescriptionController` →
`/bill/billing-description`、`TBlackRuleParkRelController` → `/bill/black-rule-park-rel`)
**去掉 `T`** 落 `/bill/vip-area-card`;CRUD 段沿用语义命名
`/list`、`/page`、`/create`、`/update`、`/delete-list`。
★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`;
`queryParkListByAreaCardNo` **完全不读登录态**。
★ 老类**零 `@SysOpLog`** ⇒ 新类零 `@OperateLog`,零写基线 **+200/轮 不变**
(`update {}` / `delete-list {}` 停在控制器 1003;`create` 冒烟只发无 body)。
★ 存量语义照搬:列表走 `getDataOrThrow`、分页走 `toPageResult`;
`updateTVipAreaCardById` **先写操作人再判 id**;`bathDelete` 的 `ids` 判空在写 opId 之前;
`queryParkListByAreaCardNo` 查不到卡规则回 `code=0 + []`(不报错)、查到取第一条的
`plGroupNo` 再查组下车场;`@Valid` 保留。
★ ⛔ 写样本红线:`saveTVipAreaCard` 服务层**无任何校验**、`{}` 会**真插一行**
`t_vip_area_card` ⇒ 冒烟只发无 body(绑定层 400);`t_vip_area_card` 已纳入零写计数表做守卫。
★★ **2026-10-18 第一百切片:⚠B 里的 `BlackListController`(6 端点整类迁完)搬走**
⇒ **⚠B 60/238 → 59/232、已迁 147/873 → 148/879、未迁 64/245 → 63/239**。
★ 落在 `bill` 域(`POST /admin-api/bill/black-list/*` + `GET /export-excel`),
老路由 `/blackListMng`(黑名单车辆管理);**3 读 + 3 写**(含 1 响应流导出 + 1 multipart 导入),
**整类 6/6 全部迁完,无 P3 尾巴**。
★ 与已迁的 `/admin-api/bill/black-list-rule`(第九十四切片)前缀不同,**不冲突、不合并**。
★ 坑 22 安全:登录态只读 `orgId`/`opId`,不读 `plNos`/`userAreas`/`resIds`。
两处例外照搬:导出老代码把登录态判断整段注释掉、`orgId` 由前端下发(缺了 1003);
导入 `sOpId` 取前端 query `userId`。
★ 老类**零 `@SysOpLog`** ⇒ 新类零 `@OperateLog`,零写基线 **+200/轮 不变**
(`addArr {}`/`save {}` 在服务层前抛 500;`invalid {}` 主键 null ⇒ 0 行)。
★ 老 `@NoAuth` 导出按 §3.3 第 1 类收紧为 `bill:black-list:export` 且方法收紧为 GET;
导入挂 `bill:black-list:import`。
★ 存量语义照搬:导出**不判** `beanBizResult.getData()` ⇒ data 为 null 时 NPE 500;
缺 `orgId` 是 **500 + 1003**(POJO 绑定无必填,不是 400);`invalidBlackListCarForPage`
无前置校验、`QueryBlackCarLimitRequest` 是死变量;导入 `HttpServletRequest` 是死参数、
不校验列数(列少即 IndexOutOfBounds 500)、忽略服务返回值恒回 true。
★★ **2026-10-18 第九十九切片:⚠B 里的 `WhitelistController`(8 端点整类迁完)搬走**
⇒ **⚠B 61/246 → 60/238、已迁 146/865 → 147/873、未迁 65/253 → 64/245**。
★ 落在 `bill` 域(`POST /admin-api/bill/whitelist/*` + `GET /export-excel`),
老路由 `/whitelist`(白名单规则);**3 读 + 3 写 + 1 响应流导出 + 1 导入解析**,
**整类 8/8 全部迁完,无 P3 尾巴**。
★ 与已有的 `/admin-api/bill/whitelist-rule-car-number` 前缀不同,**不冲突、不合并**。
★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`;
`orgId` 用于分页过滤与新增组织列,`opId`/`name` 只写审计列。
★ 老类 3 处 `@SysOpLog` ⇒ 3 个 `@OperateLog`(add/update/delete,module=白名单规则);
`OperateLogAspect` 抛异常也记账 ⇒ 冒烟 5 次写请求 ⇒ 零写基线 +195 → **+200/轮**。
★ 老 `@NoAuth` 两条按 §3.3 第 1 类收紧:导出 → `bill:whitelist:export`、
multipart 导入 → `bill:whitelist:import`,**不进 `@PermitAll`**。
★ 存量语义照搬:`queryWhiteRuleById` 的 DAO 对不存在/`data_state=0` 的 id
返回**非 null 全 null DTO** ⇒ `code=0` 而不是 404;`checkWhiteUnique {}`
无必填校验直接进 SQL ⇒ 500;导出对 `result.getData()` 不判空 ⇒ NPE 500;
`delete` 的 `setModifyDate` 写在 `userInfo != null` 判断之外;导入保留死变量
`parkingLotList`、`whiteRNo` 取两次、严格 4 列校验,
四条失败分支 = 500 / `1019` / `1003` / `1020`。
★★ **2026-10-18 第九十八切片:⚠B 里的 `IncomeCalculationNewController`(迁 5/9 端点,4 个读 `plNos` 留 P3)搬走**
⇒ **⚠B 62/255 → 61/246、已迁 145/856 → 146/865、未迁 66/262 → 65/253**。
★ 落在 `order` 域(`POST /admin-api/order/income-calculation-new/*` + `GET /export-excel` +
`GET /export-calculation-excel`),老路由 `/incomeCalculation`(收益测算·新);
**3 读 + 2 响应流导出 + 4 未迁**。
★ 与第三十八切片的 `IncomeCalculationController`(老 `/income/incomeCalculation`)**同类不同文件**,
新路由按类名 kebab 落到 `income-calculation-new`,**不合并**。
★ 坑 22:4 个未迁端点都读登录态 `userInfo.getPlNos()`(`getSummationIncomeInfoforPlTypeByTime` /
`getAllIncomeCalculationDataOfAreaByTime` / `getSummationIncomeByTimeOfArea` /
`getSummationIncomeByTimeOfPl`)⇒ **P3 阻塞**;已迁 5 条只读前端 `plNos`。
★ 老类 9 个端点**一条 `@SysOpLog` 都没有** ⇒ 新类无 `@OperateLog`,**零写**。
★ 老 `@NoAuth` 两条导出按 §3.3 第 1 类收紧为 `order:income-calculation-new:export`;
一个类两条导出 ⇒ 主导出(三 sheet)占 `/export-excel`,另一条(两 sheet)落
`/export-calculation-excel`(§3.3 第 3 条)。
★ 存量缺陷照搬:缺 8 个金额参数(`dayMinAmount`…`nightMaxAmountAfter`)时
XML 的 IF 不成立 ⇒ SQL 渲染成 `t.day_1_hour_num * +`(空表达式)⇒ 500「系统异常」;
`getInitDateList` 的 `timeType`、`queryIncomeCalculationGroupByPlNo` 的 `billType`
都是 `Integer` 拆箱 ⇒ 不传即 500;`getInitDateList` 按周分支写成 end−end,
差值恒 0 ⇒ 永远只出 1 行;导出 ① 先按 `plType` 拆 `plNos`:
缓存里「存在但全 null」的车场让两个子集都空 ⇒ 服务层 1003,
**完全不在缓存**的车场让 `getParkTypePlNos` 撞 null 元素 ⇒ NPE 500;
导出 ② 整体 `try/catch(Exception)` ⇒ 失败是 **200 + 空 body**。
★ ⛔ 全类只读/导出,无写端点;`system_operate_log` 零写基线不变。
★★ **2026-10-18 第九十七切片:⚠B 里的 `ParkLotManageController`(迁 14/18 端点,3 个读 `plNos` 留 P3 + 1 个上传挂起)搬走**
⇒ **⚠B 63/273 → 62/255、已迁 144/838 → 145/856、未迁 67/280 → 66/262**。
★ 落在 `park` 域(`POST /admin-api/park/park-lot-manage/*` + `GET /export-excel`),
老路由 `/parkLotManage`(停车场管理);**5 读 + 7 写 + 1 响应流导出 + 1 导入解析 + 4 未迁**。
★ 坑 22 安全:登录态只读 `orgId`/`opId`;`plNos` 相关 3 条未迁
(`getParkLotNamesByUserId`/`refreshParkLotCache`/`getAreatreeByOpId`)⇒ **P3 阻塞**。
★ 老类 5 处 `@SysOpLog` ⇒ 新类 5 个 `@OperateLog`;`updateBlockForParkLot` 与
`deleteParkLotPic` 老类**没有** `@SysOpLog` ⇒ 新类也不带(照搬,不"补上")。
★ 老 `@NoAuth` 三条按 §3.3 收紧:响应流导出 → `:export`、JSON 读 → `:query`、
multipart 导入解析 → `:import`(无 token 401)。
★ ⏸ `uploadParkLotPic` 挂起:`fastDFSClientWrapper.uploadFile(...)` 运行期依赖
阿里云 OSS/FastDFS 后端,属文件存储抽象(与 `ParkShareController` 同因)。
★ `queryParkingLotByPkNo` / `queryParkLotByCountryAndParkName` 与 `ParkingLbsController`
(老 `monitor/ParkingLBSController` 的迁移)同义,**按本类老路由另挂一份**;
两边去重留 P6 门户对账时统一决定,双跑期不先删。
★ ⛔ 7 个 `@RequestBody` 写端点:冒烟只发无 body(绑定层 400);
本片 5 个 `@OperateLog` ⇒ `system_operate_log` **+195/轮**(原 +190 + 5)。
★★ **2026-10-18 第九十五切片:⚠B 里的 `VipCardRuleController`(迁 4/7 端点,3 个读 `plNos` 留 P3)搬走**
⇒ **⚠B 65/288 → 64/281、已迁 142/823 → 143/830、未迁 69/295 → 68/288**。
★ 落在 `bill` 域(`POST /admin-api/bill/vip-card-rule/*`),老路由 `/vipCardRule`
(会员卡规则管理);**1 读 + 3 写 + 3 未迁**。
★ 3 个未迁端点都读登录态 `plNos`:两个分页在请求体 `plNos` 为空时回填、
`vipCardSelectParkTree` 直接用 `plNos` 当树根 ⇒ **P3 阻塞**;
两个分页还带 `queryParkName` 回填(缓存优先 + 循环 N+1)。
★ 写端点只写 `opId`/`name`/`orgId`,**不读** `plNos`/`userAreas`/`resIds`
⇒ 坑 22 安全;老代码**不判** `userInfo == null`(无 token 时框架层已拦)
⇒ 新类照搬不判,不补 `AUTH_TOKEN_NOT_EXISTS`。
★ 3 处老 `@SysOpLog` ⇒ 3 个 `@OperateLog`(module `会员卡规则`);
`checkVipCardPark` 老类无 `@SysOpLog` ⇒ 不加。
★ 存量语义照搬:`insert {}` 撞服务层 `BeanValidatorsUtils` ⇒
**code=400「请求参数不正确:xxx必填」**;`update` 的 `id`/`cardNo` 不全
⇒ DAO 提前 `return 0`(0 行、不碰库);`delete` 是**下架**(改
`card_status`/`data_state`),不存在 id 回 `0 + false`;
`check` 缺 `plNos` ⇒ `<foreach>` 展成空 `in` ⇒ **500**。
★ ⛔ 冒烟对写端点只用 `{}`、不存在 id(`999999`/`NOPE_95`)与无 body(400),
`t_vip_card` / `t_vip_card_park_sale_rel` / `t_vip_card_cust_info` 行数已实测不变。
★★ **2026-10-18 第九十四切片:⚠B 里的 `BlackListRuleController`(迁 6/7 端点,`parkTree` 留 P3)搬走**
⇒ **⚠B 66/295 → 65/288、已迁 141/816 → 142/823、未迁 70/302 → 69/295**。
★ 落在 `bill` 域(`POST /admin-api/bill/black-list-rule/*`),老路由 `/blackListRuleMng`
(黑名单规则管理);**3 读 + 3 写 + 1 未迁**(`parkTree`)。
★ 登录态只读 `orgId`/`opId`,**不读** `plNos`/`userAreas`/`resIds` ⇒ 坑 22 安全;
`parkTree` 从登录态取 `plNos` 当车场树根 ⇒ **P3 阻塞、整条不迁**(本机路由 404)。
★ 老类**没有** `@SysOpLog` ⇒ 新类**不加** `@OperateLog`。
★ `@Value("${project.syscode}")` 照搬(老门户值 `zteits-bcp-portal`),
**不要**硬编码 `"1001"`(那是另一个类的老写法)。
★ 控制器闸门照搬:`invalid` 的 `ruleNo` 判空(1030)在写操作人**之前**;
`update` 的 `ruleNo`/`ruleName`/`actionType` 三字段判空(1030);
`actionType` 在 update 是 **String**、save 是 Integer(存量不一致,照搬)。
★ 存量缺陷照搬:`queryBlackListRule` 查不到规则时 `BeanUtils.copyProperties(null, dto)`
后继续 `getStartTime()` ⇒ **NPE 500**;`save {}` 先撞服务层
1003「结束时间不能早于当前时间」,`save` 只给 `endTime` 撞
`now.after(startTime)` 拆箱 **NPE 500**;`invalid` 不存在的 `ruleNo`
是 0 行更新 `code=0 + true`(存量不校验命中行)。
★ ⛔ 冒烟对三个写端点只用「无 body(绑定层 400)」与「不存在 ruleNo(0 行)」样本,
`t_black_list_rule` / `t_black_list_park_rel` / `t_black_list` 行数已实测不变。
★★ **2026-10-18 第九十三切片:⚠B 里的 `IncomeParkAndVipController`(迁 7/7 端点)搬走**
⇒ **⚠B 67/302 → 66/295、已迁 140/809 → 141/816、未迁 71/309 → 70/302**。
★ 落在 `order` 域(`POST|GET /admin-api/order/income-park-and-vip/*`),
老路由 `/income/incomeParkAndVip`(统计报表·临停车、会员卡收入);
**5 读 + 2 响应流导出**(xlsx + PDF),无写端点、无挂起。
★ 登录态只读 `orgId`(仅 total 端点回填组织名),**不读** `plNos`/`userAreas`/`resIds`
⇒ 坑 22 安全,整类可迁、不涉及 P3。
★ 老类**没有** `@SysOpLog` ⇒ 新类**不加** `@OperateLog`。
★ 两条导出都是**直接写 `HttpServletResponse`**(不落临时文件、不过 FastDFS/模板)
⇒ 可直接迁;但异常语义相反:`/export-excel` 无 try/catch ⇒ 500 JSON、
`/export-pdf` 整段 try/catch ⇒ 200 + 0 字节。`days` 只在 PDF 必填。
★ pay-type 两个端点的 DTO 无必填校验,`{}` 撞 `1 == null` 拆箱 NPE ⇒ 500;
`reportTabTag` 只有 1/2/3 分支,4/9 是静默空结果。
★ 本机 `ParkingLotCacheUtil` 未命中这三个车场 ⇒ pay-type 三行全 null,
冒烟只断言 code=0 与条数 3。
★★ **2026-10-18 第九十二切片:⚠B 里的 `IncomeChargerCheckTaskController`(迁 5/6 端点,导出挂起)搬走**
⇒ **⚠B 68/308 → 67/302、已迁 139/803 → 140/809、未迁 72/315 → 71/309**。
★ 落在 `order` 域(`POST /admin-api/order/charger-check-task/*`),
老路由 `/incomeChargerCheckTaskManage`(收费员任务完成情况);**2 读 + 3 写 + 1 导出**。
★ 登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`
⇒ 坑 22 安全,整类可迁、不涉及 P3。
★ 老类**没有** `@SysOpLog` ⇒ 新类**不加** `@OperateLog`。
★ 本脚本按老类计数 ⇒ 挂起的导出也计入「已迁」的 6 个端点(同 AppVersionCheck 先例)。
★ ⏸ `exportIncomeChargerCheckTask` **挂起**:EasyExcel 模板写临时文件 →
`fastDFSClientWrapper.uploadFileWithName(...)` → 回**文件 URL 字符串**;
本机运行期无存储后端,且模板 `excelTemplate/ChargerCheckTask.xlsx` 未随迁。
★ 日期契约只吃 `yyyy-MM-dd HH:mm:ss`;`page` 缺起止日 → DAO generator
空值守卫抛 RuntimeException ⇒ 500(存量缺陷,红线⑥不修)。
★ ⛔ `save` 的 DTO 无必填校验 ⇒ 冒烟绝不 POST;`bathDelete` 的 DAO 是
`return 0` 死代码,回 false 不是安全保证。
★★ **2026-10-18 第九十一切片:⚠B 里的 `TdCActivityController`(迁 6/6 端点)搬走**
⇒ **⚠B 69/314 → 68/308、已迁 138/797 → 139/803、未迁 73/321 → 72/315**。
★ 落在 `pay` 域(`POST /admin-api/pay/activity/*`),老路由 `/tdCActivityManage`;
**2 读 + 4 写**,无导出;4 处 `@SysOpLog` → 4 个 `@OperateLog`。
★ 登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。
★ `save`/`update` 是 **multipart/form**(不是 `@RequestBody`):空 body 会真进业务,
`save` 无必填校验会真插 `td_c_activity` ⇒ 冒烟**只发 GET 验 405**;
`update`/`bath` 停在服务层 1003「id不能為空!」,`delete {不存在}` 是 0 行更新。
★★ **2026-10-18 第九十切片:⚠B 里的 `TpPEqpErrorRecordController`(迁 6/6 端点)搬走**
⇒ **⚠B 70/320 → 69/314、已迁 137/791 → 138/797、未迁 74/327 → 73/321**。
★ 落在 `park` 域(`POST /admin-api/park/eqp-error-record/*`),
老路由 `/tpPEqpErrorRecordManage`(设备异常记录管理);**2 读 + 4 写**,无导出。
★ 登录态只读 `orgId`(读端点查询条件)/`opId`(写端点操作人),
**不读** `plNos`/`userAreas`/`resIds` ⇒ 坑 22 安全。
★ 老类**没有** `@SysOpLog` ⇒ 新类**没有** `@OperateLog`。
★ `save {}` 走到 `insertSelective` 被 `error_name` NOT NULL 挡回;
`update {}` / `delete {id}` / `bath {ids:[]}` 生成无 SET 或 `id in ()` 的 SQL
⇒ 本机 500 但**一行不落**;`bath {ids:[不存在]}` 是 0 行 ⇒ `data=false`。
★★ **2026-10-18 第八十九切片:⚠B 里的 `BillingRuleController`(迁 9/9 端点)搬走**
⇒ **⚠B 71/329 → 70/320、已迁 136/782 → 137/791、未迁 75/336 → 74/327**。
★ 落在 `bill` 域(`POST /admin-api/bill/billing-rule/*` + `GET /export-excel`),
老路由 `/billingRule`(计费规则服务);**5 读 + 3 写 + 1 导出**。
★ 登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`
⇒ 坑 22 安全,整类可迁、不涉及 P3。
★ 3 处 `@SysOpLog` → 3 个 `@OperateLog`(`module = "计费规则服务"`);
老导出 `@NoAuth` 按 §3.3 收敛为 `bill:billing-rule:export`。
★ 老方法名 `queryBilingRuleShowInfoByCollectNo` 少个 `l`,新路由段照抄
`query-biling-rule-show-info-by-collect-no`。
★ ⚠️ 存量库排序规则冲突:`t_billing_rule_collect`(unicode_ci)与
`t_billing_rule` / `t_billing_park_rule_rel`(general_ci)JOIN ⇒ MySQL 1267
`Illegal mix of collations` ⇒ 本机 5 读 + 分页 + 导出**恒 500**
(`sql/.dump/zt_bill.sql` 确认是源库真实形态;红线⑥不改 SQL、不改库)。
★ ⛔ 写端点冒烟只发无 body / 停在闸门或服务前置校验的样本;
`t_billing_rule` / `t_billing_rule_config` / `t_billing_rule_collect` /
`t_billing_park_rule_rel` / `t_billing_description` 实测一行不动。
★★ **2026-10-18 第八十八切片:⚠B 里的 `SpecialCarManageController`(迁 6/8 端点)搬走**
⇒ **⚠B 72/337 → 71/329、已迁 135/774 → 136/782、未迁 76/344 → 75/336**。
★ 落在 `bill` 域(`POST /admin-api/bill/special-car-manage/*`),老路由
`/specialCarManage`(特殊车辆管理);**4 读 + 2 写、无导出**。
★ ⛔ 前 2 条 `/querySpecialCar`、`/initSpecialCarTree` **未迁**:
老代码 `request.setPlNos(userInfo.getPlNos())` + 固定 `sourceTypes=[17,9]`
⇒ P3 `plNos` 数据权限模型。其余 4 条只写 `orgId`、2 条完全不读登录态
⇒ 坑 22 安全。
★ 老类**无 `@SysOpLog`** ⇒ 新类**无 `@OperateLog`**(照搬,不补)。
★ `saveSpecialCar` 按 `plNo` 逗号拆行 insert;`updateSpecialCar` 按 `carNumber`
先删后插;`deleteSpecialCar` 是软删(`data_state='0'`)。
★ ⚠️ 存量 SQL 撞 MySQL 8:`viewSpecialCar` 的 `GROUP BY car_number` 选了
非聚合列 `remarks` ⇒ `only_full_group_by` ⇒ 本机**恒 500**
(老栈 5.7 正常;红线⑥不修,冒烟钉成已知行为)。
★ ⛔ 冒烟只用零写入样本:`save {specialCarList:[]}` 循环 0 次、
`update {carNumber:ZZZ_NO_88}` 删 0 行不插、`delete {id:999999999}` 软删 0 行;
`td_cl_special_car` 实测 500 行不变。
★★ **2026-10-18 第八十七切片:⚠B 里的 `ParkIllegalAreaBaseController`(整类 8/8 端点)搬走**
⇒ **⚠B 73/345 → 72/337、已迁 134/766 → 135/774、未迁 77/352 → 76/344**。
★ 落在 `park` 域(`POST /admin-api/park/park-illegal-area-base/*`),老路由
`/parkIllegalAreaBaseManage`(停车违章区域基础信息管理);**3 读 + 5 写、无导出**。
★ 坑 22 **全类安全**:8 个端点只从登录态读 `orgId`/`opId`/`name`;唯一一处
`getPlNos()` 是 `request.getPlNos()`(`saveParkIllegalAreaRel` 的**请求参数**,
不是登录数据权限)⇒ 不需要等 P3。
★ 老类 `@NoAuth` 0 处;5 处 `@SysOpLog` ⇒ 5 处 `@OperateLog`。
★ `saveParkIllegalAreaRel` 的两道控制器前置校验照搬(`parkIllegalBaseId` 为空、
`plNos` 为空 ⇒ `BizException(PARAMM_NULL, …)`),⛔ 都在 Service「先删后插」之前。
★ ⛔ 5 个写端点会**真插/真删主数据**(`save-base` 无校验注解;`save-rel` 先删旧关系)
⇒ 冒烟只发**无 body**(绑定层 400),`save-rel` 另发两道校验样本(控制器内 500)。
零写实测 `parking_lot`/`parking_lot_areas`/`parking_lot_enclosed`/
`parking_lot_geo`/`park_free_berth` 500/53/46/500/500 不变。
★★ **2026-10-18 第八十六切片:⚠B 里的 `CloseCarParkController`(迁 17/19 端点)搬走**
⇒ **⚠B 74/364 → 73/345、已迁 133/747 → 134/766、未迁 78/371 → 77/352**。
★ 落在 `park` 域(`POST /admin-api/park/close-car-park/*`),老路由
`/parkListManage`(封闭车场管理);**10 读 + 7 写、无导出**。
★ ⛔ 前 2 条 `/QueryParkingList`、`/queryParkingParkList` **未迁**:
老代码 `request.setPlNos(userInfo.getPlNos())` + 固定 `sourceType=[17,9]`
⇒ P3 `plNos` 数据权限模型。其余 17 条中只有 `areaBlockByAreaId`
读登录态且只写 `orgId` ⇒ 坑 22 安全。
★ 老类 `@NoAuth` 0 处;3 处 `@SysOpLog` ⇒ 3 处 `@OperateLog`
(`saveParkingListInfo` / `saveParkingAreaInfo` / `updateFreeBerthEnclosed`;
另有 4 处老代码本就是注释状态,不补)。
★ 存量缺陷照搬(红线⑥不修):`view-parking-area {id:0}`(BeanUtils copy null)、
`view-parking-area-by-id {ids:[]}`(空 `in ()`)、`init-area {}`(id 拆箱 NPE)、
`view-free-berth-enclosed` 无封闭扩展行(`list.get(0)` 越界)、
`view-parking-list-info` 车场不存在 均 500。
★ ⛔ 7 个写端点会**真插/真改/真软删车场与区域**(`saveParkingListInfo`
无校验注解,`{}` 会生成新编码并真插一行)⇒ 冒烟只发 **无 body**(400);
零写实测 `parking_lot`/`parking_lot_areas`/`parking_lot_enclosed`/
`parking_lot_geo`/`park_free_berth` 500/53/46/500/500 不变,
只有 `system_operate_log` +169/轮。
★★ **2026-10-18 第八十五切片:⚠B 里的 `KeyTopServiceController`(迁 7/8 端点)搬走**
⇒ **⚠B 75/372 → 74/364、已迁 132/739 → 133/747、未迁 79/379 → 78/371**。
★ 落在 `order` 域(`POST /admin-api/order/key-top-service/*`),老路由
`/keyTopService`(科拓小管家);**4 读 + 3 写、无导出**。
★ ⛔ 第 8 条 `/parkTree` **未迁**:老 `parkTree()` 直接
`plNos = userInfo.getPlNos();` 再构树(只留 KEYTOP/CLOUD_BILL)
⇒ P3 `plNos` 数据权限模型。其余 7 条只写 `orgId`/`opId`/`opName` ⇒ 坑 22 安全。
★ 老类 `@NoAuth` 0 处;4 处 `@SysOpLog` ⇒ 4 处 `@OperateLog`;
`sysCode` 走 `@Value("${project.syscode}")`(已配 `zteits-bcp-portal`)。
★ `queryOrder` 按 §3.2 直接透传 `ParkOrderDTO`(老栈多拷一层
`OrderParkForPayTypeDTO`,多出的 21 字段本就恒 null);分页参数照搬。
★ ⚠️ 存量服务层缺陷照搬:`int carType = request.getCarType()` 对 null
拆箱 NPE ⇒ `query {}` 恒 500(老栈同款,红线⑥不修);冒烟正样本必须带 carType。
★ ⛔ 4 个写端点会**真出场 / 真补录并进 MQ / 真控闸机** ⇒ 冒烟只发
**无 body**(400)与炸关口样本;零写实测 `order_park` / `park_in_out_record` /
`order_modify_record` 500/500/0 不变,只有 `system_operate_log` +169/轮。
★★ **2026-10-18 第八十四切片:⚠B 里的 `TdCCustInvoiceSaleController`(迁 11/12 端点)搬走**
⇒ **⚠B 76/384 → 75/372、已迁 131/727 → 132/739、未迁 80/391 → 79/379**。
★ 落在 `pay` 域(`POST /admin-api/pay/invoice-sale/*`),老路由
`/invoiceSaleManage`(发票-销方管理);**6 读 + 5 写、无导出**。
★ ⛔ 第 12 条 `/relParkTree` **未迁**:老 `vipCardSelectParkTree` 直接
`plNos = userInfo.getPlNos(); plNos.removeAll(relPlNos);` 裁剪可选车场树
⇒ P3 `plNos` 数据权限模型(先例 `VipCardCustInfoController` 14/15)。
★ 其余 11 条只写 `orgId`/`opId`/`createEmpid`/`modfiyEmpid` ⇒ 坑 22 安全。
★ 老类 `@NoAuth` 0 处;4 处 `@SysOpLog` ⇒ 4 处 `@OperateLog`;
`saveOrUpdateInvoiceRelPlNo` 老类没有 ⇒ 不加。
★ 照搬:`queryRelParks` 空 `sysCode` 补 `"1001"`、`relplno` **无条件**
`"1001"`;`bath-delete` 老 mapping 错拼逐字保留;
`delete`/`bath-delete` 同时写父类 `sOpId(Long)` 与 `opId(String)`。
★ ⛔ **`save {}` 会真插一行销方**(服务层无任何必填校验)⇒ 冒烟只发
**无 body**(绑定层 400);`relplno` 只用不存在的税号(删 0 行、不插)。
零写实测:只有 `system_operate_log` +161/轮,两张发票表 4/4/4 不变。
★★ **2026-10-18 第八十三切片:⚠B 里的 `ManualInvoiceController`(迁 7/7 端点)搬走**
⇒ **⚠B 77/391 → 76/384、已迁 130/720 → 131/727、未迁 81/398 → 80/391**。
★ 落在 `pay` 域(`POST /admin-api/pay/manual-invoice/*`),老路由
`/manualInvoice`(人工开具电子发票);**4 读 + 3 写、无导出**。
★ 登录态只碰 `orgId`/`loginCode`;`queryInvoiceSaleInfo` 的 `plNos`
从结果集 `PayOrderParkingInvoiceDTO::getPlNo` 现算,不是数据权限字段。
★ 老类 `@NoAuth` 1 处(`queryPayOrderForParkingInvoice`)——★ 方法体自己就抛
`AUTH_TOKEN_NOT_EXISTS`,调用方 `fillinvoice.js` 走 `sysAjax`(带 token)
⇒ §3.3 第一类,收敛为需登录;老类无 `@SysOpLog` ⇒ 无 `@OperateLog`。
★ 存量差异照搬:`park {}` 撞本机 MySQL 8 `only_full_group_by` ⇒ 500
(存量 SQL,老栈 5.7 正常,红线⑥不修);`ticket` 缺 `invoiceType`
拆箱 NPE ⇒ 500;`resend {}` 模板缺失 ⇒ `-100020`;`open {}` ⇒
「BizResult 为 null」(老类 catch 里塞 null)。
★ ⛔ 三个写端点有 body 会**真发邮件 / 真落开票历史 / 真调航信开票队列**;
冒烟只发无 body 与炸关口样本,`td_c_cust_invoice_his` / `_sale` /
`_sale_park_rel` 行数已实测不变。
★★ **2026-10-18 第八十二切片:⚠B 里的 `GuideScreenController`(迁 7/7 端点)搬走**
⇒ **⚠B 78/398 → 77/391、已迁 129/713 → 130/720、未迁 82/405 → 81/398**。
★ 落在 `park` 域(`POST /admin-api/park/guide-screen/*`),老路由
`/guideScreen`(诱导屏管理);**5 读 + 2 写、无导出**。
★ 4 处读登录态但**只碰 `orgId`/`opId`/`opName`**;`plNos` 来自
`guideScreenQueryService.getGuideScreenInfo` 的结果集,不是数据权限字段。
★ 老类 `@NoAuth` 1 处(`updateGuideScreenStates`)——★ 有**管理页面**调用方
(`screenmanage.js:415` 走 `sysAjax`,带 token)⇒ §3.3 第一类,收敛为需登录;
老类 `@SysOpLog` 1 处 ⇒ `@OperateLog` 1 处(UPDATE)。
★ 存量差异照搬:`statistics {}` 的 `WHERE 1=1` 被本机 Druid wall 拦 ⇒ 500;
`get-info` 不存在的 guideNo 拆箱 NPE ⇒ 500;`update-states {}`
撞 Example `andGuideNoEqualTo(null)` ⇒ 500。
★ ⛔ **`syn-all-guide-info` 有 body(连 `{}` 都算)会先打青岛三组 HTTP,
再进 `@Transactional` 的先删后插**;冒烟只发无 body
(`td_g_guide_screen` 97 行、`td_g_guide_park_rel` 320 行已实测不变)。
★★ **2026-10-18 第八十一切片:⚠B 里的 `BatteryRechargeCardController`(迁 7/7 端点)搬走**
⇒ **⚠B 79/405 → 78/398、已迁 128/706 → 129/713、未迁 83/412 → 82/405**。
★ 落在 `pay` 域(`POST /admin-api/pay/battery-recharge-card/*`),老路由
`/batteryRechargeCardManage`;**3 读 + 4 写、无导出**。
★ 6 处读登录态但**只碰 `orgId`/`sOpId`/操作人**;**无 `plNos`/`userAreas`/`resIds`**。
★ 老类 `@NoAuth` 1 处(分页查询)——但方法体自己就抛
`AUTH_TOKEN_NOT_EXISTS`,新栈收敛为需登录;老类无 `@SysOpLog` ⇒ 不加 `@OperateLog`。
★ `save` 与 `account-charge-wx-pay` 保留 `@Valid`(`{}` → 400);
`update {}` / `bath-delete {}` ⇒ 500「存量错误码 1003 / id为空!」。
★ `query-account-details-for-page` 撞本机 MySQL 8 `only_full_group_by` ⇒ 500
(存量 SQL,老栈同样炸,红线⑥不修)。
★ ⛔ **`account-charge-wx-pay` 有完整 body 会真建充值单并调微信下单**;
冒烟只发无 body 与 `{}`,两张电池卡表行数已实测不变。
★★ **2026-10-18 第七十七切片:⚠B 里的 `TpPEqpGatewayController`(迁 7/7 端点)搬走**
⇒ **⚠B 83/433 → 82/426、已迁 124/678 → 125/685、未迁 87/440 → 86/433**。
★ 落在 `park` 域(`POST /admin-api/park/eqp-gateway/*`),老路由
`/tpPEqpGatewayManage`(道闸网关管理);**3 读 + 4 写、无导出**。
★ 类路由按「去掉表前缀 `TpP` 再 kebab」定为 `eqp-gateway`
(先例 `TpPNetworkCardController` → `/park/network-card`);
方法段 kebab 老 mapping 逐字(同第七十五/七十六切片),`bath` 存量错拼保留。
★ 7 处读登录态但**只碰 `orgId`/`sOpId`/`sOpName`**:statistic/list/page/save/update
写 `orgId`,delete/bathDelete 只写操作人;**无 `plNos`/`userAreas`/`resIds`**。
★ 存量差异照搬:`delete` 同时写父类 `setsOpId` 与子类 `setOpId`(服务读 `getOpId()`);
`batchDelete` 只写 `setsOpId` ⇒ 操作人名称恒 null;
`bathDelete {}` 撞 `TpPEqpGatewayExample.andIdIn(null)` 的
`Value for id cannot be null` ⇒ 500。
★ ⛔ **`save-tp-p-eqp-gateway` 有 body(哪怕 `{}`)就会真插 `tp_p_eqp_gateway`**
(`TpPEqpGatewayRequest` 无任何校验注解);冒烟只发无 body,该表行数已实测不变。
⚠️ **排期用「✔ 3/3(0 个可直接开搬,全是外部副作用/文件存储前置)+ ⚠A 0/0(已清零)+ ⚠B 1/1(orgId 部分已解禁,剩余均带硬避让)」,别用 4/4**——
"就绪"只回答了"服务齐不齐",不回答"搬过来数据对不对"。端点数**不能**当"好迁"的信号:
`ChargerManageController` 与 `DutyRosterController` 是端点最多的两个,也是坑 22 命中最多的两个(各 22 处),
且这两个都在 **⚠B**(真读登录态)——**两者已分别在第六十六/第六十七切片搬走**;
当前队列里端点最多的是 `ParkLotManageController`(18,⛔ 读登录 `plNos` 三处 = P3 数据权限),其后 `VipCardCustInfoController`(15,**第七十四切片已搬走 14 个**,余 1 条「下载月卡购买协议」⛔ 依赖 poi-tl + classpath 下 docx 模板 + 本机硬编码输出目录)、
`VipCardAuthPersonBjxcController`(12,**已于第七十一切片搬走**)、`TdCCustInvoiceSaleController`(12,⛔ 读登录 `plNos` = P3 数据权限)。
"ids 大"才是危险信号,"endpoints 大"只是工量大。
退出码:
0 = 盘点本身是可信的(扫到了 Controller、也扫到了新栈的接口与实现)
1 = **盘点不可信**:老门户目录不存在、或新栈一个接口/一个实现都没扫到。
⚠️ 退出码**不是**"有没有缺口"——缺口是这份报告的**结论**,不是它的失败。
(把"有缺口"当失败的话,这个脚本会恒返回 1,等于没有退出码。)
⚠️ 四个必须保留的做法,别"顺手简化":
1. **统计端点前先剥注释 —— 而且必须把老写法 `@RequestMapping` 一起算进来。**
两个独立的坑,**第一版两个都栽了**,数字差一倍:
- *剥注释*:`invoice/InvoiceController` 看着 18 端点 / 0 会话 / 0 `@NoAuth`,
实际整个类是注释掉的(`//@RequestMapping("/invoice")`)。不剥就会把死代码排进待迁清单。
- *算 `@RequestMapping`*:老工程的**主流写法**是
`@RequestMapping(value = "/getFeedbackType", method = RequestMethod.POST)`,
**不是** `@PostMapping`。只匹配 `@(Get|Post|Put|Delete)Mapping` 会漏掉一大半端点,
并把**活着的控制器误判成死代码**(当时把整个 `personpc/**` 判成死代码,
实际 `PersonPCSuggestController:57`、`PersonPCAcctController:79` 都是活的)。
- *别把类级映射算成端点*:`@RequestMapping("equipmentManagement")` 和
`@PostMapping("/saveChannelInfo")` 在正则眼里是一个东西,于是**每个带类级映射的
Controller 都多算 1 个端点**(`EquipmentManagementController` 数出 11,实际 10)。
现在 `count_endpoints()` 按"类声明之前 = 类级、之后 = 方法级"分开数。
- 教训:**"剥注释"只解决"假活",还要再解决"假死"和"数多了"**。任何结论性的数字
("X 个是死代码"、"某类有 N 个端点")**写进文档前一定要拿一个真实文件手数核对**
—— 这三个 bug 全是这么发现的:`personpc` 是随手 grep 一个方法签名发现的,
端点数是对着 `closecarpark` 那 5 个类逐行数出来的。
2. **判定"某个 Controller 是否已迁"不靠文件名,也不靠新类名。** 两条都不可靠:
- 新类普遍按路由约定**改名**(老 `TpPInspectRecordController` → 新 `InspectRecordController`),
按新类名反查会把已迁的**全判成未迁**(只认出 13 个,实际 23 个);
- 老工程大小写不统一(老 `monitor/ParkingLBSController` → 新 `ParkingLbsController`),
逐字符比对同样漏。
本脚本用「新类 javadoc 抬头里提到老类名」+「类名忽略大小写相同」两路合并
(见 `provenance_mentions` / `PROVENANCE_PAT`,含"必须在小标题之前截断"的原因——
不截断会把 `ParkLotExtendController` 里那句"与老 `ParkLotManageController` 的端点不混在一起"
当成已迁证据,把 19 个端点的 `ParkLotManageController` 从待办队列里藏掉)。
**但这是启发式**:真要确认某个类迁没迁,`grep` 那个老名字,别信计数。
3. **"接口不在新栈"和"无实现"要分开报。** 前者要补 `parking-module-api` 的契约,
后者要搬一个 ServiceImpl 进某个 module——是两种完全不同的工。
4. **实现类是按简单类名匹配的**(`class XxxImpl implements Xxx`)。
老工程存在同名不同包的重复类,所以这是**宽松匹配**:它只证明"有一个实现",
**不证明**实现落在正确的 module 里、也不证明它被 Spring 扫到了。
就绪度是**必要不充分**条件——真迁的时候仍然要起 Jar 打端点(`tools/api_smoke.py`)。
5. **"服务就绪"之外必须再报坑 22**(`idSpaceHits`)。只报就绪会给人一个 189 个的"待办队列",
实际能开搬的只有三分之一——**把半个队列说成可做,比不说更糟**(会被当成排期依据)。
判据是 `LEGACY_ID_PAT`(从登录态读 `getOrgId()/getPlNos()/getUserAreas()`),
保守但机械;`setOpId(...)` 这类只写审计列的**不**算命中(见 §3.1 第 1 步的判据)。
6. **坑 22 必须切两刀,只切一刀会漏掉 12 个可搬的类。** 第一刀(`LEGACY_ID_PAT`)的
正则**没有接收者概念**:`request.getPlNos()`(前端请求体,与登录态无关)与
`userInfo.getPlNos()`(登录态)命中一模一样。于是 ⚠ 档里混进了 12 个 / 61 端点
"整类一处都没读登录态"的**纯误报**(第三十五切片实测)。第二刀 `IDENT_READ_PAT`
(`getUserInfo\\s*\\(|SessionCommUtil|getUserInfoForPersonPC\\s*\\(`)把它们摘出来成 ⚠A。
⚠️ ⚠A **不等于"已核销"**:脚本只证明"整类没读登录态",仍要人肉看一眼命中行落在谁身上
(`idSpaceLines` 已经把行号+原文打出来了,几秒的事),核完登记进 `IDSPACE_FALSE_POSITIVE`。
用法:
python3 tools/p2_ready_check.py # 从仓库根目录跑(parking-cloud/ 的父目录)
python3 tools/p2_ready_check.py --json # 机器可读,供挑选下一批用
python3 tools/p2_ready_check.py --top 40 # 只看端点最多的前 40 个就绪项
python3 tools/p2_ready_check.py --legacy-root /path/to/zteits-clouds \\
--new-root /path/to/parking-cloud
"""
import argparse
import glob
import json
import os
import re
import sys
DEFAULT_LEGACY = "zteits-clouds/zteits-bcp-portal/src/main/java/com/zteits/bcp/portal/web"
DEFAULT_NEW = "parking-cloud"
# ⚠️ 第一百五十四切片修正(2026-10-19):老正则只认 `class Xxx implements Yyy`,
# 漏掉「`class Xxx extends Base implements Yyy`」这种**先继承再实现**的形状。
# 新栈里有 6 个接口就是因此被误判成「无实现」(`PubParkOrderQueryService`、
# `RefundService` 等,共 8 个 Controller / 37 端点被错挡在「缺口」档)。
# 另外老新版扫描只 glob `*Impl.java`,漏掉 `RefundServiceIpml.java`(文件名把 Impl 拼成 Ipml)。
IMPL_PAT = re.compile(r'class\s+(\w+)(?:\s+extends[^{]*?)?\s+implements[^{]*?(\w+)')
IFACE_PAT = re.compile(r'public\s+interface\s+(\w+)')
# 新栈 `api.service.*` 包下的**类**(不是接口)。存量 Controller 会 import 它们当入参
# (典型:`RenewVipAreaCardOperReq` 放在 `api.service.pay.viparea.oper` 下但 `extends BaseRequest`)。
# 这些 import 只说明"传参用到这个 DTO",不构成"缺服务",不能算进 gaps。
API_CLASS_PAT = re.compile(r'\b(?:public\s+|final\s+|abstract\s+)*class\s+(\w+)')
SVC_IMPORT_PAT = re.compile(r'import\s+(com\.zteits\.clouds\.api\.service\.[\w.]+);')
ENDPOINT_PAT = re.compile(r'@(?:Get|Post|Put|Delete|Request)Mapping\(')
# 坑 22 的机械信号:老 Controller **从登录态读**这三个身份字段。它们在新栈里一个是新 id
# (`system_org.id`,seed 只有 1 行「总公司」)、一个是空集合,拿去**过滤结果集**就静默少数据。
# 只认 `get*`(读登录态)——`setOpId(...)` 写审计列不在此列,而
# `request.setOrgId(userInfo.getOrgId())` 这种"读完就喂给下游当过滤条件"的写法因为含
# `getOrgId()` 仍会被抓到。== 0 才认为"坑 22 安全"(见 §3.1 第 1 步)。
LEGACY_ID_PAT = re.compile(r'get(?:OrgId|OrgIdList|PlNos|UserAreas|UserAreaList)\s*\(')
# ⚠️⚠️ `LEGACY_ID_PAT` **不知道 `get*()` 是打在谁身上的**——它连**请求体**一起抓。
# 老 Controller 里 `request.getPlNos()`(`request` 是前端传进来的请求体)、
# `dto.getOrgId()`(下游返回的 DTO)都会命中,**与登录态毫无关系**。
# ⇒ 它是个"宁可多报"的**筛子**,不是判据。第三十五切片实测:⚠ 档 124 个里有
# **12 个 / 61 端点**整类**没有任何一处**读登录身份(`IDENT_READ_PAT` 命中 0),
# 全部是机械误报——所以本脚本在 ⚠ 档内部再切一刀成 ⚠A / ⚠B(见 main()):
# ⚠A(`hasIdentitySource == False`)= **机械误报候选**,可开搬,但每个类仍要
# 人肉核对命中行落在谁身上(`idSpaceLines` 已经把行号和原文打出来了,几秒的事);
# ⚠B(`hasIdentitySource == True`)= 真读了登录态,还要再往下追"读到的值有没有进 SQL"
# (第三十三切片:`ParkInOutRecordController` 的 `setTopOrgId` 是**注释掉的** INERT)。
IDENT_READ_PAT = re.compile(r'getUserInfo\s*\(|SessionCommUtil|getUserInfoForPersonPC\s*\(')
# 新类注释里对老类的指认:反引号或 `/` 紧跟的 `XxxController`(见 collect_new_stack 注释②)。
# 只在**类 javadoc 的第一段**里找(`provenance_mentions` 负责切段),因为约定是
# 「管理后台 - XXX Controller(老门户 `path/XxxController`,路由 …)」。
# ⚠️ **不切段会误判**:`ParkLotExtendController` 在后面的说明里写了
# 「与老 `ParkLotManageController` 的端点不混在一起」——那是在**划清界限**,
# 结果把 19 个端点的 `ParkLotManageController` 误标成"已迁"从待办队列里藏掉了
# (而它恰恰是 §4.3 高风险批次里那个不能漏的)。
# ⚠️ 它仍是启发式:切段后仍可能在某类的第一段里提到**另一个**老类名。
# 反过来的错(把已迁的算进待办)只浪费时间,这个方向的错(把未迁的藏起来)会**漏活**,
# 所以确认某个类迁没迁,一律 `grep` 那个老名字,别信这里的计数。
PROVENANCE_PAT = re.compile(r'[`/]([A-Za-z0-9_]+Controller)\b')
def strip_comments(src):
"""剥掉块注释与整行行注释——见模块注释第 1 条。"""
s = re.sub(r'/\*.*?\*/', '', src, flags=re.S)
return re.sub(r'^\s*//.*$', '', s, flags=re.M)
def strip_comments_keep_lines(src):
"""同 `strip_comments`,但块注释**只清空字符、保留换行**。
为什么需要它:上面那个 `re.S` 替换会把多行注释整段删掉、**连带删掉里面的换行**,
于是"剥注释后的第 N 行"和**文件里真正的第 N 行**就不是一回事了。剥注释后打行号
(`idSpaceLines`,给人核对 `getPlNos()` 打在谁身上用)必须对得上编辑器里看到的行号,
否则人肉核对要重新数一遍——那这一步就白做了。
两者**非换行字符完全一致**,所以 `LEGACY_ID_PAT` 在两个视图上命中数相同。
"""
s = re.sub(r'/\*.*?\*/', lambda m: re.sub(r'[^\n]', '', m.group(0)), src, flags=re.S)
return re.sub(r'^\s*//.*$', '', s, flags=re.M)
CLASS_PAT = re.compile(r'\b(?:public\s+|final\s+|abstract\s+)*class\s+\w+')
# 类级 `@RequestMapping("channelManagement")` 的路径。把它也当"指认",因为**第三种**
# provenance 写法认的是**路由**不是类名:`(老门户 \`channelManagement\`)`。
ROUTE_PAT = re.compile(r'@RequestMapping\s*\(\s*(?:value\s*=\s*)?"([^"]+)"')
# 注释抬头里的反引号词(`` `channelManagement` `` 这种)。见 new_stack_mentions。
BACKTICK_PAT = re.compile(r'`([A-Za-z0-9_./\-]+)`')
# ── 已判定表(★ 人工读源码的结论,按**老类名**索引;脚本只做标注,不改 ✔ 的机械判定) ──
# 为什么要有这张表:✔ 的判据只有「服务齐 + 没读身份字段」两条,**不回答能不能交付**
# (第三十四切片实测:✔ 5 个 / 11 端点**没有一个是能直接开搬的**)。
# 规矩:**每判定一个类就登记在这里**,否则下一批还会把同一批类当「可开搬」重走一遍。
ADJUDICATED = {
"RefreshCacheController": (
u"✅ 已搬(第四十六切片)",
u"4 个端点全 `@NoAuth` + 老代码写死的 32 位共享密钥(照搬违红线 11)⇒ 按第三十四切片 ④ 的"
u" **(B) 配置化**落地:期望值走 `parking.refresh-cache.token`(本地 profile 给测试值、生产不配即"
u" fail-closed),代码里零密钥;调用方不登录 ⇒ §3.3 第 2 类:`**.controller.app.**` → `/app-api`"
u" + `@PermitAll`(注解扫描自动进白名单)。落 `parking-module-system`,路由 `/system/refresh-cache`。"
u"★ 4 条只写 Redis、不写 DB;token 失败的「成功码 + data=token不正确」存量形状照搬"),
"PersonPCAuthController": (
u"⏸ 等个人客户登录桥",
u"类级 `@NoAuth`;`login` 把 `request.getSession().getId()` 当 token 写 `PersonSessionEnum`,"
u"`getUserInfo`/`logout` 读同一 session —— 新栈 Redis Token + Spring Security **不建 session**,"
u"且全仓**没有任何地方写** `PersonSessionEnum.USER_INFO`(只有 3 处读)⇒ 照搬即恒失败;"
u"`sendValidatorCode` 还会走 `MessageCodeUtil.sendSms` **真发短信**;"
u"`uploadUserPic` 的 `@RequestMapping` **被注释掉** ⇒ 它不是端点"),
"PersonOrderBackWxPayController": (
u"⏸ 等个人客户登录桥",
u"`sessionCommUtil.getUserInfoForPersonPC()` **只读 HTTP session、没有 LoginBridge 兜底** "
u"⇒ 照搬恒抛 1003「获取客户信息失败」(先例 `PersonPCCarNumController#getPersonCarPC` 已如此记账);"
u"且它会**真建微信支付订单**(`aliWxPayService.wxJSAPIPay`)"),
"UploadController": (
u"⏸ 等文件存储抽象",
u"`/upload/uploadPic` 是 `@NoAuth` + `FastDFSClientWrapper.uploadFile`;新栈里 "
u"`FileStorageServiceImpl.uploadFile` **整段被注释掉、直接 return null** "
u"(先例 `ParkShareController#insertParkingBerthShare`、`AppVersionCheckController#saveAppVersion`)。"
u"⚠️ 同栈里那些 `MultipartFile` 的 excel 导入端点**不算**这个卡点——卡的是上传到存储后端"),
"HomeController": (
u"⛔ 不适用",
u"14 行 `@Controller`:`@RequestMapping(根路径)` → `return redirect:swagger-ui.html`,"
u"**不是 JSON 接口**;新栈 springdoc 的 UI 在 `/swagger-ui`、knife4j 在 `/doc.html`,"
u"根路径也没有别的映射 ⇒ 判定「不迁」,不是「等」(要么落成文档页,要么不交)"),
}
# ── ⚠A 档(机械误报)核销表 ──
# 与 `ADJUDICATED` 分开两张表,因为**回答的是不同问题**:
# `ADJUDICATED` 管 ✔ 档("没读身份字段"的类到底能不能交付);
# `IDSPACE_FALSE_POSITIVE` 管 ⚠A 档("`LEGACY_ID_PAT` 命中了,但整类没读登录态"的类,
# 命中行究竟打在谁身上)。规矩同样是**每核销一个类就登记在这里**,否则下一批会重走一遍。
# ★ 2026-10-16 第三十五切片:⚠A 候选 12 个 / 61 端点逐类读完命中行,**结论全是误报**
# (命中行无一例外落在请求体 `request.getPlNos()` / 局部变量 `req.getOrgId()` /
# 下游 DTO `dto.getOrgId()` 上);其中 `EqpIncomeStatisticController` 本片已搬走 ⇒ 余 11 个 / 58 端点。
IDSPACE_FALSE_POSITIVE = {
"EqpIncomeStatisticController": (
u"✅ 已搬(第三十五切片)",
u"3 端点搬进 `park/eqp-income-statistic`;命中行 `recordRequest.setPlNos(request.getPlNos())`"
u"(`request` = 前端请求体 `IncomeRecentStatisticRequest`)"),
"RealTimeMonitorController": (
u"✅ 已搬(第四十四切片)",
u"2 处全是 `null == req.getOrgId()` —— `req` 是**方法入参**(前端请求体 `ParkDutyInfoQueryReq`),"
u"只做「没传就不查」的守卫,不是登录态;全类 **0 处** `getUserInfo(`/`SessionCommUtil`。"
u"10 端点拆成**两个新类**(本栈前缀由包路径决定,一个类不能两栖):管理端 8 条进 "
u"`park/real-time-monitor`(+ 导出收紧为 `park:real-time-monitor:export`);"
u"`queryParkLotInfoByOrgId` / `...ForHS` 两条**老 `@NoAuth` 是第 2 类**(外部黄石官网,"
u"`parkingcloud` 的两个分支都搜不到调用方)⇒ `/app-api` + `@PermitAll` + 白名单。"
u"★ 精确口径更正第四十三切片 ⑫ 的预判:**两条都是第 2 类**,不是只有 `ForHS`。"),
"BerthsStatisticController": (
u"✅ 已搬(第四十三切片)",
u"9 端点进 `park/berths-statistic`(⚠️ 不是 `BerthStatisticController`);"
u"`request.getPlNos()` 两处(老文件 `:362`/`:363`)里 `request` 是**方法入参**"
u"`ParkingLotUseStatisticForPageRequest` = 前端请求体,一处比 size、一处 for 遍历,"
u"全类 **0 处** `getUserInfo(`/`SessionCommUtil`;"
u"4 条导出全是老 `@NoAuth` ⇒ 按 §3.3 **第 1 类**收紧(一个类 4 条导出 ⇒ 主导出占 "
u"`GET /export-excel`,余三条给 `-detail`/`-month`/`-month-detail` 后缀)"),
"ReconciliationManagementController": (
u"✅ 已搬(第四十二切片)",
u"两处 `CollectionUtils.isEmpty(request.getPlNos())` / `req.getPlNos()`,均为请求体空判"
u"(`request`/`req` 是**方法入参**),全类 **0 处** `getUserInfo(`/`SessionCommUtil`;"
u"7 端点(5 读 + 2 导出)搬进 `order/reconciliation-management`。"
u"★ 老类 `extends ReconciliationBaseController`(那个只有 `getPayOrderTypeName(Integer,Integer)`"
u"的 140 行基类)——新栈没有基类可继承,那段 switch **原地抄进新控制器**(⛔ 别改成枚举/工具类)。"
u"★ 两条导出在老门户上都是 `@NoAuth` ⇒ 按 §3.3 **第 1 类**(管理页面导出)收紧成 "
u"`GET /export-excel` + `GET /export-detail-excel` + `@PreAuthorize('order:reconciliation-management:export')`;"
u"5 条读用 `order:reconciliation-management:query` ⇒ **7 条无 token 全 401**。"
u"⚠️ 两条源表样本都停在过去(`td_b_pay_blncac_result` 2018-01 且 `pl_no` 全空串、"
u"`pay_order` 停在 2017-09-25 且 `pay_type=6`)⇒ 正样本窗口得手动指定,**不是迁移回归**"),
"IndexParkSituationController": (
u"✅ 已搬(第三十七切片)",
u"5 处全打在 `request.getPlNos()` 上:3 处空判(老 `:100`/`:230`/`:286`)+ "
u"2 处 `setPlNos(request.getPlNos())` 转投下游请求(老 `:111`/`:117`)——"
u"`request` = 前端请求体,全类 **0 处** `getUserInfo(`/`SessionCommUtil`;"
u"6 端点搬进 `order/index-park-situation`(老类**没有** `@NoAuth` ⇒ 一律 `@PreAuthorize`)"),
"VehicleFlowController": (
u"✅ 已搬(第三十九切片)",
u"3 处 `request/parkingRunningStatisticForPageRequest.getPlNos()`,均为请求体;"
u"⚠️ 本类**两条端点在老门户上是 `@NoAuth`**(`exportCarFlowForList` 与 "
u"`carFlowPrint`——后者 `@RequestMapping` 连 `method` 都没写)⇒ 已按 §3.3 **第 1 类**"
u"(管理页面导出)收紧成 `GET /export-excel` + `GET /export-pdf`,同一权限点 "
u"`park:vehicle-flow:export`;5 端点搬进 `park/vehicle-flow`。★ 本片**新建 1 个老门户独有 VO**"
u"(`com.zteits.bcp.portal.vo.report.runtime.VehicleFlowVO`,只喂 PDF、不进响应体)"),
"IncomeCalculationController": (
u"✅ 已搬(第三十八切片)",
u"1 处 `CollectionUtils.isEmpty(req.getPlNos())`(请求体,控制器里那条是服务层同句的复制粘贴);"
u"⚠️ 本类另带 `@NoAuth` 导出端点(`/exportToIncomeCalculationSummaryNewOfllwTwo`,"
u"`@RequestMapping` 无 `method`)⇒ 已按 §3.3 **第 1 类**(管理页面导出)收紧成 "
u"`GET /export-excel` + `@PreAuthorize('order:income-calculation:export')`;"
u"5 端点搬进 `order/income-calculation`。★ 本片同时是**坑 25 的第二处修点**"
u"(两条走 `PageHelper.startPage` 的 select 去掉 `statementType=\"STATEMENT\"`)"),
"IncomeAllStatisticController": (
u"✅ 已搬(第四十切片)",
u"5 处 `request/incomeStatisticRequest.getPlNos()==null`,全是请求体空判(⚠A 机械误报);"
u"5 端点(5 读,无 `@NoAuth`、无 `@SysOpLog`)搬进 `order/income-all-statistic`。"
u"★ 本片**新建 3 个老门户独有 VO**(`IncomeStatisticTrentReportVO` / "
u"`IncomeStatistic2MonthFeeVO` / `IncomeStatisticDayFeeVO`)。"
u"⚠️ 其中 4 条端点的老 SQL 用 `GROUP BY x DESC`(MySQL 8.0.13 已移除的语法)⇒ "
u"本机(MySQL 8.x)恒 500,**老栈同样 500**,按红线⑥ **不改 SQL**,"
u"改以离线桩测(剥 jar + `Proxy` 桩 + 反射注入,66 断言)证明控制器逻辑等价"),
"OrderParkStatisticController": (
u"✅ 已搬(第四十一切片)",
u"1 处 `request.getOrgIds()/getPlNos()` 空判(请求体,⚠A 机械误报);"
u"4 端点(4 读、无导出、无 `@SysOpLog`)搬进 `park/order-park-statistic`。"
u"★ 老类 3 条大屏 `@NoAuth`(`queryLastOrderPark` / `queryLastOrderParkByPlNos` / "
u"`queryOrderParkNumForLastWeek`)按 §3.3 **第 1 类**(老门户自己的管理页面)收紧成 "
u"`/admin-api` + `@PreAuthorize('park:order-park-statistic:query')`(第 4 条原本就没有 "
u"`@NoAuth`,也纳入同一个安全档)⇒ **4 条端点无 token 全 401**。"
u"⚠️ 别与 `RealTimeMonitorController` 里**外部黄石官网**那条 `@NoAuth` 互推——"
u"那个才是第 2 类(`/app-api` + `@PermitAll` + 白名单)。"
u"⚠️ EP2 / EP3 / EP4 在本机**恒空**(样本表停在过去 + 窗口按今天算),"
u"**不是迁移回归**:判定靠「同一条 SQL 直连换窗口」,见 AGENTS.md 第四十一切片 ⑤"),
"VipCardQueryController": (
u"✅ 已搬(第四十五切片)",
u"1 处 `whitelistRuleForPageRequest.setPlNos(request.getPlNos())`(请求体转投下游;"
u"全类 0 处 `getUserInfo(`/`SessionCommUtil`)⇒ 3 端点(3 读、无导出、无 `@NoAuth`、无写)"
u"搬进 `order/vip-card-query`。★ 更正旧结论:**没有**「要另建 2 个老门户独有 VO」这回事——"
u"`VipCardStatisticIndexDTO`/`AuthCarNumberDTO` 早已在 `parking-module-api`,"
u"`VipCardStatisticFeeVO` 早已随 `parking-legacy-common` 搬入且**从未被 new 过**"
u"(第 3 条端点的真实线格式是 21 字段的 `IncomeStatisticFeeVipCardDTO`,老签名是假的)"),
"WxcpMessageController": (
u"✅ 已搬(第三十六切片)",
u"2 处 `request.getOrgId() == null` 空判(请求体)⇒ 落 `/app-api` + `@PermitAll`"
u"(§3.3 **第 2 类**:企微推送 H5 真免认证,已进白名单登记表 + §2af (b) 正面钉住)"),
"BerthStatisticController": (
u"✅ 已搬(第三十六切片)",
u"1 处 `appOrgRequest.setOrgId(dto.getOrgId() + \"\")` —— `dto` 是**上游服务返回的 DTO**"
u"(全类 0 处登录态读取);两个端点的 `@NoAuth` 已按 §3.3 **第 1 类**收紧成 `@PreAuthorize`"),
}
# 缺口档的人工结论(**不可迁**):机械判定仍是「缺实现」,但人读完源码判定**补不了**。
# 与 ADJUDICATED 的区别:那张表管"就绪且未迁",这张表管"缺实现",两者都只做**标注**。
GAP_ADJUDICATED = {
"OrderExamineController": (
u"⛔ 不可迁:半合并壳(实现只在未合入的特性分支)",
u"老门户 `orderexamine/OrderExamineController`(2021-08 高位订单数据检查,2 端点)在"
u" `beijing_master` / `beijing_dev` / `chifeng_master` / `chifeng_dev` **四个生产分支上只有"
u" Controller + `clouds.api` 接口与 DTO,没有任何实现**:"
u"① 全仓(HEAD)`implements OrderExamineService` = 0;② Dubbo 只有 consumer,**没有 provider**;"
u"③ `UserInfo.examinePlNos` **全仓无一处 set**(登录时不填)⇒ `queryLists` 恒抛 `AUTH_PL_NOS_EMPTY`,"
u"`updateStatus` 恒「no provider available」;④ 仓库里也没有 `order_examine_data` / `sys_op_examine_rel` 的 DDL。"
u"实现(`OrderExamineServiceImpl` + `OrderExamineData(+Example)` + 两套 Mapper/XML + "
u"`SysOpExamineRelService`)**只存在于未合入的 `origin/chifeng_dev_high_video_examine`**(最后提交 2021-08)。"
u"⇒ 照搬 Controller 在新栈必因缺 Bean 启动失败;补实现 = 把这个未发布特性整块搬进新栈(约 3000 行生成代码 +"
u" `examinePlNos` 登录桥 + 两张表),**属 P4 特性移植,不是 P2 Controller 迁移**。"
u"→ 若要上线「高位视频审核」,先由产品确认,再从该分支整体移植。"),
"CustPersonCreditController": (
u"⛔ 不可迁:服务实现整文件被注释(两栈同款)",
u"老门户 `custperson/CustPersonCreditController`(5 端点:分页/新增/审核/修改/批量删)。"
u"`CustPersonCreditServiceImpl.java` 在**老仓和新栈里都是从第 1 行 `//package` 起整文件注释**,"
u"全仓 `implements CustPersonCreditService` 的有效代码 = 0(包 `com.zteits.pay.biz.custperson.oper.impl`);"
u"`CustCreDitDicJob` 里对它的注入同样被注释。⇒ 老栈走 Dubbo **没有 provider**(端点本来就恒失败),"
u"新栈若照搬 `@Resource CustPersonCreditService` 会**没有任何 Bean**,整个应用启动即失败。"
u"补实现 = 从被注释的 200 行里恢复 Service + DAO + `td_c_cust_person_credit` 表 + 征信 WebService 调用,"
u"**属功能复活,不是 P2 Controller 迁移**(红线⑥:存量缺陷照搬不修)。"
u"★ 同一分支下 `custperson/TdCCustBusinessPicController` / `TdCCustBusinessController` 等**有活实现**,"
u"只有这一类是死的。"),
}
def gap_adjudicated(controller):
"""返回缺口档的人工结论 (标记, 依据);没登记过就是 (None, None)。"""
hit = GAP_ADJUDICATED.get(controller)
if hit is None:
return None, None
return hit
def idspace_adjudicated(controller):
"""返回 ⚠A 档的核销结论 (标记, 依据);没登记过就是 (None, None)。"""
hit = IDSPACE_FALSE_POSITIVE.get(controller)
if hit is None:
return None, None
return hit
def adjudicated(controller):
"""返回 (标记, 依据);没登记过就是 (None, None)。"""
hit = ADJUDICATED.get(controller)
if hit is None:
return None, None
return hit
def count_endpoints(src):
"""数**方法级**映射。⚠️ 类级 `@RequestMapping("equipmentManagement")` 不算端点,
但 `ENDPOINT_PAT` 认不出两者——它就是一个 `@RequestMapping(`。
第一版因此把**每个带类级映射的 Controller 都多算 1 个端点**
(`EquipmentManagementController` 数出 11,实际 10——对着文件数一遍才发现)。
做法:类声明之前的映射是类级的,之后的才是方法级的。
"""
m = CLASS_PAT.search(src)
return len(ENDPOINT_PAT.findall(src[m.end():] if m else src))
def javadoc_head(src):
"""类 javadoc 的**抬头**:首行标题 + 之后的普通段落,
到第一个 `<h2>`/`<h3>` 小标题或 `@author` 为止。
`<p>` 不算分界——三种 provenance 写法并存(反引号里是实际内容):
① 标题行里:老门户 `inspect/TpPInspectRecordController`,路由 ...
→ 反引号里是**类名**(前面还可能带老包路径)
② 第二段:来源:老门户 `zteits-bcp-portal` 的 `AreaManageController`
→ 反引号里是**类名**
③ 标题行里:老门户 `channelManagement`
→ 反引号里是**路由**,不是类名!这一种光看类名认不出来(见 scan_legacy 第二遍)
但 `<h2>`/`<h3>` 里全是"与老代码的差异"「不要碰」之类的**对照性**提及,必须排除——
否则 `ParkLotExtendController` 里那句"与老 `ParkLotManageController` 的端点不混在一起"
会把 19 个端点的 `ParkLotManageController` 当成已迁(见 PROVENANCE_PAT 注释)。
"""
m = re.search(r'/\*\*(.*?)\*/', src, flags=re.S)
if not m:
return ""
para = []
for line in m.group(1).splitlines():
body = line.strip().lstrip('*').strip()
if body.startswith('<h') or body.startswith('@'):
break
para.append(body)
return "\n".join(para)
def provenance_mentions(src):
"""抬头里指认的老**类名**(写法①②)。"""
return PROVENANCE_PAT.findall(javadoc_head(src))
def backtick_mentions(src):
"""抬头里所有反引号词,小写(含写法③的**路由**,也可能含 `BizResult` 这类噪声)。"""
return {t.lower().lstrip('/') for t in BACKTICK_PAT.findall(javadoc_head(src))}
def collect_new_stack(new_root):
"""扫新栈:接口简单名集合 + 实现类的 `implements` 目标集合 + 已迁控制器的类名集合。"""
api_dir = os.path.join(new_root, "parking-module-api/src/main/java")
ifaces = set()
api_classes = set()
for f in glob.glob(api_dir + "/com/zteits/clouds/api/service/**/*.java", recursive=True):
with open(f, encoding="utf-8", errors="replace") as fh:
src = fh.read()
m = IFACE_PAT.search(src)
if m:
ifaces.add(m.group(1))
else:
for cm in API_CLASS_PAT.finditer(strip_comments(src)):
api_classes.add(cm.group(1))
impls = set()
# ⚠️ 第一百五十四切片修正:`*Impl.java` 漏了 `RefundServiceIpml.java`(存量文件名把
# Impl 拼成 Ipml),补一条 `*Ipml.java`;两条都扫,其余逻辑不变。
# ⚠️ 第一百六十五切片修正:按文件名 glob 还会漏掉**不叫 Impl 的实现类**——桥接空实现
# `NoopDfGroundLockMsgPushService implements DfGroundLockMsgPushService` 就因此被判
# 「无实现」,让已迁的 GroundLockManageController 一直挂在缺口队列里。改为扫全部
# `*.java`(`implements` 预过滤后再套 IMPL_PAT),彻底不依赖命名约定。
# ⚠️ 第二百七十四切片修正:**注释里的 `implements` 不算实现**。
# `CustPersonCreditServiceImpl.java` 整个文件(第 1 行就是 `//package`)都被注释掉,
# 旧逻辑只做 `"implements" in src` 的文本预过滤 ⇒ 把它当成「有实现」,于是
# `CustPersonCreditController` 长期挂在「就绪且未迁」队列里;真照搬控制器会因
# **没有 Bean** 让整个应用启动失败(@Resource 注入不到)。改为先 `strip_comments`
# 再匹配,与上面 API 接口那段做法保持一致。
for f in glob.glob(new_root + "/parking-module-*/src/main/java/**/*.java", recursive=True):
with open(f, encoding="utf-8", errors="replace") as fh:
src = fh.read()
stripped = strip_comments(src)
if "implements" not in stripped:
continue
for m in IMPL_PAT.finditer(stripped):
impls.add(m.group(2))
# 已迁集合。**不能只比类名**——新类普遍按路由约定改名(老 `TpPInspectRecordController`
# → 新 `InspectRecordController`),逐名比对会把已迁的都判成未迁(第一版就栽在这,
# 只认出 13 个,实际 40+)。两路合并:
# ① 新类**注释里提到老类名**(约定形如「老门户 `inspect/TpPInspectRecordController`」)
# —— 取反引号或 `/` 紧跟的 `*Controller` 词;
# ② 类名小写相同(兼容没有 provenance 注释的老迁移,如 `ParkingLBSController`
# ↔ `ParkingLbsController`,老工程大小写不统一)。
migrated, route_mentions = set(), set()
for f in glob.glob(new_root + "/parking-module-*/src/main/java/**/*Controller.java", recursive=True):
migrated.add(os.path.basename(f)[:-len(".java")].lower())
with open(f, encoding="utf-8", errors="replace") as fh:
src = fh.read()
for name in provenance_mentions(src):
migrated.add(name.lower())
route_mentions |= backtick_mentions(src)
return ifaces, impls, migrated, route_mentions, api_classes
def scan_legacy(legacy_root, ifaces, impls, migrated, route_mentions, api_classes):
rows, dead = [], []
for f in glob.glob(legacy_root + "/**/*Controller.java", recursive=True):
with open(f, encoding="utf-8", errors="replace") as fh:
raw = fh.read()
src = strip_comments(raw)
# 整类被注释掉 / 没有任何**方法级**映射 → 死代码,不计入待迁(见模块注释第 1 条)。
# 顺手记下来,好让文档里"死代码 N 个"那句话是可复现的,而不是手数出来的。
if not count_endpoints(src):
dead.append({"controller": os.path.basename(f)[:-len(".java")],
"path": os.path.relpath(f, legacy_root)})
continue
svc = sorted(set(SVC_IMPORT_PAT.findall(src)))
gaps = []
for fqn in svc:
simple = fqn.rsplit(".", 1)[1]
# `api.service.*` 下的**类**(入参 DTO)不是服务契约,import 它不算缺口。
# 修复前 `RenewVipAreaCardOperReq` 把 TVipTimeCardCustInfo / TVipAreaCardCustInfo
# 两个类误挡在缺口档(共 15 端点)。
if simple in api_classes:
continue
if simple not in ifaces:
gaps.append((simple, "接口不在新栈"))
elif simple not in impls:
gaps.append((simple, "无实现"))
cm = CLASS_PAT.search(src)
head = src[:cm.start()] if cm else ""
routes = ROUTE_PAT.findall(head)
name = os.path.basename(f)[:-len(".java")]
ids_hits = len(LEGACY_ID_PAT.findall(src))
# 命中行的**证据**(行号 + 原文),供人肉核对 `getPlNos()` 打在谁身上——见 ⚠A/⚠B 切分。
# 只在有命中时才算(绝大部分类是 0),行号取自 `strip_comments_keep_lines`(保换行)。
id_lines = []
if ids_hits:
id_lines = [(i + 1, l.strip()) for i, l in
enumerate(strip_comments_keep_lines(raw).split("\n"))
if LEGACY_ID_PAT.search(l)]
rows.append({
"controller": name,
"path": os.path.relpath(f, legacy_root),
"endpoints": count_endpoints(src),
"services": len(svc),
"migrated": name.lower() in migrated,
"route": routes[-1].lstrip("/").lower() if routes else "",
"idSpaceHits": ids_hits,
# 坑 22 的第二刀:整类有没有**任何一处**读登录身份。False ⇒ `ids_hits` 全是
# 打在请求体/DTO 上的机械误报(见 IDENT_READ_PAT 注释)。用剥注释后的 src 判。
"hasIdentitySource": bool(IDENT_READ_PAT.search(src)),
"idSpaceLines": id_lines,
"gaps": [{"service": s, "reason": r} for s, r in gaps],
})
# 第二遍:用**类级路由**认"已迁"(provenance 写法③只给了路由,没给类名)。
# ⚠️ 只有当这个路由在**老门户里只对应一个 Controller**时才敢认——`parklotmanage` 一个路由
# 下挂了 3 个类(`ParkLotManageController`/`ParkLotExtendController`/`ParkingAssistantController`),
# 新类注释里的 `parkLotManage` 只证明**其中一个**迁了,拿它认全组就会把 19 个端点的
# `ParkLotManageController` 从待办队列里藏掉。共享路由的组进 `ambiguous`,**宁可报不确定,不猜**。
groups = {}
for r in rows:
if r["route"]:
groups.setdefault(r["route"], []).append(r)
ambiguous = []
for route, members in groups.items():
if len(members) != 1:
if route in route_mentions or any(m["migrated"] for m in members):
ambiguous.append({"route": route,
"controllers": [m["controller"] for m in members]})
continue
if route in route_mentions:
members[0]["migrated"] = True
return rows, dead, ambiguous
def main():
ap = argparse.ArgumentParser(description="存量 Controller 就绪度盘点(挑下一批迁移目标的第一步)")
ap.add_argument("--legacy-root", default=DEFAULT_LEGACY,
help="老门户 Controller 根目录(默认 %s)" % DEFAULT_LEGACY)
ap.add_argument("--new-root", default=DEFAULT_NEW,
help="新工程根目录(默认 %s)" % DEFAULT_NEW)
ap.add_argument("--json", action="store_true", help="输出 JSON")
ap.add_argument("--top", type=int, default=70, help="纯文本模式下列出多少个就绪项(默认 70)")
args = ap.parse_args()
if not os.path.isdir(args.legacy_root):
print("✘ 找不到老门户目录:%s" % args.legacy_root)
print(" 请从 parking-cloud/ 的父目录运行,或用 --legacy-root 指定。")
return 1
if not os.path.isdir(args.new_root):
print("✘ 找不到新工程目录:%s" % args.new_root)
return 1
ifaces, impls, migrated, route_mentions, api_classes = collect_new_stack(args.new_root)
if not ifaces or not impls:
print("✘ 新栈扫到 接口 %d 个 / 实现 %d 个——至少一边是 0,说明路径错了,报告不可信。"
% (len(ifaces), len(impls)))
return 1
rows, dead, ambiguous = scan_legacy(args.legacy_root, ifaces, impls, migrated,
route_mentions, api_classes)
if not rows:
print("✘ 老门户目录下没扫到任何带映射的 Controller:%s" % args.legacy_root)
return 1
ready = [r for r in rows if not r["gaps"]]
gap = [r for r in rows if r["gaps"]]
ready.sort(key=lambda r: -r["endpoints"])
todo = [r for r in ready if not r["migrated"]] # 就绪 **且** 还没迁 —— 这才是待办队列
done = [r for r in ready if r["migrated"]]
# 再按坑 22 切一刀:碰了 orgId/plNos/userAreas 的**不能先迁**(等 P3 打通 id 空间)
clean = [r for r in todo if not r["idSpaceHits"]]
haz = [r for r in todo if r["idSpaceHits"]]
# 坑 22 的**第二刀**:⚠ 档内部再按"整类读没读登录身份"一分为二。
# ⚠A `hasIdentitySource == False` —— 机械误报候选(`getPlNos()` 全打在请求体/DTO 上),
# 不阻塞迁移,但**每个类仍要人肉核对一次**(`idSpaceLines` 已经把行号+原文打出来了)。
# ⚠B `hasIdentitySource == True` —— 真读了登录态,还要追"读到的值有没有进 SQL"
# (第三十三切片的 INERT 先例),这才是真正等 P3 的那批。
hazA = [r for r in haz if not r["hasIdentitySource"]]
hazB = [r for r in haz if r["hasIdentitySource"]]
clean.sort(key=lambda r: -r["endpoints"])
haz.sort(key=lambda r: -r["endpoints"])
hazA.sort(key=lambda r: -r["endpoints"])
hazB.sort(key=lambda r: -r["endpoints"])
_adj_c = [r for r in clean if r["controller"] in ADJUDICATED]
# 缺口档也要有人工结论:有些"缺实现"不是能补的(实现只存在于未合入的特性分支)⇒ 单独标注。
_gap_adj = [r for r in gap if r["controller"] in GAP_ADJUDICATED]
_gap_open = [r for r in gap if r["controller"] not in GAP_ADJUDICATED]
if args.json:
print(json.dumps({
"ready": ready, "withGaps": gap, "dead": dead, "ambiguous": ambiguous,
"summary": {
"readyControllers": len(ready),
"readyEndpoints": sum(r["endpoints"] for r in ready),
"readyAndMigratedControllers": len(done),
"readyAndMigratedEndpoints": sum(r["endpoints"] for r in done),
"readyAndUnmigratedControllers": len(todo),
"readyAndUnmigratedEndpoints": sum(r["endpoints"] for r in todo),
"goodToGoControllers": len(clean),
"goodToGoEndpoints": sum(r["endpoints"] for r in clean),
"idSpaceHazardControllers": len(haz),
"idSpaceHazardEndpoints": sum(r["endpoints"] for r in haz),
# 第二刀的两半:A = 机械误报候选(无登录态读取),B = 真读了。A ∪ B = 上面的 haz。
"idSpaceFalsePositiveControllers": len(hazA),
"idSpaceFalsePositiveEndpoints": sum(r["endpoints"] for r in hazA),
"idSpaceRealControllers": len(hazB),
"idSpaceRealEndpoints": sum(r["endpoints"] for r in hazB),
"gapControllers": len(gap),
"gapEndpoints": sum(r["endpoints"] for r in gap),
# ⛔ 已判「不可迁」的缺口(半合并壳/上游特性未合入本分支)——**不参与**上面的机械计数,
# 单独报一份,让"可迁缺口"能归零而"机械缺口"仍说真话。
"gapAdjudicatedControllers": len(_gap_adj),
"gapAdjudicatedEndpoints": sum(r["endpoints"] for r in _gap_adj),
"gapOpenControllers": len(_gap_open),
"gapOpenEndpoints": sum(r["endpoints"] for r in _gap_open),
"deadControllers": len(dead),
"adjudicatedInClean": len(_adj_c),
"adjudicatedEndpoints": sum(r["endpoints"] for r in _adj_c),
"totalControllers": len(rows) + len(dead),
},
}, ensure_ascii=False, indent=2))
return 0
print("=" * 72)
print("就绪(依赖服务在新栈全都有接口+实现):**%d 个 Controller / %d 个端点**"
% (len(ready), sum(r["endpoints"] for r in ready)))
print(" 其中已迁 %d 个 / %d 端点;就绪且未迁 %d 个 / %d 端点 ← 待办队列"
% (len(done), sum(r["endpoints"] for r in done),
len(todo), sum(r["endpoints"] for r in todo)))
print("=" * 72)
print("就绪且未迁的待办队列按**坑 22**再切一刀(数值=从登录态读 orgId/plNos/userAreas 的次数):")
_adj_eps = sum(r["endpoints"] for r in _adj_c)
_unjudged = [r for r in clean if r["controller"] not in ADJUDICATED]
print(" ✔ %d 个 / %d 端点 **坑 22 安全**" % (len(clean), sum(r["endpoints"] for r in clean)))
if _unjudged:
print(" └ 其中已逐个判定 %d 个 / %d 端点;⚠ **还有 %d 个未判定**(先读源码再开搬)"
% (len(_adj_c), _adj_eps, len(_unjudged)))
else:
print(" └ 其中 %d 个 / %d 端点**已全部判定**(见下表)⇒ **当前 0 个可直接开搬**"
% (len(_adj_c), _adj_eps))
print(" ⚠ %d 个 / %d 端点 碰了身份字段(`LEGACY_ID_PAT` 机械命中)——**但这一档里有两拨人**:"
% (len(haz), sum(r["endpoints"] for r in haz)))
print(" ├ ⚠A %d 个 / %d 端点 **整类没有一处读登录态** ⇒ 机械误报候选"
"(`getPlNos()` 打在请求体/DTO 上),逐类核对命中行即可开搬"
% (len(hazA), sum(r["endpoints"] for r in hazA)))
print(" └ ⚠B %d 个 / %d 端点 **真读了登录态**(`getUserInfo`/`SessionCommUtil`)"
"⇒ 等 P3(迁了就是静默少数据)"
% (len(hazB), sum(r["endpoints"] for r in hazB)))
print()
print("=" * 72)
print("✔ 可以开搬(就绪 + 未迁 + 坑 22 安全),按端点数列:**%d 个 / %d 端点**"
% (len(clean), sum(r["endpoints"] for r in clean)))
print("=" * 72)
for r in clean[:args.top]:
_mark, _why = adjudicated(r["controller"])
print(" eps=%-3d %-44s %s%s"
% (r["endpoints"], r["controller"], r["path"],
(" [%s]" % _mark) if _mark else " [未判定]"))
print()
print(" 注:本脚本的 ✔ 只答「服务齐 + 没读身份字段」,**不答「能不能交付」**;下面是人工判定结论:")
for r in clean[:args.top]:
_mark, _why = adjudicated(r["controller"])
if _mark:
print(" %-24s %s" % (_mark, r["controller"]))
print(" └ %s" % _why)
else:
print(" %-24s %s ⚠ 未判定(只能算「有竞争者」,不能当可开搬)" % ("❓", r["controller"]))
if len(clean) > args.top:
print(" … 另有 %d 个(用 --top 调整,或 --json 取全量)" % (len(clean) - args.top))
print()
print("=" * 72)
print("⚠A 机械误报候选(没读登录态)——**可开搬,但逐类核对下面这几行**:**%d 个 / %d 端点**"
% (len(hazA), sum(r["endpoints"] for r in hazA)))
print("=" * 72)
_ida = [r for r in hazA if r["controller"] in IDSPACE_FALSE_POSITIVE]
print(" (已核销 %d / %d;核销结论见代码里的 `IDSPACE_FALSE_POSITIVE` 表)"
% (len(_ida), len(hazA)))
for r in hazA[:args.top]:
_mark, _why = idspace_adjudicated(r["controller"])
print(" eps=%-3d ids=%-3d %-40s %s [%s]"
% (r["endpoints"], r["idSpaceHits"], r["controller"], r["path"],
_mark or u"❓ 未核销"))
for n, line in r["idSpaceLines"][:6]:
print(" %5d| %s" % (n, line[:110]))
if len(r["idSpaceLines"]) > 6:
print(" … 另有 %d 行命中" % (len(r["idSpaceLines"]) - 6))
if _why:
print(" └ %s" % _why)
if len(hazA) > args.top:
print(" … 另有 %d 个(用 --top 调整,或 --json 取全量)" % (len(hazA) - args.top))
print()
print("=" * 72)
print("⚠B 真读了登录态 → 等 P3:**%d 个 / %d 端点**" % (len(hazB), sum(r["endpoints"] for r in hazB)))
print("=" * 72)
for r in hazB[:args.top]:
print(" eps=%-3d ids=%-3d %-40s %s"
% (r["endpoints"], r["idSpaceHits"], r["controller"], r["path"]))
print()
print("=" * 72)
print("有缺口(搬过来就是 500,先补服务):**%d 个 Controller / %d 个端点**"
% (len(gap), sum(r["endpoints"] for r in gap)))
print("=" * 72)
for r in sorted(gap, key=lambda r: -r["endpoints"]):
detail = ", ".join("%s/%s" % (g["service"], g["reason"]) for g in r["gaps"])
_mark, _why = gap_adjudicated(r["controller"])
print(" eps=%-3d %-40s 缺: %s%s"
% (r["endpoints"], r["controller"], detail,
(" [%s]" % _mark) if _mark else ""))
if _why:
print(" └ %s" % _why)
if _gap_adj:
print(" ★ 其中 %d 个 / %d 端点**已判定不可迁**(见代码里的 `GAP_ADJUDICATED` 表)"
"⇒ 真正可迁的缺口是 **%d 个 / %d 端点**"
% (len(_gap_adj), sum(r["endpoints"] for r in _gap_adj),
len(_gap_open), sum(r["endpoints"] for r in _gap_open)))
print()
print("=" * 72)
print("死代码(整个类被注释掉 / 零映射,**不迁**):**%d 个** "
"(老门户 Controller 合计 %d 个,其中 %d 个有映射)"
% (len(dead), len(rows) + len(dead), len(rows)))
print("=" * 72)
for d in sorted(dead, key=lambda d: d["controller"]):
print(" %-44s %s" % (d["controller"], d["path"]))
if ambiguous:
print()
print("=" * 72)
print("⚠ **一个类级路由下挂了多个 Controller**,仅凭路由认不出谁迁了——"
"**别把没迁的当已迁,去 grep**:")
print("=" * 72)
for a in sorted(ambiguous, key=lambda a: a["route"]):
print(" 老路由 %s:" % a["route"])
for c in a["controllers"]:
print(" %s" % c)
print()
print("⚠️ 就绪 ≠ 可迁。**还要过坑 22**(拿 `orgId`/`plNos`/`userAreas` 过滤数据的不能先迁)"
"与死代码判定——本脚本只回答「服务齐不齐」这一个问题。")
print("⚠️ 而且坑 22 的判据是**机械的**,两个方向都会错:"
"① 它**认不出接收者**——`request.getPlNos()`(前端请求体)和 `userInfo.getPlNos()`(登录态)"
"在正则眼里一样(第三十五切片:⚠ 档里 12 个类整类没读登录态,是**纯误报**);"
"② 就算命中的是登录态,它也**不说那行读到的值有没有真的进 SQL**"
"(第三十三切片就栽在这:`ParkInOutRecordController` 的 `setTopOrgId` 在老代码里本来就是注释掉的)"
"⇒ 每个候选仍要读源码/smapper(⚠A 档看 `idSpaceLines`,⚠B 档看 smapper 的 `WHERE`)。")
print("★ ✔ 池的人工结论在上面 `ADJUDICATED` 表里(脚本只标注、不改判定);**每判定一个类都要登记进去**。")
return 0
if __name__ == "__main__":
sys.exit(main())