From afed4dfc3b1b79ba1beec26e68a1ba6ac1192c15 Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期四, 17 九月 2026 14:39:59 +0800
Subject: [PATCH] fix(投资): 修复两级表头导致的取列错位 + 新增《弹窗提示》机制(14 条规则)

---
 docs/投资/工具/市州月报解析.py |   75 ++++++++++++++++++++++++++++++++-----
 1 files changed, 64 insertions(+), 11 deletions(-)

diff --git "a/docs/\346\212\225\350\265\204/\345\267\245\345\205\267/\345\270\202\345\267\236\346\234\210\346\212\245\350\247\243\346\236\220.py" "b/docs/\346\212\225\350\265\204/\345\267\245\345\205\267/\345\270\202\345\267\236\346\234\210\346\212\245\350\247\243\346\236\220.py"
index fcc2043..ce0f819 100644
--- "a/docs/\346\212\225\350\265\204/\345\267\245\345\205\267/\345\270\202\345\267\236\346\234\210\346\212\245\350\247\243\346\236\220.py"
+++ "b/docs/\346\212\225\350\265\204/\345\267\245\345\205\267/\345\270\202\345\267\236\346\234\210\346\212\245\350\247\243\346\236\220.py"
@@ -76,21 +76,45 @@
     return str(v)
 
 
-def pick_columns(cell, nrows, ncols):
-    """鎸夎〃澶村叧閿瓧瀹氫綅鍒楋紱鍚屼竴瀛楁鍛戒腑澶氭鏃讹紝鍙栤�滆鍙锋渶灏忋�佸叾娆℃槸鍛戒腑璇嶆渶鐭�濈殑閭d釜銆�"""
+def pick_columns(cell, nrows, ncols, merges=None):
+    """鎸夎〃澶村叧閿瓧瀹氫綅鍒楋紱鍚屼竴瀛楁鍛戒腑澶氭鏃讹紝鍙栤�滆鍙锋渶灏忋�佸叾娆℃槸鍛戒腑璇嶆渶鐭�濈殑閭d釜銆�
+
+    涓ょ骇琛ㄥご锛堢粍琛ㄥご锛夊鐞嗭細鑻ユ煇瀛楁鐨勫懡涓惤鍦�**璺ㄥ鍒楃殑鍚堝苟鍗曞厓鏍�**閲岋紝璇存槑瀹冩槸缁勮〃澶达紝
+    涓嶈兘鐩存帴褰撴垚璇ュ瓧娈电殑鍒楋紱姝ゆ椂鍦ㄦ湰鍚堝苟鍖哄唴鎺掗櫎鈥滃叾瀹冨瓧娈垫洿娣辩殑瀛愯〃澶粹�濇墍鍗犵殑鍒楋紝
+    鑻ュ彧鍓╁敮涓�涓�鍒楋紝鍒欒鍒楀氨鏄湰瀛楁鐨勫垪銆�
+    渚嬶細閯傚窞銆婁紒涓氭湀鎶ユ眹鎬汇�婻5 缁勮〃澶淬�岃嚜骞村垵绱瀹屾垚鎶曡祫锛堜竾鍏冿級銆嶅悎骞� I:J:K锛�
+        瀛愯〃澶� I=鏈勾璁″垝鎶曡祫锛堜竾鍏冿級銆丣=鍚堣銆並=鍏朵腑锛氭湰鏈堝畬鎴愭姇璧� 鈫�
+        銆岃嚜骞村垵绱銆嶅簲钀藉湪 J锛堜笌鎶曡祫绯荤粺 G 鍒� 5507.5 涓�鑷达級锛岃�屼笉鏄� I锛�8000锛岄偅鍏跺疄鏄湰骞磋鍒掓姇璧勶級銆�
+    """
     hits = F.scan_fields(cell, nrows, ncols)
-    cols = {}
+    best = {}
     for f in FIELDS:
