zhizhijie
2026-09-20 7540f407937eac25d13362866405c4b36e04fe41
refactor(汇总表): 数值全精度写入 + 单元格格式统一两位小数显示

需求:所有数据全精度保留,计算结果显示两位小数。

改动:
- ReportExportService:去除 40 处写入前 round(...,4/2/1) 预舍入(规上/规下/同比、
/10000 单位换算、中口径排名/分析、排名页占比、setDataCell、平均运距兜底、客运转运、
投资进度、物流占比、投资计划万吨等),数值一律全精度参与运算与写入。
- applyTwoDecimalFormat 重写:数值格格式统一 0.00(保留 ;[Red]\\(...\\) 前后缀);
3 位及以上小数格式(0.000_ 、0.0000)正则收敛为两位;%/日期/科学计数/文本不动;
整数值沿用不显示 .00 约定;格式归一判断提到整数保护之前。
- WycSplitCalc:平均运距兜底改全精度,删除已无引用的私有 round。

验证(2026-08):
- 66205 个共有数值格仅 249 格变化,最大 0.00022 万人公里;两位小数口径不一致 0 格;
错误值 589 格无新增;22 个导出接口 21 个 OK(wycSplit 为既有缺输入)。
- 有值数值格 3 位以上小数格式:汇总大表 108→0 格,中口径明细 134→0 格。
- 09-19 遗留的全省行 0.0002 万人公里已消除(母版 =SUM(O4:O20) 与明细全精度求和逐位闭合)。

