feat(holiday): support 2019 and previous-day data in new templates
| | |
| | | |
| | | ## ææ°ï¼2026-10-03ï¼ |
| | | |
| | | ### 10-03 æï¼éè¦èåæ¥æ°æ¨¡æ¿ï¼2019/å䏿¥ï¼éé
|
| | | - **模æ¿åå**ï¼`2026å¹´å䏿°æ®.xlsx` çé路表å¢å 2019 å¹´å忝2019ï¼éè·¯ãæ°èªå¢å âå䏿¥âè¡ï¼å¿«æ¥æ¨¡æ¿å¢å 忝2024 åãæ§æ¨¡æ¿å·²æ¾å¨ `docs/ä¸é¡¹ç»è®¡/éè¦è忥/å¤ä»½/`ã |
| | | - **æ°æ®åºåå§å**ï¼å¯å¨æ¶ç´æ¥åå
¥ 2019 éè·¯æ°æ®ï¼ä»¥å 2024/2025/2026 å¹´éè·¯ãæ°èªå䏿¥æ°æ®ï¼`holiday_calendar.day_index=0` 表示âå䏿¥âï¼æ§åºå¯å¨ä¼èªå¨è¡¥é½å·²æå¹´ä»½çå䏿¥é
ç½®è¡ï¼ä¸ä¸è¦ç管çå已维æ¤çæ¥æã |
| | | - **æ£å¸¸å¯¼å
¥å£å¾**ï¼åªè¯»åæé年份çå½å¹´åï¼2026 å¹´éè·¯/æ°èª/éè·¯/水路为 C åãé«é为 C åãæ®éå½çé为 D åï¼ï¼åæ¶åªé¢å¤è¯»åå½å年份éè·¯/æ°èªçâå䏿¥âè¡ã2019/2024/2025 åå²åä¸åä¸å¯¼å
¥ã |
| | | - **å¿«æ¥å£å¾**ï¼åæ¯åªä½¿ç¨ 2025ã2024ï¼åæç¬¬ 1 æ¥çéè·¯ãæ°èªç¯æ¯ä½¿ç¨âå䏿¥âæ°æ®ï¼å
¬è·¯ãæ°´è·¯ãæ»è®¡åå
¶ä»å£å¾ç¬¬ 1 æ¥ä»æ¾ç¤ºâââï¼ç¬¬ 2 æ¥èµ·æ£å¸¸è®¡ç®ç¯æ¯ã |
| | | - **è¿ç§»èæ¬**ï¼`docs/sql_holiday_previous_day_migration.sql`ï¼é¨ç½²å
åæ¥ä¸º `deploy-templates/db/sql_holiday_previous_day_migration.sql`ï¼èæ¬å¯é夿§è¡ã |
| | | - **éªè¯ï¼æ¬æº 3305 åº + ç宿¨¡æ¿ï¼**ï¼æ¥å£å¯¼å
¥ç»æ `passengerRows=10, vehicleRows=4, successRows=14, failRows=0`ï¼æ°æ®åºæ½æ¥ 2026-09-30 éè·¯=82.1ãæ°èª=6.6994ï¼2026-10-01 éè·¯=113.8ãæ°èª=6.7482ãéè·¯=78.82ãæ°´è·¯=1.311ï¼2026-10-02 éè·¯=82.0ãæ°èª=6.3914ãéè·¯=75.61ãæ°´è·¯=3.3373ï¼2026-10-01 å¿«æ¥éè·¯ç¯æ¯=+38.6%ãæ°èªç¯æ¯=+0.7%ï¼æ»è¡ç¯æ¯ä¸ºâââï¼2026-10-02 éè·¯ç¯æ¯=-27.9%ãæ°èªç¯æ¯=-5.3%ã |
| | | - **è¿è¡ç¶æ**ï¼å端 8090ï¼éå¯åå·²å è½½æ°ä»£ç ï¼ãå端 8080 åå¨è¿è¡ï¼è®¿é® `http://localhost:8080`ã |
| | | |
| | | --- |
| | | |
| | | ### è¡¥å
ï¼10-03 ä¸åï¼ï¼æå¡å¨é¨ç½²æ¥ âAccess denied ... (using password: NO)â ââ æ ¹å ä¸ä¿®å¤ |
| | | - **ç°è±¡**ï¼æ `deploy/traffic-audit-release-20261002/` é¨ç½²å° `61.183.254.94` åï¼å端å¯å¨æ¥ `java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: NO)`ã |
| | | - **æ ¹å **ï¼jar å
ç½®é
ç½®éåºå£ä»¤æ¯å ä½ç¬¦ `${TRAFFIC_DB_PASSWORD:}`ï¼é»è®¤ç©ºä¸²ï¼ï¼è 10-02 é£ä¸ªå
**åªå« jar + web + dist**ï¼æ²¡æå¤ç½® `application.yml`ãæ²¡æ `docs/`ã`db/`ãå¯å¨èæ¬ãé¨ç½²æºæ²¡æä»»ä½å¤é¨é
ç½®è¦çï¼Spring å°±æ¿ç©ºå¯ç è¿æ¬æº MySQL â `using password: NO`ï¼æ³¨æï¼è¿æ¯âæ«ä¼ å¯ç âï¼ä¸æ¯å¯ç éï¼ã |
| | |
| | | sql_holiday.sql ä¸é¡¹ç»è®¡-éè¦èåæ¥è¡¨ï¼æ§åºç¼ºè¡¨æ¶æ§è¡ï¼ |
| | | sql_holiday_decimal_migration.sql è忥æ°å¼å精度è¿ç§»ï¼æ§åºåçº§ï¼ |
| | | sql_holiday_comparison_base_migration.sql èåæ¥ç¯æ¯åºæå段è¿ç§»ï¼æ§åºåçº§ï¼ |
| | | sql_holiday_previous_day_migration.sql è忥å䏿¥é
ç½®è¿ç§»ï¼æ§åºåçº§ï¼ |
| | | README-é¨ç½²è¯´æ.txt |
| | | |
| | | ãé¨ç½²æºç¯å¢è¦æ±ã |
| | |
| | | mysql --default-character-set=utf8mb4 -u root -p traffic_audit < db/sql_city_bus.sql |
| | | mysql --default-character-set=utf8mb4 -u root -p traffic_audit < db/sql_city_taxi.sql |
| | | mysql --default-character-set=utf8mb4 -u root -p traffic_audit < db/sql_wyc.sql |
| | | 注ï¼å
¨æ°åºç init.sql å·²å«è忥 4 å¼ è¡¨ï¼è¥æ¯å¨æ©æçæ¬åºä¸è¡¥èåæ¥æ¨¡åï¼åæ§è¡ db/sql_holiday.sqlï¼å¹¶æéæ§è¡ä¸¤ä¸ªè¿ç§»èæ¬ã |
| | | 注ï¼å
¨æ°åºç init.sql å·²å«è忥 4 å¼ è¡¨ï¼è¥æ¯å¨æ©æçæ¬åºä¸è¡¥èåæ¥æ¨¡åï¼åæ§è¡ db/sql_holiday.sqlï¼å¹¶æéæ§è¡ä¸ä¸ªè¿ç§»èæ¬ï¼æ°å¼ç²¾åº¦ãç¯æ¯åºæãå䏿¥é
ç½®ï¼ãå䏿¥è¿ç§»èæ¬å¯é夿§è¡ã |
| | | é¢ç½®è´¦å·ï¼admin / admin123ï¼é¦æ¬¡ç»å½å请æ¹å¯ç ï¼ |
| | | |
| | | 2. æ¹é
ç½® backend/application.yml |
| | |
| | | year INT NOT NULL COMMENT '年份', |
| | | holiday_type VARCHAR(32) NOT NULL COMMENT 'è忥åç±»ï¼SPRING_FESTIVAL/LABOR_DAY/NATIONAL_DAY', |
| | | holiday_date DATE NOT NULL COMMENT 'è忥å
·ä½æ¥æ', |
| | | day_index INT NOT NULL COMMENT 'ç¬¬å æ¥ï¼ä» 1 å¼å§', |
| | | day_index INT NOT NULL COMMENT '0=å䏿¥ï¼1..N=åæç¬¬Næ¥', |
| | | created_at DATETIME DEFAULT CURRENT_TIMESTAMP, |
| | | updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | PRIMARY KEY (id), |
| | |
| | | year INT NOT NULL, |
| | | holiday_type VARCHAR(32) NOT NULL, |
| | | holiday_date DATE NOT NULL, |
| | | -- 0 is the configured previous day; normal holiday days are 1..N. |
| | | day_index INT NOT NULL, |
| | | created_at DATETIME DEFAULT CURRENT_TIMESTAMP, |
| | | updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| New file |
| | |
| | | -- Add the previous-day row used by rail and civil-aviation ring-ratio calculations. |
| | | -- day_index = 0 identifies the configured previous day; normal holiday days remain 1..N. |
| | | INSERT IGNORE INTO holiday_calendar |
| | | (year, holiday_type, holiday_date, day_index, created_at, updated_at) |
| | | VALUES |
| | | (2024, 'NATIONAL_DAY', '2024-09-30', 0, NOW(), NOW()), |
| | | (2025, 'NATIONAL_DAY', '2025-09-30', 0, NOW(), NOW()), |
| | | (2026, 'NATIONAL_DAY', '2026-09-30', 0, NOW(), NOW()); |
| | |
| | | |
| | | ## ä¹ãä¸é¡¹ç»è®¡-éè¦èåæ¥æ¨¡å |
| | | |
| | | > æ°æ®æ¥æºä¸æ¨¡æ¿ï¼`docs/ä¸é¡¹ç»è®¡/éè¦è忥/2026å¹´å䏿°æ®.xlsx`ï¼å¿«æ¥æ¨¡æ¿ï¼`docs/ä¸é¡¹ç»è®¡/éè¦è忥/æ¹åçå½åºåæç»¼åè¿è¾å®¢æµå¿«æ¥æ¨¡æ¿.docx`ã馿¬¡å¯å¨ä¼ä»å·¥ä½ç°¿åå§å 2024/2025 å¹´åä¸å®¢æµãè½¦æµæ°æ®ï¼å¹¶åå§å 2024-2026 年客è¿ç³»æ°ã |
| | | > æ°æ®æ¥æºä¸æ¨¡æ¿ï¼`docs/ä¸é¡¹ç»è®¡/éè¦è忥/2026å¹´å䏿°æ®.xlsx`ï¼å¿«æ¥æ¨¡æ¿ï¼`docs/ä¸é¡¹ç»è®¡/éè¦è忥/æ¹åçå½åºåæç»¼åè¿è¾å®¢æµå¿«æ¥æ¨¡æ¿.docx`ã馿¬¡å¯å¨ä¼ä»å·¥ä½ç°¿åå§å 2024/2025 å¹´åä¸å®¢æµãè½¦æµæ°æ®ã2024-2026 å¹´éè·¯/æ°èªå䏿¥æ°æ®ã2019 å¹´éè·¯æ°æ®ï¼å¹¶åå§å 2024-2026 年客è¿ç³»æ°ã |
| | | |
| | | ### holiday_calendar èåæ¥æ¥æé
ç½® |
| | | | åæ®µ | ç±»å | 说æ | |
| | |
| | | | year | int | 年份 | |
| | | | holiday_type | varchar(32) | æ¥è SPRING_FESTIVAL / äºä¸ LABOR_DAY / åä¸ NATIONAL_DAY | |
| | | | holiday_date | date | è忥å
·ä½æ¥æ | |
| | | | day_index | int | ç¬¬å æ¥ï¼ä» 1 å¼å§ | |
| | | | day_index | int | 0=å䏿¥ï¼1..N=åæç¬¬ N æ¥ | |
| | | | created_at / updated_at | datetime | å建/æ´æ°æ¶é´ | |
| | | |
| | | å¯ä¸é®ï¼`(year, holiday_type, day_index)`ã`(year, holiday_type, holiday_date)`ãä»
admin å¯éè¿ç³»ç»é
ç½®èåç»´æ¤ã |
| | | å¯ä¸é®ï¼`(year, holiday_type, day_index)`ã`(year, holiday_type, holiday_date)`ãä»
admin å¯éè¿ç³»ç»é
ç½®èåç»´æ¤ï¼`day_index=0` ç¨äºéè·¯ãæ°èªçå䏿¥ç¯æ¯åºæ°ã |
| | | |
| | | ### holiday_passenger_flow èåæ¥ç»¼åè¿è¾è¥ä¸æ§å®¢æµ |
| | | | åæ®µ | ç±»å | 说æ | |
| | |
| | | - 2026 å¹´æ®éå½çéå¦å¡«åç¯æ¯åºæï¼ååªå½±åæ®éå½çé车æµéèªèº«ç¯æ¯ï¼å¡«åæ¶æâ彿¥ / ç¯æ¯åºæ - 1âï¼æªå¡«åæ¶æâ彿¥ / å䏿¥ - 1âï¼æç®äººåæµå¨éãæ»äººåæµå¨éçç»§ç»æ Excel åå
¬å¼è®¡ç®ã2024ã2025 å岿°æ®ä¸ä½¿ç¨è¯¥å段ã |
| | | - å
¬è·¯äººåæµå¨é = éè·¯è¥ä¸æ§å®¢è¿é + éè¥ä¸æ§äººåæµå¨éã |
| | | - æ»äººåæµå¨é = å
¬è·¯ + éè·¯ + æ°èª + æ°´è·¯ã |
| | | - 导å
¥æâ年份 + è忥åç±»â读å `holiday_calendar` çæ¥æï¼å°å·¥ä½ç°¿â第Næ¥âè¡è¦çåå
¥å½å¹´å®¢æµ/车æµï¼æ¥å¸¸åªè¯»åæé年份çå½å¹´åï¼åå²å¹´ä»½ä»
馿¬¡åå§åã |
| | | - 导å
¥æâ年份 + è忥åç±»â读å `holiday_calendar` ä¸ `day_index>=1` çæ¥æï¼å°å·¥ä½ç°¿â第Næ¥âè¡è¦çåå
¥å½å¹´å®¢æµ/车æµï¼åæ¶è¯»åå½å年份éè·¯ãæ°èªçâå䏿¥âè¡ï¼åå
¥ `day_index=0` 对åºçæ¥æã |
| | | - æ¥å¸¸å¯¼å
¥åªè¯»åæé年份çå½å¹´åï¼é路表ç 2019/2024/2025 ååä¸è¯»åï¼ï¼2019 å¹´éè·¯æ°æ®å 2024/2025/2026 å¹´å䏿¥éè·¯ãæ°èªæ°æ®ç±å¯å¨åå§åç´æ¥åå
¥æ°æ®åºã |
| | | - 导å
¥åæ£æ¥æ¯æ¬æ¬¡ææ°å¯¼å
¥æ¥æ´æ©çå岿¥ææ¯å¦åçååï¼å¦æååå
ä¸ååºï¼è¿å差弿¸
åï¼ç¨æ·ç¡®è®¤åæå
许è¦çã |
| | | - 导åºä»¥æºå·¥ä½ç°¿ä¸ºåºç¨¿ï¼åå¡«åºå
æ°æ®ä¸å®¢è¿ç³»æ°ï¼å¿«æ¥ææ¥æçæ Word å¹¶æ¿æ¢æ¨¡æ¿ä¸ç彿¥ã累计ãç¯æ¯å忝å
容ã |
| | | - è®°å½âå䏿¥âçéè·¯ãæ°èªæ°æ®åï¼åæç¬¬ 1 æ¥çéè·¯ãæ°èªç¯æ¯æè¯¥æ°æ®è®¡ç®ï¼å
¬è·¯ãæ°´è·¯ãæ»äººåæµå¨éå¨ç¬¬ 1 æ¥ä»ä¸è®¡ç®ç¯æ¯ã |
| | | - 导åºä»¥æºå·¥ä½ç°¿ä¸ºåºç¨¿ï¼åå¡«åºå
æ°æ®ä¸å®¢è¿ç³»æ°ï¼å¿«æ¥ææ¥æçæ Wordï¼åæ¯åºå®ä½¿ç¨ 2025ã2024 两年ï¼ä¸ä½¿ç¨ 2019 æ°æ®ã |
| | |
| | | year INT NOT NULL COMMENT '年份', |
| | | holiday_type VARCHAR(32) NOT NULL COMMENT 'è忥åç±»ï¼SPRING_FESTIVAL/LABOR_DAY/NATIONAL_DAY', |
| | | holiday_date DATE NOT NULL COMMENT 'è忥å
·ä½æ¥æ', |
| | | day_index INT NOT NULL COMMENT 'ç¬¬å æ¥ï¼ä» 1 å¼å§', |
| | | day_index INT NOT NULL COMMENT '0=å䏿¥ï¼1..N=åæç¬¬Næ¥', |
| | | created_at DATETIME DEFAULT CURRENT_TIMESTAMP, |
| | | updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | PRIMARY KEY (id), |
| New file |
| | |
| | | -- Add the previous-day row used by rail and civil-aviation ring-ratio calculations. |
| | | -- day_index = 0 identifies the configured previous day; normal holiday days remain 1..N. |
| | | INSERT IGNORE INTO holiday_calendar |
| | | (year, holiday_type, holiday_date, day_index, created_at, updated_at) |
| | | VALUES |
| | | (2024, 'NATIONAL_DAY', '2024-09-30', 0, NOW(), NOW()), |
| | | (2025, 'NATIONAL_DAY', '2025-09-30', 0, NOW(), NOW()), |
| | | (2026, 'NATIONAL_DAY', '2026-09-30', 0, NOW(), NOW()); |
| | |
| | | â ââ sql_wyc.sql ç½çº¦è½¦å
¨çæ»é/订ååå¸å·è¡¨ï¼å¿
é¡»æ§è¡ï¼ |
| | | â ââ sql_holiday.sql ä¸é¡¹ç»è®¡-éè¦èåæ¥è¡¨ï¼æ§åºè¡¥å»ºæ¶æ§è¡ï¼ |
| | | â ââ sql_holiday_decimal_migration.sql è忥æ°å¼å精度è¿ç§»ï¼æ§åºåçº§ï¼ |
| | | â ââ sql_holiday_comparison_base_migration.sql èåæ¥ç¯æ¯åºæå段è¿ç§»ï¼æ§åºåçº§ï¼ |
| | | â ââ sql_holiday_comparison_base_migration.sql èåæ¥ç¯æ¯åºæå段è¿ç§»ï¼æ§åºåçº§ï¼ |
| | | â ââ sql_holiday_previous_day_migration.sql è忥å䏿¥é
ç½®è¿ç§»ï¼æ§åºåçº§ï¼ |
| | | ââ README-é¨ç½²è¯´æ.txt |
| | | ``` |
| | | |
| | |
| | | ``` |
| | | |
| | | > â ï¸ **`init.sql` æ¯ç ´åæ§ç**ï¼éé¢çåºç¡è¡¨é½å¸¦ `DROP TABLE IF EXISTS`ã**å·²ææ°æ®çåºä¸åä¸ä¸è¦åè· init.sql**ï¼å¦åè¡¨æ°æ®ä¼è¢«æ¸
空é建ã |
| | | > æ©æçæ¬åºè¥æ²¡æè忥 4 å¼ è¡¨ï¼æ§è¡ `sql_holiday.sql`ï¼è¥å·²æèåæ¥è¡¨ä½ç¼ºç²¾åº¦æç¯æ¯åºæåæ®µï¼æéæ§è¡ä¸¤ä¸ªè¿ç§»èæ¬ã |
| | | > æ©æçæ¬åºè¥æ²¡æè忥 4 å¼ è¡¨ï¼æ§è¡ `sql_holiday.sql`ï¼è¥å·²æèåæ¥è¡¨ä½ç¼ºç²¾åº¦ãç¯æ¯åºæå段æå䏿¥é
ç½®ï¼æéæ§è¡ `sql_holiday_decimal_migration.sql`ã`sql_holiday_comparison_base_migration.sql`ã`sql_holiday_previous_day_migration.sql`ã |
| | | > 4 ä¸ªèæ¬å
±è¦ç 33 å¼ è¡¨ï¼init.sql 28 å¼ ï¼å
¶ä¸å«è忥 4 å¼ + åå¸å®¢è¿ 3 å¼ + ç½çº¦è½¦ 2 å¼ ï¼ã |
| | | |
| | | #### 路线 Bï¼æç°ç½/å¼åæºçæ°æ®æ´å¥æ¬è¿å»ï¼æ¨èï¼ä¸ç¨è·èæ¬ï¼ |
| | |
| | | package com.trafficaudit.holiday.service; |
| | | |
| | | import com.trafficaudit.holiday.HolidayNumberUtils; |
| | | import com.trafficaudit.holiday.entity.HolidayCalendar; |
| | | import com.trafficaudit.holiday.service.HolidayStatService.DailyMetrics; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | |
| | | import org.w3c.dom.Node; |
| | | import org.w3c.dom.NodeList; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.xml.parsers.DocumentBuilderFactory; |
| | | import javax.xml.transform.OutputKeys; |
| | | import javax.xml.transform.Transformer; |
| | |
| | | private String bulletinTemplatePath; |
| | | |
| | | private final HolidayStatService statService; |
| | | @Resource |
| | | private HolidayConfigService configService; |
| | | private static final ThreadLocal<Document> CURRENT_DOCUMENT = new ThreadLocal<>(); |
| | | |
| | | public HolidayBulletinService(HolidayStatService statService) { |
| | |
| | | } |
| | | DailyMetrics previousDay = current.getDayIndex() > 1 |
| | | ? statService.dailyMetrics(current.getYear(), current.getHolidayType(), current.getDayIndex() - 1) : null; |
| | | HolidayCalendar previousDayConfig = configService.findPreviousDay(current.getYear(), current.getHolidayType()); |
| | | LocalDate previousDayDate = previousDayConfig == null || previousDayConfig.getHolidayDate() == null |
| | | ? current.getDate().minusDays(1) : previousDayConfig.getHolidayDate(); |
| | | DailyMetrics previousCalendarDay = previousDay != null |
| | | ? previousDay : statService.dailyMetricsByDate(previousDayDate); |
| | | DailyMetrics previousYear = statService.dailyMetrics(current.getYear() - 1, current.getHolidayType(), current.getDayIndex()); |
| | | DailyMetrics twoYearsAgo = statService.dailyMetrics(current.getYear() - 2, current.getHolidayType(), current.getDayIndex()); |
| | | DailyMetrics currentCumulative = statService.cumulativeMetrics(current.getYear(), current.getHolidayType(), current.getDayIndex()); |
| | | DailyMetrics previousYearCumulative = statService.cumulativeMetrics(current.getYear() - 1, current.getHolidayType(), current.getDayIndex()); |
| | | DailyMetrics twoYearsAgoCumulative = statService.cumulativeMetrics(current.getYear() - 2, current.getHolidayType(), current.getDayIndex()); |
| | | |
| | | if (current.getTotalPersonFlow() == null) { |
| | | throw new IllegalArgumentException("æéæ¥æçå
¨ç¤¾ä¼è·¨åºåäººåæµå¨é没æè¶³å¤æ°æ®ï¼æ æ³çæå¿«æ¥"); |
| | |
| | | ? null : HolidayNumberUtils.divide(currentCumulative.getTotalPersonFlow(), BigDecimal.valueOf(10000))); |
| | | String ringRatioClause = previousDay == null ? "" : "ï¼ç¯æ¯" + ratioPhrase( |
| | | current.getTotalPersonFlow(), previousDay.getTotalPersonFlow()); |
| | | String paragraph1 = String.format("%dæ%dæ¥ï¼åæç¬¬%d天ï¼ï¼å
¨çå
¨ç¤¾ä¼è·¨åºåäººåæµå¨é%sä¸äººæ¬¡%sï¼æ¯2025å¹´åæ%sã", |
| | | String paragraph1 = String.format("%dæ%dæ¥ï¼åæç¬¬%d天ï¼ï¼å
¨çå
¨ç¤¾ä¼è·¨åºåäººåæµå¨é%sä¸äººæ¬¡%sï¼æ¯2025å¹´åæ%sï¼æ¯2024å¹´åæ%sã", |
| | | date.getMonthValue(), date.getDayOfMonth(), current.getDayIndex(), currentTotal, |
| | | ringRatioClause, |
| | | ratioPhrase(current.getTotalPersonFlow(), previousYear == null ? null : previousYear.getTotalPersonFlow())); |
| | | String paragraph2 = String.format("åæä»¥æ¥ï¼å
¨çå
¨ç¤¾ä¼è·¨åºåäººåæµå¨é累计%säº¿äººæ¬¡ï¼æ¯2025å¹´åæ%sã", |
| | | ratioPhrase(current.getTotalPersonFlow(), previousYear == null ? null : previousYear.getTotalPersonFlow()), |
| | | ratioPhrase(current.getTotalPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getTotalPersonFlow())); |
| | | String paragraph2 = String.format("åæä»¥æ¥ï¼å
¨çå
¨ç¤¾ä¼è·¨åºåäººåæµå¨é累计%säº¿äººæ¬¡ï¼æ¯2025å¹´åæ%sï¼æ¯2024å¹´åæ%sã", |
| | | cumulativeTotalYi, |
| | | ratioPhrase(currentCumulative.getTotalPersonFlow(), |
| | | previousYearCumulative == null ? null : previousYearCumulative.getTotalPersonFlow())); |
| | | previousYearCumulative == null ? null : previousYearCumulative.getTotalPersonFlow()), |
| | | ratioPhrase(currentCumulative.getTotalPersonFlow(), |
| | | twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getTotalPersonFlow())); |
| | | |
| | | File template = resolveTemplateFile(); |
| | | if (!template.exists() || !template.isFile()) { |
| | |
| | | try { |
| | | replaceParagraph(document, text -> text.startsWith("10æ") && text.contains("åæç¬¬"), paragraph1); |
| | | replaceParagraph(document, text -> text.startsWith("åæä»¥æ¥"), paragraph2); |
| | | fillTable(document, current, previousDay, previousYear, currentCumulative, previousYearCumulative); |
| | | fillTable(document, current, previousDay, previousCalendarDay, previousYear, twoYearsAgo, |
| | | currentCumulative, previousYearCumulative, twoYearsAgoCumulative); |
| | | byte[] replacedXml = serializeDocument(document); |
| | | return replaceZipEntry(templateBytes, "word/document.xml", replacedXml); |
| | | } finally { |
| | |
| | | } |
| | | |
| | | private void fillTable(Document document, DailyMetrics current, DailyMetrics previousDay, |
| | | DailyMetrics previousYear, DailyMetrics currentCumulative, |
| | | DailyMetrics previousYearCumulative) { |
| | | DailyMetrics previousCalendarDay, DailyMetrics previousYear, DailyMetrics twoYearsAgo, |
| | | DailyMetrics currentCumulative, DailyMetrics previousYearCumulative, |
| | | DailyMetrics twoYearsAgoCumulative) { |
| | | DailyMetrics firstDayRailBase = previousDay == null ? previousCalendarDay : previousDay; |
| | | DailyMetrics firstDayCivilAviationBase = previousDay == null ? previousCalendarDay : previousDay; |
| | | Element table = firstElement(document, "tbl"); |
| | | if (table == null) { |
| | | throw new IllegalArgumentException("å¿«æ¥æ¨¡æ¿ä¸æ¾ä¸å°æ°æ®è¡¨"); |
| | |
| | | number(current.getNonBusinessPersonFlow()), |
| | | ratio(current.getNonBusinessPersonFlow(), previousDay == null ? null : previousDay.getNonBusinessPersonFlow()), |
| | | ratio(current.getNonBusinessPersonFlow(), previousYear == null ? null : previousYear.getNonBusinessPersonFlow()), |
| | | "â", |
| | | ratio(current.getNonBusinessPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getNonBusinessPersonFlow()), |
| | | number(currentCumulative.getNonBusinessPersonFlow()), |
| | | ratio(currentCumulative.getNonBusinessPersonFlow(), |
| | | previousYearCumulative == null ? null : previousYearCumulative.getNonBusinessPersonFlow()), |
| | | "â" |
| | | ratio(currentCumulative.getNonBusinessPersonFlow(), |
| | | twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getNonBusinessPersonFlow()) |
| | | }); |
| | | writeTableRow(table, 3, new String[]{ |
| | | number(current.getRoad()), |
| | | ratio(current.getRoad(), previousDay == null ? null : previousDay.getRoad()), |
| | | ratio(current.getRoad(), previousYear == null ? null : previousYear.getRoad()), |
| | | "â", |
| | | ratio(current.getRoad(), twoYearsAgo == null ? null : twoYearsAgo.getRoad()), |
| | | number(currentCumulative.getRoad()), |
| | | ratio(currentCumulative.getRoad(), previousYearCumulative == null ? null : previousYearCumulative.getRoad()), |
| | | "â" |
| | | ratio(currentCumulative.getRoad(), twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getRoad()) |
| | | }); |
| | | writeTableRow(table, 4, new String[]{ |
| | | number(current.getRoadPersonFlow()), |
| | | ratio(current.getRoadPersonFlow(), previousDay == null ? null : previousDay.getRoadPersonFlow()), |
| | | ratio(current.getRoadPersonFlow(), previousYear == null ? null : previousYear.getRoadPersonFlow()), |
| | | "â", |
| | | ratio(current.getRoadPersonFlow(), twoYearsAgo == null ? null : twoYearsAgo.getRoadPersonFlow()), |
| | | number(currentCumulative.getRoadPersonFlow()), |
| | | ratio(currentCumulative.getRoadPersonFlow(), previousYearCumulative == null ? null : previousYearCumulative.getRoadPersonFlow()), |
| | | "â" |
| | | ratio(currentCumulative.getRoadPersonFlow(), |
| | | previousYearCumulative == null ? null : previousYearCumulative.getRoadPersonFlow()), |
| | | ratio(currentCumulative.getRoadPersonFlow(), |
| | | twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getRoadPersonFlow()) |
| | | }); |
| | | writeTableRow(table, 5, transportRow(current.getRail(), previousDay == null ? null : previousDay.getRail(), |
| | | previousYear == null ? null : previousYear.getRail(), currentCumulative.getRail(), |
| | | previousYearCumulative == null ? null : previousYearCumulative.getRail())); |
| | | writeTableRow(table, 6, transportRow(current.getCivilAviation(), previousDay == null ? null : previousDay.getCivilAviation(), |
| | | previousYear == null ? null : previousYear.getCivilAviation(), currentCumulative.getCivilAviation(), |
| | | previousYearCumulative == null ? null : previousYearCumulative.getCivilAviation())); |
| | | writeTableRow(table, 7, transportRow(current.getWater(), previousDay == null ? null : previousDay.getWater(), |
| | | previousYear == null ? null : previousYear.getWater(), currentCumulative.getWater(), |
| | | previousYearCumulative == null ? null : previousYearCumulative.getWater())); |
| | | writeTableRow(table, 8, transportRow(current.getTotalPersonFlow(), previousDay == null ? null : previousDay.getTotalPersonFlow(), |
| | | previousYear == null ? null : previousYear.getTotalPersonFlow(), currentCumulative.getTotalPersonFlow(), |
| | | previousYearCumulative == null ? null : previousYearCumulative.getTotalPersonFlow())); |
| | | writeTableRow(table, 5, transportRow(current.getRail(), |
| | | firstDayRailBase == null ? null : firstDayRailBase.getRail(), |
| | | previousYear == null ? null : previousYear.getRail(), |
| | | twoYearsAgo == null ? null : twoYearsAgo.getRail(), |
| | | currentCumulative.getRail(), |
| | | previousYearCumulative == null ? null : previousYearCumulative.getRail(), |
| | | twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getRail())); |
| | | writeTableRow(table, 6, transportRow(current.getCivilAviation(), |
| | | firstDayCivilAviationBase == null ? null : firstDayCivilAviationBase.getCivilAviation(), |
| | | previousYear == null ? null : previousYear.getCivilAviation(), |
| | | twoYearsAgo == null ? null : twoYearsAgo.getCivilAviation(), |
| | | currentCumulative.getCivilAviation(), |
| | | previousYearCumulative == null ? null : previousYearCumulative.getCivilAviation(), |
| | | twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getCivilAviation())); |
| | | writeTableRow(table, 7, transportRow(current.getWater(), |
| | | previousDay == null ? null : previousDay.getWater(), |
| | | previousYear == null ? null : previousYear.getWater(), |
| | | twoYearsAgo == null ? null : twoYearsAgo.getWater(), |
| | | currentCumulative.getWater(), |
| | | previousYearCumulative == null ? null : previousYearCumulative.getWater(), |
| | | twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getWater())); |
| | | writeTableRow(table, 8, transportRow(current.getTotalPersonFlow(), |
| | | previousDay == null ? null : previousDay.getTotalPersonFlow(), |
| | | previousYear == null ? null : previousYear.getTotalPersonFlow(), |
| | | twoYearsAgo == null ? null : twoYearsAgo.getTotalPersonFlow(), |
| | | currentCumulative.getTotalPersonFlow(), |
| | | previousYearCumulative == null ? null : previousYearCumulative.getTotalPersonFlow(), |
| | | twoYearsAgoCumulative == null ? null : twoYearsAgoCumulative.getTotalPersonFlow())); |
| | | } |
| | | |
| | | private String[] transportRow(BigDecimal current, BigDecimal previousDay, BigDecimal previousYear, |
| | | BigDecimal currentCumulative, BigDecimal previousYearCumulative) { |
| | | BigDecimal twoYearsAgo, BigDecimal currentCumulative, |
| | | BigDecimal previousYearCumulative, BigDecimal twoYearsAgoCumulative) { |
| | | return new String[]{ |
| | | number(current), |
| | | ratio(current, previousDay), |
| | | ratio(current, previousYear), |
| | | "â", |
| | | ratio(current, twoYearsAgo), |
| | | number(currentCumulative), |
| | | ratio(currentCumulative, previousYearCumulative), |
| | | "â" |
| | | ratio(currentCumulative, twoYearsAgoCumulative) |
| | | }; |
| | | } |
| | | |
| | |
| | | int logicalColumn = 0; |
| | | for (Element cell : cells) { |
| | | int span = gridSpan(cell); |
| | | if (logicalColumn >= 2 && logicalColumn < 9) { |
| | | if (logicalColumn >= 2) { |
| | | int valueIndex = logicalColumn - 2; |
| | | if (valueIndex < values.length) { |
| | | setCellText(cell, values[valueIndex]); |
| | |
| | | return holidayCalendarMapper.selectList(new LambdaQueryWrapper<HolidayCalendar>() |
| | | .eq(HolidayCalendar::getYear, year) |
| | | .eq(HolidayCalendar::getHolidayType, holidayType) |
| | | .ge(HolidayCalendar::getDayIndex, 1) |
| | | .orderByAsc(HolidayCalendar::getDayIndex)); |
| | | } |
| | | |
| | | public HolidayCalendar findPreviousDay(int year, String holidayType) { |
| | | return holidayCalendarMapper.selectOne(new LambdaQueryWrapper<HolidayCalendar>() |
| | | .eq(HolidayCalendar::getYear, year) |
| | | .eq(HolidayCalendar::getHolidayType, holidayType) |
| | | .eq(HolidayCalendar::getDayIndex, 0) |
| | | .last("limit 1")); |
| | | } |
| | | |
| | | public HolidayCalendar findHolidayByDate(LocalDate date) { |
| | |
| | | if (holidayDate == null || holidayDate.getYear() != year) { |
| | | throw new IllegalArgumentException("æ¥æå¿
é¡»å±äºæé年份"); |
| | | } |
| | | if (dayIndex == null || dayIndex < 1) { |
| | | throw new IllegalArgumentException("ç¬¬å æ¥å¿
é¡»ä» 1 å¼å§"); |
| | | if (dayIndex == null || dayIndex < 0) { |
| | | throw new IllegalArgumentException("æ¥æåºå·ä¸è½å°äº 0"); |
| | | } |
| | | LambdaQueryWrapper<HolidayCalendar> duplicate = new LambdaQueryWrapper<HolidayCalendar>() |
| | | .eq(HolidayCalendar::getYear, year) |
| | |
| | | } |
| | | } |
| | | if (target == null) return null; |
| | | return dailyMetrics(year, holidayType, dayIndex, target.getHolidayDate()); |
| | | } |
| | | |
| | | public DailyMetrics dailyMetricsByDate(LocalDate date) { |
| | | if (date == null) return null; |
| | | return dailyMetrics(date.getYear(), null, 0, date); |
| | | } |
| | | |
| | | private DailyMetrics dailyMetrics(int year, String holidayType, int dayIndex, LocalDate date) { |
| | | Map<LocalDate, Map<String, BigDecimal>> passengerByDate = loadPassengerByDate(year); |
| | | Map<LocalDate, Map<String, BigDecimal>> vehicleByDate = loadVehicleByDate(year); |
| | | Map<LocalDate, BigDecimal> comparisonBaseByDate = loadComparisonBaseByDate(year); |
| | | Map<String, BigDecimal> passenger = passengerByDate.getOrDefault(target.getHolidayDate(), Collections.emptyMap()); |
| | | Map<String, BigDecimal> vehicle = vehicleByDate.getOrDefault(target.getHolidayDate(), Collections.emptyMap()); |
| | | Map<String, BigDecimal> passenger = passengerByDate.getOrDefault(date, Collections.emptyMap()); |
| | | Map<String, BigDecimal> vehicle = vehicleByDate.getOrDefault(date, Collections.emptyMap()); |
| | | BigDecimal coefficient = configService.coefficientOf(year, HolidayConstants.CATEGORY_HOLIDAY); |
| | | BigDecimal expressway = vehicle.get(HolidayConstants.ROAD_EXPRESSWAY); |
| | | BigDecimal nationalProvincial = vehicle.get(HolidayConstants.ROAD_NATIONAL_PROVINCIAL); |
| | | BigDecimal comparisonBase = comparisonBaseByDate.get(target.getHolidayDate()); |
| | | BigDecimal comparisonBase = comparisonBaseByDate.get(date); |
| | | BigDecimal nonBusiness = null; |
| | | if (coefficient != null && (expressway != null || nationalProvincial != null)) { |
| | | nonBusiness = HolidayNumberUtils.multiply( |
| | |
| | | metrics.setYear(year); |
| | | metrics.setHolidayType(holidayType); |
| | | metrics.setDayIndex(dayIndex); |
| | | metrics.setDate(target.getHolidayDate()); |
| | | metrics.setDate(date); |
| | | metrics.setRail(passenger.get(HolidayConstants.MODE_RAIL)); |
| | | metrics.setCivilAviation(passenger.get(HolidayConstants.MODE_CIVIL_AVIATION)); |
| | | metrics.setRoad(passenger.get(HolidayConstants.MODE_ROAD)); |
| | |
| | | |
| | | private static final Map<String, String> PASSENGER_SHEET_MODE = new LinkedHashMap<>(); |
| | | private static final int FIRST_DATA_ROW_INDEX = 2; |
| | | private static final int DAY_ROW_SCAN_LIMIT = 30; |
| | | private static final int[] REFERENCE_YEARS = {2024, 2025, 2026}; |
| | | |
| | | static { |
| | |
| | | ImportSummary summary = new ImportSummary(); |
| | | try (InputStream in = file.getInputStream(); Workbook workbook = WorkbookFactory.create(in)) { |
| | | FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); |
| | | importPassengerSheets(workbook, evaluator, year, dateByDay, summary); |
| | | importPassengerSheets(workbook, evaluator, year, holidayType, dateByDay, summary); |
| | | importVehicleSheets(workbook, evaluator, year, dateByDay, summary); |
| | | } catch (Exception e) { |
| | | throw new IllegalArgumentException("å·¥ä½ç°¿è§£æå¤±è´¥: " + e.getMessage(), e); |
| | |
| | | if (sheet == null) continue; |
| | | Integer yearColumn = findYearColumn(sheet, evaluator, year, 2, 7); |
| | | if (yearColumn == null) continue; |
| | | Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + dayIndex - 1); |
| | | Row row = findDayRow(sheet, evaluator, dayIndex); |
| | | if (row == null) continue; |
| | | BigDecimal value = numberValue(row.getCell(yearColumn), evaluator); |
| | | if (value != null) return true; |
| | |
| | | if (expressway != null) { |
| | | Integer yearColumn = findYearColumn(expressway, evaluator, year, 2, 7); |
| | | if (yearColumn != null) { |
| | | Row row = expressway.getRow(FIRST_DATA_ROW_INDEX + dayIndex - 1); |
| | | Row row = findDayRow(expressway, evaluator, dayIndex); |
| | | if (row != null && numberValue(row.getCell(yearColumn), evaluator) != null) return true; |
| | | } |
| | | } |
| | |
| | | if (nationalProvincial != null) { |
| | | Integer yearColumn = findYearColumn(nationalProvincial, evaluator, year, 3, 8); |
| | | if (yearColumn != null) { |
| | | Row row = nationalProvincial.getRow(FIRST_DATA_ROW_INDEX + dayIndex - 1); |
| | | Row row = findDayRow(nationalProvincial, evaluator, dayIndex); |
| | | if (row != null && numberValue(row.getCell(yearColumn), evaluator) != null) return true; |
| | | } |
| | | } |
| | |
| | | if (yearColumn == null) continue; |
| | | for (Map.Entry<Integer, LocalDate> day : dateByDay.entrySet()) { |
| | | if (day.getKey() >= latestDay) continue; |
| | | Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1); |
| | | Row row = findDayRow(sheet, evaluator, day.getKey()); |
| | | if (row == null) continue; |
| | | BigDecimal incoming = numberValue(row.getCell(yearColumn), evaluator); |
| | | if (incoming == null) continue; |
| | |
| | | if (yearColumn == null) return; |
| | | for (Map.Entry<Integer, LocalDate> day : dateByDay.entrySet()) { |
| | | if (day.getKey() >= latestDay) continue; |
| | | Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1); |
| | | Row row = findDayRow(sheet, evaluator, day.getKey()); |
| | | if (row == null) continue; |
| | | BigDecimal incoming = numberValue(row.getCell(yearColumn), evaluator); |
| | | if (incoming == null) continue; |
| | |
| | | if (day.getKey() >= latestDay) continue; |
| | | Map<String, BigDecimal> existingDay = existingVehicle.get(day.getValue()); |
| | | if (existingDay == null || existingDay.get(HolidayConstants.ROAD_NATIONAL_PROVINCIAL) == null) continue; |
| | | Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1); |
| | | Row row = findDayRow(sheet, evaluator, day.getKey()); |
| | | if (row == null) continue; |
| | | BigDecimal incoming; |
| | | try { |
| | |
| | | } |
| | | } |
| | | |
| | | Map<LocalDate, Map<String, BigDecimal>> result = new LinkedHashMap<>(); |
| | | private Map<LocalDate, Map<String, BigDecimal>> loadPassengerByDate(int year) { |
| | | Map<LocalDate, Map<String, BigDecimal>> result = new LinkedHashMap<>(); |
| | | List<HolidayPassengerFlow> rows = passengerFlowMapper.selectList( |
| | | new LambdaQueryWrapper<HolidayPassengerFlow>().eq(HolidayPassengerFlow::getYear, year)); |
| | | for (HolidayPassengerFlow row : rows) { |
| | |
| | | try (InputStream in = new FileInputStream(reference); Workbook workbook = WorkbookFactory.create(in)) { |
| | | FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); |
| | | seedNationalDayCalendar(); |
| | | ensurePreviousDayCalendarIfMissing(); |
| | | for (int year : REFERENCE_YEARS) { |
| | | if (passengerFlowMapper.selectCount(new LambdaQueryWrapper<HolidayPassengerFlow>() |
| | | .eq(HolidayPassengerFlow::getYear, year)) == 0) { |
| | |
| | | seedCoefficientYear(workbook, evaluator, year); |
| | | } |
| | | } |
| | | seedPreviousDayPassengerIfMissing(workbook, evaluator); |
| | | seedRoad2019IfMissing(workbook, evaluator); |
| | | } catch (Exception e) { |
| | | log.error("Failed to seed holiday reference data", e); |
| | | } |
| | |
| | | } |
| | | |
| | | private void importPassengerSheets(Workbook workbook, FormulaEvaluator evaluator, int year, |
| | | Map<Integer, LocalDate> dateByDay, ImportSummary summary) { |
| | | String holidayType, Map<Integer, LocalDate> dateByDay, |
| | | ImportSummary summary) { |
| | | for (Map.Entry<String, String> entry : PASSENGER_SHEET_MODE.entrySet()) { |
| | | Sheet sheet = workbook.getSheet(entry.getKey()); |
| | | if (sheet == null) { |
| | |
| | | continue; |
| | | } |
| | | for (Map.Entry<Integer, LocalDate> day : dateByDay.entrySet()) { |
| | | Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1); |
| | | Row row = findDayRow(sheet, evaluator, day.getKey()); |
| | | if (row == null) { |
| | | summary.addError(entry.getKey() + " 第" + day.getKey() + "æ¥ï¼å·¥ä½è¡¨ç¼ºå°è¯¥è¡"); |
| | | continue; |
| | |
| | | upsertPassenger(year, day.getValue(), entry.getValue(), value); |
| | | summary.incrementPassenger(); |
| | | } |
| | | } |
| | | importPreviousDayPassengerRows(workbook, evaluator, year, holidayType, dateByDay, summary); |
| | | } |
| | | |
| | | private void importPreviousDayPassengerRows(Workbook workbook, FormulaEvaluator evaluator, int year, |
| | | String holidayType, Map<Integer, LocalDate> dateByDay, |
| | | ImportSummary summary) { |
| | | LocalDate previousDate = previousDayDate(year, holidayType, dateByDay); |
| | | if (previousDate == null) return; |
| | | for (Map.Entry<String, String> entry : PASSENGER_SHEET_MODE.entrySet()) { |
| | | if (!HolidayConstants.MODE_RAIL.equals(entry.getValue()) |
| | | && !HolidayConstants.MODE_CIVIL_AVIATION.equals(entry.getValue())) { |
| | | continue; |
| | | } |
| | | Sheet sheet = workbook.getSheet(entry.getKey()); |
| | | if (sheet == null) continue; |
| | | Integer yearColumn = findYearColumn(sheet, evaluator, year, 2, 7); |
| | | if (yearColumn == null) continue; |
| | | Row row = findPreviousDayRow(sheet, evaluator); |
| | | if (row == null) continue; |
| | | BigDecimal value; |
| | | try { |
| | | value = numberValue(row.getCell(yearColumn), evaluator); |
| | | } catch (Exception e) { |
| | | summary.addError(entry.getKey() + " å䏿¥è¡ï¼" + e.getMessage()); |
| | | continue; |
| | | } |
| | | if (value == null) { |
| | | summary.incrementSkipped(); |
| | | continue; |
| | | } |
| | | upsertPassenger(year, previousDate, entry.getValue(), value); |
| | | summary.incrementPassenger(); |
| | | } |
| | | } |
| | | |
| | |
| | | return; |
| | | } |
| | | for (Map.Entry<Integer, LocalDate> day : dateByDay.entrySet()) { |
| | | Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1); |
| | | Row row = findDayRow(sheet, evaluator, day.getKey()); |
| | | if (row == null) { |
| | | summary.addError(sheetName + " 第" + day.getKey() + "æ¥ï¼å·¥ä½è¡¨ç¼ºå°è¯¥è¡"); |
| | | continue; |
| | |
| | | Map<LocalDate, BigDecimal> values = loadPassengerValues(year, entry.getValue()); |
| | | for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) { |
| | | if (!values.containsKey(day.getValue())) continue; |
| | | setNumeric(sheet, FIRST_DATA_ROW_INDEX + day.getKey() - 1, yearColumn, values.get(day.getValue())); |
| | | Row row = findDayRow(sheet, evaluator, day.getKey()); |
| | | if (row == null) continue; |
| | | setNumeric(sheet, row.getRowNum(), yearColumn, values.get(day.getValue())); |
| | | } |
| | | LocalDate previousDate = previousDayDate(year, holidayType, dayDates); |
| | | Row previousRow = findPreviousDayRow(sheet, evaluator); |
| | | if (previousDate != null && previousRow != null) { |
| | | BigDecimal previousValue = values.get(previousDate); |
| | | if (previousValue != null) { |
| | | setNumeric(sheet, previousRow.getRowNum(), yearColumn, previousValue); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | Map<LocalDate, BigDecimal> values = loadVehicleValues(year, roadCategory); |
| | | for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) { |
| | | if (!values.containsKey(day.getValue())) continue; |
| | | setNumeric(sheet, FIRST_DATA_ROW_INDEX + day.getKey() - 1, yearColumn, values.get(day.getValue())); |
| | | Row row = findDayRow(sheet, evaluator, day.getKey()); |
| | | if (row == null) continue; |
| | | setNumeric(sheet, row.getRowNum(), yearColumn, values.get(day.getValue())); |
| | | } |
| | | if (comparisonBaseColumn != null && HolidayConstants.supportsComparisonBase(year)) { |
| | | Map<LocalDate, BigDecimal> bases = loadComparisonBaseValues(year); |
| | | for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) { |
| | | if (!bases.containsKey(day.getValue())) continue; |
| | | setNumeric(sheet, FIRST_DATA_ROW_INDEX + day.getKey() - 1, comparisonBaseColumn, bases.get(day.getValue())); |
| | | Row row = findDayRow(sheet, evaluator, day.getKey()); |
| | | if (row == null) continue; |
| | | setNumeric(sheet, row.getRowNum(), comparisonBaseColumn, bases.get(day.getValue())); |
| | | } |
| | | for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) { |
| | | if (!bases.containsKey(day.getValue())) continue; |
| | | int rowIndex = FIRST_DATA_ROW_INDEX + day.getKey() - 1; |
| | | Row row = findDayRow(sheet, evaluator, day.getKey()); |
| | | if (row == null) continue; |
| | | int rowIndex = row.getRowNum(); |
| | | int excelRow = rowIndex + 1; |
| | | setFormula(sheet, rowIndex, 4, "=(D" + excelRow + "-C" + excelRow + ")/C" + excelRow); |
| | | } |
| | |
| | | calendar.setUpdatedAt(LocalDateTime.now()); |
| | | configService.saveHoliday(toMap(calendar)); |
| | | } |
| | | HolidayCalendar previousDay = new HolidayCalendar(); |
| | | previousDay.setYear(year); |
| | | previousDay.setHolidayType(HolidayConstants.TYPE_NATIONAL_DAY); |
| | | previousDay.setHolidayDate(LocalDate.of(year, 10, 1).minusDays(1)); |
| | | previousDay.setDayIndex(0); |
| | | previousDay.setCreatedAt(LocalDateTime.now()); |
| | | previousDay.setUpdatedAt(LocalDateTime.now()); |
| | | configService.saveHoliday(toMap(previousDay)); |
| | | } |
| | | } |
| | | |
| | | private void ensurePreviousDayCalendarIfMissing() { |
| | | for (int year : REFERENCE_YEARS) { |
| | | if (configService.findPreviousDay(year, HolidayConstants.TYPE_NATIONAL_DAY) != null) { |
| | | continue; |
| | | } |
| | | LocalDate firstHolidayDate = firstDate(dayDates(year, HolidayConstants.TYPE_NATIONAL_DAY)); |
| | | if (firstHolidayDate == null) { |
| | | continue; |
| | | } |
| | | HolidayCalendar previousDay = new HolidayCalendar(); |
| | | previousDay.setYear(year); |
| | | previousDay.setHolidayType(HolidayConstants.TYPE_NATIONAL_DAY); |
| | | previousDay.setHolidayDate(firstHolidayDate.minusDays(1)); |
| | | previousDay.setDayIndex(0); |
| | | previousDay.setCreatedAt(LocalDateTime.now()); |
| | | previousDay.setUpdatedAt(LocalDateTime.now()); |
| | | configService.saveHoliday(toMap(previousDay)); |
| | | } |
| | | } |
| | | |
| | | private void seedPreviousDayPassengerIfMissing(Workbook workbook, FormulaEvaluator evaluator) { |
| | | for (int year : REFERENCE_YEARS) { |
| | | Map<Integer, LocalDate> dayDates = dayDates(year, HolidayConstants.TYPE_NATIONAL_DAY); |
| | | LocalDate previousDate = previousDayDate(year, HolidayConstants.TYPE_NATIONAL_DAY, dayDates); |
| | | if (previousDate == null) continue; |
| | | for (Map.Entry<String, String> entry : PASSENGER_SHEET_MODE.entrySet()) { |
| | | if (!HolidayConstants.MODE_RAIL.equals(entry.getValue()) |
| | | && !HolidayConstants.MODE_CIVIL_AVIATION.equals(entry.getValue())) { |
| | | continue; |
| | | } |
| | | if (passengerFlowMapper.selectCount(new LambdaQueryWrapper<HolidayPassengerFlow>() |
| | | .eq(HolidayPassengerFlow::getYear, year) |
| | | .eq(HolidayPassengerFlow::getFlowDate, previousDate) |
| | | .eq(HolidayPassengerFlow::getTransportMode, entry.getValue())) > 0) { |
| | | continue; |
| | | } |
| | | Sheet sheet = workbook.getSheet(entry.getKey()); |
| | | if (sheet == null) continue; |
| | | Integer yearColumn = findYearColumn(sheet, evaluator, year, 2, 7); |
| | | if (yearColumn == null) continue; |
| | | Row row = findPreviousDayRow(sheet, evaluator); |
| | | if (row == null) continue; |
| | | BigDecimal value = numberValue(row.getCell(yearColumn), evaluator); |
| | | if (value != null) upsertPassenger(year, previousDate, entry.getValue(), value); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void seedRoad2019IfMissing(Workbook workbook, FormulaEvaluator evaluator) { |
| | | if (passengerFlowMapper.selectCount(new LambdaQueryWrapper<HolidayPassengerFlow>() |
| | | .eq(HolidayPassengerFlow::getYear, 2019) |
| | | .eq(HolidayPassengerFlow::getTransportMode, HolidayConstants.MODE_ROAD)) > 0) { |
| | | return; |
| | | } |
| | | Sheet sheet = workbook.getSheet("éè·¯"); |
| | | if (sheet == null) return; |
| | | Integer yearColumn = findYearColumn(sheet, evaluator, 2019, 2, 20); |
| | | if (yearColumn == null) return; |
| | | for (int rowIndex = 0; rowIndex <= Math.min(sheet.getLastRowNum(), DAY_ROW_SCAN_LIMIT); rowIndex++) { |
| | | Row row = sheet.getRow(rowIndex); |
| | | if (row == null) continue; |
| | | Integer dayIndex = parseDayIndex(row.getCell(0), evaluator); |
| | | if (dayIndex == null) continue; |
| | | BigDecimal value = numberValue(row.getCell(yearColumn), evaluator); |
| | | if (value != null) { |
| | | upsertPassenger(2019, LocalDate.of(2019, 10, dayIndex), HolidayConstants.MODE_ROAD, value); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | Integer yearColumn = findYearColumn(sheet, evaluator, year, 2, 7); |
| | | if (yearColumn == null) continue; |
| | | for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) { |
| | | Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1); |
| | | Row row = findDayRow(sheet, evaluator, day.getKey()); |
| | | if (row == null) continue; |
| | | BigDecimal value = numberValue(row.getCell(yearColumn), evaluator); |
| | | if (value != null) { |
| | | upsertPassenger(year, day.getValue(), entry.getValue(), value); |
| | | } |
| | | } |
| | | if (HolidayConstants.MODE_RAIL.equals(entry.getValue()) |
| | | || HolidayConstants.MODE_CIVIL_AVIATION.equals(entry.getValue())) { |
| | | Row previousRow = findPreviousDayRow(sheet, evaluator); |
| | | LocalDate previousDate = previousDayDate(year, HolidayConstants.TYPE_NATIONAL_DAY, dayDates); |
| | | if (previousRow != null && previousDate != null) { |
| | | BigDecimal value = numberValue(previousRow.getCell(yearColumn), evaluator); |
| | | if (value != null) { |
| | | upsertPassenger(year, previousDate, entry.getValue(), value); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | Integer yearColumn = findYearColumn(sheet, evaluator, year, startColumn, endColumn); |
| | | if (yearColumn == null) return; |
| | | for (Map.Entry<Integer, LocalDate> day : dayDates.entrySet()) { |
| | | Row row = sheet.getRow(FIRST_DATA_ROW_INDEX + day.getKey() - 1); |
| | | Row row = findDayRow(sheet, evaluator, day.getKey()); |
| | | if (row == null) continue; |
| | | BigDecimal value = numberValue(row.getCell(yearColumn), evaluator); |
| | | if (value != null) { |
| | |
| | | } |
| | | } |
| | | |
| | | private LocalDate previousDayDate(int year, String holidayType, Map<Integer, LocalDate> dateByDay) { |
| | | HolidayCalendar configured = configService.findPreviousDay(year, holidayType); |
| | | if (configured != null && configured.getHolidayDate() != null) { |
| | | return configured.getHolidayDate(); |
| | | } |
| | | LocalDate firstDate = firstDate(dateByDay); |
| | | return firstDate == null ? null : firstDate.minusDays(1); |
| | | } |
| | | |
| | | private LocalDate firstDate(Map<Integer, LocalDate> dateByDay) { |
| | | LocalDate result = null; |
| | | for (LocalDate date : dateByDay.values()) { |
| | | if (date != null && (result == null || date.isBefore(result))) result = date; |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private Row findDayRow(Sheet sheet, FormulaEvaluator evaluator, int dayIndex) { |
| | | int lastRow = Math.min(sheet.getLastRowNum(), DAY_ROW_SCAN_LIMIT); |
| | | for (int rowIndex = 0; rowIndex <= lastRow; rowIndex++) { |
| | | Row row = sheet.getRow(rowIndex); |
| | | if (row == null) continue; |
| | | Integer rowDay = parseDayIndex(row.getCell(0), evaluator); |
| | | if (rowDay != null && rowDay.intValue() == dayIndex) return row; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private Row findPreviousDayRow(Sheet sheet, FormulaEvaluator evaluator) { |
| | | int lastRow = Math.min(sheet.getLastRowNum(), DAY_ROW_SCAN_LIMIT); |
| | | DataFormatter formatter = new DataFormatter(); |
| | | for (int rowIndex = 0; rowIndex <= lastRow; rowIndex++) { |
| | | Row row = sheet.getRow(rowIndex); |
| | | if (row == null || row.getCell(0) == null) continue; |
| | | String text = formatter.formatCellValue(row.getCell(0), evaluator).trim(); |
| | | if (text.contains("å䏿¥")) return row; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private Map<Integer, LocalDate> dayDates(int year, String holidayType) { |
| | | Map<Integer, LocalDate> result = new LinkedHashMap<>(); |
| | | for (HolidayCalendar row : configService.findHolidayDays(year, holidayType)) { |
| | |
| | | <template slot-scope="scope">{{ labelOf(holidayTypeOptions, scope.row.holidayType) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="holidayDate" label="æ¥æ" width="150" align="center"></el-table-column> |
| | | <el-table-column prop="dayIndex" label="ç¬¬å æ¥" width="100" align="center"></el-table-column> |
| | | <el-table-column label="æ¥æç±»å" width="120" align="center"> |
| | | <template slot-scope="scope">{{ dayIndexLabel(scope.row.dayIndex) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="150" align="center"> |
| | | <template slot-scope="scope"> |
| | | <el-button type="text" size="mini" @click="openHolidayDialog(scope.row)">ç¼è¾</el-button> |
| | |
| | | <el-date-picker v-model="holidayForm.holidayDate" type="date" value-format="yyyy-MM-dd" |
| | | style="width:100%" placeholder="éæ©æ¥æ"></el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item label="ç¬¬å æ¥" prop="dayIndex"> |
| | | <el-input-number v-model="holidayForm.dayIndex" :min="1" :max="31" style="width:100%"></el-input-number> |
| | | <el-form-item label="æ¥æç±»å" prop="dayIndex"> |
| | | <el-select v-model="holidayForm.dayIndex" style="width:100%"> |
| | | <el-option label="å䏿¥" :value="0"></el-option> |
| | | <el-option v-for="day in 31" :key="day" :label="'第' + day + 'æ¥'" :value="day"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <span slot="footer"> |
| | |
| | | year: [{ required: true, message: 'è¯·éæ©å¹´ä»½', trigger: 'change' }], |
| | | holidayType: [{ required: true, message: 'è¯·éæ©è忥åç±»', trigger: 'change' }], |
| | | holidayDate: [{ required: true, message: 'è¯·éæ©æ¥æ', trigger: 'change' }], |
| | | dayIndex: [{ required: true, message: '请è¾å
¥ç¬¬å æ¥', trigger: 'change' }] |
| | | dayIndex: [{ required: true, message: 'è¯·éæ©æ¥æç±»å', trigger: 'change' }] |
| | | }, |
| | | coefficientRules: { |
| | | year: [{ required: true, message: 'è¯·éæ©å¹´ä»½', trigger: 'change' }], |
| | |
| | | this.loadCoefficients() |
| | | }, |
| | | methods: { |
| | | dayIndexLabel(value) { |
| | | if (Number(value) === 0) return 'å䏿¥' |
| | | return value === null || value === undefined || value === '' ? 'â' : '第' + value + 'æ¥' |
| | | }, |
| | | loadOptions() { |
| | | api.get('/holiday/config/options').then(res => { |
| | | const data = res.data || {} |