-        if f not in hits:
-            continue
-        best = sorted(hits[f], key=lambda h: (h[0], len(h[2])))[0]
-        cols[f] = F.colindex(best[1])
+        if f in hits:
+            best[f] = sorted(hits[f], key=lambda h: (h[0], len(h[2])))[0]
+    cols = {}
+    for f, h in best.items():
+        row1, letter, _txt = h
+        c0 = F.colindex(letter)
+        span = F.merge_span(merges, row1, c0)
+        if span and (span[1] - span[0]) > 1:
+            taken = set()
+            for f2, h2 in best.items():
+                if f2 == f or h2[0] <= row1:
+                    continue
+                c2 = F.colindex(h2[1])
+                if span[0] <= c2 < span[1]:
+                    taken.add(c2)
+            free = [c for c in range(span[0], span[1]) if c not in taken]
+            if len(free) == 1:
+                cols[f] = free[0]
+                continue
+        cols[f] = c0
     return cols, hits
 
 
-def parse_sheet(cell, nrows, ncols, city=None):
+def parse_sheet(cell, nrows, ncols, city=None, merges=None):
     """瑙f瀽涓�寮犲伐浣滆〃 鈫� (椤圭洰琛屽垪琛�, 娈典俊鎭�, 鏍¢獙淇℃伅)"""
-    cols, hits = pick_columns(cell, nrows, ncols)
+    cols, hits = pick_columns(cell, nrows, ncols, merges)
     namec = cols.get('椤圭洰鍚嶇О')
     if namec is None:
         return [], {}, {}
@@ -181,7 +205,7 @@
     bodies = [a.body] if a.body else ['瀹㈣繍锛堝杩愮珯锛�', '鐗╂祦锛堢墿娴佸洯鍖猴級']
     md = '%d鏈�' % int(a.month.split('-')[1])
 
-    records, report = [], []
+    records, report, checks = [], [], []
     report.append('# 甯傚窞鏈堟姤瑙f瀽鎶ュ憡锛�%s锛�' % a.month)
     report.append('')
     report.append('锛堝彧璇昏В鏋愶紱涓�琛屼竴涓」鐩锛屾槑缁嗚鍚屽悕 `.tsv`锛�')
@@ -207,23 +231,37 @@
                 sel = F.pick_sheet(sheets, ym=ym)
                 if not sel:
                     report.append('| %s | 锛堟湭閫変腑宸ヤ綔琛級 | | | | | |' % f[:44])
+                    checks.append({'鍝佺被': body_key, '甯傚窞': city_file or '', '鏂囦欢': f, '宸ヤ綔琛�': '',
+                                   '鐘舵��': '鏈�変腑宸ヤ綔琛�', '椤圭洰琛屾暟': 0, '涓�鑷存��': '鈥�'})
                     continue
                 nm, nr, nc, cell, _hits = sel
-                rows, seg, extra = parse_sheet(cell, nr, nc, city=city_file)
+                merges = F.sheet_merges(path).get(nm, [])
+                rows, seg, extra = parse_sheet(cell, nr, nc, city=city_file, merges=merges)
                 city = seg.get('甯傚窞') or city_file or city_by_override(f) or ''
                 sums = {k: sum(num(r.get(k)) or 0 for r in rows) for k in NUM_FIELDS}
                 sub = extra.get('灏忚鍚堣琛�') or {}
                 sub_txt, ok = [], '鈥�'
                 if not sub and extra.get('甯傚窞琛屽��'):
                     sub = {'R%s(甯傚窞琛�)' % (seg.get('甯傚窞琛�') or '?'): extra['甯傚窞琛屽��']}
+                tok = tval = None
                 if sub:
                     key, val = pick_total(sub)