详见 docs/功能测试报告/功能测试报告_全精度存储与两位小数显示_2026-09-20.md
2个文件已修改
2个文件已添加
287 ■■■■■ 已修改文件
docs/功能测试报告/功能测试报告_全精度存储与两位小数显示_2026-09-20.md 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/工作日结/工作日结_2026-09-20.md 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/WycSplitCalc.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/¹¦ÄܲâÊÔ±¨¸æ/¹¦ÄܲâÊÔ±¨¸æ_È«¾«¶È´æ´¢ÓëÁ½Î»Ð¡ÊýÏÔʾ_2026-09-20.md
New file
@@ -0,0 +1,86 @@
# åŠŸèƒ½æµ‹è¯•æŠ¥å‘Š_全精度存储与两位小数显示_2026-09-20
## ä¸€ã€åŠŸèƒ½è¯´æ˜Ž
**需求(用户 2026-09-20 æŒ‡ä»¤ï¼‰**:所有的数据都全部全精度保留,计算得到的结果显示两位小数。
**改前问题**:
1. åŽç«¯åœ¨**写单元格之前**就做了四舍五入,底层存储值被提前截断:
   - `round(v, 4)`:货运规上/规下/同比、累计与当月、中口径排名/分析、`÷10000` å•位换算、投资进度、物流占比、投资计划万吨;
   - `round(v, 2)`:通用数据格 `setDataCell`、平均运距兜底、客运转运写入;
   - `round(v, 1)`:排名页占比(`setFreightShare`、货运量排名/周转量排名块)。
   è¯¯å·®è™½å°ï¼Œä½†å±žäºŽã€Œè®¡ç®—口径被写死」,与人工用全精度重算的结果天然对不上(正是 09-19 ä¸­å£å¾„差异问题的同源风险)。
2. æ¯ç‰ˆä¸ªåˆ«æ ¼è‡ªå¸¦ 3~4 ä½å°æ•°**显示格式**(`0.0000_);[Red]\(0.0000\)`、`0.000_ `),展示位数不统一。
**改后实现**:
1. **全精度**:取消所有写入/运算前的预舍入,数值以 `double` åŽŸå€¼å‚ä¸Žè¿ç®—å¹¶å†™å…¥ï¼›`/10000`、占比、同比、平均运距兜底等一律不截断。
2. **两位小数展示**:由单元格数字格式承担。重写 `applyTwoDecimalFormat()`:
   - æ•°å€¼æ ¼æ ¼å¼ç»Ÿä¸€ä¸º `0.00`(`cloneStyleFrom` ä¿ç•™è¾¹æ¡†/字体/对齐,只换数字格式,`;[Red]\(...\)` ç­‰å‰åŽç¼€åŽŸæ ·ä¿ç•™ï¼‰ï¼›
   - **3 ä½åŠä»¥ä¸Šå°æ•°çš„æ ¼å¼**(`0.000_ `、`0.0000_);[Red]\(0.0000\)`)用正则 `\.0{3,}` æ”¶æ•›ä¸º `.00`;
   - ç™¾åˆ†æ¯”(`%`)、科学计数(`E`)、文本(`@`)、日期格式**不动**;
   - æ•´æ•°å€¼æ ¼æ²¿ç”¨æ—¢æœ‰çº¦å®šä¸æ˜¾ç¤º `.00`(如年份、排名)。
3. æŽ§åˆ¶å°/接口返回的**预览摘要**里的 `round`(`addSum(...)`)保留,它不进报表数据。
**改动文件**:
| æ–‡ä»¶ | æ”¹åЍ |
| --- | --- |
| `traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java` | å޻除 40 å¤„预舍入;重写 `applyTwoDecimalFormat()`(新增 `DEC_MORE_THAN_TWO` æ­£åˆ™å¸¸é‡ï¼‰ |
| `traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/WycSplitCalc.java` | å¹³å‡è¿è·å…œåº•改全精度;删除已无引用的私有 `round(double,int)` |
## äºŒã€æµ‹è¯•环境
| é¡¹ | å†…容 |
| --- | --- |
| åŽç«¯ | Spring Boot `localhost:8090`,`mvn -q -o compile` é€šè¿‡åŽç”± `start-dev.ps1` é‡å¯ï¼ˆPID 15568) |
| å‰ç«¯ | Vue `localhost:8080`(PID 28608),与后端一并重启 |
| æ•°æ®åº“ | MySQL `localhost:3308/traffic_audit` |
| æŠ¥è¡¨æœŸ | 2026-08(1-8 æœˆç´¯è®¡å— + 8 æœˆå•月块) |
| å¯¹ç…§ä»¶ | æ”¹å‰ç”Ÿæˆä»¶ `_after_busfix_2026-08.xlsx`(09-19 å£å¾„修复后、本次改造前) |
| æ”¹åŽç”Ÿæˆä»¶ | `_after_precision_2026-08.xlsx`、`_after_precision_midrank/midana/middetail.xlsx`,以及 `_out_precision\` ä¸‹ 22 ä¸ªæŽ¥å£äº§ç‰© |
| å–证脚本 | `_qa_prec_*.py`、`_regen_precision.ps1`、`_regen_all.ps1` |
## ä¸‰ã€ç”¨ä¾‹ä¸Žç»“æžœ
| ç¼–号 | ç±»åž‹ | è¾“å…¥ | é¢„期 | å®žé™… | ç»“论 |
| --- | --- | --- | --- | --- | --- |
| T1 | æ­£å‘ | `mvn -q -o compile` | ç¼–译通过 | exit=0,无告警 | é€šè¿‡ |
| T2 | æ­£å‘ | 22 ä¸ªå¯¼å‡ºæŽ¥å£ï¼ˆperiod=2026-08) | å…¨éƒ¨ 200 ä¸”非空 | 21 ä¸ª OK;`wycSplit` 500(缺输入文件,见「四-5」) | é€šè¿‡ |
| T3 | æ­£å‘ | æ”¹å‰/改后《道路运输量汇总表》逐格数值对比(66205 ä¸ªå…±æœ‰æ•°å€¼æ ¼ï¼‰ | ä»…目标格出现差异 | å®žé™…变化 **249 æ ¼**,全部落在「货运量排名」107 +「中口径排名」142 | é€šè¿‡ |
| T4 | æ­£å‘ | æœ€å¤§å˜åŒ–量 | åªåº”是去掉舍入带来的尾数差 | **0.00022 ä¸‡äººå…¬é‡Œ**(=2 äººå…¬é‡Œï¼‰ï¼Œä½ç½®ã€Œä¸­å£å¾„排名」全省行 O3 | é€šè¿‡ |
| T5 | æ­£å‘ | æ­¦æ±‰ç´¯è®¡å‘¨è½¬é‡ | å…¨ç²¾åº¦ä¿ç•™ï¼Œ4 ä½å°æ•°ä½ä»ä¸º 8912 | ä¸­å£å¾„明细 S104 = 152330.89119999998;排名页 O4 / é“路运输周转量 Q5 = 152330.8912 | é€šè¿‡ |
| T6 | æ­£å‘ | å­æ„Ÿç´¯è®¡å‘¨è½¬é‡ | ä¸å†æˆªæ–­åˆ° 64429.9445 | 64429.944543113306(排名页与明细页逐位一致) | é€šè¿‡ |
| T7 | æ­£å‘ | ã€Œä¸­å£å¾„排名」改前/改后按两位小数对比 | å®Œå…¨ä¸€è‡´ï¼ˆä¸åº”改变可见结果) | ä¸ä¸€è‡´æ ¼ **0** | é€šè¿‡ |
| T8 | æ­£å‘ | å…¨çœè¡Œ `=SUM(O4:O20)` é—­åˆ | ç¼“存值 = 17 å¸‚州全精度求和 | 1227147.4890200002,**å·® 0.0**(09-19 çš„ 0.0002 æ®‹ç•™å·²æ¶ˆé™¤ï¼‰ | é€šè¿‡ |
| T9 | æ­£å‘ | æ±‡æ€»å¤§è¡¨ã€Œæœ‰å€¼æ•°å€¼/公式格」的 3 ä½ä»¥ä¸Šå°æ•°æ ¼å¼ | 0 æ ¼ | æ”¹å‰ 108 æ ¼ â†’ æ”¹åŽ **0 æ ¼** | é€šè¿‡ |
| T10 | æ­£å‘ | ä¸­å£å¾„明细(独立件)`0.000_ ` æ ¼å¼æ ¼ | æ”¶æ•›ä¸ºä¸¤ä½ | æ”¹å‰ 134 æ ¼ â†’ æ”¹åŽ **0 æ ¼**;C19 å€¼ 13.599765541547498 ä¸å˜ï¼Œæ ¼å¼å˜ `0.00_ ` | é€šè¿‡ |
| T11 | æ­£å‘ | 22 ä¸ªäº§ç‰©çš„ 3 ä½ä»¥ä¸Šå°æ•°æ ¼å¼æ±‡æ€» | å…¨ä¸º 0 | 15 ä¸ªå¯è§£æžäº§ç‰©åˆè®¡ **0 æ ¼** | é€šè¿‡ |
| T12 | å›žå½’ | è¡¨å†…错误值(`#DIV/0!`/`#VALUE!`/`#REF!`)数量 | ä¸æ–°å¢ž | æ”¹å‰ 589 = æ”¹åŽ 589(分布逐项相同) | é€šè¿‡ |
| T13 | å›žå½’ | ç™¾åˆ†æ¯”æ ¼ / ä¸¤ä½å°æ•°æ ¼æ•°é‡ | ä¸å—影响 | 25665 / 39552,与改前一致 | é€šè¿‡ |
| T14 | å›žå½’ | ç‹¬ç«‹ä»¶ `passengerMidRank`、`passengerMidAnalysis` | æ—  3 ä½ä»¥ä¸Šå°æ•°æ ¼å¼ | 0 æ ¼ï¼ˆæ”¹å‰æœ¬å°± 0) | é€šè¿‡ |
| T15 | è¾¹ç•Œ | ç©ºå€¼æ ¼ã€æ–‡æœ¬æ ¼ï¼ˆå¦‚月份文本「2023å¹´4月」) | ä¸åЍ | ä»å¸¦ 4 ä½å°æ•°æ ¼å¼ 274 æ ¼ï¼ˆ260 ç©ºå€¼ + 14 æ–‡æœ¬ï¼‰ï¼Œæ— æ˜¾ç¤ºå€¼ï¼Œä¸å¤„理 | é€šè¿‡ |
| T16 | è¾¹ç•Œ | æ•´æ•°å€¼æ ¼ï¼ˆå¹´ä»½/排名) | ä¸æ˜¾ç¤º `.00` | æ²¿ç”¨æ—¢æœ‰ã€Œæ•´æ•°ä¸æ˜¾ç¤º .00」保护,未被本次改动破坏 | é€šè¿‡ |
| T17 | è¾¹ç•Œ | ä¸Ž `round(改后,4)` ä¸ä¸€è‡´çš„ 30 æ ¼ | å±žäºŒé˜¶é‡ç®—而非错误 | åŒæ¯”列由全精度分子分母重算,量级 1e-9~1e-8(如 0.0712378390 â†’ 0.0712378363) | é€šè¿‡ |
| T18 | è´Ÿå‘ | æ¯ç‰ˆæŽ’名页 Q åˆ—公式 `=ROUND(B/L*10,1)` | æœ¬æ¬¡ä¸æ”¹æ¯ç‰ˆ | ä»ä¿ç•™ï¼ˆ36×2 æ ¼ï¼‰ï¼ŒExcel æ‰“开会按公式重算到 1 ä½åŽå†æŒ‰ `0.00` å±•示 | è§ã€Œå››-4」待拍板 |
## å››ã€å‘现的问题与处置
1. **母版带 4 ä½å°æ•°æ ¼å¼çš„æœ‰å€¼æ ¼ 108 ä¸ª**(`货运!EO90/EO93/EO108/EO111` ç­‰ SUM å…¬å¼æ ¼ã€`公路总客运!DE2` ç­‰ï¼‰â†’ å·²ç”±æ ¼å¼å½’一处理为 `0.00`;剩余带该格式的 274 æ ¼å‡ä¸ºç©ºå€¼/文本格,无显示值。
2. **「整数不显示 .00」保护先把公式格跳过了**:首轮改造后 `货运!EO90/EO93/EO108/EO111` ä»æ˜¯ `0.0000`(缓存值为 0,被判定为整数而跳过)。已把「3 ä½ä»¥ä¸Šå°æ•°æ ¼å¼å½’一」的判断**提到整数保护之前**——这类格本来就走数值格式展示,与「General çš„æ•´æ•°å€¼ä¸æ˜¾ç¤º .00」不冲突。
3. **3 ä½å°æ•°æ ¼å¼æœªè¢«è¯†åˆ«**(`0.000_ `):原判断用 `f.contains("0.00")` è®¤ä¸ºå·²æ˜¯ä¸¤ä½ï¼Œè€Œ `0.000_ ` æ°å¥½åŒ…含子串 `0.00` è€Œè¢«è·³è¿‡ï¼Œå¯¼è‡´ä¸­å£å¾„明细 134 æ ¼æ¼æ”¹ã€‚已改为正则 `\.0{3,}` ç»Ÿä¸€æ”¶æ•›ã€‚
4. **母版排名页 Q åˆ—仍是 `=ROUND(B/L*10,1)`**(货运量排名 Q4:Q42、货运周转量排名 Q5:Q44,共 72 æ ¼ï¼‰ï¼š
   ç¨‹åºå¯¹å…¬å¼æ ¼ä¸€å¾‹ä¸è¦†å†™ï¼ˆä¿ç•™æ¯ç‰ˆå…¬å¼è®¾è®¡ï¼‰ï¼ŒExcel/WPS æ‰“开时会按公式重算回 1 ä½å°æ•°å†ä»¥ `0.00` æ ¼å¼å±•示。
   è‹¥è¦è¿žè¿™ 72 æ ¼ä¹Ÿå…¨ç²¾åº¦ï¼Œéœ€è¦æŠŠä¸‰ä¸ªæ¯ç‰ˆçš„公式改成 `=B/L*10`(并同步 R åˆ— `=10-Q`)——**待用户确认后再动母版**。
5. **`wycSplit` æŽ¥å£ 500**:`loadWycMonthlyInput()` æŠ›ã€Œæœªæ‰¾åˆ°ç½‘络约车(全省)汇总.xlsx,请放到 docs/网络客运 ç›®å½•」,而 `docs\网络客运` ç›®å½•当前不存在——属**缺输入数据的既有现象**,与本次改造无关。
## äº”、结论
- æŠ¥è¡¨æ•°å€¼å·²å…¨éƒ¨æ”¹ä¸º**全精度写入/运算**,展示位数统一由单元格格式承担(两位小数;百分比、日期、文本格式不变)。
- é€æ ¼å¯¹æ¯”证实:改后与改前的可见结果(两位小数口径)**完全一致**,差异仅出现在被去掉舍入的尾数位,最大 0.00022 ä¸‡äººå…¬é‡Œã€‚
- é¡ºå¸¦æ¶ˆé™¤äº† 09-19 é—留的「全省行 0.0002 ä¸‡äººå…¬é‡Œã€ï¼šå„市州改为全精度后,母版公式 `=SUM(O4:O20)` ä¸Žæ˜Žç»†å…¨ç²¾åº¦æ±‚å’Œ**逐位闭合**。
- 22 ä¸ªå¯¼å‡ºæŽ¥å£å›žå½’通过(`wycSplit` ä¸ºæ—¢æœ‰ç¼ºæ•°æ®é—®é¢˜ï¼‰ï¼›é”™è¯¯å€¼æ•°é‡ã€ç™¾åˆ†æ¯”格数量均无变化。
- é—留一项待拍板:母版排名页 Q/R å æ¯”公式的 `ROUND(...,1)`。
测试人:Codex(AI) 测试日期:2026-09-20
docs/¹¤×÷ÈÕ½á/¹¤×÷ÈÕ½á_2026-09-20.md
New file
@@ -0,0 +1,64 @@
# å·¥ä½œæ—¥ç»“_2026-09-20
## ä¸€ã€ä»Šå¤©åšäº†ä»€ä¹ˆ
### 1. å›žç­”用户核对差异的疑问(承接 09-19)
用户把人工算的一列放进桌面《生成_道路运输量汇总表 (1).xlsx》,问差异原因。
用户追问「是不是取数维度不对造成的——程序直接加『城际城乡』,人工用『总量−城市内』」——
**是的,正是这个原因**,已在 09-19 ä¿®å¤å¹¶æŽ¨é€ï¼ˆé—®é¢˜è®°äºŽã€ŠåŠŸèƒ½æµ‹è¯•æŠ¥å‘Š_中口径排名周转量口径统一_2026-09-19.md》)。
### 2. å…¨ç²¾åº¦å­˜å‚¨ + ä¸¤ä½å°æ•°æ˜¾ç¤ºæ”¹é€ ï¼ˆä»Šå¤©ä¸»ä»»åŠ¡ï¼‰
**用户指令**:「所有的数据都全部全精度保留,计算得到的结果显示两位小数。」
- **去预舍入**:`ReportExportService.java` åŽ»æŽ‰ 40 å¤„写入前的 `round(...)`——
  è´§è¿è§„上/规下/同比(累计与当月)、`÷10000` å•位换算(省/市 Ã— æœˆ/累计四处)、
  ä¸­å£å¾„排名/分析写入、排名页占比(`setFreightShare`、货运量排名/周转量排名块)、
  é€šç”¨æ•°æ®æ ¼ `setDataCell`、平均运距兜底、客运转运写入、投资进度、物流占比、投资计划万吨;
  `WycSplitCalc.java` å¹³å‡è¿è·å…œåº•改全精度并删除已无引用的私有 `round`。
- **展示位数交给单元格格式**:重写 `applyTwoDecimalFormat()`
  â€”—数值格统一 `0.00`(保留 `;[Red]\(...\)` ç­‰å‰åŽç¼€ï¼‰ï¼Œ**3 ä½åŠä»¥ä¸Šå°æ•°çš„æ ¼å¼**(`0.000_ `、`0.0000`)
  ç”¨æ­£åˆ™ `\.0{3,}` æ”¶æ•›ä¸ºä¸¤ä½ï¼›ç™¾åˆ†æ¯”/日期/科学计数/文本格式不动;整数值沿用「不显示 .00」约定。
- æŽ§åˆ¶å°é¢„览摘要里的 `round` ä¿ç•™ï¼ˆä¸è¿›æŠ¥è¡¨æ•°æ®ï¼‰ã€‚
**过程中修掉的两个自作聪明的坑**(均已在测试报告「四、发现的问题与处置」留证):
1. ã€Œæ•´æ•°ä¸æ˜¾ç¤º .00」的保护先把公式格跳过了,首轮改造后 `货运!EO90/EO93/EO108/EO111`
   ä»æ˜¯ `0.0000`(缓存值 0 è¢«åˆ¤ä¸ºæ•´æ•°ï¼‰â†’ æŠŠæ ¼å¼å½’一判断**提到整数保护之前**。
2. æ—§åˆ¤æ–­ `f.contains("0.00")` æŠŠ `0.000_ ` è¯¯åˆ¤ä¸ºã€Œå·²æ˜¯ä¸¤ä½å°æ•°ã€ï¼Œä¸­å£å¾„明细 134 æ ¼æ¼æ”¹
   â†’ æ”¹æ­£åˆ™åŽå½’零。
## äºŒã€æµ‹è¯•结果摘要
见《功能测试报告_全精度存储与两位小数显示_2026-09-20.md》:18 æ¡ç”¨ä¾‹ï¼ˆ11 æ­£å‘ / 4 å›žå½’ / 3 è¾¹ç•Œ / 1 è´Ÿå‘挂起)通过。
- ç¼–译通过;22 ä¸ªå¯¼å‡ºæŽ¥å£å›žå½’,21 ä¸ª OK(`wycSplit` å› ç¼º `docs\网络客运` è¾“入目录 500,属既有问题)。
- æ”¹å‰ vs æ”¹åŽã€Šé“路运输量汇总表》逐格对比 66205 ä¸ªæ•°å€¼æ ¼ï¼š**仅 249 æ ¼å˜åŒ–**,最大 0.00022 ä¸‡äººå…¬é‡Œï¼›
  ä¸¤ä½å°æ•°å£å¾„的不一致格 **0**;错误值数量 589 æ— å˜åŒ–。
- å…³é”®å€¼ï¼šæ­¦æ±‰ç´¯è®¡å‘¨è½¬é‡ 152330.8912(全精度 152330.89119999998)、孝感 64429.944543113306 ä¸å†è¢«æˆªæ–­æˆ 64429.9445。
- **09-19 é—留的「全省行 0.0002 ä¸‡äººå…¬é‡Œã€å·²æ¶ˆé™¤**:市州全精度后母版 `=SUM(O4:O20)` ä¸Žæ˜Žç»†æ±‚和逐位闭合(差 0.0)。
- æ ¼å¼ï¼šæœ‰å€¼æ•°å€¼/公式格中 3 ä½ä»¥ä¸Šå°æ•°æ ¼å¼ **108 æ ¼ â†’ 0 æ ¼**(汇总大表)、**134 æ ¼ â†’ 0 æ ¼**(中口径明细),
  15 ä¸ªäº§ç‰©åˆè®¡ 0 æ ¼ã€‚
## ä¸‰ã€é—留问题与待办
1. **母版排名页 Q/R å æ¯”公式仍是 `=ROUND(B/L*10,1)`**(货运量排名 Q4:Q42、货运周转量排名 Q5:Q44,共 72 æ ¼ï¼‰ã€‚
   ç¨‹åºä¸è¦†å†™å…¬å¼æ ¼ï¼ŒExcel æ‰“开会重算回 1 ä½å†æŒ‰ `0.00` å±•示。若要彻底全精度,需把三个母版改成 `=B/L*10`(R åˆ— `=10-Q`)——**待用户拍板**。
2. ä»å¸¦ 4 ä½å°æ•°æ ¼å¼çš„ 274 æ ¼ä¸ºç©ºå€¼/文本格(无显示值),本次不动;若后续要在这些格填数再评估。
3. `wycSplit` æŽ¥å£ä¾èµ– `docs\网络客运\网络约车(全省)汇总.xlsx`,该目录当前不存在,导出报 500,待补输入。
4. 09-19 çš„遗留项(用户表 O åˆ—高速运距同比来源、545 å®¶ vs å…¨å£å¾„上报选择)仍未闭环。
## å››ã€æ˜Žå¤©è®¡åˆ’
1. æŒ‰ç”¨æˆ·åé¦ˆå†³å®šæ˜¯å¦è¿žæ¯ç‰ˆ Q/R å æ¯”公式一起去掉 `ROUND(...,1)`。
2. è‹¥ç”¨æˆ·æœ‰å…¶ä»–表的显示位数要求,按同一套「全精度写入 + æ ¼å¼æŽ§åˆ¶å±•示」规则处理。
3. è¡¥é½ `docs\网络客运` è¾“入后回归 `wycSplit` å¯¼å‡ºã€‚
---
**本次改动文件**
- `traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java`
- `traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/WycSplitCalc.java`
- æ–‡æ¡£ï¼š`docs/功能测试报告/功能测试报告_全精度存储与两位小数显示_2026-09-20.md`、本文件
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/WycSplitCalc.java
@@ -190,8 +190,9 @@
            if (r.getAvgDistanceCity() != null && r.getAvgDistanceCity() > 0) a[5] = r.getAvgDistanceCity();
        }
        for (double[] a : map.values()) {
            if (a[4] <= 0 && a[0] > 0) a[4] = round(a[1] / a[0], 2);
            if (a[5] <= 0 && a[2] > 0) a[5] = round(a[3] / a[2], 2);
            // å¹³å‡è¿è·å…œåº•:全精度保留,展示位数交给单元格格式
            if (a[4] <= 0 && a[0] > 0) a[4] = a[1] / a[0];
            if (a[5] <= 0 && a[2] > 0) a[5] = a[3] / a[2];
        }
        return map;
    }
@@ -317,8 +318,4 @@
    private double nz(Double v) { return v == null ? 0.0 : v; }
    private double round(double v, int scale) {
        double factor = Math.pow(10, scale);
        return Math.round(v * factor) / factor;
    }
}
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -840,36 +840,36 @@
        for (Double v : h2032FreightCum.values()) {
            if (v != null) provAbove += v;
        }
        Double provAboveWan = round(provAbove / 10000.0, 4);
        Double provAboveWan = provAbove / 10000.0;
        Double provTotal = freightCum(ftMap.get("湖北省"), month);
        Double provBelow = provTotal == null ? null : round(provTotal - provAboveWan, 4);
        Double provBelow = provTotal == null ? null : provTotal - provAboveWan;
        Double provTotalYoy = freightCumYoy(ftMap.get("湖北省"), month);
        double provAboveLast = 0.0;
        for (Double v : h2032FreightCumLast.values()) {
            if (v != null) provAboveLast += v;
        }
        Double provAboveYoy = provAboveLast == 0.0 ? null : round((provAbove - provAboveLast) / provAboveLast, 4);
        Double provAboveYoy = provAboveLast == 0.0 ? null : (provAbove - provAboveLast) / provAboveLast;
        Double provTotalLast = lastFreightCum(ftMap.get("湖北省"), month);
        Double provBelowLast = provTotalLast == null ? null : round(provTotalLast - provAboveLast / 10000.0, 4);
        Double provBelowLast = provTotalLast == null ? null : provTotalLast - provAboveLast / 10000.0;
        Double provBelowYoy = provBelowLast == null || provBelowLast == 0.0 ? null
            : round((provBelow - provBelowLast) / provBelowLast, 4);
            : (provBelow - provBelowLast) / provBelowLast;
        for (String city : RegionUtil.cityList()) {
            Double a = h2032FreightCum.get(city);
            double aWan = a == null ? 0.0 : round(a / 10000.0, 4);
            double aWan = a == null ? 0.0 : a / 10000.0;
            Double t = freightCum(ftMap.get(city), month);
            above.put(city, aWan);
            total.put(city, t);
            below.put(city, t == null ? null : round(t - aWan, 4));
            below.put(city, t == null ? null : t - aWan);
            aboveYoy.put(city, h2032FreightYoy.get(city));
            // è§„下同比 = (规下今年 - è§„下去年) / è§„下去年;规下去年 = æ¨¡æ¿åŽ»å¹´åˆè®¡ - H2032去年规上
            Double tLast = lastFreightCum(ftMap.get(city), month);
            Double aLast = h2032FreightCumLast.get(city);
            double aLastWan = aLast == null ? 0.0 : round(aLast / 10000.0, 4);
            Double belowLast = tLast == null ? null : round(tLast - aLastWan, 4);
            double aLastWan = aLast == null ? 0.0 : aLast / 10000.0;
            Double belowLast = tLast == null ? null : tLast - aLastWan;
            Double b = below.get(city);
            belowYoy.put(city, belowLast == null || belowLast == 0.0 || b == null
                ? null : round((b - belowLast) / belowLast, 4));
                ? null : (b - belowLast) / belowLast);
            totalYoy.put(city, freightCumYoy(ftMap.get(city), month));
        }
