From c4ab66f2e68948bf4095e25e29dad49c55fc4ba8 Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期五, 18 九月 2026 16:35:01 +0800
Subject: [PATCH] feat(货运月报): 新增持续在规企业分市州1-8月累计运距及同比台账与测试报告
---
docs/工作日结/工作日结_2026-09-18.md | 47 +++++
docs/功能测试报告/功能测试报告_货运月报持续在规企业分市州累计运距_2026-09-18.md | 75 ++++++++
docs/工具/持续在规企业累计运距生成.py | 381 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 503 insertions(+), 0 deletions(-)
diff --git "a/docs/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212_\350\264\247\350\277\220\346\234\210\346\212\245\346\214\201\347\273\255\345\234\250\350\247\204\344\274\201\344\270\232\345\210\206\345\270\202\345\267\236\347\264\257\350\256\241\350\277\220\350\267\235_2026-09-18.md" "b/docs/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212_\350\264\247\350\277\220\346\234\210\346\212\245\346\214\201\347\273\255\345\234\250\350\247\204\344\274\201\344\270\232\345\210\206\345\270\202\345\267\236\347\264\257\350\256\241\350\277\220\350\267\235_2026-09-18.md"
new file mode 100644
index 0000000..28fb00d
--- /dev/null
+++ "b/docs/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212_\350\264\247\350\277\220\346\234\210\346\212\245\346\214\201\347\273\255\345\234\250\350\247\204\344\274\201\344\270\232\345\210\206\345\270\202\345\267\236\347\264\257\350\256\241\350\277\220\350\267\235_2026-09-18.md"
@@ -0,0 +1,75 @@
+# 鍔熻兘娴嬭瘯鎶ュ憡_璐ц繍鏈堟姤鎸佺画鍦ㄨ浼佷笟鍒嗗競宸炵疮璁¤繍璺漘2026-09-18
+
+## 涓�銆佸姛鑳借鏄�
+
+- **闇�姹�**锛氬皢璐ц繍鏈堟姤銆屾寔缁湪瑙勪紒涓氥�嶅垎甯傚窞 1-8 鏈堢疮璁¤繍璺濆強鍚屾瘮鎯呭喌鏁寸悊鎴� Excel 琛ㄦ牸锛岀敓鎴愬埌妗岄潰銆�
+- **鍙e緞**锛�
+ - 鎸佺画鍦ㄨ浼佷笟 = 2026 骞� 1-8 鏈堣繛缁� 8 涓湀鍧囦笂鎶ャ�婇亾璺揣鐗╄繍杈撴湀搴︾敓浜ф儏鍐点�嬶紙H203-2锛夋湀鎶ョ殑浼佷笟锛屽叡 **636 瀹�**锛�
+ - 绱骞冲潎杩愯窛锛堝叕閲岋級= 1-8 鏈堢疮璁¤揣鐗╁懆杞噺 梅 1-8 鏈堢疮璁¤揣杩愰噺锛堝姞鏉冨钩鍧囷級锛�
+ - 鍚屾瘮 = 2026 骞� 1-8 鏈堢疮璁¤繍璺� 梅 2025 骞� 1-8 鏈堢疮璁¤繍璺� 鈭� 1锛屼负淇濊瘉鍙瘮锛屽彧鍦�**涓ゅ勾鍧囨寔缁湪瑙勭殑 545 瀹朵紒涓�**涓婅绠椼��
+- **鏁版嵁鏉ユ簮**锛歚traffic_audit.h2032_enterprise_monthly`锛�2025-01 ~ 2026-08 浼佷笟绾ф湀鎶ワ級銆�
+- **浜у嚭**锛歚C:\Users\閮戠帀娲乗Desktop\璐ц繍鏈堟姤_鎸佺画鍦ㄨ浼佷笟_鍒嗗競宸�1-8鏈堢疮璁¤繍璺濆強鍚屾瘮_2026-09-18.xlsx`锛屽惈 5 涓〉绛撅細
+ `璇存槑涓庡彛寰刞銆乣鍒嗗競宸炴眹鎬籤銆乣鍒嗗競宸炲垎鏈坄銆乣浼佷笟鏄庣粏`锛�636 琛岋級銆乣鍙e緞鏍¢獙`銆�
+- **鐢熸垚鑴氭湰**锛歚docs\宸ュ叿\鎸佺画鍦ㄨ浼佷笟绱杩愯窛鐢熸垚.py`锛堝璺戦渶鍏堢敓鎴愪袱浠� TSV 瀵煎嚭浠讹紝SQL 瑙侀檮褰曪級銆�
+
+## 浜屻�佹祴璇曠幆澧�
+
+| 椤� | 鍐呭 |
+| --- | --- |
+| 鏁版嵁搴� | MySQL 127.0.0.1:3308 / traffic_audit锛岃〃 `h2032_enterprise_monthly` |
+| 杩愯鐜 | Python 3.14.2 + openpyxl 3.1.5 |
+| 瀵圭収鐗� 1 | 銆�2026骞�8鏈堣涓婅揣鐗╁懆杞噺涓庡钩鍧囪繍璺濆垎鏋愭姤鍛婏紙鑽夌锛夈�嬪叏鐪佸綋鏈�/绱鍙e緞 |
+| 瀵圭収鐗� 2 | `docs\鐢熸垚姹囨�诲ぇ琛╘2026骞�8鏈堥亾璺繍杈撻噺姹囨�昏〃.xlsx`銆� 璐ц繍銆嶉〉銆屽叾涓涓婅揣杩愰噺/瑙勪笂璐х墿鍛ㄨ浆閲忋�� |
+| 杈撳嚭鐩綍 | 宸ヤ綔鍖� `outputs\`锛堢敓鎴愶級+ 妗岄潰锛堜氦浠橈級锛屼袱浠� SHA256 涓�鑷� |
+
+## 涓夈�佺敤渚嬭〃
+
+| 缂栧彿 | 绫诲瀷 | 杈撳叆 | 棰勬湡 | 瀹為檯 | 缁撹 |
+| --- | --- | --- | --- | --- | --- |
+| TC01 | 姝e悜 | 2026-08 鍏ㄧ渷瑙勪笂璐ц繍閲� | 3665.36 涓囧惃 | 3665.36 涓囧惃 | 閫氳繃 |
+| TC02 | 姝e悜 | 2026-08 鍏ㄧ渷瑙勪笂璐х墿鍛ㄨ浆閲� | 570004.44 涓囧惃鍏噷 | 570004.44 涓囧惃鍏噷 | 閫氳繃 |
+| TC03 | 姝e悜 | 2026 骞� 1-8 鏈堢疮璁¤揣杩愰噺/鍛ㄨ浆閲� | 31399.82 / 4927161.77 | 31399.82 / 4927161.77 | 閫氳繃 |
+| TC04 | 姝e悜 | 2026-01~07 閫愭湀瑙勪笂璐ц繍閲忋�佸懆杞噺涓庢眹鎬昏〃銆� 璐ц繍銆嶉〉姣斿锛�14 椤癸級 | 鍏ㄩ儴涓�鑷� | 14/14 涓�鑷达紙4 浣嶅皬鏁帮級 | 閫氳繃 |
+| TC05 | 姝e悜 | 17 甯傚窞瑕嗙洊闈� | 鍏ㄨ鐩栥�佹棤鏈瘑鍒湴鍖虹爜 | 17/17锛屾湭璇嗗埆 0 鏉� | 閫氳繃 |
+| TC06 | 姝e悜 | 浼佷笟鏄庣粏 636 琛屾眰鍜� | = 鍒嗗競宸炴眹鎬诲悎璁� | 31399.8248 / 4927161.7743锛屽樊寮� 0 | 閫氳繃 |
+| TC07 | 姝e悜 | 鍒嗗競宸炲垎鏈堥〉 | 8 鏈堝叏鐪� = 鍙戝竷鍙e緞 155.51 鍏噷锛涘悇甯傚窞绱鍒� = 姹囨�婚〉绱鍒� | 155.51锛�17 涓競宸為�愰」 0 宸紓 | 閫氳繃 |
+| TC08 | 姝e悜 | 鍏ㄧ渷 1-8 鏈堢疮璁″钩鍧囪繍璺� | = 绱鍛ㄨ浆閲� 梅 绱璐ц繍閲� | 156.92 = 4927161.77 梅 31399.82 | 閫氳繃 |
+| TC09 | 杈圭晫 | 2026 鍚勬湀涓婃姤浼佷笟鏁� | 鍚勬湀涓�鑷达紝鏀拺銆屾寔缁湪瑙勩�嶅垽瀹� | 鍚勬湀鍧囦负 636 瀹讹紝鍏ㄦ湡涓�鑷� | 閫氳繃 |
+| TC10 | 杈圭晫 | 5 瀹� 1-8 鏈堣揣杩愰噺銆佸懆杞噺鍏ㄤ负 0 鐨勪紒涓� | 璁″叆浼佷笟鏁帮紝涓嶅奖鍝嶅競宸炶繍璺� | 杩愯窛姹囨�绘湭鍙楀奖鍝� | 閫氳繃 |
+| TC11 | 杈圭晫 | 姝︽眽鐭冲寲浜ら�氳繍杈撴湁闄愬叕鍙搞�佹箹鍖楁嵎榫欎氦閫氳繍涓氭湁闄愬叕鍙� 1 鏈堝湴鍖虹爜 420100銆佸叾鍚� 420107/420103 | 鍚屽睘姝︽眽甯傦紝褰掗泦涓嶅彉 | 鍧囧綊鍏ユ姹夊競 | 閫氳繃 |
+| TC12 | 璐熷悜 | 浠欐甯傘�屼袱骞村潎鎸佺画鍦ㄨ銆嶄紒涓氭暟 = 0 | 鍚屾瘮鐣欑┖ + 鏄庣‘澶囨敞锛屼笉闄ら浂鎶ラ敊 | 鍚屾瘮鍒楃┖锛屽娉ㄣ�屽悓姣斾笉閫傜敤锛�2025 骞� 1-8 鏈堟棤杩炵画涓婃姤璁板綍銆� | 閫氳繃 |
+| TC13 | 璐熷悜 | 绱璐ц繍閲忎负 0 鐨勪紒涓� | 绱杩愯窛鐣欑┖锛屼笉闄ら浂鎶ラ敊 | 閫昏緫宸查槻闄ら浂锛堟湰琛� 636 瀹舵棤姝ょ被浼佷笟锛� | 閫氳繃 |
+| TC14 | 璐熷悜 | 鍦板尯鐮佷笉鍦� 17 甯傚窞鏄犲皠鍐� | 褰掑叆銆屾湭璇嗗埆銆嶅苟鍦ㄦ牎楠岄〉鏆撮湶 | 瀹為檯 0 鏉� | 閫氳繃 |
+
+## 鍥涖�佸彂鐜扮殑闂涓庡缃�
+
+1. **鐢熸垚鑴氭湰琛屽彿閿欎綅锛堝凡淇級**锛氶娆$敓鎴愭椂姹囨�婚〉琛ㄥご琚暟鎹鐩栥�佹湯琛屻�岀鍐滄灦鏋楀尯銆嶈鍚堣琛岃鐩栵紝鏍瑰洜鏄� `rr` 鑷娆℃暟涓庡啓琛屼綅缃笉鍖归厤銆備慨姝e悗閲嶈窇锛宍鍒嗗競宸炴眹鎬籤 涓鸿〃澶� + 17 甯傚窞 + 鍚堣鍏� 18 琛岋紝閫愯鏍稿鏃犺銆�
+2. **妗岄潰鍓湰涓�搴︿负淇鍓嶇増鏈紙宸蹭慨锛�**锛氶噸鏂板悓姝ュ苟鏍¢獙宸ヤ綔鍖轰笌妗岄潰鏂囦欢 SHA256 涓�鑷达紙94869 瀛楄妭锛夈��
+3. **鍙e緞寰呬笟鍔$‘璁わ紙2 椤癸級**锛�
+ - 銆屾寔缁湪瑙勪紒涓氥�嶇洰鍓嶆寜銆�2026 骞� 1-8 鏈堣繛缁� 8 涓湀涓婃姤銆嶅畾涔変负 636 瀹讹紱鑻ヤ笟鍔″彛寰勬槸銆岃繛缁袱骞村湪瑙勩�嶆垨銆岃繛缁� 12 涓湀鍦ㄨ銆嶏紝闇�閲嶆柊鍙栨暟銆�
+ - 鍚屾瘮閲囩敤 545 瀹跺彲姣斿彛寰勶紙鍓旈櫎 91 瀹� 2025 骞存棤鍚屾湡杩炵画涓婃姤鐨勪紒涓氾級锛屼粰妗冨競鍥犳棤鍙瘮浼佷笟鍚屾瘮鐣欑┖銆�
+
+## 浜斻�佺粨璁�
+
+14 鏉$敤渚嬶紙8 姝e悜 / 3 杈圭晫 / 3 璐熷悜锛夊叏閮ㄩ�氳繃锛涘叏鐪佸綋鏈堛�佺疮璁°�佸垎甯傚窞涓変釜灞傜骇涓庡凡鍙戝竷鍙e緞**闆跺樊寮�**锛屾槑缁嗕笌姹囨�婚�愰」瀵硅处涓�鑷达紝浜や粯浠跺凡鐢熸垚鍒版闈€�備粎閬楃暀 2 椤瑰彛寰勭‘璁や簨椤癸紝涓嶅奖鍝嶆湰娆′氦浠樻暟鎹湰韬殑姝g‘鎬с��
+
+## 闄勫綍锛氬璺戠敤 SQL
+
+```sql
+-- 浼佷笟绾х疮璁★紙瀵煎嚭涓� _tmp_persist_scope.tsv锛�
+SELECT MAX(region_code) rc, enterprise_name en, IFNULL(MAX(enterprise_code),'') ec,
+ IFNULL(MAX(unified_credit_code),'') ucc,
+ SUM(CASE WHEN report_period BETWEEN '2026-01' AND '2026-08' THEN IFNULL(freight_total,0) ELSE 0 END) f26,
+ SUM(CASE WHEN report_period BETWEEN '2026-01' AND '2026-08' THEN IFNULL(turnover_total,0) ELSE 0 END) t26,
+ COUNT(DISTINCT CASE WHEN report_period BETWEEN '2026-01' AND '2026-08' THEN report_period END) m26,
+ SUM(CASE WHEN report_period BETWEEN '2025-01' AND '2025-08' THEN IFNULL(freight_total,0) ELSE 0 END) f25,
+ SUM(CASE WHEN report_period BETWEEN '2025-01' AND '2025-08' THEN IFNULL(turnover_total,0) ELSE 0 END) t25,
+ COUNT(DISTINCT CASE WHEN report_period BETWEEN '2025-01' AND '2025-08' THEN report_period END) m25
+FROM h2032_enterprise_monthly
+WHERE report_period BETWEEN '2025-01' AND '2026-08'
+GROUP BY enterprise_name HAVING m26 = 8;
+
+-- 浼佷笟绾у垎鏈堬紙瀵煎嚭涓� _tmp_persist_scope_monthly.tsv锛�
+SELECT report_period, region_code, enterprise_name, IFNULL(freight_total,0), IFNULL(turnover_total,0)
+FROM h2032_enterprise_monthly WHERE report_period BETWEEN '2025-01' AND '2026-08';
+```
diff --git "a/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-18.md" "b/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-18.md"
index 033c6b2..3c48fb8 100644
--- "a/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-18.md"
+++ "b/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-18.md"
@@ -139,3 +139,50 @@
2. 缃戠害杞﹁鍗曞鍏ョ┖鍒楁彁绀猴紙椤甸潰鏄庣‘鎻愮ず"璇ユ湡璁㈠崟鍒椾负绌猴紝鏈啓鍏ヨ鍗曟暟"锛夆�斺�旀暟鎹櫧宸茶ˉ榻愶紝浣嗛潤榛樻垚鍔熶粛鏄殣鎮c��
3. 鎶曡祫"鏂板椤圭洰鎻掕 + 鍒嗙粍琛屽叆搴擄紙`fund_channel`锛�"鏂规钀藉湴锛堢瓑鐢ㄦ埛瀵�"妯″潡"鍙e緞鐨勬渶鍚庣‘璁わ級銆�
4. 鎶曡祫涓ゅ紶鏄庣粏琛ㄧ敓鎴愬櫒锛坄X鏈堝杩愮珯鎶曡祫鏄庣粏琛╜ / `X鏈堢墿娴佺珯鍦烘姇璧勬槑缁哷锛夈��
+
+---
+
+## 浜斻�佸倣鏅氳拷鍔狅細璐ц繍鏈堟姤銆屾寔缁湪瑙勪紒涓氥�嶅垎甯傚窞 1-8 鏈堢疮璁¤繍璺濆強鍚屾瘮锛堟闈氦浠橈級
+
+### 5.1 闇�姹備笌鍙e緞
+
+鐢ㄦ埛瑕佹眰锛氭妸璐ц繍鏈堟姤銆屾寔缁湪瑙勪紒涓氥�嶅垎甯傚窞 1-8 鏈堢疮璁¤繍璺濆強鍚屾瘮鎯呭喌鏁寸悊鎴� Excel锛岀敓鎴愬埌妗岄潰銆�
+
+- **鎸佺画鍦ㄨ浼佷笟** = 2026 骞� 1-8 鏈堣繛缁� 8 涓湀鍧囦笂鎶� H203-2 鏈堟姤鐨勪紒涓氾紝**636 瀹�**锛�2026 骞村悇鏈堜笂鎶ヤ紒涓氭暟鍧囦负 636锛屽叏鏈熶竴鑷达級銆�
+- **绱骞冲潎杩愯窛** = 1-8 鏈堢疮璁¤揣鐗╁懆杞噺 梅 绱璐ц繍閲忥紙鍔犳潈骞冲潎锛夈��
+- **鍚屾瘮** = 2026 骞� 1-8 鏈堢疮璁¤繍璺� 梅 2025 骞� 1-8 鏈堢疮璁¤繍璺� 鈭� 1锛屽彧鍦�**涓ゅ勾鍧囨寔缁湪瑙勭殑 545 瀹�**涓婅绠楋紙鍙瘮鍙e緞锛夈��
+
+### 5.2 鏁版嵁涓庝骇鍑�
+
+鏁版嵁婧愶細`traffic_audit.h2032_enterprise_monthly`锛�2025-01 ~ 2026-08锛�13,056 鏉′紒涓氱骇璁板綍锛夈��
+
+浜у嚭锛歚C:\Users\閮戠帀娲乗Desktop\璐ц繍鏈堟姤_鎸佺画鍦ㄨ浼佷笟_鍒嗗競宸�1-8鏈堢疮璁¤繍璺濆強鍚屾瘮_2026-09-18.xlsx`锛�5 椤电锛氳鏄庝笌鍙e緞 / 鍒嗗競宸炴眹鎬� / 鍒嗗競宸炲垎鏈� / 浼佷笟鏄庣粏 636 琛� / 鍙e緞鏍¢獙锛夛紝鐢熸垚鑴氭湰 `docs\宸ュ叿\鎸佺画鍦ㄨ浼佷笟绱杩愯窛鐢熸垚.py`銆�
+
+### 5.3 鍏抽敭缁撴灉
+
+| 甯傚窞 | 鎸佺画鍦ㄨ(瀹�) | 1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷) | 鍚屾瘮 | 甯傚窞 | 鎸佺画鍦ㄨ(瀹�) | 1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷) | 鍚屾瘮 |
+| --- | --- | --- | --- | --- | --- | --- | --- |
+| 姝︽眽甯� | 199 | 162.11 | +13.5% | 榛勫唸甯� | 71 | 135.14 | +1.1% |
+| 榛勭煶甯� | 20 | 117.63 | +20.2% | 鍜稿畞甯� | 17 | 113.33 | 鈭�24.1% |
+| 鍗佸牥甯� | 13 | 321.13 | 鈭�2.9% | 闅忓窞甯� | 26 | 187.28 | 鈭�17.1% |
+| 瀹滄槍甯� | 65 | 88.55 | +2.6% | 鎭╂柦宸� | 4 | 130.11 | +10.3% |
+| 瑗勯槼甯� | 81 | 274.50 | +8.4% | 浠欐甯� | 2 | 65.61 | 涓嶉�傜敤 |
+| 閯傚窞甯� | 6 | 38.15 | 鈭�59.0% | 娼滄睙甯� | 13 | 394.20 | +10.9% |
+| 鑽嗛棬甯� | 40 | 281.41 | 鈭�4.7% | 澶╅棬甯� | 4 | 61.02 | 鈭�54.6% |
+| 瀛濇劅甯� | 21 | 164.00 | 鈭�7.4% | 绁炲啘鏋舵灄鍖� | 2 | 57.68 | 鈭�49.1% |
+| 鑽嗗窞甯� | 52 | 324.47 | 鈭�4.7% | **鍏ㄧ渷** | **636** | **156.92** | **+2.9%** |
+
+- 鍏ㄧ渷绱璐ц繍閲� 31399.82 涓囧惃銆佺疮璁″懆杞噺 4927161.77 涓囧惃鍏噷锛屼笌銆�2026骞�8鏈堣涓婅揣鐗╁懆杞噺涓庡钩鍧囪繍璺濆垎鏋愭姤鍛娿��**瀹屽叏涓�鑷�**锛�8 鏈堝綋鏈堝叏鐪佸钩鍧囪繍璺� 155.51 鍏噷浜︿笌鍙戝竷鍙e緞涓�鑷淬��
+- 绱杩愯窛鏄庢樉鎷夐暱鐨勫彧鏈夋姹夛紙+13.5%锛夈�侀粍鐭筹紙+20.2%锛夛紱閯傚窞锛堚垝59.0%锛夈�佸ぉ闂紙鈭�54.6%锛夈�佹灄鍖猴紙鈭�49.1%锛夈�佸捀瀹侊紙鈭�24.1%锛夈�侀殢宸烇紙鈭�17.1%锛変负澶у箙缂╃煭锛屽缓璁笟鍔′晶鍏虫敞銆�
+
+### 5.4 娴嬭瘯涓庨棶棰�
+
+銆婂姛鑳芥祴璇曟姤鍛奯璐ц繍鏈堟姤鎸佺画鍦ㄨ浼佷笟鍒嗗競宸炵疮璁¤繍璺漘2026-09-18.md銆嬶細14 鏉$敤渚嬶紙8 姝e悜 / 3 杈圭晫 / 3 璐熷悜锛夊叏閮ㄩ�氳繃锛�2026-01~07 閫愭湀瑙勪笂璐ц繍閲忋�佸懆杞噺涓庢眹鎬昏〃銆� 璐ц繍銆嶉〉 14 椤规瘮瀵� 0 宸紓銆�
+
+- 鐢熸垚鑴氭湰琛屽彿鑷閿欎綅瀵艰嚧琛ㄥご/鏈琚鐩� 鈫� 宸蹭慨姝e苟閫愯澶嶆牳锛堣〃澶� + 17 甯傚窞 + 鍚堣鍏� 18 琛岋級銆�
+- 妗岄潰鍓湰绗竴鐗堜负淇鍓嶆枃浠� 鈫� 宸查噸鏂板悓姝ワ紝SHA256 涓�鑷淬��
+
+### 5.5 寰呭姙
+
+1. 璇蜂笟鍔$‘璁ゃ�屾寔缁湪瑙勪紒涓氥�嶅畾涔夛紙鐜版寜 2026 骞� 1-8 鏈堣繛缁� 8 涓湀涓婃姤锛�636 瀹讹級锛涜嫢搴旀槸銆岃繛缁袱骞村湪瑙勩�嶇瓑鍙e緞锛岄渶閲嶆柊鍙栨暟銆�
+2. 浠欐甯� 2026 骞� 2 瀹舵寔缁湪瑙勪紒涓� 2025 骞� 1-8 鏈堟棤杩炵画涓婃姤璁板綍锛屽悓姣旂暀绌猴紝寤鸿鏍稿璇ュ競宸炲巻鍙蹭笂鎶ャ��
diff --git "a/docs/\345\267\245\345\205\267/\346\214\201\347\273\255\345\234\250\350\247\204\344\274\201\344\270\232\347\264\257\350\256\241\350\277\220\350\267\235\347\224\237\346\210\220.py" "b/docs/\345\267\245\345\205\267/\346\214\201\347\273\255\345\234\250\350\247\204\344\274\201\344\270\232\347\264\257\350\256\241\350\277\220\350\267\235\347\224\237\346\210\220.py"
new file mode 100644
index 0000000..6eddd68
--- /dev/null
+++ "b/docs/\345\267\245\345\205\267/\346\214\201\347\273\255\345\234\250\350\247\204\344\274\201\344\270\232\347\264\257\350\256\241\350\277\220\350\267\235\347\224\237\346\210\220.py"
@@ -0,0 +1,381 @@
+# -*- coding: utf-8 -*-
+"""璐ц繍鏈堟姤銆屾寔缁湪瑙勪紒涓氥�嶅垎甯傚窞 1-8 鏈堢疮璁¤繍璺濆強鍚屾瘮 鍙拌处鐢熸垚
+
+鏁版嵁婧愶細traffic_audit.h2032_enterprise_monthly锛堛�婇亾璺揣鐗╄繍杈撴湀搴︾敓浜ф儏鍐点�婬203-2 浼佷笟鏈堟姤锛�
+ 瀵煎嚭浠� _tmp_persist_scope.tsv锛堜紒涓氱骇绱锛夈�乢tmp_persist_scope_monthly.tsv锛堜紒涓氱骇鍒嗘湀锛�
+鍙e緞锛�
+ 路 鎸佺画鍦ㄨ浼佷笟 = 2026 骞� 1-8 鏈堣繛缁� 8 涓湀鍧囦笂鎶ユ湀鎶ョ殑浼佷笟锛�
+ 路 绱骞冲潎杩愯窛锛堝叕閲岋級= 绱璐х墿鍛ㄨ浆閲� 梅 绱璐ц繍閲忥紙鍔犳潈骞冲潎锛夛紱
+ 路 鍚屾瘮 = 2026 骞� 1-8 鏈堢疮璁″钩鍧囪繍璺� 梅 2025 骞� 1-8 鏈堢疮璁″钩鍧囪繍璺� 鈭� 1锛�
+ 涓轰繚璇佸彲姣旓紝鍙湪涓ゅ勾鍧囨寔缁湪瑙勭殑浼佷笟涓婅绠椼��
+"""
+import io, os
+from openpyxl import Workbook
+from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
+from openpyxl.utils import get_column_letter
+
+SRC = r'D:\鏂囨。\ChatGPT\traffic-audit\_tmp_persist_scope.tsv'
+MONTHLY_SRC = r'D:\鏂囨。\ChatGPT\traffic-audit\_tmp_persist_scope_monthly.tsv'
+OUT_DIR = r'D:\鏂囨。\ChatGPT\traffic-audit\outputs'
+OUT = os.path.join(OUT_DIR, '璐ц繍鏈堟姤_鎸佺画鍦ㄨ浼佷笟_鍒嗗競宸�1-8鏈堢疮璁¤繍璺濆強鍚屾瘮_2026-09-18.xlsx')
+
+PROV_VOL_PUB = 31399.82 # 涓囧惃锛屽凡鍙戝竷銆�2026骞�8鏈堣涓婅揣鐗╁懆杞噺涓庡钩鍧囪繍璺濆垎鏋愭姤鍛娿��1-8鏈堢疮璁�
+PROV_TURN_PUB = 4927161.77 # 涓囧惃鍏噷锛屽悓涓�
+
+CITY_ORDER = ['姝︽眽甯�', '榛勭煶甯�', '鍗佸牥甯�', '瀹滄槍甯�', '瑗勯槼甯�', '閯傚窞甯�', '鑽嗛棬甯�', '瀛濇劅甯�', '鑽嗗窞甯�',
+ '榛勫唸甯�', '鍜稿畞甯�', '闅忓窞甯�', '鎭╂柦宸�', '浠欐甯�', '娼滄睙甯�', '澶╅棬甯�', '绁炲啘鏋舵灄鍖�']
+CITY = {'4201': '姝︽眽甯�', '4202': '榛勭煶甯�', '4203': '鍗佸牥甯�', '4205': '瀹滄槍甯�', '4206': '瑗勯槼甯�',
+ '4207': '閯傚窞甯�', '4208': '鑽嗛棬甯�', '4209': '瀛濇劅甯�', '4210': '鑽嗗窞甯�', '4211': '榛勫唸甯�',
+ '4212': '鍜稿畞甯�', '4213': '闅忓窞甯�', '4228': '鎭╂柦宸�',
+ '429004': '浠欐甯�', '429005': '娼滄睙甯�', '429006': '澶╅棬甯�', '429021': '绁炲啘鏋舵灄鍖�'}
+MONTHS26 = ['2026-%02d' % m for m in range(1, 9)]
+
+
+def city_of(rc):
+ rc = (rc or '').strip()
+ return CITY[rc] if rc in CITY else CITY.get(rc[:4], '鏈瘑鍒�(%s)' % rc)
+
+
+def num(x):
+ try:
+ return float(x)
+ except Exception:
+ return 0.0
+
+
+def dist(f, t):
+ return (t / f) if f else None
+
+
+def growth(now, base):
+ return (now / base - 1) if (now is not None and base) else None
+
+
+# ---------- 璇讳紒涓氱骇绱鏁版嵁 ----------
+rows = []
+with io.open(SRC, 'r', encoding='utf-8') as f:
+ hdr = f.readline().rstrip('\r\n').split('\t')
+ for line in f:
+ p = line.rstrip('\r\n').split('\t')
+ if len(p) < len(hdr):
+ continue
+ d = dict(zip(hdr, p))
+ if int(num(d['m26'])) != 8:
+ continue
+ rows.append(dict(city=city_of(d['rc']), rc=d['rc'], en=d['en'], ec=d['ec'], ucc=d['ucc'],
+ f26=num(d['f26']), t26=num(d['t26']),
+ f25=num(d['f25']), t25=num(d['t25']), cmp=(int(num(d['m25'])) == 8)))
+
+by_city = {}
+for r in rows:
+ c = by_city.setdefault(r['city'], dict(n=0, n_cmp=0, f26=0.0, t26=0.0, f26c=0.0, t26c=0.0, f25c=0.0, t25c=0.0))
+ c['n'] += 1
+ c['f26'] += r['f26']; c['t26'] += r['t26']
+ if r['cmp']:
+ c['n_cmp'] += 1
+ c['f26c'] += r['f26']; c['t26c'] += r['t26']
+ c['f25c'] += r['f25']; c['t25c'] += r['t25']
+
+tot = dict(n=0, n_cmp=0, f26=0.0, t26=0.0, f26c=0.0, t26c=0.0, f25c=0.0, t25c=0.0)
+for c in by_city.values():
+ for k in tot:
+ tot[k] += c[k]
+unknown = sorted(k for k in by_city if k.startswith('鏈瘑鍒�'))
+
+persist_names = set(r['en'] for r in rows)
+cmp_names = set(r['en'] for r in rows if r['cmp'])
+
+# ---------- 璇讳紒涓氱骇鍒嗘湀鏁版嵁 ----------
+cust = {}
+with io.open(MONTHLY_SRC, 'r', encoding='utf-8') as f:
+ for line in f:
+ p = line.rstrip('\r\n').split('\t')
+ if len(p) < 5:
+ continue
+ per, rc, en, fr, tu = p[0], p[1], p[2], p[3], p[4]
+ if per == 'report_period':
+ continue
+ cust.setdefault((per, city_of(rc)), {})[en] = (num(fr), num(tu))
+
+
+def city_month(period, city, names):
+ d = cust.get((period, city))
+ if not d:
+ return None, 0.0
+ fr = tu = 0.0
+ for en, v in d.items():
+ if en in names:
+ fr += v[0]; tu += v[1]
+ return dist(fr, tu), fr
+
+
+# ---------- 鏍峰紡 ----------
+THIN = Side(style='thin', color='BFBFBF')
+BORDER = Border(left=THIN, right=THIN, top=THIN, bottom=THIN)
+HDR_FILL = PatternFill('solid', fgColor='DDEBF7')
+TOT_FILL = PatternFill('solid', fgColor='FFF2CC')
+TITLE_FILL = PatternFill('solid', fgColor='1F4E79')
+CT = Alignment(horizontal='center', vertical='center', wrap_text=True)
+LT = Alignment(horizontal='left', vertical='center', wrap_text=True)
+RT = Alignment(horizontal='right', vertical='center')
+NF2, NF1 = '#,##0.00', '#,##0.0'
+
+wb = Workbook()
+
+# ---------------- Sheet1 璇存槑涓庡彛寰� ----------------
+ws = wb.active
+ws.title = '璇存槑涓庡彛寰�'
+ws.sheet_view.showGridLines = False
+ws.column_dimensions['A'].width = 16
+ws.column_dimensions['B'].width = 104
+t = ws.cell(1, 1, '璐ц繍鏈堟姤銆屾寔缁湪瑙勪紒涓氥�嶅垎甯傚窞 1-8 鏈堢疮璁¤繍璺濆強鍚屾瘮鎯呭喌琛�')
+t.font = Font(bold=True, size=15, color='FFFFFF')
+t.fill = TITLE_FILL
+t.alignment = Alignment(horizontal='center', vertical='center')
+ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=2)
+ws.row_dimensions[1].height = 30
+
+info = [
+ ('鏁版嵁鏉ユ簮', '绯荤粺鏁版嵁搴� traffic_audit.h2032_enterprise_monthly銆婇亾璺揣鐗╄繍杈撴湀搴︾敓浜ф儏鍐点�嬶紙H203-2锛変紒涓氭湀鎶ワ紝'
+ '鍙� 2025 骞� 1 鏈堚��2026 骞� 8 鏈堝叏閮ㄤ紒涓氱骇涓婃姤璁板綍銆�'),
+ ('鎸佺画鍦ㄨ浼佷笟', '2026 骞� 1鈥�8 鏈堣繛缁� 8 涓湀鍧囦笂鎶ユ湀鎶ョ殑浼佷笟锛屽叡 %d 瀹讹紙2026 骞村悇鏈堜笂鎶ヤ紒涓氭暟鍧囦负 636 瀹讹紝鍏ㄦ湡涓�鑷达級銆�' % tot['n']),
+ ('绱杩愯窛鍙e緞', '绱骞冲潎杩愯窛锛堝叕閲岋級= 1鈥�8 鏈堢疮璁¤揣鐗╁懆杞噺 梅 1鈥�8 鏈堢疮璁¤揣杩愰噺锛堝姞鏉冨钩鍧囷級銆傛簮琛ㄥ崟浣嶄负鍚ㄣ�佸惃鍏噷锛�'
+ '鏈〃鎶樼畻涓轰竾鍚ㄣ�佷竾鍚ㄥ叕閲岋紝姣斿�间笉鍙樸��'),
+ ('鍚屾瘮鍙e緞', '鍚屾瘮 = 2026 骞� 1鈥�8 鏈堢疮璁″钩鍧囪繍璺� 梅 2025 骞� 1鈥�8 鏈堢疮璁″钩鍧囪繍璺� 鈭� 1銆�'
+ '涓轰繚璇佸彲姣旓紝鍚屾瘮鍙湪銆屼袱骞村潎鎸佺画鍦ㄨ銆嶇殑 %d 瀹朵紒涓氫笂璁$畻锛堝彟鏈� %d 瀹� 2026 骞存寔缁湪瑙勪紒涓氾紝'
+ '2025 骞� 1鈥�8 鏈堟棤杩炵画涓婃姤璁板綍锛屼笉鍙備笌鍚屾瘮锛夈��' % (tot['n_cmp'], tot['n'] - tot['n_cmp'])),
+ ('鍏ㄧ渷鍚堣', '涓�寰嬩负 17 涓競宸炰箣鍜岋紙涓嶅惈璺ㄥ競宸為噸鍙犲彛寰勶級銆�'),
+ ('鎶ヨ〃鏈�', '鏈湡绱 2026-01-01鈥�2026-08-31锛涗笂骞村悓鏈� 2025-01-01鈥�2025-08-31銆�'),
+ ('椤电璇存槑', '銆屽垎甯傚窞姹囨�汇�嶆寜甯傚窞姹囨�� 1-8 鏈堢疮璁¤繍璺濅笌鍚屾瘮锛涖�屽垎甯傚窞鍒嗘湀銆嶄负鍚勫競宸� 2026 骞撮�愭湀骞冲潎杩愯窛涓庡悓鏈熷姣旓紱'
+ '銆屼紒涓氭槑缁嗐�嶄负 636 瀹舵寔缁湪瑙勪紒涓氶�愭埛鏁版嵁锛涖�屽彛寰勬牎楠屻�嶄负鏄庣粏涓庢眹鎬诲璐︺�佷互鍙婁笌宸插彂甯冨叏鐪佹暟鎹殑姣斿銆�'),
+ ('娉ㄦ剰浜嬮」', '鈶� 鏈� 5 瀹朵紒涓� 1鈥�8 鏈堟湀鎶ヨ揣杩愰噺銆佸懆杞噺鍧囦负 0锛岃鍏ヤ紒涓氭暟浣嗕笉褰卞搷杩愯窛锛�'
+ '鈶� 姝︽眽鐭冲寲浜ら�氳繍杈撴湁闄愬叕鍙搞�佹箹鍖楁嵎榫欎氦閫氳繍涓氭湁闄愬叕鍙� 2026 骞� 1 鏈堢殑鍦板尯鐮佷笌鍏跺悗鏈堜唤涓嶅悓锛�420100 鈫� 420107 / 420103锛夛紝'
+ '浣嗗悓灞炴姹夊競锛屼笉褰卞搷甯傚窞褰掗泦锛涒憿 绱璐ц繍閲忎负 0 鐨勪紒涓氱疮璁¤繍璺濈暀绌恒��'),
+ ('鐢熸垚鏃堕棿', '2026-09-18'),
+]
+r = 3
+for k, v in info:
+ a = ws.cell(r, 1, k); a.font = Font(bold=True); a.alignment = LT; a.border = BORDER
+ b = ws.cell(r, 2, v); b.alignment = LT; b.border = BORDER
+ r += 1
+
+# ---------------- Sheet2 鍒嗗競宸炴眹鎬� ----------------
+ws2 = wb.create_sheet('鍒嗗競宸炴眹鎬�')
+heads = ['搴忓彿', '甯傚窞', '鎸佺画鍦ㄨ浼佷笟鏁�(瀹�)', '鍏朵腑涓ゅ勾鍧囨寔缁湪瑙�(瀹�)',
+ '2026骞�1-8鏈堢疮璁¤揣杩愰噺(涓囧惃)', '2026骞�1-8鏈堢疮璁¤揣鐗╁懆杞噺(涓囧惃鍏噷)', '2026骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)',
+ '2025骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)[鍙瘮鍙e緞]', '绱杩愯窛鍚屾瘮(%)',
+ '绱璐ц繍閲忓悓姣�(%)', '绱璐х墿鍛ㄨ浆閲忓悓姣�(%)', '澶囨敞']
+widths = [6, 14, 16, 18, 20, 24, 20, 22, 14, 14, 16, 40]
+ws2.cell(1, 1, '璐ц繍鏈堟姤鎸佺画鍦ㄨ浼佷笟鍒嗗競宸� 1-8 鏈堢疮璁″钩鍧囪繍璺濆強鍚屾瘮鎯呭喌').font = Font(bold=True, size=13, color='FFFFFF')
+ws2.cell(1, 1).fill = TITLE_FILL
+ws2.cell(1, 1).alignment = Alignment(horizontal='center', vertical='center')
+ws2.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(heads))
+ws2.row_dimensions[1].height = 26
+ws2.cell(2, 1, '娉細绗� 5鈥�7 鍒椾负 2026 骞存寔缁湪瑙勪紒涓氬叏鍙e緞锛�%d 瀹讹級锛涚 8鈥�11 鍒椾负涓ゅ勾鍧囨寔缁湪瑙勪紒涓氬彲姣斿彛寰勶紙%d 瀹讹級鐨勫悓姣旇绠楀熀鏁般��'
+ % (tot['n'], tot['n_cmp']))
+ws2.cell(2, 1).font = Font(size=9, italic=True, color='808080')
+ws2.merge_cells(start_row=2, start_column=1, end_row=2, end_column=len(heads))
+for j, h in enumerate(heads, 1):
+ c = ws2.cell(3, j, h)
+ c.font = Font(bold=True); c.fill = HDR_FILL; c.alignment = CT; c.border = BORDER
+ ws2.column_dimensions[get_column_letter(j)].width = widths[j - 1]
+ws2.row_dimensions[3].height = 46
+
+rr = 3
+idx = 0
+for city in CITY_ORDER:
+ c = by_city.get(city)
+ if not c or c['n'] == 0:
+ continue
+ idx += 1
+ rr += 1
+ d26, d26c, d25c = dist(c['f26'], c['t26']), dist(c['f26c'], c['t26c']), dist(c['f25c'], c['t25c'])
+ if c['n_cmp'] == 0:
+ note = '鍚屾瘮涓嶉�傜敤锛氳甯傚窞 2026 骞存寔缁湪瑙勪紒涓氬湪 2025 骞� 1-8 鏈堟棤杩炵画涓婃姤璁板綍'
+ elif c['n_cmp'] == c['n']:
+ note = ''
+ else:
+ note = '鍚屾瘮鎸� %d 瀹朵袱骞村潎鎸佺画鍦ㄨ浼佷笟璁$畻' % c['n_cmp']
+ vals = [idx, city, c['n'], c['n_cmp'], c['f26'] / 10000.0, c['t26'] / 10000.0, d26, d25c,
+ growth(d26c, d25c), growth(c['f26c'], c['f25c']), growth(c['t26c'], c['t25c']), note]
+ for j, v in enumerate(vals, 1):
+ cell = ws2.cell(rr, j, v)
+ cell.border = BORDER
+ cell.alignment = LT if j == 12 else (CT if j <= 4 else RT)
+ if j in (5, 6, 7, 8):
+ cell.number_format = NF2
+ elif j in (9, 10, 11):
+ cell.number_format = NF1
+rr += 1
+vals = ['', '鍏ㄧ渷鍚堣', tot['n'], tot['n_cmp'], tot['f26'] / 10000.0, tot['t26'] / 10000.0,
+ dist(tot['f26'], tot['t26']), dist(tot['f25c'], tot['t25c']),
+ growth(dist(tot['f26c'], tot['t26c']), dist(tot['f25c'], tot['t25c'])),
+ growth(tot['f26c'], tot['f25c']), growth(tot['t26c'], tot['t25c']), '']
+for j, v in enumerate(vals, 1):
+ cell = ws2.cell(rr, j, v)
+ cell.border = BORDER
+ cell.fill = TOT_FILL
+ cell.font = Font(bold=True)
+ cell.alignment = CT if j <= 4 else RT
+ if j in (5, 6, 7, 8):
+ cell.number_format = NF2
+ elif j in (9, 10, 11):
+ cell.number_format = NF1
+ws2.freeze_panes = 'C4'
+
+# ---------------- Sheet3 鍒嗗競宸炲垎鏈� ----------------
+ws3 = wb.create_sheet('鍒嗗競宸炲垎鏈�')
+h3 = ['搴忓彿', '甯傚窞', '鎸佺画鍦ㄨ浼佷笟鏁�(瀹�)'] + ['%s鏈堝钩鍧囪繍璺�(鍏噷)' % m for m in range(1, 9)] + \
+ ['2026骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)', '2025骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)', '绱杩愯窛鍚屾瘮(%)']
+w3 = [6, 14, 16] + [14] * 8 + [24, 24, 14]
+ws3.cell(1, 1, '鍒嗗競宸� 2026 骞撮�愭湀骞冲潎杩愯窛锛堟寔缁湪瑙勪紒涓氬彛寰勶紝鍚屾瘮鍩烘湡 2025 骞� 1-8 鏈堬級').font = Font(bold=True, size=13, color='FFFFFF')
+ws3.cell(1, 1).fill = TITLE_FILL
+ws3.cell(1, 1).alignment = Alignment(horizontal='center', vertical='center')
+ws3.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(h3))
+ws3.row_dimensions[1].height = 26
+for j, h in enumerate(h3, 1):
+ c = ws3.cell(2, j, h)
+ c.font = Font(bold=True); c.fill = HDR_FILL; c.alignment = CT; c.border = BORDER
+ ws3.column_dimensions[get_column_letter(j)].width = w3[j - 1]
+ws3.row_dimensions[2].height = 40
+
+rr = 2
+idx = 0
+for city in CITY_ORDER:
+ c = by_city.get(city)
+ if not c or c['n'] == 0:
+ continue
+ idx += 1
+ rr += 1
+ vals = [idx, city, c['n']]
+ for per in MONTHS26:
+ vals.append(city_month(per, city, persist_names)[0])
+ vals += [dist(c['f26'], c['t26']), dist(c['f25c'], c['t25c']), growth(dist(c['f26c'], c['t26c']), dist(c['f25c'], c['t25c']))]
+ for j, v in enumerate(vals, 1):
+ cell = ws3.cell(rr, j, v)
+ cell.border = BORDER
+ cell.alignment = CT if j <= 3 else RT
+ if j >= 4:
+ cell.number_format = NF2 if j <= 13 else NF1
+rr += 1
+# 鍏ㄧ渷鍒嗘湀锛氭寜 17 甯傚窞閫愭湀鍚堣閲嶇畻
+vals = ['', '鍏ㄧ渷鍚堣', tot['n']]
+for per in MONTHS26:
+ fr = tu = 0.0
+ for city in CITY_ORDER:
+ _, f0 = city_month(per, city, persist_names)
+ fr += f0
+ d = cust.get((per, city), {})
+ for en, v in d.items():
+ if en in persist_names:
+ tu += v[1]
+ vals.append(dist(fr, tu))
+vals += [dist(tot['f26'], tot['t26']), dist(tot['f25c'], tot['t25c']),
+ growth(dist(tot['f26c'], tot['t26c']), dist(tot['f25c'], tot['t25c']))]
+for j, v in enumerate(vals, 1):
+ cell = ws3.cell(rr, j, v)
+ cell.border = BORDER
+ cell.fill = TOT_FILL
+ cell.font = Font(bold=True)
+ cell.alignment = CT if j <= 3 else RT
+ if j >= 4:
+ cell.number_format = NF2 if j <= 13 else NF1
+ws3.freeze_panes = 'D3'
+
+# ---------------- Sheet4 浼佷笟鏄庣粏 ----------------
+ws4 = wb.create_sheet('浼佷笟鏄庣粏')
+h4 = ['搴忓彿', '甯傚窞', '浼佷笟鍚嶇О', '浼佷笟浠g爜', '缁熶竴绀句細淇$敤浠g爜',
+ '2026骞�1-8鏈堣揣杩愰噺(涓囧惃)', '2026骞�1-8鏈堝懆杞噺(涓囧惃鍏噷)', '2026骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)',
+ '2025骞�1-8鏈堣揣杩愰噺(涓囧惃)', '2025骞�1-8鏈堝懆杞噺(涓囧惃鍏噷)', '2025骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)',
+ '骞冲潎杩愯窛鍚屾瘮(%)', '鏄惁涓ゅ勾鍧囨寔缁湪瑙�']
+w4 = [6, 12, 42, 22, 24, 20, 24, 22, 20, 24, 22, 14, 18]
+ws4.cell(1, 1, '鎸佺画鍦ㄨ浼佷笟閫愭埛绱骞冲潎杩愯窛锛�2026 骞� 1-8 鏈堬紝鍚屾瘮鍩烘湡 2025 骞� 1-8 鏈堬級').font = Font(bold=True, size=13, color='FFFFFF')
+ws4.cell(1, 1).fill = TITLE_FILL
+ws4.cell(1, 1).alignment = Alignment(horizontal='center', vertical='center')
+ws4.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(h4))
+ws4.row_dimensions[1].height = 26
+for j, h in enumerate(h4, 1):
+ c = ws4.cell(2, j, h)
+ c.font = Font(bold=True); c.fill = HDR_FILL; c.alignment = CT; c.border = BORDER
+ ws4.column_dimensions[get_column_letter(j)].width = w4[j - 1]
+ws4.row_dimensions[2].height = 40
+
+order = {c: i for i, c in enumerate(CITY_ORDER)}
+rows_sorted = sorted(rows, key=lambda x: (order.get(x['city'], 99), -(x['t26'])))
+rr = 2
+for i, x in enumerate(rows_sorted, 1):
+ rr += 1
+ d26, d25 = dist(x['f26'], x['t26']), dist(x['f25'], x['t25'])
+ vals = [i, x['city'], x['en'], x['ec'], x['ucc'],
+ x['f26'] / 10000.0, x['t26'] / 10000.0, d26,
+ (x['f25'] / 10000.0) if x['cmp'] else None,
+ (x['t25'] / 10000.0) if x['cmp'] else None,
+ d25 if x['cmp'] else None,
+ growth(d26, d25) if x['cmp'] else None,
+ '鏄�' if x['cmp'] else '鍚�']
+ for j, v in enumerate(vals, 1):
+ cell = ws4.cell(rr, j, v)
+ cell.border = BORDER
+ cell.alignment = LT if j in (3, 4, 5) else (CT if j in (1, 2, 13) else RT)
+ if j in (6, 7, 8, 9, 10, 11):
+ cell.number_format = NF2
+ elif j == 12:
+ cell.number_format = NF1
+ if j == 12 and isinstance(v, float) and v > 0.5:
+ cell.font = Font(color='C00000', bold=True)
+ws4.freeze_panes = 'D3'
+ws4.auto_filter.ref = 'A2:M%d' % rr
+
+# ---------------- Sheet5 鍙e緞鏍¢獙 ----------------
+ws5 = wb.create_sheet('鍙e緞鏍¢獙')
+ws5.sheet_view.showGridLines = False
+ws5.column_dimensions['A'].width = 42
+for col, w in (('B', 20), ('C', 20), ('D', 14), ('E', 40)):
+ ws5.column_dimensions[col].width = w
+ws5.cell(1, 1, '鍙e緞鏍¢獙涓庡璐�').font = Font(bold=True, size=13, color='FFFFFF')
+ws5.cell(1, 1).fill = TITLE_FILL
+ws5.merge_cells(start_row=1, start_column=1, end_row=1, end_column=5)
+sum_f = sum(r['f26'] for r in rows) / 10000.0
+sum_t = sum(r['t26'] for r in rows) / 10000.0
+aug_f = sum(v[0] for c in CITY_ORDER for en, v in cust.get(("2026-08", c), {}).items() if en in persist_names) / 10000.0
+aug_t = sum(v[1] for c in CITY_ORDER for en, v in cust.get(("2026-08", c), {}).items() if en in persist_names) / 10000.0
+chk = [
+ ("鏍¢獙椤�", "鏈〃鏁板��", "瀵圭収鏁板��", "宸紓", "璇存槑"),
+ ("鏄庣粏琛ㄤ紒涓氭暟 = 姹囨�昏〃浼佷笟鏁�", len(rows), tot["n"], len(rows) - tot["n"], "636 瀹舵寔缁湪瑙勪紒涓氶�愭埛涓�琛岋紝鏃犻噸澶嶆棤閬楁紡"),
+ ("鏄庣粏琛ㄨ揣杩愰噺鍚堣(涓囧惃)", round(sum_f, 2), round(tot["f26"] / 10000.0, 2), round(sum_f - tot["f26"] / 10000.0, 2), "鏄庣粏姹傚拰 = 17 甯傚窞姹囨�诲悎璁�"),
+ ("鏄庣粏琛ㄥ懆杞噺鍚堣(涓囧惃鍏噷)", round(sum_t, 2), round(tot["t26"] / 10000.0, 2), round(sum_t - tot["t26"] / 10000.0, 2), "鏄庣粏姹傚拰 = 17 甯傚窞姹囨�诲悎璁�"),
+ ("涓庡凡鍙戝竷鍏ㄧ渷 1-8 鏈堢疮璁¤揣杩愰噺姣斿(涓囧惃)", round(tot["f26"] / 10000.0, 2), PROV_VOL_PUB, round(tot["f26"] / 10000.0 - PROV_VOL_PUB, 2), "瀵圭収銆�2026骞�8鏈堣涓婅揣鐗╁懆杞噺涓庡钩鍧囪繍璺濆垎鏋愭姤鍛娿��"),
+ ("涓庡凡鍙戝竷鍏ㄧ渷 1-8 鏈堢疮璁″懆杞噺姣斿(涓囧惃鍏噷)", round(tot["t26"] / 10000.0, 2), PROV_TURN_PUB, round(tot["t26"] / 10000.0 - PROV_TURN_PUB, 2), "鍚屼笂"),
+ ("2026骞�8鏈堝叏鐪佽揣杩愰噺姣斿(涓囧惃)", round(aug_f, 2), 3665.36, round(aug_f - 3665.36, 2), "瀵圭収宸插彂甯� 8 鏈堝叏鐪佽涓婅揣杩愰噺锛岄獙璇佸競宸炲綊闆嗘棤璇�"),
+ ("2026骞�8鏈堝叏鐪佸懆杞噺姣斿(涓囧惃鍏噷)", round(aug_t, 2), 570004.44, round(aug_t - 570004.44, 2), "瀵圭収宸插彂甯� 8 鏈堝叏鐪佽涓婂懆杞噺"),
+ ("鍏ㄧ渷 1-8 鏈堢疮璁″钩鍧囪繍璺�(鍏噷)", round(dist(tot["f26"], tot["t26"]), 2), None, None, "鐢辨湰琛ㄧ疮璁″懆杞噺 梅 绱璐ц繍閲忚绠�"),
+ ("瑕嗙洊甯傚窞鏁�", len([c for c in CITY_ORDER if by_city.get(c, {}).get("n")]), 17, None, "17 涓競宸炲叏瑕嗙洊"),
+ ("鏈瘑鍒湴鍖虹爜", len(unknown), 0, None, ("銆�".join(unknown) if unknown else "鏃�")),
+]
+for i, row in enumerate(chk, 1):
+ for j, v in enumerate(row, 1):
+ cell = ws5.cell(i + 2, j, v)
+ cell.border = BORDER
+ cell.alignment = CT if i == 1 else (LT if j in (1, 5) else RT)
+ if i == 1:
+ cell.font = Font(bold=True); cell.fill = HDR_FILL
+ elif j in (2, 3, 4):
+ cell.number_format = NF2
+
+os.makedirs(OUT_DIR, exist_ok=True)
+wb.save(OUT)
+print('OK ->', OUT)
+print('鎸佺画鍦ㄨ浼佷笟鏁�: %d 涓ゅ勾鍙瘮: %d' % (tot['n'], tot['n_cmp']))
+print('鍏ㄧ渷绱璐ц繍閲�(涓囧惃): %.2f 绱鍛ㄨ浆閲�(涓囧惃鍏噷): %.2f 绱骞冲潎杩愯窛: %.2f' %
+ (tot['f26'] / 10000.0, tot['t26'] / 10000.0, dist(tot['f26'], tot['t26'])))
+print('鍙瘮鍙e緞 2026 绱杩愯窛: %.2f 2025: %.2f 鍚屾瘮: %.2f%%' %
+ (dist(tot['f26c'], tot['t26c']), dist(tot['f25c'], tot['t25c']), growth(dist(tot['f26c'], tot['t26c']), dist(tot['f25c'], tot['t25c'])) * 100))
+for city in CITY_ORDER:
+ c = by_city.get(city)
+ if c and c['n']:
+ g = growth(dist(c['f26c'], c['t26c']), dist(c['f25c'], c['t25c']))
+ print('%-8s n=%-4d 2026绱=%8s 2025绱=%8s 鍚屾瘮=%s' % (
+ city, c['n'],
+ '%.2f' % dist(c['f26'], c['t26']),
+ '%.2f' % dist(c['f25c'], c['t25c']) if dist(c['f25c'], c['t25c']) else '-',
+ '%.1f%%' % (g * 100) if g is not None else '-'))
--
Gitblit v1.9.1