wanghc
2023-03-03 5378165da6ad9f748a8a3e4599743d0bfbb4835b
浦发分行小系统
13 files added
2281 ■■■■■ changed files
.gitignore 6 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/pom.xml 87 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/Application.java 24 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/content/ZxmxContent.java 45 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/controller/ZxmxManagerController.java 39 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/service/ZxmxManagerService.java 700 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/util/DateUtil.java 955 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/util/XmlUtils.java 95 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/vo/UserInfoVo.java 62 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/vo/req/ZxmxGetScoreReqVo.java 69 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/vo/resp/ZxmxGetScoreRespVo.java 125 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/src/main/resources/application.properties 2 ●●●●● patch | view | raw | blame | history
cmci-pfcs-gateway/src/main/resources/logback.xml 72 ●●●●● patch | view | raw | blame | history
.gitignore
New file
@@ -0,0 +1,6 @@
*.iml
*.class
target/
.idea/
*.prefs
*.log
cmci-pfcs-gateway/pom.xml
New file
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.jttech.zxmx.plat</groupId>
    <artifactId>zxmx-plat-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cmci-pfcs-gateway</name>
    <description>Jwssw Simple Service</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <skipTests>true</skipTests>
    </properties>
    <dependencies>
        <!-- 节选 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j2 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jdom</groupId>
            <artifactId>jdom</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.60</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
    </build>
</project>
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/Application.java
New file
@@ -0,0 +1,24 @@
package com.jttech.cmci.pfcs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * 启动类
 *
 * @author carsonwang
 * @version 1.0
 * @date 2022/4/6 18:00
 * @since JDK 1.8
 */