@@ -894,20 +894,20 @@
            if (maLast != null) provMonthAboveLast += maLast;
            monthAbove.put(city, ma);
            monthTotal.put(city, mt);
            monthBelow.put(city, mt == null ? null : round(mt - (ma == null ? 0.0 : ma), 4));
            monthBelow.put(city, mt == null ? null : mt - (ma == null ? 0.0 : ma));
            monthAboveYoy.put(city, growthYoy(ma, maLast));
            Double mb = monthBelow.get(city);
            Double mbLast = mtLast == null ? null : round(mtLast - (maLast == null ? 0.0 : maLast), 4);
            Double mbLast = mtLast == null ? null : mtLast - (maLast == null ? 0.0 : maLast);
            monthBelowYoy.put(city, growthYoy(mb, mbLast));
            monthTotalYoy.put(city, freightYoy(ftMap.get(city), month));
        }
        Double provMonthAboveWan = provMonthAbove == 0.0 ? null : round(provMonthAbove, 4);
        Double provMonthAboveWan = provMonthAbove == 0.0 ? null : provMonthAbove;
        Double provMonthTotal = freightMonth(ftMap.get("湖北省"), month);
        Double provMonthBelow = provMonthTotal == null ? null : round(provMonthTotal - provMonthAbove, 4);
        Double provMonthBelow = provMonthTotal == null ? null : provMonthTotal - provMonthAbove;
        Double provMonthAboveYoy = growthYoy(provMonthAbove == 0.0 ? null : provMonthAbove, provMonthAboveLast);
        Double provMonthTotalLast = lastFreightMonth(ftMap.get("湖北省"), month);
        Double provMonthBelowLast = provMonthTotalLast == null ? null
            : round(provMonthTotalLast - provMonthAboveLast, 4);
            : provMonthTotalLast - provMonthAboveLast;
        Double provMonthBelowYoy = growthYoy(provMonthBelow, provMonthBelowLast);
        Double provMonthTotalYoy = freightYoy(ftMap.get("湖北省"), month);
