| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.project.service.impl; |
| | | |
| | | import com.ruoyi.project.domain.BaseFinancesubject; |
| | | import com.ruoyi.project.domain.BaseItemexpense; |
| | | import com.ruoyi.project.domain.ServiceFund; |
| | | import com.ruoyi.project.domain.ServiceFunddetail; |
| | | import com.ruoyi.project.domain.ServiceFunddetailVO; |
| | | import com.ruoyi.project.domain.vo.ServiceFundVO; |
| | | import com.ruoyi.project.mapper.BaseFinancesubjectMapper; |
| | | import com.ruoyi.project.mapper.BaseItemexpenseMapper; |
| | | import com.ruoyi.project.mapper.ServiceFundMapper; |
| | | import com.ruoyi.project.mapper.ServiceFunddetailMapper; |
| | | import com.ruoyi.project.service.ExpertFeeCheckResult; |
| | | import com.ruoyi.project.service.ExpertFeeCheckResult.DetailRow; |
| | | import com.ruoyi.system.service.ISysConfigService; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * ä¸å®¶å³å¡è´¹æ ¡éªæå¡ï¼9 æ¡è§åï¼ |
| | | * |
| | | * <p>ä¸å¡èæ¯ï¼åä¸æç®æ¡ä¾ï¼service_fund.infoidï¼å¯è½æå¤æ¡ä¸å®¶å³å¡è´¹æ¥éå |
| | | * ï¼service_fund.applytype = '1'ï¼ï¼ææè´¹ç¨æ§å¶é½æ¯é对å䏿¡ä¾è¿è¡ç累计æ§å¶ã |
| | | * |
| | | * <p>æ ¡éªæ¶æºï¼ä¸¤å¤é½è°ç¨ï¼ï¼ |
| | | * <ul> |
| | | * <li>æ¥éåå¡«æ¥ä¿åå ââ ServiceFundServiceImpl#addOrUpdateNew</li> |
| | | * <li>æ¥éåå®¡æ ¸éè¿å ââ ServiceFundServiceImpl#checkFund</li> |
| | | * </ul> |
| | | * |
| | | * <p>è§åæç»ï¼ |
| | | * <ol> |
| | | * <li>æç®å»é¢å¨å®åè½ç»´æ¤è´¹(itemid=5) å计 ⤠60000ï¼ç¨åï¼</li> |
| | | * <li>ä¸çº§æç®å»é¢å»çå¢éå¨å®åè½ç»´æ¤è´¹(itemid=7) å计 ⤠11000ï¼ç¨åï¼</li> |
| | | * <li>ä¸çº§æç®å»é¢æ¤çå¢éå¨å®åè½ç»´æ¤è´¹(itemid=8) å计 ⤠1000ï¼ç¨åï¼</li> |
| | | * <li>ECOMå¢éå³å¡è´¹(itemid=2)ï¼é¢å
⤠5000 / å¸å
⤠10000 / å¸å¤ ⤠12000ï¼æè´¹ç¨è¯´æåæ¡£ï¼</li> |
| | | * <li>åä¸ä¸å®¶ä¸å¾åæ¶é¢åãæç®å»é¢åæ¥è¯ä¼°è´¹ç¨(1)ããæç®ç
æ
è¯ä¼°ä¸å®¶è´¹(18)ã |
| | | * ãèæ»äº¡ä¸´åºå¤å®ä¸å®¶è´¹(19)ãï¼åªè½å°±é«åå
¶ä¸ ââ ä»
æç¤ºï¼ä¸é»æ</li> |
| | | * <li><b>åºæ¬è§åï¼2026-10-08 æ°å¢ï¼</b>ï¼applytype=1 ç<b>å
¨é¨æç»</b>ï¼ |
| | | * å䏿¡ä¾ à åä¸ä¸å®¶ à åä¸é¡¹ç® à åä¸è´¹ç¨è¯´æç<b>å计</b>ä¸å¾è¶
è¿ |
| | | * base_itemexpenseï¼del_flag=0 ä¸ UseState=1ï¼ä¸è¯¥é¡¹ç®çè´¹ç¨æ åï¼ |
| | | * éé¢ä¸º 0ãè´¹ç¨è¯´æä¸ºç©ºãå¹é
ä¸å°æ åï¼ä¸å¾é»æï¼åç»åºç°å¤æ¡åªæç¤ºä¸é»æ</li> |
| | | * <li>å¨å®è·åå æï¼2026-10-08 éåï¼itemid 32~39ï¼ï¼å䏿¡ä¾åä¸ä¸å®¶å¨è¯¥ç±»é¡¹ç®ä¸ |
| | | * è·åå¤ä¸ªå¨å®æ¶ï¼æéé¢éåºä¿é«ï¼ç¬¬äºä¸ªå以åçæ¯æ¡éé¢ä¸é = |
| | | * è´¹ç¨è¯´æå«ãåä¾§ãå 1000ãå
¶ä»å 500ï¼é»æï¼åæ°è§ sys_configï¼</li> |
| | | * <li>å©æè´¹ç¨ï¼æå©æç±»åï¼itemid 41ã71ã72ã73ã74ï¼<b>åå«</b>å计ï¼å¸å
⤠1000 / å¸å¤ ⤠2000</li> |
| | | * <li>ææ¯å®¤äººå·¥è´¹-å·¡å(itemid=43 ä¸è´¹ç¨è¯´æå«âå·¡åâ) ⤠500</li> |
| | | * <li>ææ¯å®¤äººå·¥è´¹-麻é(itemid=43 ä¸è´¹ç¨è¯´æå«â麻éâ) ⤠500</li> |
| | | * </ol> |
| | | * |
| | | * <p>éé¢ä¸å¾ä½¿ç¨ BigDecimal ç´¯å ï¼ç¦æ¢ double è¿ç®ï¼æ¬é¡¹ç®æ¤ååºç°è¿é颿µ®ç¹ç²¾åº¦é®é¢ï¼ã |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class ExpertFeeValidateService { |
| | | |
| | | /** ä¸å®¶å³å¡è´¹ç applytype åå¼ */ |
| | | private static final String APPLYTYPE_EXPERT = "1"; |
| | | |
| | | /** è´¹ç¨è¯´æä¸çåºæ¯å
³é®åï¼å®¢æ·ç¯å¢å®é
ä¸æææ¬ä¸ºãé¢å
é¢åº / å¸å
/ å¸å¤ãï¼åæ¶å
¼å®¹ãæ¬å¸ / å¤å¸ãæ§åæ³ï¼ */ |
| | | private static final String[] SCOPE_IN_HOSPITAL = {"é¢å
"}; |
| | | private static final String[] SCOPE_IN_CITY = {"å¸å
", "æ¬å¸"}; |
| | | private static final String[] SCOPE_OUT_CITY = {"å¸å¤", "å¤å¸"}; |
| | | private static final String SCOPE_TAXED = "ç¨å"; |
| | | private static final String SCOPE_CIRCUIT = "å·¡å"; |
| | | private static final String SCOPE_ANESTHESIA = "麻é"; |
| | | /** è´¹ç¨è¯´æä¸çãåä¾§ãå
³é®åï¼è¾ãèºçè·ååºååä¾§/åä¾§ï¼ */ |
| | | private static final String SCOPE_DOUBLE_SIDE = "åä¾§"; |
| | | |
| | | // ---- è´¹ç¨é¡¹ç®ï¼å
¨é¨æé¡¹ç® id å¤å® ---- |
| | | // è§åä¸åä¾èµé¡¹ç®åç§°ï¼åç§°æ¯å¹´å¸¦å¹´åº¦åç¼ãåé¢ä¹å¯è½ä¸åï¼ï¼ |
| | | // ç»ä¸ç± sys_config ç sys.expertfee.itemid.* é
ç½®é¡¹ç® idï¼éå·åéï¼å¯å¤ä¸ªï¼ã |
| | | |
| | | // ---- å
¼å®¹åå²å¹´åº¦çé¡¹ç® idï¼é¡¹ç®è¡¨æ¹é å使ç¨ï¼ ---- |
| | | // ä¸é
置项æ±å¹¶éï¼ä¿è¯åå²åæ®ä»è½è¢«è¯å«ï¼é
置项æ¯ä¸»è¦ä¾æ®ã |
| | | private static final Set<Long> FALLBACK_ORGAN_MAINTAIN = setOf(5L); |
| | | private static final Set<Long> FALLBACK_SUPERIOR_MEDICAL = setOf(7L); |
| | | private static final Set<Long> FALLBACK_SUPERIOR_NURSING = setOf(8L); |
| | | private static final Set<Long> FALLBACK_ECOM = setOf(2L); |
| | | private static final Set<Long> FALLBACK_OPERATION_ROOM = setOf(43L); |
| | | private static final Set<Long> FALLBACK_ASSESS = setOf(1L, 18L, 19L); |
| | | private static final Set<Long> FALLBACK_DOCTOR_ORGAN = setOf(32L, 33L, 34L, 35L, 36L, 37L, 38L, 39L); |
| | | private static final Set<Long> FALLBACK_ASSISTANT = setOf(41L, 71L, 72L, 73L, 74L); |
| | | |
| | | // ---- sys_config é
ç½®é® ---- |
| | | private static final String CFG_ORGAN_MAINTAIN = "sys.expertfee.organMaintain.limit"; |
| | | private static final String CFG_SUPERIOR_MEDICAL = "sys.expertfee.superiorMedical.limit"; |
| | | private static final String CFG_SUPERIOR_NURSING = "sys.expertfee.superiorNursing.limit"; |
| | | private static final String CFG_ECOM_IN_HOSPITAL = "sys.expertfee.ecom.limit.inhospital"; |
| | | private static final String CFG_ECOM_IN_CITY = "sys.expertfee.ecom.limit.incity"; |
| | | private static final String CFG_ECOM_OUT_CITY = "sys.expertfee.ecom.limit.outcity"; |
| | | private static final String CFG_ASSISTANT_IN_CITY = "sys.expertfee.assistant.limit.incity"; |
| | | private static final String CFG_ASSISTANT_OUT_CITY = "sys.expertfee.assistant.limit.outcity"; |
| | | /** ææ¯å®¤äººå·¥è´¹-å·¡åï¼å计éé¢ï¼itemid=43 ä¸è´¹ç¨è¯´æå«ãå·¡åãï¼ */ |
| | | private static final String CFG_OPERATION_ROOM_CIRCUIT = "sys.expertfee.operationroom.circuit.limit"; |
| | | /** ææ¯å®¤äººå·¥è´¹-麻éï¼å计éé¢ï¼itemid=43 ä¸è´¹ç¨è¯´æå«ã麻éãï¼ */ |
| | | private static final String CFG_OPERATION_ROOM_ANESTHESIA = "sys.expertfee.operationroom.anesthesia.limit"; |
| | | /** |
| | | * 第äºä¸ªå以åçå¨å®è·åï¼åä¾§ä¸éï¼itemid 32~39ï¼è´¹ç¨è¯´æå«ãåä¾§ãï¼ã |
| | | * 该é弿 æ³ç¨ base_itemexpense è¡¨è¾¾ï¼æ å表éè®°çæ¯é¦ä¸ªå¨å®çè¶³é¢æ åï¼ï¼æä»¥æ¾ sys_configã |
| | | */ |
| | | private static final String CFG_ORGAN_SECOND_DOUBLE = "sys.expertfee.organ.second.double.limit"; |
| | | /** 第äºä¸ªå以åçå¨å®è·åï¼å
¶ä»ï¼åä¾§ãä»¥åæ ååä¾§æ¦å¿µç项ç®ï¼ä¸é */ |
| | | private static final String CFG_ORGAN_SECOND_OTHER = "sys.expertfee.organ.second.other.limit"; |
| | | |
| | | // ---- è´¹ç¨é¡¹ç® id é
ç½®ï¼éå·åéï¼å¯å¤ä¸ªï¼ãå
¨é¨è§åç»ä¸æé¡¹ç® id å¤å®ï¼ |
| | | // æªé
ç½®æ¶ä»
é 䏿¹åå² id å
¼å®¹ï¼å¯¹åºè§åä¼è®° warn æ¥å¿ï¼ä¾¿äºåç°æ¼é
ï¼ ---- |
| | | private static final String CFG_ITEM_ORGAN_MAINTAIN = "sys.expertfee.itemid.organMaintain"; |
| | | private static final String CFG_ITEM_SUPERIOR_MEDICAL = "sys.expertfee.itemid.superiorMedical"; |
| | | private static final String CFG_ITEM_SUPERIOR_NURSING = "sys.expertfee.itemid.superiorNursing"; |
| | | private static final String CFG_ITEM_ECOM = "sys.expertfee.itemid.ecom"; |
| | | private static final String CFG_ITEM_OPERATION_ROOM = "sys.expertfee.itemid.operationRoom"; |
| | | private static final String CFG_ITEM_ASSESS = "sys.expertfee.itemid.assess"; |
| | | private static final String CFG_ITEM_ASSISTANT = "sys.expertfee.itemid.assistant"; |
| | | private static final String CFG_ITEM_DOCTOR = "sys.expertfee.itemid.doctor"; |
| | | |
| | | // ---- 9 æ¡è§åæ ¡éªæ»å¼å
³ ---- |
| | | /** |
| | | * ä¸å®¶å³å¡è´¹ 9 æ¡è§åæ ¡éªæ»å¼å
³ï¼åå¼ï¼1/true/y/on = å¼å¯ï¼0/false/n/off = å
³éã |
| | | * |
| | | * <p>è§åä»å¨ä¸ä¸å¡æ¹ç¡®è®¤ä¸ï¼2026-09-28 èµ·<b>ææ¶å
³é</b>ï¼ |
| | | * è§å确认åæè¯¥é
ç½®æ¹ä¸º true å³å¯éæ°å¼å¯ï¼æ 鿹代ç ãæ éåçï¼ã |
| | | */ |
| | | private static final String CFG_VALIDATE_ENABLED = "sys.expertfee.validate.enabled"; |
| | | /** |
| | | * æ»å¼å
³ç¼ºçå¼ï¼<b>å
³é</b>ã |
| | | * |
| | | * <p>缺çåå
³éï¼å¯ä¿è¯å³ä½¿ sys_config æ¼é
ä¹ä¸ä¼è¯¯æ¦ä¸å¡ï¼ |
| | | * è§å确认åéæ¾å¼é
置为 true æå¼å¯æ ¡éªã |
| | | */ |
| | | private static final boolean DEFAULT_VALIDATE_ENABLED = false; |
| | | |
| | | // ---- é
置缺çå¼ï¼sys_config æªé
ç½®æ¶å
åºï¼é¿å
æ ¡éªå¤±æï¼ ---- |
| | | private static final BigDecimal DEFAULT_ORGAN_MAINTAIN = new BigDecimal("60000"); |
| | | private static final BigDecimal DEFAULT_SUPERIOR_MEDICAL = new BigDecimal("11000"); |
| | | private static final BigDecimal DEFAULT_SUPERIOR_NURSING = new BigDecimal("1000"); |
| | | private static final BigDecimal DEFAULT_ECOM_IN_HOSPITAL = new BigDecimal("5000"); |
| | | private static final BigDecimal DEFAULT_ECOM_IN_CITY = new BigDecimal("10000"); |
| | | private static final BigDecimal DEFAULT_ECOM_OUT_CITY = new BigDecimal("12000"); |
| | | private static final BigDecimal DEFAULT_ASSISTANT_IN_CITY = new BigDecimal("1000"); |
| | | private static final BigDecimal DEFAULT_ASSISTANT_OUT_CITY = new BigDecimal("2000"); |
| | | /** ææ¯å®¤äººå·¥è´¹-å·¡åå计éé¢ */ |
| | | private static final BigDecimal DEFAULT_OPERATION_ROOM_CIRCUIT = new BigDecimal("500"); |
| | | /** ææ¯å®¤äººå·¥è´¹-麻éå计éé¢ */ |
| | | private static final BigDecimal DEFAULT_OPERATION_ROOM_ANESTHESIA = new BigDecimal("500"); |
| | | /** 第äºä¸ªå以åçå¨å®è·åï¼åä¾§ä¸é缺çå¼ */ |
| | | private static final BigDecimal DEFAULT_ORGAN_SECOND_DOUBLE = new BigDecimal("1000"); |
| | | /** 第äºä¸ªå以åçå¨å®è·åï¼å
¶ä»ä¸é缺çå¼ */ |
| | | private static final BigDecimal DEFAULT_ORGAN_SECOND_OTHER = new BigDecimal("500"); |
| | | |
| | | @Autowired |
| | | private ServiceFundMapper serviceFundMapper; |
| | | |
| | | @Autowired |
| | | private ServiceFunddetailMapper serviceFunddetailMapper; |
| | | |
| | | /** è´¹ç¨é¡¹ç®ä¸»è¡¨ï¼base_financesubjectï¼ï¼ç¨äºæé¡¹ç®åç§°å¨æè§£æé¡¹ç® id */ |
| | | @Autowired |
| | | private BaseFinancesubjectMapper baseFinancesubjectMapper; |
| | | |
| | | /** è´¹ç¨æ å表ï¼base_itemexpenseï¼ï¼åºæ¬è§åæãé¡¹ç® id + è´¹ç¨è¯´æãåæ åéé¢ */ |
| | | @Autowired |
| | | private BaseItemexpenseMapper baseItemexpenseMapper; |
| | | |
| | | @Autowired |
| | | private ISysConfigService sysConfigService; |
| | | |
| | | /** é¡¹ç® id -> 项ç®åç§°ï¼å¸¦ç¼åï¼ï¼ä»
ç¨äºæ¼æ¥å¯è¯»çé误æç¤ºææ¡ */ |
| | | private volatile Map<Long, String> expertFeeItemNameCache; |
| | | |
| | | /** |
| | | * è´¹ç¨æ åç¼åï¼itemid -> (è´¹ç¨è¯´æ -> æ åéé¢)ã |
| | | * |
| | | * <p>带 60 ç§è¿ææ¶é´ï¼æ å表æ¯å¯ç»´æ¤çåºç¡æ°æ®ï¼æ¹äºä¸å¿
éå¯å³å¯çæï¼ |
| | | * åæ¶é¿å
䏿¬¡æ ¡éªéæé¡¹ç®å夿¥åºã |
| | | */ |
| | | private final Map<Long, ItemExpenseEntry> itemExpenseCache = new ConcurrentHashMap<>(); |
| | | |
| | | /** è´¹ç¨æ åç¼åæææï¼æ¯«ç§ï¼ */ |
| | | private static final long ITEM_EXPENSE_CACHE_MILLIS = 60 * 1000L; |
| | | |
| | | /** è´¹ç¨æ åç¼å项 */ |
| | | private static final class ItemExpenseEntry { |
| | | private final Map<String, BigDecimal> standards; |
| | | private final long expireAt; |
| | | |
| | | private ItemExpenseEntry(Map<String, BigDecimal> standards, long expireAt) { |
| | | this.standards = standards; |
| | | this.expireAt = expireAt; |
| | | } |
| | | } |
| | | |
| | | // ================================================================== |
| | | // 对å¤å
¥å£ |
| | | // ================================================================== |
| | | |
| | | /** |
| | | * æ¥éåå¡«æ¥ä¿ååçæ ¡éªã |
| | | * |
| | | * <p>æ¤æ¶æ¬æ¬¡æäº¤çæç»å°æªè½åºï¼éè¦æãæ¬æ¬¡æäº¤çæç»ãä¸ãæ¡ä¾ä¸å·²è½åºçå
¶ä»åæ®æç»ã |
| | | * åå¹¶åå¤å®ï¼æ¬æ¬¡æäº¤çæç»ä¼æ id è¦çåºä¸å䏿¡è®°å½ï¼é¿å
éå¤è®¡ç®ã |
| | | * |
| | | * @param serviceFundVO æ¬æ¬¡æäº¤ç表å |
| | | * @return æ ¡éªç»æï¼errors 黿ä¿åï¼warnings ä»
æç¤ºï¼ |
| | | */ |
| | | public ExpertFeeCheckResult validateBeforeSave(ServiceFundVO serviceFundVO) { |
| | | if (!isValidateEnabled()) { |
| | | log.info("[ä¸å®¶è´¹æ ¡éª-ä¿å] 9 æ¡è§åæ ¡éªæ»å¼å
³å·²å
³éï¼{}ï¼ï¼è·³è¿æ ¡éª", |
| | | CFG_VALIDATE_ENABLED); |
| | | return new ExpertFeeCheckResult(); |
| | | } |
| | | if (serviceFundVO == null) { |
| | | log.info("[ä¸å®¶è´¹æ ¡éª-ä¿å] å
¥å为空ï¼è·³è¿"); |
| | | return new ExpertFeeCheckResult(); |
| | | } |
| | | log.info("[ä¸å®¶è´¹æ ¡éª-ä¿å] å¼å§ applytype={} infoid={} fundId={} æ¬æ¬¡æç»æ°={}", |
| | | serviceFundVO.getApplytype(), serviceFundVO.getInfoid(), serviceFundVO.getId(), |
| | | CollectionUtils.isEmpty(serviceFundVO.getServiceFunddetails()) |
| | | ? 0 : serviceFundVO.getServiceFunddetails().size()); |
| | | if (!isExpertFee(serviceFundVO.getApplytype())) { |
| | | log.info("[ä¸å®¶è´¹æ ¡éª-ä¿å] applytype={} 䏿¯ä¸å®¶å³å¡è´¹(1)ï¼è·³è¿", serviceFundVO.getApplytype()); |
| | | return new ExpertFeeCheckResult(); |
| | | } |
| | | Long infoid = serviceFundVO.getInfoid(); |
| | | if (infoid == null) { |
| | | log.warn("[ä¸å®¶è´¹æ ¡éª-ä¿å] infoid ä¸ºç©ºï¼æ æ³åæ¡ä¾å
累计ï¼è·³è¿ï¼fundId={}ï¼", serviceFundVO.getId()); |
| | | return new ExpertFeeCheckResult(); |
| | | } |
| | | |
| | | List<Long> fundIds = listExpertFundIds(infoid); |
| | | List<DetailRow> storedRows = loadDetailRows(fundIds); |
| | | log.info("[ä¸å®¶è´¹æ ¡éª-ä¿å] infoid={} å½ä¸ä¸å®¶è´¹åæ® {} å¼ ï¼åºä¸æç» {} æ¡", |
| | | infoid, fundIds.size(), storedRows.size()); |
| | | |
| | | // ç¨æ¬æ¬¡æäº¤çæç»è¦çåºä¸å id çè®°å½ï¼æ°å¢ç追å è¿æ¥ï¼ |
| | | Map<String, DetailRow> merged = new LinkedHashMap<>(); |
| | | for (DetailRow row : storedRows) { |
| | | merged.put(rowKey(row), row); |
| | | } |
| | | List<ServiceFunddetailVO> submitDetails = serviceFundVO.getServiceFunddetails(); |
| | | if (!CollectionUtils.isEmpty(submitDetails)) { |
| | | for (ServiceFunddetailVO vo : submitDetails) { |
| | | if (vo == null) { |
| | | continue; |
| | | } |
| | | if (vo.getInfoid() == null) { |
| | | vo.setInfoid(infoid); |
| | | } |
| | | merged.put(rowKey(vo), DetailRow.of(vo)); |
| | | } |
| | | } |
| | | |
| | | return doValidate(new ArrayList<>(merged.values())); |
| | | } |
| | | |
| | | /** |
| | | * æ¥éåå®¡æ ¸éè¿åçæ ¡éªãæ°æ®ä»¥æ°æ®åºä¸ºåã |
| | | * |
| | | * @param fundid å¾
å®¡æ ¸çæ¥éå id |
| | | * @return æ ¡éªç»æ |
| | | */ |
| | | public ExpertFeeCheckResult validateBeforeCheck(Long fundid) { |
| | | if (!isValidateEnabled()) { |
| | | log.info("[ä¸å®¶è´¹æ ¡éª-å®¡æ ¸] 9 æ¡è§åæ ¡éªæ»å¼å
³å·²å
³éï¼{}ï¼ï¼è·³è¿æ ¡éª", |
| | | CFG_VALIDATE_ENABLED); |
| | | return new ExpertFeeCheckResult(); |
| | | } |
| | | ServiceFund fund = serviceFundMapper.selectById(fundid); |
| | | if (fund == null || !isExpertFee(applytypeOf(fund.getApplytype()))) { |
| | | return new ExpertFeeCheckResult(); |
| | | } |
| | | if (fund.getInfoid() == null) { |
| | | log.warn("ä¸å®¶å³å¡è´¹å®¡æ ¸æ ¡éªè·³è¿ï¼infoid 为空ï¼fundId={}", fundid); |
| | | return new ExpertFeeCheckResult(); |
| | | } |
| | | return doValidate(loadDetailRows(listExpertFundIds(fund.getInfoid()))); |
| | | } |
| | | |
| | | // ================================================================== |
| | | // æ ¡éªä¸»ä½ |
| | | // ================================================================== |
| | | |
| | | private ExpertFeeCheckResult doValidate(List<DetailRow> rows) { |
| | | ExpertFeeCheckResult result = new ExpertFeeCheckResult(); |
| | | if (CollectionUtils.isEmpty(rows)) { |
| | | log.info("[ä¸å®¶è´¹æ ¡éª] å䏿 ¡éªçæç»ä¸ºç©ºï¼ç´æ¥éè¿"); |
| | | return result; |
| | | } |
| | | log.info("[ä¸å®¶è´¹æ ¡éª] å䏿 ¡éªæç» {} æ¡ï¼{}", rows.size(), describeRows(rows)); |
| | | |
| | | // è§å 1~4ã7~9ï¼å计éé¢ç±»ï¼é»æï¼ï¼å
¨é¨æé¡¹ç® id å¤å® |
| | | checkSingleItemLimit(rows, resolveItemIds(CFG_ITEM_ORGAN_MAINTAIN, "æç®å»é¢å¨å®åè½ç»´æ¤è´¹", FALLBACK_ORGAN_MAINTAIN), |
| | | false, |
| | | cfg(CFG_ORGAN_MAINTAIN, DEFAULT_ORGAN_MAINTAIN), result, "æç®å»é¢å¨å®åè½ç»´æ¤è´¹ç¨"); |
| | | checkSingleItemLimit(rows, resolveItemIds(CFG_ITEM_SUPERIOR_MEDICAL, "ä¸çº§å»çå¢éå¨å®åè½ç»´æ¤è´¹", FALLBACK_SUPERIOR_MEDICAL), |
| | | true, |
| | | cfg(CFG_SUPERIOR_MEDICAL, DEFAULT_SUPERIOR_MEDICAL), result, "ä¸çº§æç®å»é¢å»çå¢éå¨å®åè½ç»´æ¤è´¹ç¨"); |
| | | checkSingleItemLimit(rows, resolveItemIds(CFG_ITEM_SUPERIOR_NURSING, "ä¸çº§æ¤çå¢éå¨å®åè½ç»´æ¤è´¹", FALLBACK_SUPERIOR_NURSING), |
| | | true, |
| | | cfg(CFG_SUPERIOR_NURSING, DEFAULT_SUPERIOR_NURSING), result, "ä¸çº§æç®å»é¢æ¤çå¢éå¨å®åè½ç»´æ¤è´¹ç¨"); |
| | | checkEcom(rows, result); |
| | | checkAssistant(rows, result); |
| | | checkOperationRoom(rows, result); |
| | | |
| | | // è§å 5ï¼ä»
æç¤ºï¼åºæ¬è§åä¸å¨å®è·åå æï¼ç¡¬æ¦æª |
| | | checkAssessConflict(rows, result); |
| | | checkItemStandard(rows, result); |
| | | checkDoctorOrganSecond(rows, result); |
| | | |
| | | log.info("[ä¸å®¶è´¹æ ¡éª] æ ¡éªå®æ 黿={} æç¤º={} é»æä¿¡æ¯={} æç¤ºä¿¡æ¯={}", |
| | | result.getErrors().size(), result.getWarnings().size(), |
| | | result.getErrors(), result.getWarnings()); |
| | | return result; |
| | | } |
| | | |
| | | /** æå䏿 ¡éªçæç»æ¼æä¸è¡ï¼ä¾¿äºææ¥ã为ä»ä¹æ²¡æ¦ä½ãï¼éé¢ã项ç®ãåºæ¯ä¸ç®äºç¶ï¼ */ |
| | | private String describeRows(List<DetailRow> rows) { |
| | | StringBuilder sb = new StringBuilder(); |
| | | for (DetailRow row : rows) { |
| | | if (sb.length() > 0) { |
| | | sb.append(" | "); |
| | | } |
| | | sb.append("[fundid=").append(row.getFundid()) |
| | | .append(" itemid=").append(row.getItemid()) |
| | | .append(" éé¢=").append(row.getAmount()) |
| | | .append(" ç¨å=").append(row.getTaxedamount()) |
| | | .append(" åºæ¯=").append(row.getServicesscopename()) |
| | | .append("]"); |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * è§å 1ã2ã3ï¼åä¸é¡¹ç®å计éé¢ã |
| | | * |
| | | * @param taxed é颿¯å¦åç¨åï¼è§å 2ã3 åç¨åï¼å
¶ä½åç¨åï¼ |
| | | */ |
| | | private void checkSingleItemLimit(List<DetailRow> rows, Set<Long> itemids, boolean taxed, |
| | | BigDecimal limit, ExpertFeeCheckResult result, String itemLabel) { |
| | | List<DetailRow> matched = new ArrayList<>(); |
| | | BigDecimal total = BigDecimal.ZERO; |
| | | for (DetailRow row : rows) { |
| | | if (isTargetItem(row, itemids)) { |
| | | matched.add(row); |
| | | total = total.add(taxed ? row.getTaxedamount() : row.getAmount()); |
| | | } |
| | | } |
| | | if (matched.isEmpty()) { |
| | | return; |
| | | } |
| | | log.info("[ä¸å®¶è´¹æ ¡éª] {} å½ä¸{}æ¡ å计={} éé¢={} æ¯å¦è¶
é={}", |
| | | itemLabel, matched.size(), stripZero(total), stripZero(limit), total.compareTo(limit) > 0); |
| | | if (total.compareTo(limit) > 0) { |
| | | result.addError(String.format( |
| | | "%så计%så
ï¼è¶
è¿éé¢%så
ï¼æ¡ä¾å
累计ï¼å
±%dæ¡æç»ï¼", |
| | | itemLabel, stripZero(total), stripZero(limit), matched.size())); |
| | | } |
| | | } |
| | | |
| | | /** è§å 4ï¼ECOMå¢éå³å¡è´¹ï¼æè´¹ç¨è¯´æåé¢å
/å¸å
/å¸å¤ä¸æ¡£ */ |
| | | private void checkEcom(List<DetailRow> rows, ExpertFeeCheckResult result) { |
| | | BigDecimal inHospital = BigDecimal.ZERO; |
| | | BigDecimal inCity = BigDecimal.ZERO; |
| | | BigDecimal outCity = BigDecimal.ZERO; |
| | | int count = 0; |
| | | int unknownScopeRows = 0; |
| | | Set<Long> ecomItemIds = resolveItemIds(CFG_ITEM_ECOM, "ECOMå¢éå³å¡è´¹", FALLBACK_ECOM); |
| | | for (DetailRow row : rows) { |
| | | if (!isTargetItem(row, ecomItemIds)) { |
| | | continue; |
| | | } |
| | | count++; |
| | | if (row.scopeContainsAny(SCOPE_IN_HOSPITAL)) { |
| | | inHospital = inHospital.add(row.getAmount()); |
| | | } else if (row.scopeContainsAny(SCOPE_OUT_CITY)) { |
| | | outCity = outCity.add(row.getAmount()); |
| | | } else if (row.scopeContainsAny(SCOPE_IN_CITY)) { |
| | | inCity = inCity.add(row.getAmount()); |
| | | } else if (row.getAmount().compareTo(BigDecimal.ZERO) > 0) { |
| | | // åºæ¯æ æ³è¯å«æ¶ä¸å½å
¥ä»»ä½æ¡£ï¼é¿å
误å¤ï¼ï¼ä½ä¸è½é黿¾è¿ï¼æç¤ºç»åäººæ ¸å® |
| | | unknownScopeRows++; |
| | | log.warn("[ä¸å®¶è´¹æ ¡éª] ECOMå¢éå³å¡è´¹å卿 æ³è¯å«åºæ¯çæç»ï¼fundid={} itemid={} è´¹ç¨è¯´æ={} éé¢={}", |
| | | row.getFundid(), row.getItemid(), row.getServicesscopename(), row.getAmount()); |
| | | } |
| | | } |
| | | if (count == 0) { |
| | | return; |
| | | } |
| | | if (unknownScopeRows > 0) { |
| | | result.addWarning(String.format( |
| | | "ECOMå¢éå³å¡è´¹æ%dæ¡æç»çè´¹ç¨è¯´æä¸å¨ãé¢å
/å¸å
/å¸å¤ãèå´å
ï¼æªçº³å
¥é颿 ¡éªï¼è¯·æ ¸å®", |
| | | unknownScopeRows)); |
| | | } |
| | | checkLimit(result, "ECOMå¢éå³å¡è´¹ï¼é¢å
ï¼", inHospital, cfg(CFG_ECOM_IN_HOSPITAL, DEFAULT_ECOM_IN_HOSPITAL)); |
| | | checkLimit(result, "ECOMå¢éå³å¡è´¹ï¼å¸å
ï¼", inCity, cfg(CFG_ECOM_IN_CITY, DEFAULT_ECOM_IN_CITY)); |
| | | checkLimit(result, "ECOMå¢éå³å¡è´¹ï¼å¸å¤ï¼", outCity, cfg(CFG_ECOM_OUT_CITY, DEFAULT_ECOM_OUT_CITY)); |
| | | } |
| | | |
| | | /** |
| | | * è§å 7ï¼å©æè´¹ç¨ââæãå©æç±»åï¼é¡¹ç® idï¼ãåå«åè®¡ï¼ |
| | | * æ¯ä¸ªç±»ååèªï¼å¸å
⤠1000ãå¤å¸ ⤠2000ï¼itemid 41/71/72/73/74ï¼ã |
| | | * |
| | | * <p>2026-09-30 鿱忴ï¼åå®ç°"ä¸åºåå
·ä½æ¯åªä¸ªå¨å®/åªä¸ªå©æé¡¹ç®ï¼ææå©ææç»å并累计" |
| | | * ï¼ä¸ä¸ªå¸å
å计 + ä¸ä¸ªå¤å¸å计ï¼ï¼ä¸åå§éæ±ãå个å¨å®å¤ä¸ªå©æå计éé¢ãä¸ç¬¦ï¼ä¼å¤§é¢ç§¯è¯¯æ¦ |
| | | * ï¼æ¬å°åº 1004 个å«å©ææç»çæ¡ä¾ä¸ï¼æ§å£å¾æ¦ 647 ä¸ªï¼æç±»ååå¼ååªæ¦ 75 个ï¼ã |
| | | * ç°æ¹ä¸ºï¼å䏿¡ä¾ãåä¸å©æç±»åå
累计ï¼ä¸åç±»åä¹é´ä¸äºç¸å ç¨é¢åº¦ï¼ |
| | | * å¸å
å¤ä»ç± servicesscopenameï¼è´¹ç¨è¯´æï¼å¤å®ã |
| | | */ |
| | | private void checkAssistant(List<DetailRow> rows, ExpertFeeCheckResult result) { |
| | | BigDecimal inCityLimit = cfg(CFG_ASSISTANT_IN_CITY, DEFAULT_ASSISTANT_IN_CITY); |
| | | BigDecimal outCityLimit = cfg(CFG_ASSISTANT_OUT_CITY, DEFAULT_ASSISTANT_OUT_CITY); |
| | | Set<Long> assistantItemIds = resolveItemIds(CFG_ITEM_ASSISTANT, "å©æè´¹ç¨", FALLBACK_ASSISTANT); |
| | | |
| | | // key = itemidï¼å©æç±»åï¼ï¼LinkedHashMap + æ«å°¾æ itemid æåºï¼ä¿è¯æç¤ºé¡ºåºç¨³å® |
| | | Map<Long, AssistantTotal> groups = new LinkedHashMap<>(); |
| | | int unknownScopeCount = 0; |
| | | for (DetailRow row : rows) { |
| | | if (!isAssistantItem(row, assistantItemIds)) { |
| | | continue; |
| | | } |
| | | boolean outCity = row.scopeContainsAny(SCOPE_OUT_CITY); |
| | | boolean inCity = !outCity && row.scopeContainsAny(SCOPE_IN_CITY); |
| | | if (!outCity && !inCity) { |
| | | // æªæ 注å¸å
å¤çä¸å½å
¥ä»»ä½æ¡£ï¼é¿å
误å¤ï¼ä»
è®°æ¥å¿ï¼ä¸ææ°ä¸å¡ï¼ |
| | | unknownScopeCount++; |
| | | continue; |
| | | } |
| | | AssistantTotal group = groups.computeIfAbsent(row.getItemid(), |
| | | key -> new AssistantTotal(key, itemNameOf(row))); |
| | | if (outCity) { |
| | | group.outCity = group.outCity.add(row.getAmount()); |
| | | group.outCityCount++; |
| | | } else { |
| | | group.inCity = group.inCity.add(row.getAmount()); |
| | | group.inCityCount++; |
| | | } |
| | | } |
| | | |
| | | List<AssistantTotal> ordered = new ArrayList<>(groups.values()); |
| | | ordered.sort((a, b) -> { |
| | | if (a.itemid == null) { |
| | | return b.itemid == null ? 0 : 1; |
| | | } |
| | | return b.itemid == null ? -1 : a.itemid.compareTo(b.itemid); |
| | | }); |
| | | |
| | | for (AssistantTotal group : ordered) { |
| | | log.info("[ä¸å®¶è´¹æ ¡éª] å©æè´¹ç¨[{}] å¸å
{}æ¡å计={} éé¢={}ï¼å¤å¸{}æ¡å计={} éé¢={}", |
| | | group.itemName, group.inCityCount, stripZero(group.inCity), stripZero(inCityLimit), |
| | | group.outCityCount, stripZero(group.outCity), stripZero(outCityLimit)); |
| | | if (group.inCityCount > 0 && group.inCity.compareTo(inCityLimit) > 0) { |
| | | result.addError(String.format( |
| | | "å©æè´¹ç¨ï¼%sï¼å¸å
ï¼å计%så
ï¼è¶
è¿éé¢%så
ï¼å䏿¡ä¾ä¸è¯¥ç±»å婿å计ï¼å
±%dæ¡ï¼", |
| | | group.itemName, stripZero(group.inCity), stripZero(inCityLimit), group.inCityCount)); |
| | | } |
| | | if (group.outCityCount > 0 && group.outCity.compareTo(outCityLimit) > 0) { |
| | | result.addError(String.format( |
| | | "å©æè´¹ç¨ï¼%sï¼å¤å¸ï¼å计%så
ï¼è¶
è¿éé¢%så
ï¼å䏿¡ä¾ä¸è¯¥ç±»å婿å计ï¼å
±%dæ¡ï¼", |
| | | group.itemName, stripZero(group.outCity), stripZero(outCityLimit), group.outCityCount)); |
| | | } |
| | | } |
| | | |
| | | if (unknownScopeCount > 0) { |
| | | log.warn("[ä¸å®¶è´¹æ ¡éª] å©æè´¹ç¨æ{}æ¡æç»çè´¹ç¨è¯´ææ¢ä¸å«å¸å
/æ¬å¸ä¹ä¸å«å¸å¤/å¤å¸ï¼æªçº³å
¥é颿 ¡éª", |
| | | unknownScopeCount); |
| | | } |
| | | } |
| | | |
| | | /** è§å 7 ç累计åå
ï¼åä¸å©æç±»åï¼é¡¹ç® idï¼å¨å䏿¡ä¾ä¸çå¸å
/å¤å¸å计 */ |
| | | private static final class AssistantTotal { |
| | | private final Long itemid; |
| | | private final String itemName; |
| | | private BigDecimal inCity = BigDecimal.ZERO; |
| | | private int inCityCount; |
| | | private BigDecimal outCity = BigDecimal.ZERO; |
| | | private int outCityCount; |
| | | |
| | | private AssistantTotal(Long itemid, String itemName) { |
| | | this.itemid = itemid; |
| | | this.itemName = itemName; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è§å 8ã9ï¼ææ¯å®¤äººå·¥è´¹æè´¹ç¨è¯´æåæ¡£ï¼åèªç¬ç«éé¢ ââ |
| | | * ãå·¡åãå计 ⤠500ãã麻éãå计 ⤠500ï¼itemid=43ï¼ã |
| | | * |
| | | * <p>2026-09-30 鿱忴ï¼åå®ç°æå·¡åä¸éº»éåå¹¶æä¸ä¸ª 500 çæ»éé¢ï¼ |
| | | * ç°æä¸å¡è¦æ±ææä¸¤æ¡£åèªæ ¡éªï¼å¦å·¡å 300 + 麻é 300 ä¸åå¤è¶
éï¼ã |
| | | * |
| | | * <p>è´¹ç¨è¯´ææ¢ä¸å«ãå·¡åãä¹ä¸å«ã麻éãçæç»ä¸è®¡å
¥ä»»ä½æ¡£ï¼ä»
ç»æç¤ºï¼ |
| | | * åæ¶å«ä¸¤ä¸ªè¯çæç»åå«è®¡å
¥ä¸¤æ¡£ï¼å¹¶è®° warn æ¥å¿ï¼ã |
| | | */ |
| | | private void checkOperationRoom(List<DetailRow> rows, ExpertFeeCheckResult result) { |
| | | Set<Long> roomItemIds = resolveItemIds(CFG_ITEM_OPERATION_ROOM, "ææ¯å®¤äººå·¥è´¹", FALLBACK_OPERATION_ROOM); |
| | | |
| | | BigDecimal circuitTotal = BigDecimal.ZERO; |
| | | int circuitCount = 0; |
| | | BigDecimal anesthesiaTotal = BigDecimal.ZERO; |
| | | int anesthesiaCount = 0; |
| | | int unknownScopeCount = 0; |
| | | int bothScopeCount = 0; |
| | | |
| | | for (DetailRow row : rows) { |
| | | if (!isTargetItem(row, roomItemIds)) { |
| | | continue; |
| | | } |
| | | boolean circuit = row.scopeContains(SCOPE_CIRCUIT); |
| | | boolean anesthesia = row.scopeContains(SCOPE_ANESTHESIA); |
| | | if (circuit && anesthesia) { |
| | | bothScopeCount++; |
| | | } |
| | | if (circuit) { |
| | | circuitTotal = circuitTotal.add(row.getAmount()); |
| | | circuitCount++; |
| | | } |
| | | if (anesthesia) { |
| | | anesthesiaTotal = anesthesiaTotal.add(row.getAmount()); |
| | | anesthesiaCount++; |
| | | } |
| | | if (!circuit && !anesthesia) { |
| | | unknownScopeCount++; |
| | | } |
| | | } |
| | | |
| | | if (bothScopeCount > 0) { |
| | | log.warn("[ä¸å®¶è´¹æ ¡éª] ææ¯å®¤äººå·¥è´¹æ{}æ¡æç»çè´¹ç¨è¯´æåæ¶å«ãå·¡åãä¸ã麻éãï¼å·²åå«è®¡å
¥ä¸¤æ¡£", |
| | | bothScopeCount); |
| | | } |
| | | |
| | | BigDecimal circuitLimit = cfg(CFG_OPERATION_ROOM_CIRCUIT, DEFAULT_OPERATION_ROOM_CIRCUIT); |
| | | if (circuitCount > 0) { |
| | | log.info("[ä¸å®¶è´¹æ ¡éª] ææ¯å®¤äººå·¥è´¹(å·¡å) å½ä¸{}æ¡ å计={} éé¢={} æ¯å¦è¶
é={}", |
| | | circuitCount, stripZero(circuitTotal), stripZero(circuitLimit), |
| | | circuitTotal.compareTo(circuitLimit) > 0); |
| | | if (circuitTotal.compareTo(circuitLimit) > 0) { |
| | | result.addError(String.format( |
| | | "ææ¯å®¤äººå·¥è´¹ï¼å·¡åï¼å计%så
ï¼è¶
è¿éé¢%så
ï¼å䏿¡ä¾ä¸å计ï¼å
±%dæ¡ï¼", |
| | | stripZero(circuitTotal), stripZero(circuitLimit), circuitCount)); |
| | | } |
| | | } |
| | | |
| | | BigDecimal anesthesiaLimit = cfg(CFG_OPERATION_ROOM_ANESTHESIA, DEFAULT_OPERATION_ROOM_ANESTHESIA); |
| | | if (anesthesiaCount > 0) { |
| | | log.info("[ä¸å®¶è´¹æ ¡éª] ææ¯å®¤äººå·¥è´¹(麻é) å½ä¸{}æ¡ å计={} éé¢={} æ¯å¦è¶
é={}", |
| | | anesthesiaCount, stripZero(anesthesiaTotal), stripZero(anesthesiaLimit), |
| | | anesthesiaTotal.compareTo(anesthesiaLimit) > 0); |
| | | if (anesthesiaTotal.compareTo(anesthesiaLimit) > 0) { |
| | | result.addError(String.format( |
| | | "ææ¯å®¤äººå·¥è´¹ï¼éº»éï¼å计%så
ï¼è¶
è¿éé¢%så
ï¼å䏿¡ä¾ä¸å计ï¼å
±%dæ¡ï¼", |
| | | stripZero(anesthesiaTotal), stripZero(anesthesiaLimit), anesthesiaCount)); |
| | | } |
| | | } |
| | | |
| | | if (unknownScopeCount > 0) { |
| | | result.addWarning(String.format( |
| | | "ææ¯å®¤äººå·¥è´¹æ%dæ¡æç»çè´¹ç¨è¯´ææ¢ä¸å«ãå·¡åãä¹ä¸å«ã麻éãï¼æªçº³å
¥é颿 ¡éªï¼è¯·æ ¸å®", |
| | | unknownScopeCount)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è§å 5ï¼åä¸ä¸å®¶ä¸å¾åæ¶å¨ãæç®å»é¢åæ¥è¯ä¼°è´¹ç¨ããæç®ç
æ
è¯ä¼°ä¸å®¶è´¹ã |
| | | * ãèæ»äº¡ä¸´åºå¤å®ä¸å®¶è´¹ãä¸åºç°ï¼åªè½å°±é«åå
¶ä¸ã |
| | | * |
| | | * <p>ç¡¬æ¦æªï¼è¿åæ¶ä¸å
许ä¿å/å®¡æ ¸éè¿ï¼ä¸èªå¨å é¤ä¹ä¸èªå¨æ¹æ°ã |
| | | */ |
| | | private void checkAssessConflict(List<DetailRow> rows, ExpertFeeCheckResult result) { |
| | | Set<Long> assessItemIds = resolveItemIds(CFG_ITEM_ASSESS, "è¯ä¼°ç±»è´¹ç¨", FALLBACK_ASSESS); |
| | | Map<String, Map<Long, String>> expertItems = new LinkedHashMap<>(); |
| | | for (DetailRow row : rows) { |
| | | if (!isTargetItem(row, assessItemIds)) { |
| | | continue; |
| | | } |
| | | String expertKey = expertKey(row); |
| | | if (expertKey == null) { |
| | | continue; |
| | | } |
| | | // è®°å½å½ä¸ç项ç®ååç§°ï¼ä¾¿äºæç¤ºæ¶åç¥å
·ä½æ¯åªå ä¸ªé¡¹ç® |
| | | expertItems.computeIfAbsent(expertKey, k -> new LinkedHashMap<>()) |
| | | .put(row.getItemid(), itemNameOf(row)); |
| | | } |
| | | for (Map.Entry<String, Map<Long, String>> entry : expertItems.entrySet()) { |
| | | if (entry.getValue().size() > 1) { |
| | | result.addError(String.format( |
| | | "ä¸å®¶[%s]åæ¶å¨ %s çå¤ä¸ªè¯ä¼°ç±»é¡¹ç®ä¸é¢åè´¹ç¨ï¼åä¸ä¸å®¶åªè½å°±é«åå
¶ä¸ï¼è¯·å é¤å
¶ä½æ¡æç»", |
| | | entry.getKey(), entry.getValue().values())); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // ================================================================== |
| | | // åºæ¬è§åï¼2026-10-08 æ°å¢ï¼ï¼å项éé¢ä¸å¾è¶
è¿ base_itemexpense çè´¹ç¨æ å |
| | | // ================================================================== |
| | | |
| | | /** |
| | | * åºæ¬è§åï¼applytype=1ï¼ä¸å®¶å³å¡è´¹ï¼ç<b>å
¨é¨æç»</b>éæ¡ä¸è´¹ç¨æ 忝坹ã |
| | | * |
| | | * <p>夿®ï¼2026-10-08 ä¸å¡æ¹ç¡®è®¤ï¼ï¼ |
| | | * <ol> |
| | | * <li>å䏿¡ä¾ à åä¸ä¸å®¶ à åä¸é¡¹ç® à åä¸è´¹ç¨è¯´æç<b>å计</b> ⤠该项ç®è´¹ç¨æ åï¼</li> |
| | | * <li>éé¢ä¸º 0 çæç»ä¸å¾é»æï¼æç»éé¢ä¸è½ä¸º 0ï¼ï¼</li> |
| | | * <li>è´¹ç¨è¯´æä¸ºç©ºãæå¹é
ä¸å°æ åï¼å« UseStateâ 1ãå·²å é¤ï¼ï¼ä¸å¾é»æï¼</li> |
| | | * <li>åä¸ç»åºç°å¤æ¡æ¶<b>åªæç¤ºä¸é»æ</b>ï¼æ¯å¦è¶
éç±ä¸é¢çå计å¤å®ï¼ã</li> |
| | | * </ol> |
| | | * |
| | | * <p>éé¢å£å¾ï¼è´¹ç¨è¯´æå«ãç¨åãçå<b>ç¨åéé¢</b>ï¼taxedamountï¼ï¼å
¶ä½åç¨åéé¢ï¼amountï¼ã |
| | | * æ å表éãç¨åãç±»æ åç Expense æ¬èº«å°±æ¯ç¨åå£å¾ï¼ |
| | | * è¥æ¿ç¨åéé¢ï¼å«ç¨å ä»·åçå¼ï¼å»æ¯ï¼ä¼æå¤§éæ£å¸¸åæ®è¯¯å¤ä¸ºè¶
æ åï¼å®æµ 3168 ç» vs 162 ç»ï¼ã |
| | | */ |
| | | private void checkItemStandard(List<DetailRow> rows, ExpertFeeCheckResult result) { |
| | | Map<String, List<DetailRow>> groups = new LinkedHashMap<>(); |
| | | Map<String, BigDecimal> groupStandards = new LinkedHashMap<>(); |
| | | Map<String, String> groupLabels = new LinkedHashMap<>(); |
| | | |
| | | for (DetailRow row : rows) { |
| | | BigDecimal amount = effectiveAmount(row); |
| | | if (amount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | result.addError(String.format("%séé¢ä¸º%så
ï¼æç»éé¢ä¸è½ä¸º0ï¼è¯·å¡«åé颿å é¤è¯¥æ¡æç»", |
| | | itemLabel(row), stripZero(amount))); |
| | | continue; |
| | | } |
| | | String scope = scopeOf(row); |
| | | if (scope.isEmpty()) { |
| | | result.addError(String.format("%sæªå¡«åè´¹ç¨è¯´æï¼æ æ³æ ¡éªè´¹ç¨æ åï¼è¯·è¡¥å
è´¹ç¨è¯´æ", itemLabel(row))); |
| | | continue; |
| | | } |
| | | BigDecimal standard = loadItemStandard(row.getItemid(), scope); |
| | | if (standard == null) { |
| | | result.addError(String.format( |
| | | "%sæªæ¾å°å¯¹åºçè´¹ç¨æ åï¼é¡¹ç®id=%sï¼è´¹ç¨è¯´æ=%sï¼ï¼ä¸å
许æ¥éï¼è¯·è系管çåç»´æ¤è´¹ç¨æ å", |
| | | itemLabel(row), row.getItemid(), scope)); |
| | | continue; |
| | | } |
| | | String key = groupKey(row, scope); |
| | | groups.computeIfAbsent(key, k -> new ArrayList<>()).add(row); |
| | | groupStandards.putIfAbsent(key, standard); |
| | | groupLabels.putIfAbsent(key, groupLabel(row)); |
| | | } |
| | | |
| | | for (Map.Entry<String, List<DetailRow>> entry : groups.entrySet()) { |
| | | String key = entry.getKey(); |
| | | List<DetailRow> list = entry.getValue(); |
| | | BigDecimal total = BigDecimal.ZERO; |
| | | for (DetailRow row : list) { |
| | | total = total.add(effectiveAmount(row)); |
| | | } |
| | | BigDecimal standard = groupStandards.get(key); |
| | | String label = groupLabels.get(key); |
| | | log.info("[ä¸å®¶è´¹æ ¡éª] åºæ¬è§å {} å½ä¸{}æ¡ å计={} æ å={} æ¯å¦è¶
æ ={}", |
| | | label, list.size(), stripZero(total), stripZero(standard), |
| | | total.compareTo(standard) > 0); |
| | | if (total.compareTo(standard) > 0) { |
| | | result.addError(String.format( |
| | | "%så计%så
ï¼è¶
è¿è´¹ç¨æ å%så
ï¼å䏿¡ä¾åä¸ä¸å®¶åä¸é¡¹ç®ç累计ï¼å
±%dæ¡æç»ï¼", |
| | | label, stripZero(total), stripZero(standard), list.size())); |
| | | } |
| | | if (list.size() > 1) { |
| | | result.addWarning(String.format("%så¨å䏿¡ä¾ä¸æ%dæ¡æç»ï¼å计%så
ï¼ï¼è¯·ç¡®è®¤æ¯å¦éå¤å½å
¥", |
| | | label, list.size(), stripZero(total))); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å¨å®è·åå æï¼2026-10-08 éåï¼å代åãåä¸ä¸å®¶åªå
è®¸ä¸æ¡è¶
è¿ 500ãï¼ã |
| | | * |
| | | * <p>å䏿¡ä¾å
ï¼åä¸ä¸å®¶å¨å»çè·å/ä¿®æ´ç±»é¡¹ç®ï¼é»è®¤ itemid 32~39ï¼ä¸è·åå¤ä¸ªå¨å®æ¶ï¼ |
| | | * æéé¢éåºä¿é«ï¼<b>第äºä¸ªå以åçæ¯æ¡</b>éé¢ä¸é = è´¹ç¨è¯´æå«ãåä¾§ãå 1000ãå
¶ä»å 500ã |
| | | * 第ä¸ä¸ªï¼é颿é«çï¼é£æ¡æè´¹ç¨æ åæ ¡éªï¼ç±ä¸é¢çãåºæ¬è§åãè´è´£ã |
| | | * |
| | | * <p>该é弿 æ³ç¨ base_itemexpense è¡¨è¾¾ï¼æ å表éè®°çæ¯é¦ä¸ªå¨å®çè¶³é¢æ åï¼ï¼å æ¤æ¾ sys_configã |
| | | */ |
| | | private void checkDoctorOrganSecond(List<DetailRow> rows, ExpertFeeCheckResult result) { |
| | | BigDecimal doubleSideLimit = cfg(CFG_ORGAN_SECOND_DOUBLE, DEFAULT_ORGAN_SECOND_DOUBLE); |
| | | BigDecimal otherLimit = cfg(CFG_ORGAN_SECOND_OTHER, DEFAULT_ORGAN_SECOND_OTHER); |
| | | Set<Long> doctorItemIds = resolveItemIds(CFG_ITEM_DOCTOR, "å»çè·å/ä¿®æ´ç±»è´¹ç¨", FALLBACK_DOCTOR_ORGAN); |
| | | |
| | | Map<String, List<DetailRow>> byExpert = new LinkedHashMap<>(); |
| | | for (DetailRow row : rows) { |
| | | if (!isDoctorOrganItem(row, doctorItemIds)) { |
| | | continue; |
| | | } |
| | | String expertKey = expertKey(row); |
| | | if (expertKey == null) { |
| | | continue; |
| | | } |
| | | byExpert.computeIfAbsent(expertKey, k -> new ArrayList<>()).add(row); |
| | | } |
| | | |
| | | for (Map.Entry<String, List<DetailRow>> entry : byExpert.entrySet()) { |
| | | List<DetailRow> list = new ArrayList<>(entry.getValue()); |
| | | if (list.size() < 2) { |
| | | continue; |
| | | } |
| | | // éé¢éåºä¿é«ï¼æé«ç飿¡æè´¹ç¨æ åï¼åºæ¬è§åå·²æ ¡éªï¼ï¼å
¶ä½æ 500/1000 |
| | | list.sort((a, b) -> effectiveAmount(b).compareTo(effectiveAmount(a))); |
| | | log.info("[ä¸å®¶è´¹æ ¡éª] å¨å®è·åå æ ä¸å®¶={} å¨å®è·åæç»{}æ¡ ä¸é(åä¾§/å
¶ä»)={}/{}", |
| | | entry.getKey(), list.size(), stripZero(doubleSideLimit), stripZero(otherLimit)); |
| | | for (int i = 1; i < list.size(); i++) { |
| | | DetailRow row = list.get(i); |
| | | BigDecimal amount = effectiveAmount(row); |
| | | BigDecimal limit = row.scopeContains(SCOPE_DOUBLE_SIDE) ? doubleSideLimit : otherLimit; |
| | | if (amount.compareTo(limit) > 0) { |
| | | result.addError(String.format( |
| | | "ä¸å®¶[%s]ç第%d个å¨å®è·å%séé¢%så
ï¼è¶
è¿ä¸é%så
" |
| | | + "ï¼å䏿¡ä¾ä¸ç¬¬äºä¸ªå以åçå¨å®è·åï¼åä¾§ä¸é%så
ãå
¶ä»ä¸é%så
ï¼ï¼è¯·è°æ´éé¢", |
| | | entry.getKey(), i + 1, itemLabel(row), stripZero(amount), stripZero(limit), |
| | | stripZero(doubleSideLimit), stripZero(otherLimit))); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** æç»çãææéé¢ãï¼è´¹ç¨è¯´æå«ãç¨åãåç¨åéé¢ï¼å
¶ä½åç¨åéé¢ */ |
| | | private BigDecimal effectiveAmount(DetailRow row) { |
| | | return row.scopeContains(SCOPE_TAXED) ? row.getTaxedamount() : row.getAmount(); |
| | | } |
| | | |
| | | /** æç»çè´¹ç¨è¯´æï¼å»ç©ºæ ¼ï¼ */ |
| | | private String scopeOf(DetailRow row) { |
| | | return row.getServicesscopename() == null ? "" : row.getServicesscopename().trim(); |
| | | } |
| | | |
| | | /** åç»é®ï¼ä¸å®¶ + é¡¹ç® + è´¹ç¨è¯´æï¼æ¡ä¾èå´å·²ç± rows éå®ï¼ */ |
| | | private String groupKey(DetailRow row, String scope) { |
| | | String expert = row.getIdcardno() == null ? "" : row.getIdcardno().trim(); |
| | | return expert + "|" + row.getItemid() + "|" + scope; |
| | | } |
| | | |
| | | /** æç¤ºææ¡ï¼ã项ç®Â·è´¹ç¨è¯´æã */ |
| | | private String itemLabel(DetailRow row) { |
| | | String scope = scopeOf(row); |
| | | return "ã" + itemNameOf(row) + (scope.isEmpty() ? "" : "·" + scope) + "ã"; |
| | | } |
| | | |
| | | /** æç¤ºææ¡ï¼ä¸å®¶[身份è¯]ã项ç®Â·è´¹ç¨è¯´æã */ |
| | | private String groupLabel(DetailRow row) { |
| | | String expert = row.getIdcardno() == null ? "" : row.getIdcardno().trim(); |
| | | return (expert.isEmpty() ? "" : "ä¸å®¶[" + expert + "]") + itemLabel(row); |
| | | } |
| | | |
| | | /** |
| | | * åãé¡¹ç® id + è´¹ç¨è¯´æãçè´¹ç¨æ åã |
| | | * |
| | | * <p>æ å表æ¯å¯ç»´æ¤çåºç¡æ°æ®ï¼åªè®¤ {@code del_flag = 0 ä¸ UseState = 1} çè®°å½ |
| | | * ï¼å®ä½æ²¡æ delFlag åæ®µï¼å æ¤ del_flag ç¨ SQL çæ®µéå®ï¼ã |
| | | * |
| | | * @return æ¾ä¸å°æ¶è¿å {@code null}ï¼è°ç¨æ¹æãå¹é
ä¸å°æ åã黿 |
| | | */ |
| | | private BigDecimal loadItemStandard(Long itemid, String scope) { |
| | | return loadItemStandards(itemid).get(scope); |
| | | } |
| | | |
| | | /** æé¡¹ç® id å è½½è´¹ç¨æ åï¼ç»æç¼å 60 ç§ï¼ï¼key = è´¹ç¨è¯´æ */ |
| | | private Map<String, BigDecimal> loadItemStandards(Long itemid) { |
| | | if (itemid == null) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | long now = System.currentTimeMillis(); |
| | | ItemExpenseEntry cached = itemExpenseCache.get(itemid); |
| | | if (cached != null && cached.expireAt > now) { |
| | | return cached.standards; |
| | | } |
| | | Map<String, BigDecimal> standards = new LinkedHashMap<>(); |
| | | try { |
| | | // ç¨å符串ååå
è£
å¨ï¼BaseItemexpense å®ä½æ²¡æ delFlag åæ®µï¼æ æ³ç¨ lambda å¼ç¨ï¼ï¼ |
| | | // ä¸å¯é¿å
ä¾èµ MyBatis-Plus ç lambda ç¼å |
| | | QueryWrapper<BaseItemexpense> wrapper = new QueryWrapper<>(); |
| | | wrapper.eq("ItemId", itemid); |
| | | wrapper.eq("UseState", "1"); |
| | | wrapper.apply("del_flag = 0"); |
| | | wrapper.orderByAsc("Id"); |
| | | List<BaseItemexpense> list = baseItemexpenseMapper.selectList(wrapper); |
| | | if (list != null) { |
| | | for (BaseItemexpense item : list) { |
| | | if (item == null || item.getExpensedescribe() == null || item.getExpense() == null) { |
| | | continue; |
| | | } |
| | | standards.putIfAbsent(item.getExpensedescribe().trim(), item.getExpense()); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("[ä¸å®¶è´¹æ ¡éª] 读åè´¹ç¨æ å失败ï¼itemid={}", itemid, e); |
| | | } |
| | | itemExpenseCache.put(itemid, new ItemExpenseEntry(standards, now + ITEM_EXPENSE_CACHE_MILLIS)); |
| | | return standards; |
| | | } |
| | | |
| | | // ================================================================== |
| | | // è´¹ç¨é¡¹ç®å¨æè§£æ |
| | | // |
| | | // èæ¯ï¼è´¹ç¨é¡¹ç®è¡¨ base_financesubject çé¡¹ç®æå¹´åº¦é建ï¼ä¾ï¼âï¼2026ï¼æç®å»é¢å¨å®åè½ç»´æ¤è´¹ç¨â |
| | | // æ¯ id=75ï¼èæ©æå¹´åº¦ä¸º 5ï¼ï¼å æ¤ä¸è½æ itemid åæ»å¨ä»£ç éã |
| | | // åæ³ï¼æé¡¹ç®åç§°å
³é®è¯å°åºç¡è¡¨è§£æå½åææçé¡¹ç® idï¼åä¸âæ©æå¹´åº¦ idâæ±å¹¶éã |
| | | // ================================================================== |
| | | |
| | | /** |
| | | * è§£æè§å对åºçè´¹ç¨é¡¹ç® id éåã |
| | | * |
| | | * <p>ä¼å
çº§ï¼ |
| | | * <ol> |
| | | * <li>sys_config 䏿¾å¼é
ç½®çé¡¹ç® idï¼éå·åéï¼ââ项ç®åç§°ä¸è§èæ¶ç¨è¿ä¸ªå
åº</li> |
| | | * <li>æé¡¹ç®åç§°å
³é®è¯å°åºç¡è¡¨è§£æï¼èªå¨éåºé¡¹ç®å¹´åº¦é建ï¼</li> |
| | | * <li>æ©æå¹´åº¦çåå²é¡¹ç® id</li> |
| | | * </ol> |
| | | * ä»
æé¡¹ç® id å¤å®ï¼å sys_config ä¸é
ç½®çé¡¹ç® id å表ï¼ä¸åå²å¹´åº¦ id æ±å¹¶éã |
| | | * é
ç½®ä¸ºç©ºä¸æ åå² id æ¶è¿å空éï¼æ¤æ¶è¯¥è§åä¸çæå¹¶ä¼è®° warn æ¥å¿ã |
| | | * |
| | | * @param itemIdConfigKey sys_config ä¸é¡¹ç® id å表çé
ç½®é® |
| | | * @param ruleLabel è§ååç§°ï¼ä»
ç¨äºæ¥å¿å¯è¯»æ§ |
| | | * @param fallback åå²å¹´åº¦ï¼é¡¹ç®è¡¨æ¹é åï¼ä½¿ç¨çé¡¹ç® id |
| | | */ |
| | | private Set<Long> resolveItemIds(String itemIdConfigKey, String ruleLabel, Set<Long> fallback) { |
| | | Set<Long> ids = new LinkedHashSet<>(); |
| | | ids.addAll(parseItemIdConfig(itemIdConfigKey)); |
| | | if (fallback != null) { |
| | | ids.addAll(fallback); |
| | | } |
| | | if (ids.isEmpty()) { |
| | | log.warn("[ä¸å®¶è´¹æ ¡éª] è§å[{}]æªé
置项ç®idï¼sys_config.{}ï¼ï¼è¯¥è§åæ¬æ¬¡ä¸çæ", |
| | | ruleLabel, itemIdConfigKey); |
| | | } |
| | | return ids; |
| | | } |
| | | |
| | | /** 读å sys_config ä¸éå·åéçé¡¹ç® id åè¡¨ï¼æªé
ç½®ææ ¼å¼é误æ¶è¿å空éï¼ */ |
| | | private Set<Long> parseItemIdConfig(String configKey) { |
| | | Set<Long> ids = new LinkedHashSet<>(); |
| | | if (configKey == null) { |
| | | return ids; |
| | | } |
| | | try { |
| | | String value = sysConfigService.selectConfigByKey(configKey); |
| | | if (StringUtils.isEmpty(value)) { |
| | | return ids; |
| | | } |
| | | for (String part : value.split(",")) { |
| | | String trimmed = part.trim(); |
| | | if (StringUtils.isEmpty(trimmed)) { |
| | | continue; |
| | | } |
| | | try { |
| | | ids.add(Long.valueOf(trimmed)); |
| | | } catch (NumberFormatException e) { |
| | | log.warn("[ä¸å®¶è´¹æ ¡éª] 项ç®idé
ç½®[{}]ä¸åå¨éæ³å¼â{}âï¼å·²å¿½ç¥", configKey, trimmed); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("[ä¸å®¶è´¹æ ¡éª] 读å项ç®idé
置失败ï¼key={}", configKey, e); |
| | | } |
| | | return ids; |
| | | } |
| | | |
| | | /** æç»è¡æ¯å¦å±äºç»å®é¡¹ç® id éåï¼å
¨é¨è§åç»ä¸æé¡¹ç® id å¤å®ï¼ */ |
| | | private boolean isTargetItem(DetailRow row, Set<Long> itemids) { |
| | | return !CollectionUtils.isEmpty(itemids) |
| | | && row.getItemid() != null |
| | | && itemids.contains(row.getItemid()); |
| | | } |
| | | |
| | | /** è§å 6ï¼å»çâXXè·å/ä¿®æ´â类项ç®ï¼ä»
æé¡¹ç® idï¼ */ |
| | | private boolean isDoctorOrganItem(DetailRow row, Set<Long> itemids) { |
| | | return isTargetItem(row, itemids); |
| | | } |
| | | |
| | | /** è§å 7ï¼å©æç±»é¡¹ç®ï¼ä»
æé¡¹ç® idï¼ */ |
| | | private boolean isAssistantItem(DetailRow row, Set<Long> itemids) { |
| | | return isTargetItem(row, itemids); |
| | | } |
| | | |
| | | /** åæç»ç项ç®åç§°ï¼æç»æªæºå¸¦æ¶åæ¥åºç¡è¡¨ï¼ï¼ä»
ç¨äºæç¤ºææ¡ */ |
| | | private String itemNameOf(DetailRow row) { |
| | | String name = row.getItemname(); |
| | | if (StringUtils.hasText(name)) { |
| | | return name; |
| | | } |
| | | if (row.getItemid() != null) { |
| | | String cached = loadExpertFeeItemNames().get(row.getItemid()); |
| | | if (StringUtils.hasText(cached)) { |
| | | return cached; |
| | | } |
| | | } |
| | | return "itemid=" + row.getItemid(); |
| | | } |
| | | |
| | | /** é¡¹ç® id -> 项ç®åç§°ï¼å¸¦ç¼åï¼ï¼ä»
ç¨äºæç»æªæºå¸¦åç§°æ¶åæ¥ãæ¼æ¥æç¤ºææ¡ */ |
| | | private Map<Long, String> loadExpertFeeItemNames() { |
| | | Map<Long, String> cache = this.expertFeeItemNameCache; |
| | | if (cache != null) { |
| | | return cache; |
| | | } |
| | | synchronized (this) { |
| | | if (this.expertFeeItemNameCache == null) { |
| | | Map<Long, String> names = new LinkedHashMap<>(); |
| | | for (BaseFinancesubject subject : loadExpertFeeItems()) { |
| | | if (subject.getId() != null && subject.getItemname() != null) { |
| | | names.put(subject.getId(), subject.getItemname()); |
| | | } |
| | | } |
| | | this.expertFeeItemNameCache = names; |
| | | cache = names; |
| | | } else { |
| | | cache = this.expertFeeItemNameCache; |
| | | } |
| | | } |
| | | return cache; |
| | | } |
| | | |
| | | /** 读åä¸å®¶å³å¡è´¹ï¼è´¹ç¨å¤§ç±»=1ï¼çå
¨é¨å¨ç¨é¡¹ç® */ |
| | | private List<BaseFinancesubject> loadExpertFeeItems() { |
| | | List<BaseFinancesubject> items = baseFinancesubjectMapper.getAllInfoByExpenseType(APPLYTYPE_EXPERT); |
| | | return CollectionUtils.isEmpty(items) ? new ArrayList<>() : items; |
| | | } |
| | | |
| | | /** åé¢ééåæé ï¼ä¾ static final 常é使ç¨ï¼ */ |
| | | private static Set<Long> setOf(Long... values) { |
| | | Set<Long> set = new LinkedHashSet<>(); |
| | | if (values != null) { |
| | | for (Long value : values) { |
| | | set.add(value); |
| | | } |
| | | } |
| | | return set; |
| | | } |
| | | |
| | | // ================================================================== |
| | | // å·¥å
·æ¹æ³ |
| | | // ================================================================== |
| | | |
| | | private void checkLimit(ExpertFeeCheckResult result, String label, BigDecimal total, BigDecimal limit) { |
| | | if (total.compareTo(BigDecimal.ZERO) > 0 && total.compareTo(limit) > 0) { |
| | | result.addError(String.format("%så计%så
ï¼è¶
è¿éé¢%så
ï¼æ¡ä¾å
累计ï¼", |
| | | label, stripZero(total), stripZero(limit))); |
| | | } |
| | | } |
| | | |
| | | /** å该æ¡ä¾ä¸å
¨é¨ä¸å®¶å³å¡è´¹æ¥éåç id */ |
| | | private List<Long> listExpertFundIds(Long infoid) { |
| | | List<ServiceFund> funds = serviceFundMapper.getInfoByInfoId(infoid); |
| | | if (CollectionUtils.isEmpty(funds)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return funds.stream() |
| | | .filter(f -> f != null && isExpertFee(applytypeOf(f.getApplytype()))) |
| | | .map(ServiceFund::getId) |
| | | .filter(java.util.Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | /** ææ¥éå id å è½½æç»ï¼XML ä¸å·²å¸¦ del_flag = '0'ï¼ */ |
| | | private List<DetailRow> loadDetailRows(List<Long> fundIds) { |
| | | if (CollectionUtils.isEmpty(fundIds)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<DetailRow> rows = new ArrayList<>(); |
| | | for (Long fundId : fundIds) { |
| | | List<ServiceFunddetail> details = serviceFunddetailMapper.selectFundDetailListById(fundId); |
| | | if (CollectionUtils.isEmpty(details)) { |
| | | continue; |
| | | } |
| | | for (ServiceFunddetail detail : details) { |
| | | DetailRow row = new DetailRow(); |
| | | row.setId(detail.getId()); |
| | | row.setFundid(detail.getFundid()); |
| | | row.setInfoid(detail.getInfoid()); |
| | | row.setItemid(detail.getItemid()); |
| | | row.setItemname(detail.getItemname()); |
| | | row.setIdcardno(detail.getIdcardno()); |
| | | row.setServicesscopename(detail.getServicesscopename()); |
| | | row.setAmount(toDecimal(detail.getAmount())); |
| | | row.setTaxedamount(toDecimal(detail.getTaxedamount())); |
| | | rows.add(row); |
| | | } |
| | | } |
| | | return rows; |
| | | } |
| | | |
| | | /** |
| | | * æç»è¡çå¯ä¸é®ã |
| | | * |
| | | * <p>å·²è½åºçæç»ç¨ä¸»é® idï¼æ°å¢ï¼å°æ idï¼çç¨ |
| | | * ãæ¥éå + é¡¹ç® + èº«ä»½è¯ + è´¹ç¨è¯´æ + éé¢ãç»åï¼ |
| | | * ç¡®ä¿åä¸é¡¹ç®ä¸çå¤è¡æç»ä¸ä¼è¢«åå¹¶ï¼å¦å伿¼ç®æéå¤è®¡ç®ï¼ã |
| | | */ |
| | | private String rowKey(DetailRow row) { |
| | | if (row.getId() != null) { |
| | | return "id:" + row.getId(); |
| | | } |
| | | return "new:" + row.getFundid() + "|" + row.getItemid() + "|" + row.getIdcardno() |
| | | + "|" + row.getServicesscopename() + "|" + row.getAmount(); |
| | | } |
| | | |
| | | private String rowKey(ServiceFunddetailVO vo) { |
| | | if (vo.getId() != null) { |
| | | return "id:" + vo.getId(); |
| | | } |
| | | // æ°å¢æç»å°æ idï¼ç¨ãæ¥éå + é¡¹ç® + èº«ä»½è¯ + è´¹ç¨è¯´æ + éé¢ãè¿ä¼¼å®ä½ï¼ä¿è¯åä¸é¡¹ç®å¤è¡ä¸ä¼è¢«åå¹¶ |
| | | return "new:" + vo.getFundid() + "|" + vo.getItemid() + "|" + vo.getIdcardno() |
| | | + "|" + vo.getServicesscopename() + "|" + vo.getAmount(); |
| | | } |
| | | |
| | | /** ä¸å®¶çè¯å«é®ï¼ä¼å
身份è¯å·ï¼å
¶æ¬¡åç人ç¼å·ï¼é½æ²¡ææ¶è·³è¿è¯¥è¡ */ |
| | | private String expertKey(DetailRow row) { |
| | | if (StringUtils.hasText(row.getIdcardno())) { |
| | | return row.getIdcardno().trim(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private boolean isExpertFee(String applytype) { |
| | | return APPLYTYPE_EXPERT.equals(applytype); |
| | | } |
| | | |
| | | private String applytypeOf(String applytype) { |
| | | return applytype; |
| | | } |
| | | |
| | | /** |
| | | * 9 æ¡è§åæ ¡éªæ¯å¦å¼å¯ã |
| | | * |
| | | * <p>å
³éæ¶ææå
¥å£ç´æ¥è¿å"éè¿"ï¼æ¢ä¸ååºæ¥è¯¢ï¼ä¹ä¸äº§ç errors/warningsï¼ï¼ |
| | | * åç«¯é¢æ ¡éªãä¿åãå®¡æ ¸ä¸æ¡é¾è·¯åæ¶æ¾è¡ã |
| | | */ |
| | | private boolean isValidateEnabled() { |
| | | try { |
| | | String value = sysConfigService.selectConfigByKey(CFG_VALIDATE_ENABLED); |
| | | if (StringUtils.hasText(value)) { |
| | | String v = value.trim(); |
| | | return "1".equals(v) || "true".equalsIgnoreCase(v) |
| | | || "y".equalsIgnoreCase(v) || "on".equalsIgnoreCase(v); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("读åä¸å®¶å³å¡è´¹æ ¡éªæ»å¼å
³å¤±è´¥ï¼key={}ï¼æé»è®¤å¼({})å¤ç", |
| | | CFG_VALIDATE_ENABLED, DEFAULT_VALIDATE_ENABLED, e); |
| | | } |
| | | return DEFAULT_VALIDATE_ENABLED; |
| | | } |
| | | |
| | | private BigDecimal cfg(String key, BigDecimal defaultValue) { |
| | | try { |
| | | String value = sysConfigService.selectConfigByKey(key); |
| | | if (StringUtils.hasText(value)) { |
| | | return new BigDecimal(value.trim()); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("读åä¸å®¶å³å¡è´¹éé¢é
置失败ï¼key={}ï¼ä½¿ç¨é»è®¤å¼{}", key, defaultValue, e); |
| | | } |
| | | return defaultValue; |
| | | } |
| | | |
| | | private static BigDecimal toDecimal(Double value) { |
| | | return value == null ? BigDecimal.ZERO : BigDecimal.valueOf(value); |
| | | } |
| | | |
| | | /** 廿é颿«å°¾å¤ä½ç 0ï¼ä¾¿äºæç¤ºææ¡é
读ï¼60000.00 -> 60000ï¼ */ |
| | | private static String stripZero(BigDecimal value) { |
| | | return value.stripTrailingZeros().toPlainString(); |
| | | } |
| | | } |