+                    tok, tval = key, val
                     sub_txt.append('%s=%s' % (key, '/'.join(pretty(val.get(k)) for k in ('鑷紑濮嬪缓璁剧疮璁�', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚'))))
                     if len(sub) > 1:
                         sub_txt.append('锛堝彟鏈� %d 琛屽皬璁�/鍚堣锛�' % (len(sub) - 1))
                     same = all(abs((val.get(k) or 0) - sums[k]) < 0.05
                                for k in ('鑷紑濮嬪缓璁剧疮璁�', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚'))
                     ok = '鉁� 涓�鑷�' if same else '鈿狅笍 涓嶄竴鑷�'
+                ck = {'鍝佺被': body_key, '甯傚窞': city, '鏂囦欢': f, '宸ヤ綔琛�': nm, '鐘舵��': 'OK',
+                      '椤圭洰琛屾暟': len(rows), '鍚堣琛�': '', '涓�鑷存��': ok}
+                for k in ('鑷紑濮嬪缓璁剧疮璁�', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚'):
+                    ck['姹傚拰_' + k] = sums[k]
+                if sub:
+                    ck['鍚堣琛�'] = tok
+                    for k in ('鑷紑濮嬪缓璁剧疮璁�', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚'):
+                        ck['鍚堣_' + k] = tval.get(k)
+                checks.append(ck)
                 report.append('| %s | %s | %s | %d | %s | %s | %s |' % (
                     f[:44], nm[:14], city, len(rows),
                     '/'.join(pretty(sums[k]) for k in ('鑷紑濮嬪缓璁剧疮璁�', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚')),
@@ -234,6 +272,9 @@
                     records.append(rec)
             except Exception as e:
                 report.append('| %s | 锛堣В鏋愬け璐ワ細%s: %s锛� | | | | | |' % (f[:44], type(e).__name__, str(e)[:60]))
+                checks.append({'鍝佺被': body_key, '甯傚窞': city_file or '', '鏂囦欢': f, '宸ヤ綔琛�': '',
+                               '鐘舵��': '瑙f瀽澶辫触锛�%s: %s' % (type(e).__name__, str(e)[:80]),
+                               '椤圭洰琛屾暟': 0, '涓�鑷存��': '鈥�'})
         report.append('')
 
     # 甯傚窞姹囨�伙紙璺ㄦ枃浠跺悎骞跺悓甯傚窞锛�
@@ -254,6 +295,17 @@
             *(pretty(s[f]) for f in ('鎬绘姇璧�', '鑷紑濮嬪缓璁剧疮璁�', '鏈勾璁″垝鎶曡祫', '鑷勾鍒濈疮璁�', '鏈湀瀹屾垚'))))
     report.append('')
 
+    ck_cols = ['鍝佺被', '甯傚窞', '鏂囦欢', '宸ヤ綔琛�', '鐘舵��', '椤圭洰琛屾暟', '涓�鑷存��', '鍚堣琛�',
+               '姹傚拰_鑷紑濮嬪缓璁剧疮璁�', '姹傚拰_鑷勾鍒濈疮璁�', '姹傚拰_鏈湀瀹屾垚',
+               '鍚堣_鑷紑濮嬪缓璁剧疮璁�', '鍚堣_鑷勾鍒濈疮璁�', '鍚堣_鏈湀瀹屾垚']
+    p_ck = os.path.join(out, '瑙f瀽鏍¢獙_%s.tsv' % a.month)
+    with io.open(p_ck, 'w', encoding='utf-8-sig') as fh:
+        fh.write('\t'.join(ck_cols) + '\n')
+        for c in checks:
+            fh.write('\t'.join(pretty(c.get(k, '')) if isinstance(c.get(k), float) else str(c.get(k, ''))
+                                for k in ck_cols) + '\n')
+    print('鏍¢獙璁板綍鏁� = %d' % len(checks))
+
     cols = ['鍝佺被', '甯傚窞', '鏂囦欢', '宸ヤ綔琛�', '琛屽彿', '搴忓彿', '鍒嗙被'] + FIELDS
     tsv = os.path.join(out, '瑙f瀽缁撴灉_%s.tsv' % a.month)
     with io.open(tsv, 'w', encoding='utf-8-sig') as fh:
@@ -265,6 +317,7 @@
     print('椤圭洰琛屽悎璁� = %d' % len(records))
     print('wrote', p_md)
     print('wrote', tsv)
+    print('wrote', p_ck)
 
 
 if __name__ == '__main__':

--
Gitblit v1.9.1