@@ -1244,7 +1244,7 @@
    private Double monthFreightWan(Map<String, Map<Integer, Double>> byCityMonth, String city, int month) {
        Map<Integer, Double> byMonth = byCityMonth == null ? null : byCityMonth.get(city);
        Double v = byMonth == null ? null : byMonth.get(month);
        return v == null ? null : round(v / 10000.0, 4);
        return v == null ? null : v / 10000.0;
    }
    /** åŒæ¯” = (本期-基期)/基期;任一期缺失或基期为 0 è¿”回 null */
@@ -1346,12 +1346,12 @@
        }
    }
    /** å‘¨è½¬é‡æŽ’名 Q/R å æ¯”:模板为缓存值,按新数据重算(规上占比 1 ä½å°æ•°ï¼‰ */
    /** å‘¨è½¬é‡æŽ’名 Q/R å æ¯”:模板为缓存值,按新数据重算(全精度写入,两位小数展示交给单元格格式) */
    private void setFreightShare(Row row, Double above, Double total) {
        if (total != null && total > 0 && above != null) {
            double share = round(above * 10.0 / total, 1);
            double share = above * 10.0 / total;
            setFreightCell(row, 16, share);
            setFreightCell(row, 17, round(10.0 - share, 1));
            setFreightCell(row, 17, 10.0 - share);
        } else {
            setFreightCell(row, 16, (Double) null);
            setFreightCell(row, 17, (Double) null);
@@ -1561,9 +1561,9 @@
        setNumeric(row, 15, isProvince ? null : yoyRankOfMap(totalYoy, city));
        if (t != null && t > 0 && a != null) {
            double share = round(a * 10.0 / t, 1);
            double share = a * 10.0 / t;
            setNumeric(row, 16, share);
            setNumeric(row, 17, round(10.0 - share, 1));
            setNumeric(row, 17, 10.0 - share);
        }
        return rowIdx + 1;
    }
@@ -1604,9 +1604,9 @@
        setNumeric(row, 15, isProvince || cum == null ? null : yoyRankOfTurnover(cumMap, city, 2));
        if (total != null && total > 0 && above != null) {
            double share = round(above * 10.0 / total, 1);
            double share = above * 10.0 / total;
            setNumeric(row, 16, share);
            setNumeric(row, 17, round(10.0 - share, 1));
            setNumeric(row, 17, 10.0 - share);
        }
        return rowIdx + 1;
    }