@SpringBootApplication
public class Application {
    /**
     * simple service 启动入口
     *
     * @param args 参数集
     */
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/content/ZxmxContent.java
New file
@@ -0,0 +1,45 @@
package com.jttech.cmci.pfcs.content;
/**
 * @Description:常量
 * @Author:carsonwang
 * @Date:Created in 2022-04-06 16:16
 * @Version: 1.0
 */
public class ZxmxContent {
    /**
     * ""
     */
    public static final String EMPTY_STR               = "";
    /**
     * 逗号
     */
    public final static String SIGN_COMMA              = ",";
    /**
     * 竖线
     */
    public final static String VERTICAL_LINE           = "\\|";
    public final static String reportMessage           = "ReportMessage";
    public final static String QueryInfo               = "QueryInfo";
    public final static String QueryDate               = "QueryDate";
    public final static String creditTranDetailsInfo   = "CreditTranDetailsInfo";
    public final static String beRecoveryDetailsInfo   = "BeRecoveryDetailsInfo";
    public final static String baseInfo                = "BaseInfo";
    public final static String money                   = "Money";
    public final static String nonrevolvingLoanDetInfo = "NonrevolvingLoanDetInfo";
    public final static String latestMonPerfmInfo      = "LatestMonPerfmInfo";
    public final static String currentOverdueTotal     = "CurrentOverdueTotal";
    public final static String avgUsedOfSixMonth       = "AvgUsedOfSixMonth";
    public final static String LoopQuotaSubAct         = "LoopQuotaSubAct";
    public final static String RevolvinLoanAct         = "RevolvinLoanAct";
    public final static String CreditCardAct           = "CreditCardAct";
    public final static String SemiCreditCardAct       = "SemiCreditCardAct";
    public final static String Lines                   = "Lines";
    public final static String Last5YearsHisPerFmInfo  = "Last5YearsHisPerFmInfo";
    public final static String Content                 = "Content";
    public final static String ReportHead              = "ReportHead";
    public final static String ThisQueryReqInfo        = "ThisQueryReqInfo";
    public final static String QueryName               = "QueryName";
    public final static String QueryCredNum            = "QueryCredNum";
}
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/controller/ZxmxManagerController.java
New file
@@ -0,0 +1,39 @@
package com.jttech.cmci.pfcs.controller;
import com.jttech.cmci.pfcs.service.ZxmxManagerService;
import com.jttech.cmci.pfcs.vo.req.ZxmxGetScoreReqVo;
import com.jttech.cmci.pfcs.vo.resp.ZxmxGetScoreRespVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
 * 征信模型
 *
 * @author carsonwang
 * @version 1.0
 * @date 2022/4/6 18:00
 * @since JDK 1.8
 */
@RestController
@RequestMapping("/zxmx")
public class ZxmxManagerController {
    private Logger             mLogger = LoggerFactory.getLogger(getClass());
    @Autowired
    private ZxmxManagerService mZxmxManagerService;
    /**
     * 获取分数
     *
     * @return 当前环境
     */
    @PostMapping("/getScore")
    public @ResponseBody
    ZxmxGetScoreRespVo getScore(@RequestBody ZxmxGetScoreReqVo creditInfo) {
        mLogger.info("请求参数:{}", creditInfo);
        return mZxmxManagerService.getScore(creditInfo);
    }
}
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/service/ZxmxManagerService.java
New file
@@ -0,0 +1,700 @@
package com.jttech.cmci.pfcs.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jttech.cmci.pfcs.util.DateUtil;
import com.jttech.cmci.pfcs.vo.UserInfoVo;
import com.jttech.cmci.pfcs.vo.req.ZxmxGetScoreReqVo;
import com.jttech.cmci.pfcs.vo.resp.ZxmxGetScoreRespVo;
import com.jttech.cmci.pfcs.content.ZxmxContent;
import com.jttech.cmci.pfcs.util.XmlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.util.Date;
/**
 * @Description:征信模型加工
 * @Author:carsonwang
 * @Date:Created in 2022-04-06 14:39
 * @Version: 1.0
 */
@Service
public class ZxmxManagerService {
    private Logger mLogger = LoggerFactory.getLogger(getClass());
    public ZxmxGetScoreRespVo getScore(ZxmxGetScoreReqVo pReqVo) {
        mLogger.info("请求报文:{}", pReqVo);
        JSONObject creditInfo = XmlUtils.xml2Json(pReqVo.getZxData());
        creditInfo = creditInfo.getJSONObject(ZxmxContent.reportMessage);
        // 获取用户信息
        UserInfoVo userInfo = getUserInfo(creditInfo);
        mLogger.info("=================征信模型加工开始,当前用户为:{}=================", userInfo);
        // 获取逾期总金额(被追偿信息债权金额+非循环贷账户当前逾期总额+循环额度下分账户当前逾期总额+循环贷账户当前逾期总额+贷记卡账户当前逾期总额)
        Integer overdueTotal = getOverdueTotal(userInfo, creditInfo);
        // 获取贷记卡授信总额度(所有贷记卡授信金额求和)
        Integer creditLines = getCreditLines(userInfo, creditInfo);
        // 获取近六个月平均应还款额
        Integer avgUsedOfSixMonth = getAvgUsedOfSixMonth(userInfo, creditInfo);
        // 获取近一年逾期次数
        Integer overdueNumOfYear = getOverdueNumOfYear(userInfo, creditInfo);
        // 获取近一年查征次数
        Integer queryNumOfYear = getQueryNumOfYear(userInfo, creditInfo);
        ZxmxGetScoreRespVo respVo = new ZxmxGetScoreRespVo(overdueTotal, creditLines, avgUsedOfSixMonth,
                overdueNumOfYear, queryNumOfYear);
        mLogger.info("=================征信模型加工结束,当前用户为:{},加工结果为:{}=================", userInfo, respVo);
        return respVo;
    }
    /**
     * 获取用户信息
     *
     * @param pCreditInfo
     * @return
     */
    private UserInfoVo getUserInfo(JSONObject pCreditInfo) {
        JSONObject reportHead = getJsonObject(pCreditInfo, ZxmxContent.ReportHead);
        JSONObject thisQueryReqInfo = getJsonObject(reportHead, ZxmxContent.ThisQueryReqInfo);
        String username = thisQueryReqInfo.getString(ZxmxContent.QueryName);
        String idNo = thisQueryReqInfo.getString(ZxmxContent.QueryCredNum);
        UserInfoVo userInfo = new UserInfoVo(username, idNo);
        return userInfo;
    }
    /**
     * 近一年查征次数(根据查征记录中近一年查询次数计算)
     *
     * @param pCreditInfo
     * @return
     */
    private Integer getQueryNumOfYear(UserInfoVo userInfo, JSONObject pCreditInfo) {
        Integer totalQueryNumOfYear = 0;
        JSONArray queryInfos = pCreditInfo.getJSONArray(ZxmxContent.QueryInfo);
        if (!CollectionUtils.isEmpty(queryInfos)) {
            Date currTime = DateUtil.getDayEndTime(new Date());
            Date yearTime = DateUtil.addYear(currTime, -1);
            for (Object obj : queryInfos) {
                if (obj != null) {
                    JSONObject queryInfo = (JSONObject) obj;
                    String queryDateStr = queryInfo.getString(ZxmxContent.QueryDate);
                    if (!StringUtils.isEmpty(queryDateStr)) {
                        Date queryDate = DateUtil.parseDate(DateUtil.pattern[2], queryDateStr);
                        if (queryDate.after(yearTime) && queryDate.before(currTime)) {
                            totalQueryNumOfYear++;
                        }
                    }
                }
            }
        }
        // 获取近一年查征次数
        Integer queryNumOfYear = getQueryNumOfYear(totalQueryNumOfYear);
        mLogger.info("==========================用户:{},近一年查询征信总数为:{},评级为:{}=======================", userInfo,
                totalQueryNumOfYear, queryNumOfYear);
        return queryNumOfYear;
    }
    /**
     * 获取近一年查征次数 评级 0 0 1 (0,5] 2 (5,10] 3 (10,15] 4 (15,20] 5 (20,25] 6 (25,30]
     * 7 (30,35] 8 (35,40] 9 >40
     *
     * @param pTotalQueryNumOfYear
     * @return
     */
    private Integer getQueryNumOfYear(Integer pTotalQueryNumOfYear) {
        Integer queryNumOfYear = 0;
        if (pTotalQueryNumOfYear == 0) {
            queryNumOfYear = 0;
        } else if (pTotalQueryNumOfYear > 0 && pTotalQueryNumOfYear <= 5) {
            queryNumOfYear = 1;
        } else if (pTotalQueryNumOfYear > 5 && pTotalQueryNumOfYear <= 10) {
            queryNumOfYear = 2;
        } else if (pTotalQueryNumOfYear > 10 && pTotalQueryNumOfYear <= 15) {
            queryNumOfYear = 3;
        } else if (pTotalQueryNumOfYear > 15 && pTotalQueryNumOfYear <= 20) {
            queryNumOfYear = 4;
        } else if (pTotalQueryNumOfYear > 20 && pTotalQueryNumOfYear <= 25) {
            queryNumOfYear = 5;
        } else if (pTotalQueryNumOfYear > 25 && pTotalQueryNumOfYear <= 30) {
            queryNumOfYear = 6;
        } else if (pTotalQueryNumOfYear > 30 && pTotalQueryNumOfYear <= 35) {
            queryNumOfYear = 7;
        } else if (pTotalQueryNumOfYear > 35 && pTotalQueryNumOfYear <= 40) {
            queryNumOfYear = 8;
        } else if (pTotalQueryNumOfYear > 40) {
            queryNumOfYear = 9;
        }
        return queryNumOfYear;
    }
    /**
     * 近一年逾期次数("非循环贷账户/循环额度下分账户/循环贷账户/贷记卡账户/准贷记卡账户 账户中近一年还款月中为逾期的总次数")
     *
     * @param pCreditInfo
     * @return
     */
    private Integer getOverdueNumOfYear(UserInfoVo userInfo, JSONObject pCreditInfo) {
        JSONObject creditTranDetailsInfo = getJsonObject(pCreditInfo, ZxmxContent.creditTranDetailsInfo);
        if (creditTranDetailsInfo == null) {
            return -1;
        }
        // 6.4.2 非循环贷账户 D1【NonrevolvingLoanDetInfo】
        JSONArray nonrevolvingLoanDetInfos = creditTranDetailsInfo.getJSONArray(ZxmxContent.nonrevolvingLoanDetInfo);
        // 6.4.2.4 最近五年内历史表现信息【Last5YearsHisPerFmInfo】 content
        Integer nonrevolvingOverdueNum = 0;
        if (!CollectionUtils.isEmpty(nonrevolvingLoanDetInfos)) {
            for (Object obj : nonrevolvingLoanDetInfos) {
                if (obj != null) {
                    JSONObject nonrevolvingLoanDetInfo = (JSONObject) obj;
                    JSONObject last5YearsHisPerFmInfo = getJsonObject(nonrevolvingLoanDetInfo,
                            ZxmxContent.Last5YearsHisPerFmInfo);
                    if (last5YearsHisPerFmInfo != null) {
                        nonrevolvingOverdueNum = nonrevolvingOverdueNum + getOverdueNum(last5YearsHisPerFmInfo);
                    }
                }
            }
        }
        mLogger.info("非循环贷账户,最近五年内历史表现信息,逾期次数:{}", nonrevolvingOverdueNum);
        // 6.4.3 循环额度下分账户 R4【LoopQuotaSubAct】
        JSONArray loopQuotaSubActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.LoopQuotaSubAct);
        // 6.4.3.4最近五年内历史表现信息【Last5YearsHisPerfmInfo】 content
        Integer loopOverdueNum = 0;
        if (!CollectionUtils.isEmpty(loopQuotaSubActs)) {
            for (Object obj : loopQuotaSubActs) {
                if (obj != null) {
                    JSONObject loopQuotaSubAct = (JSONObject) obj;
                    JSONObject last5YearsHisPerFmInfo = getJsonObject(loopQuotaSubAct,
                            ZxmxContent.Last5YearsHisPerFmInfo);
                    if (last5YearsHisPerFmInfo != null) {
                        loopOverdueNum = loopOverdueNum + getOverdueNum(last5YearsHisPerFmInfo);
                    }
                }
            }
        }
        mLogger.info("循环额度下分账户,最近五年内历史表现信息,逾期次数:{}", loopOverdueNum);
        // 6.4.4 循环贷账户 R1【RevolvinLoanAct】
        JSONArray revolvinLoanActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.RevolvinLoanAct);
        // 6.4.4.4最近五年内历史表现信息【Last5YearsHisPerfmInfo】 content
        Integer revolvinOverdueNum = 0;
        if (!CollectionUtils.isEmpty(revolvinLoanActs)) {
            for (Object obj : revolvinLoanActs) {
                if (obj != null) {
                    JSONObject revolvinLoanAct = (JSONObject) obj;
                    JSONObject last5YearsHisPerFmInfo = getJsonObject(revolvinLoanAct,
                            ZxmxContent.Last5YearsHisPerFmInfo);
                    if (last5YearsHisPerFmInfo != null) {
                        revolvinOverdueNum = revolvinOverdueNum + getOverdueNum(last5YearsHisPerFmInfo);
                    }
                }
            }
        }
        mLogger.info("循环贷账户,最近五年内历史表现信息,逾期次数:{}", revolvinOverdueNum);
        // 6.4.5 贷记卡账户 R2【CreditCardAct】
        JSONArray creditCardActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.CreditCardAct);
        // 6.4.5.5最近五年内历史表现信息【Last5YearsHisPerfmInfo】 content
        Integer creditCardOverdueNum = 0;
        if (!CollectionUtils.isEmpty(creditCardActs)) {
            for (Object obj : creditCardActs) {
                if (obj != null) {
                    JSONObject creditCardAct = (JSONObject) obj;
                    JSONObject last5YearsHisPerFmInfo = getJsonObject(creditCardAct,
                            ZxmxContent.Last5YearsHisPerFmInfo);
                    if (last5YearsHisPerFmInfo != null) {
                        creditCardOverdueNum = creditCardOverdueNum + getOverdueNum(last5YearsHisPerFmInfo);
                    }
                }
            }
        }
        mLogger.info("贷记卡账户,最近五年内历史表现信息,逾期次数:{}", creditCardOverdueNum);
        // 6.4.6 准贷记卡账户 R3【SemiCreditCardAct】
        JSONArray semiCreditCardActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.SemiCreditCardAct);
        // 6.4.6.4最近五年内历史表现信息【Last5YearsHisPerfmInfo】 content
        Integer semiCreditCardOverdueNum = 0;
        if (!CollectionUtils.isEmpty(semiCreditCardActs)) {
            for (Object obj : semiCreditCardActs) {
                if (obj != null) {
                    JSONObject semiCreditCardAct = (JSONObject) obj;
                    JSONObject last5YearsHisPerFmInfo = getJsonObject(semiCreditCardAct,
                            ZxmxContent.Last5YearsHisPerFmInfo);
                    if (last5YearsHisPerFmInfo != null) {
                        semiCreditCardOverdueNum = semiCreditCardOverdueNum + getOverdueNum(last5YearsHisPerFmInfo);
                    }
                }
            }
        }
        mLogger.info("准贷记卡账户,最近五年内历史表现信息,逾期次数:{}", semiCreditCardOverdueNum);
        Integer totalOverdueNum = nonrevolvingOverdueNum + loopOverdueNum + revolvinOverdueNum + creditCardOverdueNum
                + semiCreditCardOverdueNum;
        Integer overdueNumOfYear = getOverdueNumOfYear(totalOverdueNum);
        mLogger.info("==========================用户:{},近一年逾期次数总数:{},评级为:{}=======================", userInfo,
                totalOverdueNum, overdueNumOfYear);
        return overdueNumOfYear;
    }
    /**
     * 近一年逾期次数 评级 0 0 1 (0,5] 2 (5,10] 3 (10,15] 4 (15,20] 5 (20,25] 6 (25,30] 7
     * (30,35] 8 (35,40] 9 >40
     *
     * @param pTotalOverdueNum
     * @return
     */
    private Integer getOverdueNumOfYear(Integer pTotalOverdueNum) {
        Integer overdueNumOfYear = 0;
        if (pTotalOverdueNum == 0) {
            overdueNumOfYear = 0;
        } else if (pTotalOverdueNum > 0 && pTotalOverdueNum <= 5) {
            overdueNumOfYear = 1;
        } else if (pTotalOverdueNum > 5 && pTotalOverdueNum <= 10) {
            overdueNumOfYear = 2;
        } else if (pTotalOverdueNum > 10 && pTotalOverdueNum <= 15) {
            overdueNumOfYear = 3;
        } else if (pTotalOverdueNum > 15 && pTotalOverdueNum <= 20) {
            overdueNumOfYear = 4;
        } else if (pTotalOverdueNum > 20 && pTotalOverdueNum <= 25) {
            overdueNumOfYear = 5;
        } else if (pTotalOverdueNum > 25 && pTotalOverdueNum <= 30) {
            overdueNumOfYear = 6;
        } else if (pTotalOverdueNum > 30 && pTotalOverdueNum <= 35) {
            overdueNumOfYear = 7;
        } else if (pTotalOverdueNum > 35 && pTotalOverdueNum <= 40) {
            overdueNumOfYear = 8;
        } else if (pTotalOverdueNum > 40) {
            overdueNumOfYear = 9;
        }
        return overdueNumOfYear;
    }
    /**
     * 各个账户明细还款记录,从最后一个还款日往前推12个还款日,还款记录  近一年非*且非n且非m且非c次数之和
     *
     * @param pLast5YearsHisPerFmInfo
     * @return
     */
    private Integer getOverdueNum(JSONObject pLast5YearsHisPerFmInfo) {
        Integer overdueNum = 0;
        String content = pLast5YearsHisPerFmInfo.getString(ZxmxContent.Content);
        if (!StringUtils.isEmpty(content)) {
            String[] repayInfos = content.split(ZxmxContent.VERTICAL_LINE);
            for (int i = 0; i < 12; i++) {
                // 如果索引大于长度,直接跳出循环
                if (i > repayInfos.length - 1) {
                    break;
                }
                String repayInfo = repayInfos[i];
                if (!(repayInfo.startsWith("*") || repayInfo.startsWith("N") || repayInfo.startsWith("M")
                        || repayInfo.startsWith("C"))) {
                    overdueNum++;
                }
            }
        }
        return overdueNum;
    }
    /**
     * 近六个月平均应还款额(信贷交易授信及负债信息概要
     * :非循环贷账户信息汇总-最近6个月平均应还款+循环额度下分账户信息汇总-最近6个月平均应还款+循环贷账户信息汇总-最近6个月平均应还+
     * 贷记卡账户信息汇总-最近6个月平均使用额度+准贷记卡账户信息汇总-最近6个月平均透支余额)
     *
     * @param pCreditInfo
     * @return
     */
    private Integer getAvgUsedOfSixMonth(UserInfoVo userInfo, JSONObject pCreditInfo) {
        JSONObject creditTranDetailsInfo = getJsonObject(pCreditInfo, ZxmxContent.creditTranDetailsInfo);
        if (creditTranDetailsInfo == null) {
            return -1;
        }
        // 非循环贷账户信息汇总 - 6.4.2.3 最近一次月度表现信息 最近 6 个月平均使用额度
        BigDecimal nonrevolvingAvgUsedOfSixMonth = BigDecimal.ZERO;
        JSONArray nonrevolvingLoanDetInfos = creditTranDetailsInfo.getJSONArray(ZxmxContent.nonrevolvingLoanDetInfo);
        if (!CollectionUtils.isEmpty(nonrevolvingLoanDetInfos)) {
            for (Object obj : nonrevolvingLoanDetInfos) {
                if (obj != null) {
                    JSONObject nonrevolvingLoanDetInfo = (JSONObject) obj;
                    JSONObject latestMonPerfmInfo = getJsonObject(nonrevolvingLoanDetInfo,
                            ZxmxContent.latestMonPerfmInfo);
                    if (latestMonPerfmInfo != null) {
                        nonrevolvingAvgUsedOfSixMonth = nonrevolvingAvgUsedOfSixMonth
                                .add(getAmount(latestMonPerfmInfo, ZxmxContent.avgUsedOfSixMonth));
                    }
                }
            }
        }
        mLogger.info("非循环贷账户信息汇总 - 最近一次月度表现信息 最近 6 个月平均使用额度:{}", nonrevolvingAvgUsedOfSixMonth);
        // 循环额度下分账户信息汇总 - 6.4.3.3 最近一次月度表现信息 最近 6 个月平均使用额度
        BigDecimal loopAvgUsedOfSixMonth = BigDecimal.ZERO;
        JSONArray loopQuotaSubActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.LoopQuotaSubAct);
        if (!CollectionUtils.isEmpty(loopQuotaSubActs)) {
            for (Object obj : loopQuotaSubActs) {
                if (obj != null) {
                    JSONObject loopQuotaSubAct = (JSONObject) obj;
                    JSONObject latestMonPerfmInfo = getJsonObject(loopQuotaSubAct, ZxmxContent.latestMonPerfmInfo);
                    if (latestMonPerfmInfo != null) {
                        loopAvgUsedOfSixMonth = loopAvgUsedOfSixMonth
                                .add(getAmount(latestMonPerfmInfo, ZxmxContent.avgUsedOfSixMonth));
                    }
                }
            }
        }
        mLogger.info("循环额度下分账户信息汇总 - 最近一次月度表现信息 最近 6 个月平均使用额度:{}", loopAvgUsedOfSixMonth);
        // 循环贷账户信息汇总 - 6.4.4.3 最近一次月度表现信息 最近 6 个月平均使用额度
        BigDecimal revolvinAvgUsedOfSixMonth = BigDecimal.ZERO;
        JSONArray revolvinLoanActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.RevolvinLoanAct);
        if (!CollectionUtils.isEmpty(revolvinLoanActs)) {
            for (Object obj : revolvinLoanActs) {
                if (obj != null) {
                    JSONObject revolvinLoanAct = (JSONObject) obj;
                    JSONObject latestMonPerfmInfo = getJsonObject(revolvinLoanAct, ZxmxContent.latestMonPerfmInfo);
                    if (latestMonPerfmInfo != null) {
                        revolvinAvgUsedOfSixMonth = revolvinAvgUsedOfSixMonth
                                .add(getAmount(latestMonPerfmInfo, ZxmxContent.avgUsedOfSixMonth));
                    }
                }
            }
        }
        mLogger.info("循环贷账户信息汇总 - 最近一次月度表现信息 最近 6 个月平均使用额度:{}", revolvinAvgUsedOfSixMonth);
        // 贷记卡账户信息汇总 - 6.4.5.3 最近一次月度表现信息 最近 6 个月平均使用额度
        BigDecimal creditCardAvgUsedOfSixMonth = BigDecimal.ZERO;
        JSONArray creditCardActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.CreditCardAct);
        if (!CollectionUtils.isEmpty(creditCardActs)) {
            for (Object obj : creditCardActs) {
                if (obj != null) {
                    JSONObject creditCardAct = (JSONObject) obj;
                    JSONObject latestMonPerfmInfo = getJsonObject(creditCardAct, ZxmxContent.latestMonPerfmInfo);
                    if (latestMonPerfmInfo != null) {
                        creditCardAvgUsedOfSixMonth = creditCardAvgUsedOfSixMonth
                                .add(getAmount(latestMonPerfmInfo, ZxmxContent.avgUsedOfSixMonth));
                    }
                }
            }
        }
        mLogger.info("贷记卡账户信息汇总 - 最近一次月度表现信息 最近 6 个月平均使用额度:{}", creditCardAvgUsedOfSixMonth);
        // 准贷记卡账户信息汇总 - 6.4.6.3 最近一次月度表现信息 最近 6 个月平均使用额度
        BigDecimal semiCreditCardAvgUsedOfSixMonth = BigDecimal.ZERO;
        JSONArray semiCreditCardActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.SemiCreditCardAct);
        if (!CollectionUtils.isEmpty(semiCreditCardActs)) {
            for (Object obj : semiCreditCardActs) {
                if (obj != null) {
                    JSONObject semiCreditCardAct = (JSONObject) obj;
                    JSONObject latestMonPerfmInfo = getJsonObject(semiCreditCardAct, ZxmxContent.latestMonPerfmInfo);
                    if (latestMonPerfmInfo != null) {
                        semiCreditCardAvgUsedOfSixMonth = semiCreditCardAvgUsedOfSixMonth
                                .add(getAmount(latestMonPerfmInfo, ZxmxContent.avgUsedOfSixMonth));
                    }
                }
            }
        }
        mLogger.info("准贷记卡账户信息汇总 - 最近一次月度表现信息 最近 6 个月平均使用额度:{}", semiCreditCardAvgUsedOfSixMonth);
        BigDecimal totalAvgUsedOfSixMonth = nonrevolvingAvgUsedOfSixMonth.add(loopAvgUsedOfSixMonth)
                .add(revolvinAvgUsedOfSixMonth).add(creditCardAvgUsedOfSixMonth).add(semiCreditCardAvgUsedOfSixMonth);
        // 获取近六个月平均应还款额
        Integer avgUsedOfSixMonth = getAvgUsedOfSixMonth(totalAvgUsedOfSixMonth);
        mLogger.info("==========================用户:{},近六个月平均应还款总额:{},评级为:{}======================", userInfo,
                totalAvgUsedOfSixMonth, avgUsedOfSixMonth);
        return avgUsedOfSixMonth;
    }
    /**
     * 获取近六个月平均应还款额评级 0 0 1 (0,10000) 2 (10000,20000] 3 (20000,30000] 4
     * (30000,40000] 5 (40000,50000] 6 (50000,60000] 7 (60000,70000] 8
     * (70000,80000] 9 >80000
     *
     * @param pTotalAvgUsedOfSixMonth
     * @return
     */
    private Integer getAvgUsedOfSixMonth(BigDecimal pTotalAvgUsedOfSixMonth) {
        Integer avgUsedOfSixMonth = 0;
        if (pTotalAvgUsedOfSixMonth.compareTo(BigDecimal.ZERO) == 0) {
            avgUsedOfSixMonth = 0;
        } else if (pTotalAvgUsedOfSixMonth.compareTo(BigDecimal.ZERO) > 0
                && pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("10000")) <= 0) {
            avgUsedOfSixMonth = 1;
        } else if (pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("10000")) > 0
                && pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("20000")) <= 0) {
            avgUsedOfSixMonth = 2;
        } else if (pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("20000")) > 0
                && pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("30000")) <= 0) {
            avgUsedOfSixMonth = 3;
        } else if (pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("30000")) > 0
                && pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("40000")) <= 0) {
            avgUsedOfSixMonth = 4;
        } else if (pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("40000")) > 0
                && pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("50000")) <= 0) {
            avgUsedOfSixMonth = 5;
        } else if (pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("50000")) > 0
                && pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("60000")) <= 0) {
            avgUsedOfSixMonth = 6;
        } else if (pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("60000")) > 0
                && pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("70000")) <= 0) {
            avgUsedOfSixMonth = 7;
        } else if (pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("70000")) > 0
                && pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("80000")) <= 0) {
            avgUsedOfSixMonth = 8;
        } else if (pTotalAvgUsedOfSixMonth.compareTo(new BigDecimal("80000")) > 0) {
            avgUsedOfSixMonth = 9;
        }
        return avgUsedOfSixMonth;
    }
    /**
     * 所有贷记卡授信金额求和
     *
     * @param pCreditInfo
     * @return
     */
    private Integer getCreditLines(UserInfoVo userInfo, JSONObject pCreditInfo) {
        JSONObject creditTranDetailsInfo = getJsonObject(pCreditInfo, ZxmxContent.creditTranDetailsInfo);
        if (creditTranDetailsInfo == null) {
            return -1;
        }
        BigDecimal lines = BigDecimal.ZERO;
        JSONArray creditCardActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.CreditCardAct);
        if (!CollectionUtils.isEmpty(creditCardActs)) {
            for (Object obj : creditCardActs) {
                JSONObject creditCardAct = (JSONObject) obj;
                JSONObject baseInfo = getJsonObject(creditCardAct, ZxmxContent.baseInfo);
                if (baseInfo != null) {
                    lines = lines.add(getAmount(baseInfo, ZxmxContent.Lines));
                }
            }
        }
        Integer creditLines = getCreditLines(lines);
        mLogger.info("==========================用户:{},所有贷记卡授信金额:{},评级为:{}========================", userInfo, lines,
                creditLines);
        return creditLines;
    }
    /**
     * 授信额度分级 0 0 1 (0,30000] 2 (30000,50000] 3 (50000,70000] 4 (70000,90000] 5
     * (90000,110000] 6 (110000,130000] 7 (130000,150000] 8 (150000,200000] 9
     * >200000
     *
     * @param lines
     * @return
     */
    private Integer getCreditLines(BigDecimal lines) {
        Integer overdueTotal = 0;
        if (lines.compareTo(BigDecimal.ZERO) == 0) {
            overdueTotal = 0;
        } else if (lines.compareTo(BigDecimal.ZERO) > 0 && lines.compareTo(new BigDecimal("30000")) <= 0) {
            overdueTotal = 1;
        } else if (lines.compareTo(new BigDecimal("30000")) > 0 && lines.compareTo(new BigDecimal("50000")) <= 0) {
            overdueTotal = 2;
        } else if (lines.compareTo(new BigDecimal("50000")) > 0 && lines.compareTo(new BigDecimal("70000")) <= 0) {
            overdueTotal = 3;
        } else if (lines.compareTo(new BigDecimal("70000")) > 0 && lines.compareTo(new BigDecimal("90000")) <= 0) {
            overdueTotal = 4;
        } else if (lines.compareTo(new BigDecimal("90000")) > 0 && lines.compareTo(new BigDecimal("110000")) <= 0) {
            overdueTotal = 5;
        } else if (lines.compareTo(new BigDecimal("110000")) > 0 && lines.compareTo(new BigDecimal("130000")) <= 0) {
            overdueTotal = 6;
        } else if (lines.compareTo(new BigDecimal("130000")) > 0 && lines.compareTo(new BigDecimal("150000")) <= 0) {
            overdueTotal = 7;
        } else if (lines.compareTo(new BigDecimal("150000")) > 0 && lines.compareTo(new BigDecimal("200000")) <= 0) {
            overdueTotal = 8;
        } else if (lines.compareTo(new BigDecimal("200000")) > 0) {
            overdueTotal = 9;
        }
        return overdueTotal;
    }
    /**
     * 获取逾期总金额(被追偿信息债权金额+非循环贷账户当前逾期总额+循环额度下分账户当前逾期总额+循环贷账户当前逾期总额+贷记卡账户当前逾期总额)
     *
     * @param pCreditInfo
     * @return
     */
    private Integer getOverdueTotal(UserInfoVo userInfo, JSONObject pCreditInfo) {
        JSONObject creditTranDetailsInfo = getJsonObject(pCreditInfo, ZxmxContent.creditTranDetailsInfo);
        if (creditTranDetailsInfo == null) {
            return -1;
        }
        // 6.4.1.1 基本信息【BaseInfo】 Money 债权金额
        BigDecimal money = BigDecimal.ZERO;
        JSONObject beRecoveryDetailsInfo = getJsonObject(creditTranDetailsInfo, ZxmxContent.beRecoveryDetailsInfo);
        if (beRecoveryDetailsInfo != null) {
            JSONObject baseInfo = getJsonObject(beRecoveryDetailsInfo, ZxmxContent.baseInfo);
            if (baseInfo != null) {
                money = getAmount(baseInfo, ZxmxContent.money);
            }
        }
        mLogger.info("被追偿信息债权金额,基本信息,债权金额:{}", money);
        // 6.4.2.3 最近一次月度表现信息 CurrentOverdueTotal
        BigDecimal nonrevolvingOverdueTotal = BigDecimal.ZERO;
        JSONArray nonrevolvingLoanDetInfos = creditTranDetailsInfo.getJSONArray(ZxmxContent.nonrevolvingLoanDetInfo);
        if (!CollectionUtils.isEmpty(nonrevolvingLoanDetInfos)) {
            for (Object obj : nonrevolvingLoanDetInfos) {
                if (obj != null) {
                    JSONObject nonrevolvingLoanDetInfo = (JSONObject) obj;
                    JSONObject latestMonPerfmInfo = getJsonObject(nonrevolvingLoanDetInfo,
                            ZxmxContent.latestMonPerfmInfo);
                    if (latestMonPerfmInfo != null) {
                        nonrevolvingOverdueTotal = nonrevolvingOverdueTotal
                                .add(getAmount(latestMonPerfmInfo, ZxmxContent.currentOverdueTotal));
                    }
                }
            }
        }
        mLogger.info("非循环贷账户,最近一次月度表现信息,当前逾期总额:{}", nonrevolvingOverdueTotal);
        // 6.4.3.3 最近一次月度表现信息 CurrentOverdueTotal
        BigDecimal loopOverdueTotal = BigDecimal.ZERO;
        JSONArray loopQuotaSubActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.LoopQuotaSubAct);
        if (!CollectionUtils.isEmpty(loopQuotaSubActs)) {
            for (Object obj : loopQuotaSubActs) {
                if (obj != null) {
                    JSONObject loopQuotaSubAct = (JSONObject) obj;
                    JSONObject latestMonPerfmInfo = getJsonObject(loopQuotaSubAct, ZxmxContent.latestMonPerfmInfo);
                    if (latestMonPerfmInfo != null) {
                        loopOverdueTotal = loopOverdueTotal
                                .add(getAmount(latestMonPerfmInfo, ZxmxContent.currentOverdueTotal));
                    }
                }
            }
        }
        mLogger.info("循环额度下分账户,最近一次月度表现信息,当前逾期总额:{}", loopOverdueTotal);
        // 6.4.4.3 最近一次月度表现信息 CurrentOverdueTotal
        BigDecimal revolvinOverdueTotal = BigDecimal.ZERO;
        JSONArray revolvinLoanActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.RevolvinLoanAct);
        if (!CollectionUtils.isEmpty(revolvinLoanActs)) {
            for (Object obj : revolvinLoanActs) {
                if (obj != null) {
                    JSONObject revolvinLoanAct = (JSONObject) obj;
                    JSONObject latestMonPerfmInfo = getJsonObject(revolvinLoanAct, ZxmxContent.latestMonPerfmInfo);
                    if (latestMonPerfmInfo != null) {
                        revolvinOverdueTotal = revolvinOverdueTotal
                                .add(getAmount(latestMonPerfmInfo, ZxmxContent.currentOverdueTotal));
                    }
                }
            }
        }
        mLogger.info("循环贷账户,最近一次月度表现信息,当前逾期总额:{}", revolvinOverdueTotal);
        // 6.4.5.3 最近一次月度表现信息 CurrentOverdueTotal
        BigDecimal creditCardOverdueTotal = BigDecimal.ZERO;
        JSONArray creditCardActs = creditTranDetailsInfo.getJSONArray(ZxmxContent.CreditCardAct);
        if (!CollectionUtils.isEmpty(creditCardActs)) {
            for (Object obj : creditCardActs) {
                if (obj != null) {
                    JSONObject creditCardAct = (JSONObject) obj;
                    JSONObject latestMonPerfmInfo = getJsonObject(creditCardAct, ZxmxContent.latestMonPerfmInfo);
                    if (latestMonPerfmInfo != null) {
                        creditCardOverdueTotal = creditCardOverdueTotal
                                .add(getAmount(latestMonPerfmInfo, ZxmxContent.currentOverdueTotal));
                    }
                }
            }
        }
        mLogger.info("贷记卡账户,最近一次月度表现信息,当前逾期总额:{}", creditCardOverdueTotal);
        BigDecimal totalOverdueTotal = money.add(nonrevolvingOverdueTotal).add(loopOverdueTotal)
                .add(revolvinOverdueTotal).add(creditCardOverdueTotal);
        Integer overdueTotal = getOverdueTotal(totalOverdueTotal);
        mLogger.info("==========================用户:{},获取逾期总金额:{},评级:{}==========================", userInfo,
                totalOverdueTotal, overdueTotal);
        return overdueTotal;
    }
    /**
     * 0 0 1 (0,10000) 2 (10000,20000] 3 (20000,30000] 4 (30000,40000] 5
     * (40000,50000] 6 (50000,60000] 7 (60000,70000] 8 (70000,80000] 9 >80000
     *
     * @param totalOverdueTotal
     * @return
     */
    private Integer getOverdueTotal(BigDecimal totalOverdueTotal) {
        Integer overdueTotal = 0;
        if (totalOverdueTotal.compareTo(BigDecimal.ZERO) == 0) {
            overdueTotal = 0;
        } else if (totalOverdueTotal.compareTo(BigDecimal.ZERO) > 0
                && totalOverdueTotal.compareTo(new BigDecimal("10000")) <= 0) {
            overdueTotal = 1;
        } else if (totalOverdueTotal.compareTo(new BigDecimal("10000")) > 0
                && totalOverdueTotal.compareTo(new BigDecimal("20000")) <= 0) {
            overdueTotal = 2;
        } else if (totalOverdueTotal.compareTo(new BigDecimal("20000")) > 0
                && totalOverdueTotal.compareTo(new BigDecimal("30000")) <= 0) {
            overdueTotal = 3;
        } else if (totalOverdueTotal.compareTo(new BigDecimal("30000")) > 0
                && totalOverdueTotal.compareTo(new BigDecimal("40000")) <= 0) {
            overdueTotal = 4;
        } else if (totalOverdueTotal.compareTo(new BigDecimal("40000")) > 0
                && totalOverdueTotal.compareTo(new BigDecimal("50000")) <= 0) {
            overdueTotal = 5;
        } else if (totalOverdueTotal.compareTo(new BigDecimal("50000")) > 0
                && totalOverdueTotal.compareTo(new BigDecimal("60000")) <= 0) {
            overdueTotal = 6;
        } else if (totalOverdueTotal.compareTo(new BigDecimal("60000")) > 0
                && totalOverdueTotal.compareTo(new BigDecimal("70000")) <= 0) {
            overdueTotal = 7;
        } else if (totalOverdueTotal.compareTo(new BigDecimal("70000")) > 0
                && totalOverdueTotal.compareTo(new BigDecimal("80000")) <= 0) {
            overdueTotal = 8;
        } else if (totalOverdueTotal.compareTo(new BigDecimal("80000")) > 0) {
            overdueTotal = 9;
        }
        return overdueTotal;
    }
    /**
     * 获取金额
     *
     * @param pJsonObject
     * @param pKeyName
     * @return
     */
    private BigDecimal getAmount(JSONObject pJsonObject, String pKeyName) {
        mLogger.info("获取参数:{},在对象:{}的值", pKeyName, pJsonObject);
        String num = pJsonObject.getString(pKeyName);
        if (StringUtils.isEmpty(num)) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(num.replaceAll(ZxmxContent.SIGN_COMMA, ZxmxContent.EMPTY_STR));
    }
    /**
     * 获取json对象
     *
     * @param pJsonObject
     * @param pKeyName
     * @return
     */
    private JSONObject getJsonObject(JSONObject pJsonObject, String pKeyName) {
        mLogger.info("获取参数:{},在对象:{}的值", pKeyName, pJsonObject);
        JSONArray jsonArray = pJsonObject.getJSONArray(pKeyName);
        if (CollectionUtils.isEmpty(jsonArray)) {
            return null;
        }
        return (JSONObject) jsonArray.get(0);
    }
}
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/util/DateUtil.java
New file
@@ -0,0 +1,955 @@
package com.jttech.cmci.pfcs.util;
import org.springframework.util.StringUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class DateUtil {
    public static final SimpleDateFormat         yyyyMMdd    = new SimpleDateFormat("yyyy-MM-dd");
    private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
                                                                 @Override
                                                                 protected SimpleDateFormat initialValue() {
                                                                     return new SimpleDateFormat("yyyy-MM-dd");
                                                                 }
                                                             };
    public static String[]                       pattern     = new String[] { "yyyyMMdd", "yyyy-MM-dd", "yyyy.MM.dd", "yyyy/MM/dd",
            "yyyy-MM", "yyyyMM", "yyyy/MM", "yyyyMMddHHmmss", "yyyy-MM-dd HH:mm:ss", "yyyy/MM/dd HH:mm:ss",
            "yyyyMMddHHmmssSSS", "HH:mm", "HHmmss", "MM-dd HH:mm", "YYYY-MM-dd HH:mm", "MM-dd HH:mm:ss" };
    public static Date previous(int days) {
        return new Date(System.currentTimeMillis() - days * 3600000L * 24L);
    }
    public static Date addDay(Date date, int days) {
        return new Date(date.getTime() + days * 3600000L * 24L);
    }
    public static Date addMin(Date date, int mins) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.MINUTE, mins);
        // System.out.println(c.getTime());
        return c.getTime();
    }
    public static Date addHour(Date date, int hours) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.HOUR_OF_DAY, hours);
        // System.out.println(c.getTime());
        return c.getTime();
    }
    public static String formatDateTime(String format, long d) {
        return new SimpleDateFormat(format).format(d);
    }
    public static String formatDate(String format, Date d) {
        return StringUtils.isEmpty(format) || null == d ? "" : new SimpleDateFormat(format).format(d);
    }
    public static String formatDate(Date d) {
        return formatDate(pattern[7], d);
    }
    public static Date parseDate(String format, String d) {
        try {
            return new SimpleDateFormat(format).parse(d);
        } catch (Exception e) {
            throw new RuntimeException("需要时间格式为:" + format);
        }
    }
    public static Date getNextWeekDay(int weekDay) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        int addDateNumber = 0;
        if (Calendar.SUNDAY == calendar.get(Calendar.DAY_OF_WEEK)) {
            if (Calendar.SUNDAY == weekDay) {
                addDateNumber = 7;
            }
        } else {
            addDateNumber = 7;
            if (Calendar.SUNDAY == weekDay) {
                addDateNumber = 14;
            }
        }
        calendar.add(Calendar.DATE, addDateNumber);
        calendar.set(Calendar.DAY_OF_WEEK, weekDay);
        return calendar.getTime();
    }
    public static Date getThisWeekMonday() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        int min = calendar.getActualMinimum(Calendar.DAY_OF_WEEK); // 获取周开始基准
        int current = calendar.get(Calendar.DAY_OF_WEEK); // 获取当天周内天数
        calendar.add(Calendar.DAY_OF_WEEK, min - current + 1); // 当天-基准,获取周开始日期
        return calendar.getTime();
    }
    /**
     * 获取所在日期周的星期一(周一到周日为一周)
     *
     * @return 日期所在周的星期一
     */
    public static Date getWeekMonday(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        int min = calendar.getActualMinimum(Calendar.DAY_OF_WEEK); // 获取周开始基准
        int current = calendar.get(Calendar.DAY_OF_WEEK); // 获取当天周内天数
        calendar.add(Calendar.DAY_OF_WEEK, min - current + 1 + (current == Calendar.SUNDAY ? -7 : 0)); // 当天-基准,获取周开始日期
        return calendar.getTime();
    }
    public static Date parse(String str) {
        try {
            return threadLocal.get().parse(str);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 获取字符串当月第一天,需要date(yyyy-MM-dd)
     *
     * @param date
     * @return
     */
    public static String getFirstDayOfMonth(String date) {
        return getFirstDayOfMonth(parse(date));
    }
    /**
     * 获取字符串当月最后一天
     *
     * @param date
     * @return
     */
    public static String getFirstDayOfMonth(String format, String date) {
        return getFirstDayOfMonth(parseDate(format, date));
    }
    /**
     * 获取字符串当月第一天,需要date(yyyy-MM-dd)
     *
     * @param date
     * @return
     */
    public static String getEndDayOfMonth(String date) {
        return getEndDayOfMonth(parse(date));
    }
    /**
     * 获取字符串当月最后一天
     *
     * @param date
     * @return
     */
    public static String getEndDayOfMonth(String format, String date) {
        return getEndDayOfMonth(parseDate(format, date));
    }
    /**
     * 获取字符串当月最后一天
     *
     * @param date
     * @return
     */
    public static Date getFirstDayDateOfMonth(String format, String date) {
        return getFirstDayDateOfMonth(parseDate(format, date));
    }
    /**
     * 获取字符串当月最后一天
     *
     * @param date
     * @return
     */
    public static Date getEndDayDateOfMonth(String format, String date) {
        return getEndDayDateOfMonth(parseDate(format, date));
    }
    public static String getFirstDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DATE, 1);
        return threadLocal.get().format(cal.getTime());
    }
    public static String getEndDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        return threadLocal.get().format(cal.getTime());
    }
    public static Date getFirstDayDateOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DATE, 1);
        return cal.getTime();
    }
    public static Date getEndDayDateOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        return cal.getTime();
    }
    public static Date addMonth(Date date, int count) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(2, count);
        return c.getTime();
    }
    public static Date addYear(Date date, int count) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.YEAR, count);
        return c.getTime();
    }
    public static String getCurrentDate() {
        return threadLocal.get().format(new Date());
    }
    public static Date getTodayDate() {
        try {
            return threadLocal.get().parse(getCurrentDate());
        } catch (ParseException e) {
            throw new RuntimeException();
        }
    }
    public static int getDayOfWeek() {
        Calendar cal = Calendar.getInstance();
        return cal.get(7);
    }
    public static int getDayOfWeek(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(7);
    }
    public static int getDayOfWeek(String date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(parse(date));
        return cal.get(7);
    }
    public static int getMaxDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.getActualMaximum(5);
    }
    public static String toString(Date date) {
        if (date == null)
            return "";
        else
            return threadLocal.get().format(date);
    }
    public static String toString(Date date, String format) {
        SimpleDateFormat t = new SimpleDateFormat(format);
        return t.format(date);
    }
    // 获取当前年
    public static String getYear() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy");
        return formatter.format(new Date());
    }
    // 获取年
    public static String getYear(Date date) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy");
        return formatter.format(date);
    }
    // 获取当年月
    public static String getMonth() {
        SimpleDateFormat formatter = new SimpleDateFormat("MM");
        return formatter.format(new Date());
    }
    // 获取当年月
    public static String getYearMonth() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMM");
        return formatter.format(new Date());
    }
    // 获取月
    public static String getMonth(Date date) {
        SimpleDateFormat formatter = new SimpleDateFormat("MM");
        return formatter.format(date);
    }
    // 获取当日
    public static String getDay() {
        SimpleDateFormat formatter = new SimpleDateFormat("dd");
        return formatter.format(new Date());
    }
    // 获取日
    public static String getDay(Date date) {
        SimpleDateFormat formatter = new SimpleDateFormat("dd");
        return formatter.format(date);
    }
    /**
     * 月
     *
     * @return
     */
    public static List<String> getMonthList() {
        List<String> list = new ArrayList<String>();
        list.add("01");
        list.add("02");
        list.add("03");
        list.add("04");
        list.add("05");
        list.add("06");
        list.add("07");
        list.add("08");
        list.add("09");
        list.add("10");
        list.add("11");
        list.add("12");
        return list;
    }
    // 计算相差时间
    public static double dateDiff(String startTime, String endTime, String format) {
        SimpleDateFormat sd = new SimpleDateFormat(format);
        long nd = 1000 * 24 * 60 * 60;// 一天的毫秒数
        long nh = 1000 * 60 * 60;// 一小时的毫秒数
        long diff;
        double hour = 0;
        try {
            // 获得两个时间的毫秒时间差异
            diff = sd.parse(endTime).getTime() - sd.parse(startTime).getTime();
            hour = (diff % nd) * 1.0f / nh;// 计算差多少小时
        } catch (ParseException e) {
            e.printStackTrace();
        }
        // DecimalFormat df = new DecimalFormat("#.00");
        System.out.println(new Double(String.format("%.2f", hour)));
        return new Double(String.format("%.2f", hour));
    }
    // 计算相差天数时间
    public static int dateDayDiff(String startTime, String endTime, String format) {
        SimpleDateFormat sd = new SimpleDateFormat(format);
        long nd = 1000 * 24 * 60 * 60;// 一天的毫秒数
        long diff;
        int day = 0;
        try {
            // 获得两个时间的毫秒时间差异
            diff = sd.parse(endTime).getTime() - sd.parse(startTime).getTime();
            day = (int) (diff / nd);// 计算差多少天
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return day;
    }
    public static List<WeekDay> getDisplayWeekDays(Date startTime, Date endTime) {
        List<WeekDay> weekDays = new ArrayList<WeekDay>();
        Date date = startTime;
        long endTimeLong = endTime.getTime();
        while (date.getTime() <= endTimeLong) {
            weekDays.add(new WeekDay(date));
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            calendar.add(Calendar.DATE, 1);
            date = calendar.getTime();
        }
        return weekDays;
    }
    public static class WeekDay {
        private Date                  date;
        private static final String[] WEEK_DAY_NAME = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
        public WeekDay() {
        }
        public WeekDay(Date date) {
            this.setDate(date);
        }
        public Date getDate() {
            return date;
        }
        public void setDate(Date date) {
            this.date = date;
        }
        @SuppressWarnings("deprecation")
        @Override
        public String toString() {
            return threadLocal.get().format(date) + "(" + WEEK_DAY_NAME[date.getDay()] + ")";
        }
        @SuppressWarnings("deprecation")
        public String getDay() {
            return WEEK_DAY_NAME[date.getDay()];
        }
    }
    // 指定日期所在月的第一天日期
    public static Date getBeginDayOfMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
    // 获取指定日期的下个月的日期
    public static Date getNextBeginDayOfMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.MONTH, +1);
        return calendar.getTime();
    }
    // 指定日期所在年的第一天日期
    public static Date getBeginDayOfYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.MONTH, 0);
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
    public static Date getEndDayOfYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.MONTH, calendar.getActualMaximum(Calendar.MONTH));
        calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));
        calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMaximum(Calendar.HOUR_OF_DAY));
        calendar.set(Calendar.MINUTE, calendar.getActualMaximum(Calendar.MINUTE));
        calendar.set(Calendar.SECOND, calendar.getActualMaximum(Calendar.SECOND));
        calendar.set(Calendar.MILLISECOND, calendar.getActualMaximum(Calendar.MILLISECOND));
        return calendar.getTime();
    }
    // 获取指定日期的下年的日期
    public static Date getNextBeginDayOfYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.MONTH, 1);
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.YEAR, +1);
        return calendar.getTime();
    }
    /**
     * @param startDate
     * @param endDate
     * @return
     * @功能描述:
     *        <p>
     *        获取两个时间相差秒
     *        </p>
     * @创建作者: lance
     * @创建日期: 2016年12月12日 下午12:02:30
     */
    public static Integer getTimeSecond(Date startDate, Date endDate) {
        long start = startDate.getTime();
        long end = endDate.getTime();
        int second = (int) ((end - start) / 1000);
        return second;
    }
    /**
     * @param beginDate
     * @param endDate
     * @return
     * @功能描述:
     *        <p>
     *        获取两个日期之间的天数
     *        </p>
     * @创建作者: lance
     * @创建日期: 2017年1月6日 上午10:52:47
     */
    public static Integer getDays(String beginDate, String endDate) {
        SimpleDateFormat sd = new SimpleDateFormat(DateUtil.pattern[1]);
        try {
            return (int) ((sd.parse(endDate).getTime() - sd.parse(beginDate).getTime()) / 86400000);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * @param beginDate
     * @param endDate
     * @return
     * @功能描述:
     *        <p>
     *        获取两个日期之间的天数
     *        </p>
     * @创建作者: lance
     * @创建日期: 2017年1月6日 上午10:52:47
     */
    public static Integer getDays(Date beginDate, Date endDate) {
        return (int) ((getDayBeginTime(endDate).getTime() - getDayBeginTime(beginDate).getTime()) / 86400000);
    }
    /**
     * @param date
     * @return
     * @功能描述:
     *        <p>
     *        获取两个日期之间的天数
     *        </p>
     * @创建作者: lance
     * @创建日期: 2017年1月6日 上午10:52:47
     */
    public static Date getDayBeginTime(Date date) {
        try {
            String dateStr = threadLocal.get().format(date);
            return threadLocal.get().parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
    public static Integer getDayOfHour(Date date) {
        String hour = new SimpleDateFormat("HH").format(date);
        return Integer.valueOf(hour);
    }
    public static Integer getDayOfMinute(Date date) {
        String hour = new SimpleDateFormat("mm").format(date);
        return Integer.valueOf(hour);
    }
    public static Date getDayEndTime(Date date) {
        try {
            return new Date(getDayBeginTime(date).getTime() + 0x5265BFFL);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 获取当前时间距离当天结束的分钟数
     *
     * @param date
     * @return
     */
    public static Integer getDayEndMinute(Date date) {
        Date dayEndTime = getDayEndTime(date);
        Integer timeSecond = getTimeSecond(date, dayEndTime);
        return timeSecond / 60;
    }
    /**
     * 计算两点时间间隔年
     *
     * @param startTime
     * @param endTime
     * @param format
     * @return
     */
    public static int betweenTimesYear(String startTime, String endTime, String format) {
        SimpleDateFormat sd = new SimpleDateFormat(format);
        int year = 0;
        try {
            long end = sd.parse(endTime).getTime();
            long start = sd.parse(startTime).getTime();
            long oneYear = 1000l * 60 * 60 * 24 * 365;
            year = (int) ((end - start) / oneYear);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return year;
    }
    public static int getAgeByBirth(Date birthday) {
        int age = 0;
        try {
            Calendar now = Calendar.getInstance();
            now.setTime(new Date());// 当前时间
            Calendar birth = Calendar.getInstance();
            birth.setTime(birthday);
            if (birth.after(now)) {// 如果传入的时间,在当前时间的后面,返回0岁
                age = 0;
            } else {
                age = now.get(Calendar.YEAR) - birth.get(Calendar.YEAR);
                if (now.get(Calendar.DAY_OF_YEAR) < birth.get(Calendar.DAY_OF_YEAR)) {
                    age -= 1;
                }
            }
            return age;
        } catch (Exception e) {
            throw new RuntimeException("身份证号异常");
        }
    }
    /**
     * 获取精确到秒的时间戳
     *
     * @param pDate
     * @return
     */
    public static int getSecondTimestamp(Date pDate) {
        if (null == pDate) {
            return 0;
        }
        String timestamp = String.valueOf(pDate.getTime() / 1000);
        return Integer.valueOf(timestamp);
    }
    /**
     * 秒时间戳转成时间
     *
     * @param pSecondTimestamp
     * @return
     */
    public static Date parseSecondTimestamp(int pSecondTimestamp) {
        Date date = null;
        if (pSecondTimestamp != 0) {
            date = new Date(pSecondTimestamp * 1000L);
        }
        return date;
    }
    public static Date getLastQuarterStartTime() {
        Calendar startCalendar = Calendar.getInstance();
        startCalendar.set(Calendar.MONTH, (startCalendar.get(Calendar.MONTH) / 3 - 1) * 3);
        startCalendar.set(Calendar.DAY_OF_MONTH, 1);
        setMinTime(startCalendar);
        return startCalendar.getTime();
    }
    public static Date getLastQuarterEndTime() {
        Calendar endCalendar = Calendar.getInstance();
        endCalendar.set(Calendar.MONTH, (endCalendar.get(Calendar.MONTH) / 3 - 1) * 3 + 2);
        endCalendar.set(Calendar.DAY_OF_MONTH, endCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        setMaxTime(endCalendar);
        return endCalendar.getTime();
    }
    public static Date getQuarterStartTime(Integer year, Integer quarter) {
        Calendar quarterCalendar = Calendar.getInstance();
        quarterCalendar.set(Calendar.YEAR, year);
        switch (quarter) {
        case 1:
            quarterCalendar.set(Calendar.MONTH, 0);
            break;
        case 2:
            quarterCalendar.set(Calendar.MONTH, 3);
            break;
        case 3:
            quarterCalendar.set(Calendar.MONTH, 6);
            break;
        case 4:
            quarterCalendar.set(Calendar.MONTH, 9);
            break;
        default:
        }
        quarterCalendar.set(Calendar.DAY_OF_MONTH, 1);
        setMinTime(quarterCalendar);
        return quarterCalendar.getTime();
    }
    public static Date getQuarterEndTime(Integer year, Integer quarter) {
        Calendar quarterCalendar = Calendar.getInstance();
        quarterCalendar.set(Calendar.YEAR, year);
        switch (quarter) {
        case 1:
            quarterCalendar.set(Calendar.MONTH, 2);
            break;
        case 2:
            quarterCalendar.set(Calendar.MONTH, 5);
            break;
        case 3:
            quarterCalendar.set(Calendar.MONTH, 8);
            break;
        case 4:
            quarterCalendar.set(Calendar.MONTH, 11);
            break;
        default:
        }
        quarterCalendar.set(Calendar.DAY_OF_MONTH, quarterCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        setMaxTime(quarterCalendar);
        return quarterCalendar.getTime();
    }
    public static Date getMonthStartTime(Integer year, Integer month) {
        Calendar monthCalendar = Calendar.getInstance();
        monthCalendar.set(Calendar.YEAR, year);
        monthCalendar.set(Calendar.MONTH, month - 1);
        monthCalendar.set(Calendar.DAY_OF_MONTH, 1);
        setMinTime(monthCalendar);
        return monthCalendar.getTime();
    }
    public static Date getMonthEndTime(Integer year, Integer month) {
        Calendar monthCalendar = Calendar.getInstance();
        monthCalendar.set(Calendar.YEAR, year);
        monthCalendar.set(Calendar.MONTH, month - 1);
        monthCalendar.set(Calendar.DAY_OF_MONTH, monthCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        setMaxTime(monthCalendar);
        return monthCalendar.getTime();
    }
    public static Date getYearStartTime(Integer year) {
        Calendar quarterCalendar = Calendar.getInstance();
        quarterCalendar.set(Calendar.YEAR, year);
        quarterCalendar.set(Calendar.MONTH, 0);
        quarterCalendar.set(Calendar.DAY_OF_MONTH, 1);
        setMinTime(quarterCalendar);
        return quarterCalendar.getTime();
    }
    public static Date getYearEndTime(Integer year) {
        Calendar quarterCalendar = Calendar.getInstance();
        quarterCalendar.set(Calendar.YEAR, year);
        quarterCalendar.set(Calendar.MONTH, 11);
        quarterCalendar.set(Calendar.DAY_OF_MONTH, quarterCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        setMaxTime(quarterCalendar);
        return quarterCalendar.getTime();
    }
    public static Date getDayEndTimeWithoutMillisecond(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
    private static void setMinTime(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
    }
    private static void setMaxTime(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMaximum(Calendar.HOUR_OF_DAY));
        calendar.set(Calendar.MINUTE, calendar.getActualMaximum(Calendar.MINUTE));
        calendar.set(Calendar.SECOND, calendar.getActualMaximum(Calendar.SECOND));
        calendar.set(Calendar.MILLISECOND, calendar.getActualMaximum(Calendar.MILLISECOND));
    }
}
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/util/XmlUtils.java
New file
@@ -0,0 +1,95 @@
package com.jttech.cmci.pfcs.util;
import com.alibaba.fastjson.JSONObject;
import com.jttech.cmci.pfcs.vo.req.ZxmxGetScoreReqVo;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.springframework.util.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
/**
 * @author Skaði the Corrupting Heart
 * @version 1.0.0
 * @ClassName xml.java
 * @Description TODO
 * @createTime 2022年04月02日 17:12
 */
public class XmlUtils {
    public static JSONObject xml2Json(String xmlStr) {
        try {
            if (StringUtils.isEmpty(xmlStr)) {
                return null;
            }
            xmlStr = xmlStr.replaceAll("\\\n", "");
            byte[] xml = xmlStr.getBytes("UTF-8");
            JSONObject json = new JSONObject();
            InputStream is = new ByteArrayInputStream(xml);
            SAXBuilder sb = new SAXBuilder();
            Document doc = sb.build(is);
            Element root = doc.getRootElement();
            json.put(root.getName(), iterateElement(root));
            return json;
        } catch (Exception pE) {
            throw new RuntimeException("xml文件解析失败", pE);
        }
    }
    private static JSONObject iterateElement(Element element) {
        List<Element> node = element.getChildren();
        JSONObject obj = new JSONObject();
        List list = null;
        for (Element child : node) {
            list = new LinkedList();
            String text = child.getTextTrim();
            if (StringUtils.isEmpty(text)) {
                if (child.getChildren().size() == 0) {
                    continue;
                }
                if (obj.containsKey(child.getName())) {
                    list = (List) obj.get(child.getName());
                }
                list.add(iterateElement(child)); // 遍历child的子节点
                obj.put(child.getName(), list);
            } else {
                if (obj.containsKey(child.getName())) {
                    Object value = obj.get(child.getName());
                    try {
                        list = (List) value;
                    } catch (ClassCastException e) {
                        list.add(value);
                    }
                }
                if (child.getChildren().size() == 0) { // child无子节点时直接设置text
                    obj.put(child.getName(), text);
                } else {
                    list.add(text);
                    obj.put(child.getName(), list);
                }
            }
        }
        return obj;
    }
    public static void main(String[] args) {
        String a = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ReportMessage><ReportHead><ReportIdentInfo><ReportId>2022051909455761166981</ReportId><ReportTime>2022.05.19 09:45:57</ReportTime></ReportIdentInfo><ThisQueryReqInfo><QueryName>罗玉林</QueryName><QueryCertType>身份证</QueryCertType><QueryCredNum>522424200009281017</QueryCredNum><QueryOrg>深圳市中裔信息工程融资担保有限公司</QueryOrg><QueryReasonCode>担保资格审查</QueryReasonCode></ThisQueryReqInfo><ObjPromptMsg><Info>信息主体对信用报告内容提出了0笔异议且正在处理中,请浏览时注意阅读相关内容。</Info></ObjPromptMsg></ReportHead><BaseInfo><IdentityInfo><SurveyInfo><Gender>男</Gender><DateOfBirth>2000.09.28</DateOfBirth><MaritalStatus>未婚</MaritalStatus><Education>初中及以下</Education><Degree>其他</Degree><WorkStatus>--</WorkStatus><Citizenship>--</Citizenship><Email>--</Email><ContactAddress>广东揭阳揭东区磐东镇河中村新世纪商场附近</ContactAddress><ResidenceAddress>--</ResidenceAddress></SurveyInfo><PhoneInfo><No>1</No><Phone>17685170689</Phone><InfoUpdateDate>2018.10.04</InfoUpdateDate></PhoneInfo></IdentityInfo><ResideInfo><No>1</No><ResideAddr>广东揭阳揭东区磐东镇河中村新世纪商场附近</ResideAddr><ResidePhone>--</ResidePhone><ResideStatus>--</ResideStatus><InfoUpdateDate>2018.10.04</InfoUpdateDate></ResideInfo></BaseInfo><ReportSumamry><CreditTranPromptInfo><No>1</No><BusinessType>个人住房贷款</BusinessType><AccountNum>--</AccountNum><FirstBusinessReleaseMonth>--</FirstBusinessReleaseMonth></CreditTranPromptInfo><CreditTranPromptInfo><No>2</No><BusinessType>个人商用房贷款(包括商住两用房)</BusinessType><AccountNum>--</AccountNum><FirstBusinessReleaseMonth>--</FirstBusinessReleaseMonth></CreditTranPromptInfo><CreditTranPromptInfo><No>3</No><BusinessType>其他类贷款</BusinessType><AccountNum>1</AccountNum><FirstBusinessReleaseMonth>2018.10</FirstBusinessReleaseMonth></CreditTranPromptInfo><CreditTranPromptInfo><No>4</No><BusinessType>贷记卡</BusinessType><AccountNum>--</AccountNum><FirstBusinessReleaseMonth>--</FirstBusinessReleaseMonth></CreditTranPromptInfo><CreditTranPromptInfo><No>5</No><BusinessType>准贷记卡</BusinessType><AccountNum>--</AccountNum><FirstBusinessReleaseMonth>--</FirstBusinessReleaseMonth></CreditTranPromptInfo><CreditTranPromptInfo><No>6</No><BusinessType>--</BusinessType><AccountNum>--</AccountNum><FirstBusinessReleaseMonth>--</FirstBusinessReleaseMonth></CreditTranPromptInfo><CreditTranPromptInfo><No>7</No><BusinessType>合计</BusinessType><AccountNum>1</AccountNum><FirstBusinessReleaseMonth>--</FirstBusinessReleaseMonth></CreditTranPromptInfo><CreditDefaultSumInfo><BadDebtsSummInfo><AccountNum>1</AccountNum><Balance>1,919</Balance></BadDebtsSummInfo></CreditDefaultSumInfo><CreditTranAndDebtSummInfo><NonrevolvingLoanSummInfo><OrgNum>1</OrgNum><AccountNum>1</AccountNum><AuthorizationTotal>2,665</AuthorizationTotal><Balance>1,919</Balance><AvgRepaymentLastSixMonth>2,170</AvgRepaymentLastSixMonth></NonrevolvingLoanSummInfo><RevolvingLoanAccountSummInfo/></CreditTranAndDebtSummInfo><QueryRecordSummary><LastQueryRecordInfo><LastQueryDate>2022.04.12</LastQueryDate><LastQueryOrg>商业银行\"CW\"</LastQueryOrg><LastQueryReason>信用卡审批</LastQueryReason></LastQueryRecordInfo><QueryRecordSummInfo><NumQueryOrgLoanApprovalLastOneMonth>0</NumQueryOrgLoanApprovalLastOneMonth><NumQueryOrgCreditCardApprovalLastOneMonth>0</NumQueryOrgCreditCardApprovalLastOneMonth><NumQueryLoanApprovalLastOneMonth>0</NumQueryLoanApprovalLastOneMonth><NumQueryCreditCardApprovalLastOneMonth>0</NumQueryCreditCardApprovalLastOneMonth><NumQuerySelfQueryLastOneMonth>0</NumQuerySelfQueryLastOneMonth><NumQueryPostLoanManagementLastTwoYear>3</NumQueryPostLoanManagementLastTwoYear><NumQueryGuaranteeQualificationReviewLastTwoYear>0</NumQueryGuaranteeQualificationReviewLastTwoYear><NumQuerySpecialMerchantRealNameReviewLastTwoYear>0</NumQuerySpecialMerchantRealNameReviewLastTwoYear></QueryRecordSummInfo></QueryRecordSummary></ReportSumamry><CreditTranDetailsInfo><NonrevolvingLoanDetInfo><BaseInfo><CreditPrtlIdent>账户</CreditPrtlIdent><ManagmtOrg>消费金融公司\"YY\"</ManagmtOrg><AccountId>******</AccountId><IssuanceDate>2018.10.04</IssuanceDate><DueDate>2019.08.31</DueDate><Money>2,665</Money><Currency>人民币元</Currency><Type>其他个人消费贷款</Type><GuratMode>信用/免担保</GuratMode><RepayPerid>10</RepayPerid><RepayFrequency>月</RepayFrequency><RepayType>--</RepayType><CommonBwMark>无</CommonBwMark></BaseInfo><LatestPerformanceInfo><Title>截至2022年04月30日</Title><AccountStatus>呆账</AccountStatus><Balance>1,919</Balance><LastRepayDate>2019.02.20</LastRepayDate></LatestPerformanceInfo><Last5YearsHisPerFmInfo><Title>2018年10月 —2022年04月的还款记录</Title><Content>7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|7_2,170|6_1,856|5_0|4_1,232|3_920|2_608|1_296|N_0|N_0|N_0|*_0|</Content></Last5YearsHisPerFmInfo></NonrevolvingLoanDetInfo></CreditTranDetailsInfo><NonCreditTranDetailsInfo/><PublicInfo/><OpenInfo/><QueryInfo><No>1</No><QueryDate>2022.04.12</QueryDate><QueryOrg>商业银行\"CW\"</QueryOrg><Reason>信用卡审批</Reason></QueryInfo><QueryInfo><No>2</No><QueryDate>2020.10.29</QueryDate><QueryOrg>商业银行\"AV\"</QueryOrg><Reason>信用卡审批</Reason></QueryInfo><QueryInfo><No>3</No><QueryDate>2020.10.23</QueryDate><QueryOrg>小额贷款公司\"KU\"</QueryOrg><Reason>贷款审批</Reason></QueryInfo><QueryInfo><No>4</No><QueryDate>2020.10.07</QueryDate><QueryOrg>消费金融公司\"QY\"</QueryOrg><Reason>贷款审批</Reason></QueryInfo><RptExplain><Title>报告说明</Title><Content>1. 本报告中的“数字解读”仅供使用本信用报告的银行等授信机构参考,授信机构应自行承担使用“数字解读”的相关法律责任。2. “数字解读”将信用报告内容解读为一个数值,是对信用主体未来信贷违约可能性的预测,其取值范围为 0 到 1000,分值越高,违约可能性越低;“相对位置”是信用主体的数字解读值在全部人群中的百分比排序位置,比如“&gt;50%”代表该数字解读值高于 50%的信用主体;“说明”中的“影响因素”是影响信用主体获得更高数字解读值的原因,根据当前信用报告的实际情况给出,最多有两条。“数字解读”显示为“--”的,仅代表无法根据当前信用报告内容给出数字解读值,并无其他含义。无法给出数字解读值的具体原因见“说明” 。3. 本报告的信贷交易信息提示中,“业务类型”为“其他” 的汇总信息不包含“资产处置” 和“垫款” 业务。4. 本报告中如果没有“信贷交易违约信息概要”信息,说明信用主体最近 5 年内没有连续逾期。5. 对于存在授信限额的协议信息,信息主体的可用额度需结合“授信协议信息” 中的授信额度、 授信限额信息和余额进行估算。6. 本报告中的信贷交易授信及负债信息概要展示的信息是指未结清/未销户的授信及负债信息。7. 本报告的借贷交易明细信息中,循环贷账户的到期日期是指账户授信额度的到期日期。8. 本报告的借贷交易明细信息中,借贷账户展示最近 5 年的还款情况,包括当前还款状态和当前逾期总额。9.对于通过自助渠道办理的“小额、 高频” 业务,金融机构将合并报送相关账户,展示在本报告的借贷交易明细信息中; 此时账户的还款方式为“不区分还款方式”,该账户的还款频率统一约定为“月”,“还款期数”按月计算, 其还款信息按月进行观测和更新。10.本报告中的逾期准贷记卡账户是指该账户 60 天以上的透支行为。11.本报告中的还款期数为“--”是指该账户是非分期还款。12.本报告不展示 5 年前已经结束的违约行为,以及 5 年前的欠税记录、强制执行记录、民事判决记录、行政处罚记录、电信欠费记录。13.机构说明是数据提供机构对具体业务添加的特别说明信息。14.本人声明是信息主体对信用报告中的信息所附注的简要说明,信用主体对本人声明的真实性负责。15.异议标注是征信中心添加的,用于说明信用主体对信用报告中的哪些信息有异议。16.本报告内容涉及个人隐私,查询者应依法使用、妥善保管。因使用不当造成个人信息泄露的,征信中心将不承担相关责任。17.本报告中所有金额(除“有相关还款责任的企业借款”中的金额外)均为人民币金额,参照查询日前一天的汇。18.本报告整合了数据提供机构以信息主体不同证件报送的信息。</Content></RptExplain></ReportMessage>";
//        System.out.println(xml2Json(a));
        ZxmxGetScoreReqVo zxmxGetScoreReqVo = new ZxmxGetScoreReqVo();
        zxmxGetScoreReqVo.setZxData(a);
        System.out.println(JSONObject.toJSONString(zxmxGetScoreReqVo));
    }
}
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/vo/UserInfoVo.java
New file
@@ -0,0 +1,62 @@
package com.jttech.cmci.pfcs.vo;
/**
 * @Description:用户信息
 * @Author:carsonwang
 * @Date:Created in 2022-04-07 10:52
 * @Version: 1.0
 */
public class UserInfoVo {
    /**
     * 用户名
     */
    private String username;
    /**
     * 身份证号码
     */
    private String idNo;
    public String getUsername() {
        return username;
    }
    public void setUsername(String pUsername) {
        username = pUsername;
    }
    public String getIdNo() {
        return idNo;
    }
    public void setIdNo(String pIdNo) {
        idNo = pIdNo;
    }
    public UserInfoVo() {
    }
    public UserInfoVo(String pUsername, String pIdNo) {
        username = pUsername;
        idNo = pIdNo;
    }
    @Override
    public String toString() {
        return "{\"UserInfoVo\":{" + "\"username\":\"" + username + '\"' + ",\"idNo\":\"" + idNo + '\"' + "}}";
    }
}
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/vo/req/ZxmxGetScoreReqVo.java
New file
@@ -0,0 +1,69 @@
package com.jttech.cmci.pfcs.vo.req;
import java.io.Serializable;
/**
 * @Description:征信信息请求VO
 * @Author:carsonwang
 * @Date:Created in 2022-04-06 15:27
 * @Version: 1.0
 */
public class ZxmxGetScoreReqVo implements Serializable {
    /**
     * 征信数据
     */
    private String zxData;
    /**
     * 流水号
     */
    private String serialNum;
    /**
     * 请求编号
     */
    private String requestId;
    public String getZxData() {
        return zxData;
    }
    public void setZxData(String pZxData) {
        zxData = pZxData;
    }
    public String getSerialNum() {
        return serialNum;
    }
    public void setSerialNum(String pSerialNum) {
        serialNum = pSerialNum;
    }
    public String getRequestId() {
        return requestId;
    }
    public void setRequestId(String pRequestId) {
        requestId = pRequestId;
    }
    @Override
    public String toString() {
        return "{\"ZxmxGetScoreReqVo\":{" + "\"zxData\":\"" + zxData + '\"' + ",\"serialNum\":\"" + serialNum + '\"'
                + ",\"requestId\":\"" + requestId + '\"' + "}}";
    }
}
cmci-pfcs-gateway/src/main/java/com/jttech/cmci/pfcs/vo/resp/ZxmxGetScoreRespVo.java
New file
@@ -0,0 +1,125 @@
package com.jttech.cmci.pfcs.vo.resp;
import java.io.Serializable;
/**
 * @Description:征信信息请求VO
 * @Author:carsonwang
 * @Date:Created in 2022-04-06 15:27
 * @Version: 1.0
 */
public class ZxmxGetScoreRespVo implements Serializable {
    /**
     * 逾期总金额(被追偿信息债权金额+非循环贷账户当前逾期总额+循环额度下分账户当前逾期总额+循环贷账户当前逾期总额+贷记卡账户当前逾期总额)
     */
    private Integer overdueTotal      = 0;
    /**
     * 贷记卡授信总额度(所有贷记卡授信金额求和)
     */
    private Integer creditLines       = 0;
    /**
     * 近六个月平均应还款额(信贷交易授信及负债信息概要
     * :非循环贷账户信息汇总-最近6个月平均应还款+循环额度下分账户信息汇总-最近6个月平均应还款+循环贷账户信息汇总-最近6个月平均应还+
     * 贷记卡账户信息汇总-最近6个月平均使用额度+准贷记卡账户信息汇总-最近6个月平均透支余额)
     */
    private Integer avgUsedOfSixMonth = 0;
    /**
     * 近一年逾期次数("非循环贷账户/循环额度下分账户/循环贷账户/贷记卡账户/准贷记卡账户 账户中近一年还款月中为逾期的总次数")
     */
    private Integer overdueNumOfYear  = 0;
    /**
     * 近一年查征次数(根据查征记录中近一年查询次数计算)
     */
    private Integer queryNumOfYear    = 0;
    public Integer getOverdueTotal() {
        return overdueTotal;
    }
    public void setOverdueTotal(Integer pOverdueTotal) {
        overdueTotal = pOverdueTotal;
    }
    public Integer getCreditLines() {
        return creditLines;
    }
    public void setCreditLines(Integer pCreditLines) {
        creditLines = pCreditLines;
    }
    public Integer getAvgUsedOfSixMonth() {
        return avgUsedOfSixMonth;
    }
    public void setAvgUsedOfSixMonth(Integer pAvgUsedOfSixMonth) {
        avgUsedOfSixMonth = pAvgUsedOfSixMonth;
    }
    public Integer getOverdueNumOfYear() {
        return overdueNumOfYear;
    }
    public void setOverdueNumOfYear(Integer pOverdueNumOfYear) {
        overdueNumOfYear = pOverdueNumOfYear;
    }
    public Integer getQueryNumOfYear() {
        return queryNumOfYear;
    }
    public void setQueryNumOfYear(Integer pQueryNumOfYear) {
        queryNumOfYear = pQueryNumOfYear;
    }
    public ZxmxGetScoreRespVo() {
    }
    public ZxmxGetScoreRespVo(Integer pOverdueTotal, Integer pCreditLines, Integer pAvgUsedOfSixMonth,
            Integer pOverdueNumOfYear, Integer pQueryNumOfYear) {
        overdueTotal = pOverdueTotal;
        creditLines = pCreditLines;
        avgUsedOfSixMonth = pAvgUsedOfSixMonth;
        overdueNumOfYear = pOverdueNumOfYear;
        queryNumOfYear = pQueryNumOfYear;
    }
    @Override
    public String toString() {
        return "{\"ZxmxGetScoreRespVo\":{" + "\"overdueTotal\":" + overdueTotal + ",\"creditLines\":" + creditLines
                + ",\"avgUsedOfSixMonth\":" + avgUsedOfSixMonth + ",\"overdueNumOfYear\":" + overdueNumOfYear
                + ",\"queryNumOfYear\":" + queryNumOfYear + "}}";
    }
}
cmci-pfcs-gateway/src/main/resources/application.properties
New file
@@ -0,0 +1,2 @@
server.port=8400
logging.config=classpath:logback.xml
cmci-pfcs-gateway/src/main/resources/logback.xml
New file
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="WARN" monitorInterval="1800">
    <Properties>
        <!-- 日志默认存放的位置,这里设置为项目根路径下,也可指定绝对路径 -->
        <!-- ${web:rootDir}是web项目根路径,java项目没有这个变量,需要删掉,否则会报异常 -->
        <!--<property name="basePath">D://log4j2Logs</property>-->
<!--        <property name="basePath">/home/log/zxmx-plat</property>-->
        <property name="basePath">logs</property>
        <!-- 控制台默认输出格式,"%-5level":日志级别,"%l":输出完整的错误位置,是小写的L,因为有行号显示,所以影响日志输出的性能 -->
        <property name="console_log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %l - %m%n</property>
        <!-- 日志文件默认输出格式,不带行号输出(行号显示会影响日志输出性能);%C:大写,类名;%M:方法名;%m:错误信息;%n:换行 -->
        <property name="log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %C.%M - %m%n</property>
        <!-- 日志默认切割的最小单位 -->
        <property name="every_file_size">20MB</property>
        <!-- 日志默认输出级别 -->
        <property name="output_log_level">INFO</property>
        <!-- 日志默认存放路径(所有级别日志) -->
        <property name="rolling_fileName">${basePath}/log/zxmx-plat-gateway.log</property>
        <!-- 日志默认压缩路径,将超过指定文件大小的日志,自动存入按"年月"建立的文件夹下面并进行压缩,作为存档 -->
        <property name="rolling_filePattern">${basePath}/back/%d{yyyy-MM}/zxmx-plat-gateway-%d{yyyy-MM-dd}-%i.log.gz</property>
        <!-- 日志默认同类型日志,同一文件夹下可以存放的数量,不设置此属性则默认为7个 -->
        <property name="rolling_max">50</property>
        <!-- 控制台显示的日志最低级别 -->
        <property name="console_print_level">INFO</property>
    </Properties>
    <!--定义appender -->
    <appenders>
        <!-- 用来定义输出到控制台的配置 -->
        <Console name="Console" target="SYSTEM_OUT">
            <!-- 设置控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="${console_print_level}" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 设置输出格式,不设置默认为:%m%n -->
            <PatternLayout pattern="${console_log_pattern}"/>
        </Console>
        <!-- 打印root中指定的level级别以上的日志到文件 -->
        <RollingFile name="RollingFile" fileName="${rolling_fileName}" filePattern="${rolling_filePattern}">
            <PatternLayout pattern="${log_pattern}"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="${every_file_size}"/>
            </Policies>
            <!-- 设置同类型日志,同一文件夹下可以存放的数量,如果不设置此属性则默认存放7个文件 -->
            <DefaultRolloverStrategy max="${rolling_max}" />
            <!-- 匹配INFO以及以上级别 -->
            <Filters>
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingFile>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!-- 设置对打印sql语句的支持 -->
        <logger name="java.sql" level="info" additivity="false">
            <appender-ref ref="Console"/>
        </logger>
        <!--建立一个默认的root的logger-->
        <root level="${output_log_level}">
            <appender-ref ref="RollingFile"/>
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>