@@ -1626,7 +1626,7 @@
                Double v = byMonth == null ? null : byMonth.get(month);
                if (v != null) sum += v;
            }
            return round(sum / 10000.0, 4);
            return sum / 10000.0;
        }
        Double total = freightMonth(ft, month);
        Double above = getProvinceMonthValue(provinceMonthMap, ft, month, "freight", "above", h2032Freight);
@@ -1657,7 +1657,7 @@
            for (Double v : h2032FreightCum.values()) {
                if (v != null) sum += v;
            }
            return round(sum / 10000.0, 4);
            return sum / 10000.0;
        }
        Double total = freightCum(ft, monthCount);
        Double above = getProvinceCumValue(provinceCum, ft, "freight", "above", h2032FreightCum, monthCount);
@@ -1687,7 +1687,7 @@
        if ("above".equals(scale)) {
            Map<Integer, Double> byMonth = h2032Freight.get(city);
            Double v = byMonth == null ? null : byMonth.get(month);
            return v == null ? 0.0 : round(v / 10000.0, 4);
            return v == null ? 0.0 : v / 10000.0;
        }
        Double total = freightMonth(ft, month);
        Double above = getCityMonthValue(monthData, ft, city, month, "freight", "above", h2032Freight);
@@ -1716,7 +1716,7 @@
        if ("total".equals(scale)) return freightCum(ft, monthCount);
        if ("above".equals(scale)) {
            Double v = h2032FreightCum.get(city);
            return v == null ? 0.0 : round(v / 10000.0, 4);
            return v == null ? 0.0 : v / 10000.0;
        }
        Double total = freightCum(ft, monthCount);
        Double above = getCityCumValue(cum, ft, "freight", "above", h2032FreightCum, city, monthCount);
@@ -1953,20 +1953,20 @@
                    double v = passengerMonthVal(data, indi, currentYear, m, region, metricIdx);
                    if (!keepFormula) {
                        if (c == null) c = row.createCell(col);
                        if (v == 0.0) c.setBlank(); else c.setCellValue(round(v, 4));
                        if (v == 0.0) c.setBlank(); else c.setCellValue(v);
                    }
                    // åŒæ¯”列:库内去年同月同比(模板 #REF! å…¬å¼æ›¿æ¢ä¸ºæ•°å€¼ï¼Œå…¬å¼è¡Œä¹Ÿè¦†å†™ï¼‰
                    double lv = passengerMonthVal(data, indi, currentYear - 1, m, region, metricIdx);
                    Cell yc = row.getCell(col + 1);
                    if (yc == null) yc = row.createCell(col + 1);
                    if (v == 0.0 || lv == 0.0) yc.setBlank(); else yc.setCellValue(round((v - lv) / lv, 4));
                    if (v == 0.0 || lv == 0.0) yc.setBlank(); else yc.setCellValue((v - lv) / lv);
                }
                // ç´¯è®¡åŒæ¯”列(模板 #REF! â†’ åº“内去年 1..N æœˆç´¯è®¡åŒæ¯”数值)
                int cumCol = extended ? 26 : 14;
                Double cumYoy = passengerCumYoy(data, indi, currentYear, fillMonths, region, metricIdx);
                Cell yc2 = row.getCell(cumCol + 1);
                if (yc2 == null) yc2 = row.createCell(cumCol + 1);
                if (cumYoy == null) yc2.setBlank(); else yc2.setCellValue(round(cumYoy, 4));
                if (cumYoy == null) yc2.setBlank(); else yc2.setCellValue(cumYoy);
            }
            recalc(wb);
            return toBytes(wb);
@@ -2047,7 +2047,7 @@
    private Double yoy(Double cur, Double base) {
        if (cur == null || base == null || base == 0.0) return null;
        return round((cur - base) / base, 4);
        return (cur - base) / base;
    }
    /** æŒ‡æ ‡å–值(已换算为万人/万人公里/公里;无数据返回 null) */
@@ -2286,7 +2286,7 @@
        if (v == 0.0) return; // åº“内无源:保留母版/定稿原值,缺数据不覆盖
        // POI setCellValue(double) å¯¹å…¬å¼å•元格只更新缓存不移除公式,必须先 setBlank å†å†™å€¼
        c.setBlank();
        c.setCellValue(round(v, 4));
        c.setCellValue(v);
    }
    /** æ˜¾å¼å†™å…¥ 0(POI setCellValue(0.0) ä¼šè½¬ blank,需操作底层 XML) */
@@ -2582,12 +2582,12 @@
                    int monthCount = (y == currentYear) ? maxMonth : 12;
                    for (int m = 1; m <= monthCount; m++) {
                        Double cur = midMetricValue(aggOf(data, y, m, region), i, volume);
                        setNumeric(row, col++, cur == null ? null : round(cur, 4));
                        setNumeric(row, col++, cur);
                        Double base = midMetricValue(aggOf(data, y - 1, m, region), i, volume);
                        setNumeric(row, col++, yoy(cur, base));
                    }
                    Double cum = midCumOf(data, y, region, monthCount, i, volume);
                    setNumeric(row, col++, cum == null ? null : round(cum, 4));
                    setNumeric(row, col++, cum);
                    Double cumBase = midCumOf(data, y - 1, region, monthCount, i, volume);
                    setNumeric(row, col++, yoy(cum, cumBase));
                }
@@ -2670,18 +2670,18 @@
                double turn = midCumClass(mid, currentYear, city, maxMonth, 0, false);
                double lastPass = midCumClass(mid, currentYear - 1, city, maxMonth, 0, true);
                double lastTurn = midCumClass(mid, currentYear - 1, city, maxMonth, 0, false);
                setValOrBlank(sheet, r0, 1, pass == 0 ? null : round(pass, 4));
                setValOrBlank(sheet, r0, 1, pass == 0 ? null : pass);
                setValOrBlank(sheet, r0, 3, midCumYoy(mid, currentYear, city, maxMonth, 0, true));
                setValOrBlank(sheet, r0, 5, turn == 0 ? null : round(turn, 4));
                setValOrBlank(sheet, r0, 5, turn == 0 ? null : turn);
                setValOrBlank(sheet, r0, 7, midCumYoy(mid, currentYear, city, maxMonth, 0, false));
                // å³å—当月:L/P å€¼ã€N/R å½“月同比(M/O/Q/S æŽ’名公式保留)
                double mPass = midClassVal(mid, currentYear, maxMonth, city, 0, true);
                double mTurn = midClassVal(mid, currentYear, maxMonth, city, 0, false);
                double mLastPass = midClassVal(mid, currentYear - 1, maxMonth, city, 0, true);
                double mLastTurn = midClassVal(mid, currentYear - 1, maxMonth, city, 0, false);
                setValOrBlank(sheet, r0, 11, mPass == 0 ? null : round(mPass, 4));
                setValOrBlank(sheet, r0, 11, mPass == 0 ? null : mPass);
                setValOrBlank(sheet, r0, 13, mPass == 0 ? null : midMonthYoy(mid, currentYear, maxMonth, city, 0, true));
                setValOrBlank(sheet, r0, 15, mTurn == 0 ? null : round(mTurn, 4));
                setValOrBlank(sheet, r0, 15, mTurn == 0 ? null : mTurn);
                setValOrBlank(sheet, r0, 17, mTurn == 0 ? null : midMonthYoy(mid, currentYear, maxMonth, city, 0, false));
            }
            recalc(wb);
@@ -2701,7 +2701,7 @@
    private Double yoyOf(double cur, double base) {
        if (base == 0.0) return null;
        return round((cur - base) / base, 4);
        return (cur - base) / base;
    }
    /** ä¸­å£å¾„分类累计(1..monthCount æœˆæ±‚和) */
@@ -2743,9 +2743,9 @@
                double turn = midCumClass(mid, currentYear, "湖北省", maxMonth, dims[i], false);
                double lastPass = midCumClass(mid, currentYear - 1, "湖北省", maxMonth, dims[i], true);
                double lastTurn = midCumClass(mid, currentYear - 1, "湖北省", maxMonth, dims[i], false);
                setValOrBlank(sheet, r0, 1, pass == 0 ? null : round(pass, 4));
                setValOrBlank(sheet, r0, 1, pass == 0 ? null : pass);
                setValOrBlank(sheet, r0, 2, midCumYoy(mid, currentYear, "湖北省", maxMonth, dims[i], true));
                setValOrBlank(sheet, r0, 5, turn == 0 ? null : round(turn, 4));
                setValOrBlank(sheet, r0, 5, turn == 0 ? null : turn);
                setValOrBlank(sheet, r0, 6, midCumYoy(mid, currentYear, "湖北省", maxMonth, dims[i], false));
            }
            // ä¸‹åŠå—(当月):标题行9、数据行11-15(0-based 8、10-14)
@@ -2759,9 +2759,9 @@
                double turn = midClassVal(mid, currentYear, maxMonth, "湖北省", dims[i], false);
                double lastPass = midClassVal(mid, currentYear - 1, maxMonth, "湖北省", dims[i], true);
                double lastTurn = midClassVal(mid, currentYear - 1, maxMonth, "湖北省", dims[i], false);
                setValOrBlank(sheet, r0, 1, pass == 0 ? null : round(pass, 4));
                setValOrBlank(sheet, r0, 1, pass == 0 ? null : pass);
                setValOrBlank(sheet, r0, 2, pass == 0 ? null : midMonthYoy(mid, currentYear, maxMonth, "湖北省", dims[i], true));
                setValOrBlank(sheet, r0, 5, turn == 0 ? null : round(turn, 4));
                setValOrBlank(sheet, r0, 5, turn == 0 ? null : turn);
                setValOrBlank(sheet, r0, 6, turn == 0 ? null : midMonthYoy(mid, currentYear, maxMonth, "湖北省", dims[i], false));
            }
            // ä¸‹åŠå—占比公式由样例指向上半块(B$3/F$3),改写为本块总行(B$11/F$11),行12-15
@@ -3179,7 +3179,7 @@
        Cell c = row.getCell(colIdx);
        if (c != null && c.getCellType() == org.apache.poi.ss.usermodel.CellType.FORMULA) return;
        if (c == null) c = row.createCell(colIdx);
        if (v == 0) c.setBlank(); else c.setCellValue(round(v, 2));
        if (v == 0) c.setBlank(); else c.setCellValue(v);
    }
    /** æ˜¾å¼å†™å…¥ 0(setDataCell å¯¹ 0 ç½®ç©ºï¼›æ±‡æ€»è¡¨ç½‘约车行需要显示 0) */
@@ -4643,8 +4643,8 @@
            if (r.getAvgDistanceCity() != null && r.getAvgDistanceCity() > 0) a[5] = r.getAvgDistanceCity();
        }
        for (double[] a : map.values()) {
            if (a[4] <= 0 && a[0] > 0) a[4] = round(a[1] / a[0], 2);
            if (a[5] <= 0 && a[2] > 0) a[5] = round(a[3] / a[2], 2);
            if (a[4] <= 0 && a[0] > 0) a[4] = a[1] / a[0];
            if (a[5] <= 0 && a[2] > 0) a[5] = a[3] / a[2];
        }
        return map;
    }
@@ -4960,19 +4960,19 @@
                    Row row = sheet.getRow(rowIdx);
                    if (row == null) row = sheet.createRow(rowIdx);
                    if (a > 0) {
                        // 2026å¹´ 1..N æœˆï¼šåº“内有值则填(2位小数),无值清空模板样例
                        // 2026å¹´ 1..N æœˆï¼šåº“内有值则填(全精度写入,两位小数由单元格格式展示),无值清空模板样例
                        for (int m = 1; m <= month; m++) {
                            double v = cityVal(monthCity, area, m, k);
                            Cell c = row.getCell(2 + (m - 1) * 2);
                            if (c == null) c = row.createCell(2 + (m - 1) * 2);
                            if (v == 0) c.setBlank(); else c.setCellValue(round(v, 2));
                            if (v == 0) c.setBlank(); else c.setCellValue(v);
                        }
                        // 2025å¹´ 1..12 æœˆï¼šåº“内有去年数据则填,否则清空模板样例
                        for (int m = 1; m <= 12; m++) {
                            double v = cityVal(lastYearMonthCity, area, m, k);
                            Cell c = row.getCell(19 + (m - 1) * 2);
                            if (c == null) c = row.createCell(19 + (m - 1) * 2);
                            if (v == 0) c.setBlank(); else c.setCellValue(round(v, 2));
                            if (v == 0) c.setBlank(); else c.setCellValue(v);
                        }
                    }
                    rowIdx++;
@@ -5465,7 +5465,7 @@
        hssfNum(row, 8, agg.startCum == 0 ? null : agg.startCum);
        hssfNum(row, 9, agg.yearPlan == 0 ? null : agg.yearPlan);
        hssfNum(row, 22, agg.yearCum == 0 ? null : agg.yearCum);
        if (agg.yearPlan > 0) hssfNum(row, 23, round(agg.yearCum / agg.yearPlan, 4));
        if (agg.yearPlan > 0) hssfNum(row, 23, agg.yearCum / agg.yearPlan);
    }
@@ -5797,7 +5797,7 @@
                putNum(row, 2, s[3] == 0 ? null : s[3]);
                putNum(row, 3, s[4] == 0 ? null : s[4]);
                double b = numOf(row.getCell(1));
                putNum(row, 4, b > 0 ? round(s[3] / b * 100, 4) : null);
                putNum(row, 4, b > 0 ? s[3] / b * 100 : null);
                cT += s[3];
                dT += s[4];
            }
@@ -5806,7 +5806,7 @@
                putNum(tot, 2, cT == 0 ? null : cT);
                putNum(tot, 3, dT == 0 ? null : dT);
                double b = numOf(tot.getCell(1));
                putNum(tot, 4, b > 0 ? round(cT / b * 100, 4) : null);
                putNum(tot, 4, b > 0 ? cT / b * 100 : null);
            }
        }
        try {
@@ -6027,7 +6027,7 @@
        for (InvestmentMonthly m : monthlies) provinceYearPlan += nz(m.getYearPlan());
        HSSFRow rowOther = s1.getRow(9);
        for (int c = 1; c <= 5; c++) hssfNum(rowOther, c, null);
        hssfNum(rowOther, 5, provinceYearPlan == 0 ? null : round(provinceYearPlan / 10000, 2));
        hssfNum(rowOther, 5, provinceYearPlan == 0 ? null : provinceYearPlan / 10000);
        // ---- sheet2 è§„上项目表:表头年份 + åˆè®¡ + é¡¹ç›®è¡Œ ----
        HSSFSheet s2 = wb.getSheetAt(1);
        hssfText(s2.getRow(0), 0, year + "年交通建设项目储备情况(规模以上项目)");
@@ -6259,6 +6259,8 @@
    private double nz(Double v) {
        return v == null ? 0.0 : v;
    }
    /** æ•°å€¼æ ¼å¼é‡Œ 3 ä½åŠä»¥ä¸Šçš„小数部分(如 0.0000 / 0.000_ ï¼‰ */
    private static final java.util.regex.Pattern DEC_MORE_THAN_TWO = java.util.regex.Pattern.compile("\\.0{3,}");
    /** ç”Ÿæˆè¡¨æ•°å€¼åˆ—统一样式:保留全精度数值,显示两位小数;整数值与 %/日期/科学计数等既有样式不改变 */
    private void applyTwoDecimalFormat(org.apache.poi.ss.usermodel.Workbook wb) {
        if (wb == null) return;
@@ -6275,6 +6277,17 @@
                        CellType ct = cell.getCellType();
                        if (ct != CellType.NUMERIC && ct != CellType.FORMULA) continue;
                        if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) continue;
                        org.apache.poi.ss.usermodel.CellStyle cs = cell.getCellStyle();
                        if (cs == null) continue;
                        String f = cs.getDataFormatString();
                        if (f == null) f = "";
                        if (f.contains("%") || f.contains("E") || f.contains("@")) continue;
                        String normalized = null;
                        if (DEC_MORE_THAN_TWO.matcher(f).find()) {
                            // æ¯ç‰ˆä¸ªåˆ«æ ¼å¸¦ 3~4 ä½å°æ•°æ ¼å¼ï¼ˆå¦‚ 0.000_ ã€0.0000_);[Red]\(0.0000\)):数值仍全精度,
                            // å±•示统一收敛到两位。这类格本就按数值格式展示,故不受「整数不显示 .00」保护影响。
                            normalized = DEC_MORE_THAN_TWO.matcher(f).replaceAll(".00");
                        } else {
                        boolean integral = false;
                        if (ct == CellType.NUMERIC) {
                            integral = Math.rint(cell.getNumericCellValue()) == cell.getNumericCellValue();
@@ -6282,17 +6295,14 @@
                            integral = Math.rint(cell.getNumericCellValue()) == cell.getNumericCellValue();
                        }
                        if (integral) continue; // æ•´æ•°ä¸æ˜¾ç¤º .00(含公式结果为整数的格)
                        org.apache.poi.ss.usermodel.CellStyle cs = cell.getCellStyle();
                        if (cs == null) continue;
                        String f = cs.getDataFormatString();
                        if (f == null) f = "";
                        if (f.contains("%") || f.contains("E") || f.contains("0.00")) continue;
                            if (f.contains("0.00")) continue;
                        boolean plain = f.isEmpty() || "General".equals(f) || "general".equals(f)
                            || "0".equals(f) || "#,##0".equals(f) || "0.0".equals(f) || "#,##0.0".equals(f);
                        if (!plain) continue;
                        }
                        org.apache.poi.ss.usermodel.CellStyle ns = wb.createCellStyle();
                        ns.cloneStyleFrom(cs);
                        ns.setDataFormat(fmtTwo);
                        ns.setDataFormat(normalized == null ? fmtTwo : df.getFormat(normalized));
                        cell.setCellStyle(ns);
                    } catch (Exception ignore) {
                        // ä¸ªåˆ«å•元格格式异常不阻塞导出