package com.nova.sankuai.infra.utils.zhd; import cn.hutool.core.util.IdUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.nova.sankuai.domain.api.hht.HhtIncomeResponse; import com.nova.sankuai.domain.api.zhd.ZhdResponse; import com.nova.sankuai.domain.entity.*; import com.nova.sankuai.domain.enums.capitalInfo.CapitalInfoTypeCodeEnum; import com.nova.sankuai.domain.vo.capitalresult.LvdiPushResultVo; import com.nova.sankuai.infra.config.CommonException; import com.nova.sankuai.infra.mapper.CustomerCapitalResultMapper; import com.nova.sankuai.infra.mapper.CustomerMapper; import com.nova.sankuai.infra.utils.ResultJson; import com.nova.sankuai.infra.utils.UserUtil; import com.nova.sankuai.security.HttpClientUtil; import com.nova.sankuai.security.UserDetail; import com.nova.sankuai.service.ICustomerScyIncomeService; import com.nova.sankuai.service.ICustomerUserService; import com.nova.sankuai.service.IPlatformCapitalService; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.DigestUtils; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.HttpServletRequest; import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.*; import static java.util.Map.Entry.comparingByKey; import static java.util.stream.Collectors.toMap; /** * 助贷对接 */ @Component @RequiredArgsConstructor public class ZhdUtil { private static final Logger log = LoggerFactory.getLogger("capitalLogger"); private static final String CHARSET_NAME = "utf8"; private static final Long capitalId = 22L; @Value("${zhd.zkUrl}") private String zkUrl; @Value("${zhd.jkUrl}") private String jkUrl; @Value("${zhd.channelKey}") private String channelKey; @Value("${zhd.secretKey}") private String secretKey; private String getJobAage(String workStartTime, String workEndTime) { if (StringUtils.isBlank(workStartTime)) {// 若为空,则不取该条数据 return ""; } workStartTime = workStartTime.substring(0, 10); LocalDate endDate = null; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); LocalDate startDate = LocalDate.parse(workStartTime, formatter); if (StringUtils.isNotBlank(workEndTime)) { workEndTime = workEndTime.substring(0, 10); endDate = LocalDate.parse(workEndTime, formatter); } else { endDate = LocalDate.now(); } long month = ChronoUnit.MONTHS.between(startDate, endDate); // F:0-6个月 G:6-12个月 H:12个月以上 if (month < 6) { return "F"; } else if (month >= 6 && month < 12) { return "G"; } return "H"; } private final CustomerMapper customerMapper; private final CustomerCapitalResultMapper capitalResultMapper; private final IPlatformCapitalService platformCapitalService; private final ICustomerUserService customerUserService; private final ICustomerScyIncomeService customerZcyIncomeService; private JSONObject getParam(Customer customer) { JSONObject param = new JSONObject(); /*申请人身份证*/ param.put("id_card_no", customer.getIdCard()); /*性别 SM:男 SW:女*/ String sex = customer.getSex(); param.put("sex", "男".equals(sex) ? 2 : 1); /*年龄 10-99的正整数。如34*/ Integer age = customer.getAge(); param.put("age", age.intValue()); /*申请金额 单位:万*/ param.put("demand_amount", customer.getLoanAmount() / 10000); /*贷款天数暂只支持传:30的倍数*/ param.put("demand_days", customer.getLoanTerm() / 30); /*公积金 100【无】;101【3个月以下】;102【3-6个月】;103【6个月以上】*/ Integer accumulation = customer.getProvidentFund(); int at = Objects.isNull(accumulation) ? 0 : accumulation.intValue(); int accu = 100; if (at == 1) accu = 101; else if (at == 2) accu = 102; else if (at == 3) accu = 103; param.put("fund", accu); /*社保: 200【无】;201【3个月以下】;202【3-6个月】;203【6个月以上】*/ Integer social = customer.getSocialSecurity(); int soc = Objects.isNull(social) ? 0 : social.intValue(); int soci = 200; if (soc == 1) { soci = 201; } else if (soc == 2) { soci = 202; } else if (soc == 3) { soci = 203; } param.put("social", soci); /* 保险情况 :300【无】;301【投保人寿险且保两年以下】;302【投保人寿险且保两年以上】*/ Integer insurancePolicy = customer.getInsurancePolicy(); int ipy = Objects.isNull(insurancePolicy) ? 0 : insurancePolicy.intValue(); int hasPolicy = 300; if (ipy == 6) { hasPolicy = 301; } else if (ipy == 7) { hasPolicy = 302; } param.put("insurance", hasPolicy); /*芝麻分 :500【无】;501【580分以下】;502【580-599分】;503【600-619分】;504【620-649分】;505【650-699分】;506【700分以上】;*/ Integer customerSesame = customer.getSesame(); int cus = Objects.isNull(customerSesame) ? 1 : customerSesame.intValue(); int sesame = 500;// 默认 1 无 if (cus == 2) { sesame = 504; } else if (cus == 3) { sesame = 505; } else if (cus == 4) { sesame = 506; } param.put("zhima_score", sesame); /* 信用卡额度 :600【无】;601【3000以下】;602【3000-1万】;603【1-3万】;604【3万以上】;上*/ Integer cc = customer.getCreditCard(); int ccV = Objects.isNull(cc) ? 0 : cc.intValue(); int creditCard = 600; if (ccV == 3) { creditCard = 602; } else if (ccV == 4) { creditCard = 603; } else if (ccV == 5) { creditCard = 604; } param.put("credit_card_limit", creditCard); /*微粒贷 :700【无】;701【3000以下】;702【3000-1万】;703【1-3万】;704【3万以上】;*/ param.put("wld", 700); /*花呗 :800【无】;801【3000以下】;802【3000-1万】;803【1-3万】;804【3万以上】;*/ param.put("huabei", 800); /*白条 :900【无】;901【3000以下】;902【3000-1万】;903【1-3万】;904【3万以上】;*/ param.put("baitiao", 900); /*教育 1000【初中】;1001【高中】;1002【中专】;1003【大专】;1004【本科】;1005【硕士】;1006【博士及以上】*/ String et = customer.getEducation(); int education = 1001; if ("01".equals(et)) { education = 1004; } else if ("02".equals(et)) { education = 1000; } else if ("03".equals(et)) { education = 1001; } else if ("04".equals(et)) { education = 1003; } else if ("05".equals(et)) { education = 1004; } else if ("06".equals(et)) { education = 1005; } else if ("07".equals(et)) { education = 1006; } param.put("education_level", education); /*职业 A:上班族 B:个体户 C:自由职业 BS:企业主*/ Integer pi = customer.getProfessionInfo(); int professionInfo = 1;// 默认自由职业 int job = 1103; if (!Objects.isNull(pi)) { professionInfo = pi.intValue(); } if (professionInfo == 0) { job = 1100; } else if (professionInfo == 1) { job = 1103; } else if (professionInfo == 2) { job = 1101; } else if (professionInfo == 3) { job = 1102; } param.put("career", job); /*营业执照 : 1200【无】;1201【有】*/ param.put("business_license", 1200); /*信用逾期情况:1300【无信用卡或贷款】;1301【1年内逾期超过3次或者90天】;1302【1年内逾期少于3次且少于90天】;1303【信用良好,无逾期】;1304【近一年无逾期】*/ param.put("credit_card_overdue", 1300); /* house int 否 1 1400:无房产 1401:有按揭商品房 1402:有全款商品房 1403:有自建房 1404: 仅外地有房 */ Integer houseStatus = customer.getHouseStatus(); int hs = Objects.isNull(houseStatus) ? 0 : houseStatus.intValue(); int house = 1400; if (hs == 6) { house = 1404; } else if (hs == 5) { house = 1402; } else if (hs == 4) { house = 1401; } param.put("house", house); /* car int 否 1 车产类型 (1500无 1502全款 1501按揭) */ Integer carStatus = customer.getCarStatus(); int cs = Objects.isNull(carStatus) ? 0 : carStatus.intValue(); int car = 1500; if (cs == 4) {// 有按揭车 car = 1501; } else if (cs == 5) {// 全款车 car = 1502; } param.put("car", car); /* 工资/收入发放方式: 1900【无】;1901【现金发放】;1902【转账工资】;1903【银行代发】 */ param.put("monthly_income_type", 1902); /*月收入 单位:元*/ param.put("monthly_income", customer.getMonthlyIncome()); /*公司名称*/ param.put("company_name", customer.getCompanyName()); /* 城市 不带市 例如:北京 上海*/ param.put("city_name", customer.getCityName()); /*唯一订单号 最大长度64*/ param.put("order_sn", UUID.randomUUID().toString()); return param; } /** * 中诚易数据推送到助贷操作 */ public boolean zcyPushToZhd(Customer customer, String logNumber) { boolean isSucc = false; log.info("日志编号:" + logNumber + ",手机号:{},中诚易数据推送到助贷流程开始", customer.getPhone()); try { JSONObject param = getParam(customer); isSucc = push(customer, param, logNumber); Integer zhd = 0; if (!isSucc) { log.error("日志编号:" + logNumber + ",手机号:{},中诚易数据推送到助贷失败", customer.getPhone()); } else { zhd = 1; } saveStatus(customer, zhd); } catch (Exception ex) { log.error("日志编号:" + logNumber + ",手机号:{},中诚易数据推送到助贷失败", customer.getPhone()); } return isSucc; } public LvdiPushResultVo zhdPush(CustomerUser customerUser, String channelCode) { String logNumber = IdUtil.randomUUID(); log.info("日志编号:" + logNumber + "助贷 channelCode:" + channelCode + " 推送流程开始"); Customer customer = customerMapper.getLatestCustomerRecord(customerUser.getPhone()); CustomerCapitalResult capitalResult = capitalResultMapper.selectOne(new LambdaQueryWrapper() .eq(CustomerCapitalResult::getCustomerUserId, customerUser.getId()) .eq(CustomerCapitalResult::getCustomerRecordId, customer.getId()) .eq(CustomerCapitalResult::getCapitalInfoCode, CapitalInfoTypeCodeEnum.ZHD.getCode())); if (capitalResult == null) { log.info("日志编号:" + logNumber + " 手机号:" + customerUser.getPhone() + " 【助贷】数据库结果不存在, customerUser id:" + customerUser.getId() + " customer id:" + customer.getId()); throw new CommonException("资方对接结果不存在"); } JSONObject param = getParam(customer); // 开始撞库 boolean isSucc = push(customer, param, logNumber); if (!isSucc) { return null; } // 开始进件操作 // 转成 LvdiPushResultVo LvdiPushResultVo result = new LvdiPushResultVo(); result.setCheckResult(true); result.setUrlType("1");// APP下载链接 return result; } /** * 撞库 * @param customer * @param body * @param logNumber * @return */ private boolean push(Customer customer, JSONObject body, String logNumber) { // 撞库签名参数 String timestamp = String.valueOf(System.currentTimeMillis()); JSONObject jsonData = new JSONObject(); jsonData.put("channel", channelKey); jsonData.put("timestamp", timestamp); String signs = getSignContent(jsonData); jsonData.put("sign", DigestUtils.md5DigestAsHex(signs.getBytes())); /*申请人姓名的MD5*/ body.put("realname", DigestUtils.md5DigestAsHex(customer.getName().getBytes())); /* 申请人手机号的MD5*/ body.put("mobile", DigestUtils.md5DigestAsHex(customer.getPhone().getBytes())); jsonData.put("bizParam", encrypt(body.toJSONString())); String result = null; try { // 撞库 result = HttpClientUtil.getInstance().postJsonData(zkUrl, jsonData.toJSONString()); log.info("日志编号:" + logNumber + " 手机号为:" + customer.getPhone() + ",【助贷】撞库返回参数:{}", result); } catch (Exception ex) { log.error("日志编号:" + logNumber + " 手机号为:{},【助贷】撞库失败,传参:{} ex:{}", customer.getPhone(), jsonData, ex); } try { ZhdResponse res = JSON.parseObject(result, ZhdResponse.class); if (res.getStatus().intValue() == 1) { log.info("日志编号:" + logNumber + " 手机号为:" + customer.getPhone() + ",【助贷】推送成功"); // 解析 返回的signs String signsResult = JSON.toJSONString(res.getData());// 返回的结果转成字符串 PlatformCapital platformCapital = JSON.parseObject(signsResult, PlatformCapital.class); // 删除同一资方的同一用户数据 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(PlatformCapital::getCustomerId, customer.getId()); wrapper.eq(PlatformCapital::getCapitalId, capitalId); platformCapitalService.remove(wrapper); // 录入到数据库 platformCapital.setCustomerId(customer.getId()); platformCapital.setCapitalId(capitalId); platformCapital.setProtocolList(res.getData().getProductinfo().toString()); platformCapitalService.save(platformCapital); return true; } else { log.error("日志编号:" + logNumber + " 手机号为:{},【助贷】推送失败:{},jsonData:{}", customer.getPhone(), res.getInfo(),jsonData); } } catch (Exception ex) { log.error("日志编号:" + logNumber + " 手机号为:{},【助贷】推送原始返回参数转换失败,jsonData:{} ex:{}, res: {}", customer.getPhone(), jsonData , ex); } return false; } /** * 中诚易进件数据给助贷操作 */ public void zcyIncomeToZhd(Customer customer, String logNumber) { log.info("日志编号:" + logNumber + ",手机号:{},中诚易数据进件到助贷流程开始", customer.getPhone()); try { Integer zhd = 3; ResultJson rs = getPushResult(logNumber, customer); if (rs == null) { log.error("日志编号:" + logNumber + ",手机号:{},中诚易数据进件到助贷失败", customer.getPhone()); } else { zhd = 4; log.info("日志编号:" + logNumber + ",手机号:{},中诚易数据进件到助贷成功", customer.getPhone()); } saveStatus(customer, zhd); } catch (Exception ex) { log.error("日志编号:" + logNumber + ",手机号:{},中诚易数据进件到助贷失败", customer.getPhone()); } } private void saveStatus(Customer customer, Integer zhd) { // 录入或更新到customer_scy_income表 LambdaQueryWrapper wr = new LambdaQueryWrapper<>(); wr.eq(CustomerScyIncome::getCustomerZcyId, customer.getId()); wr.last(" limit 1 "); CustomerScyIncome csi = customerZcyIncomeService.getOne(wr); if (Objects.isNull(csi)) { csi = new CustomerScyIncome(); } csi.setCustomerZcyId(customer.getId()); csi.setZhd(zhd); customerZcyIncomeService.saveOrUpdate(csi); } /** * 进件(导单) */ public ResultJson income(HttpServletRequest request) { String logNumber = IdUtil.randomUUID(); log.info("日志编号:" + logNumber + " 【助贷】进件流程开始"); String token = request.getHeader("Authorization"); UserDetail user = UserUtil.getUser(token); if (user == null) { log.info("日志编号:" + logNumber + " 【助贷】进件失败,原因:用户Token无效"); throw new CommonException("用户Token无效"); } Customer customer = customerMapper.getLatestCustomerRecord(user.getPhone()); LambdaQueryWrapper wr = new LambdaQueryWrapper<>(); wr.eq(CustomerUser::getPhone, user.getPhone()); wr.eq(CustomerUser::getDeleteFlag, 0); wr.last(" limit 1 "); CustomerUser customerUser = customerUserService.getOne(wr); if (customerUser == null) { log.info("日志编号:" + logNumber + " 手机号:" + user.getPhone() + " 【助贷】进件失败,原因:用户不存在"); throw new CommonException("用户不存在"); } CustomerCapitalResult capitalResult = capitalResultMapper.selectOne(new LambdaQueryWrapper() .eq(CustomerCapitalResult::getCustomerUserId, customerUser.getId()) .eq(CustomerCapitalResult::getCustomerRecordId, customer.getId()) .eq(CustomerCapitalResult::getCapitalInfoCode, CapitalInfoTypeCodeEnum.ZHD.getCode())); if (capitalResult == null) { log.info("日志编号:" + logNumber + " 手机号:" + customerUser.getPhone() + "进件失败,原因:【【助贷】数据库结果不存在, customerUser id:" + customerUser.getId() + " customer id:" + customer.getId()); throw new CommonException("资方对接结果不存在"); } return getPushResult(logNumber, customer); } @Nullable private ResultJson getPushResult(String logNumber, Customer customer) { // 判断是否数据库中是否有数据,有过数据就不推送了 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(PlatformCapital::getCustomerId, customer.getId()); wrapper.eq(PlatformCapital::getCapitalId, capitalId); wrapper.last(" limit 1 "); PlatformCapital platformCapital = platformCapitalService.getOne(wrapper); if (Objects.isNull(platformCapital)) {// 若没有数据 log.info("日志编号:" + logNumber + " 手机号:" + customer.getPhone() + "进件失败,原因:【助贷】推送结果不存在, customer id:" + customer.getId()); throw new CommonException("资方推送结果不存在"); } if (platformCapital.getIncomeStatus() == 1) {// 已进件成功 log.info("日志编号:" + logNumber + " 手机号:" + customer.getPhone() + "进件失败,原因:【助贷】已经进件成功过,不再进件, customer id:" + customer.getId()); return null; } JSONObject param = getParam(customer); /*申请人姓名的MD5*/ param.put("realname", customer.getName()); /* 申请人手机号的MD5*/ param.put("mobile", customer.getPhone()); // 进件签名 String timestamp = String.valueOf(System.currentTimeMillis()); JSONObject jsonData = new JSONObject(); jsonData.put("channel", channelKey); jsonData.put("timestamp", timestamp); String signs = getSignContent(jsonData); jsonData.put("sign", DigestUtils.md5DigestAsHex(signs.getBytes())); jsonData.put("bizParam", encrypt(param.toJSONString())); log.info("日志编号:" + logNumber + " 手机号:" + customer.getPhone() + "开始进件给资方【助贷】,参数:{}", jsonData); String result = null; try { // 进件 result = HttpClientUtil.getInstance().postJsonData(jkUrl, jsonData.toJSONString()); log.info("日志编号:" + logNumber + " 手机号为:" + customer.getPhone() + ",【助贷】进件原始返回参数:{}", result); } catch (Exception ex) { log.error("日志编号:" + logNumber + " 手机号为:{},【助贷】进件失败,传参:{} ex:{}", customer.getPhone(), jsonData, ex); } if (StringUtils.isBlank(result)) { log.error("日志编号:" + logNumber + " 手机号为:{},【助贷】进件返回数据为空,传参:{} ", customer.getPhone(), jsonData); } try { ZhdResponse res = JSON.parseObject(result, ZhdResponse.class); if (res.getStatus() == 1) { log.info("日志编号:" + logNumber + " 手机号为:" + customer.getPhone() + ",【助贷】进件成功"); // 更新数据库 platformCapital.setIncomeStatus(1); platformCapitalService.updateById(platformCapital); LvdiPushResultVo rs = new LvdiPushResultVo(); rs.setCheckResult(true); rs.setUrlType("1");// APP下载链接 return ResultJson.ok(rs); } else { log.error("日志编号:" + logNumber + " 手机号为:{},【助贷】进件失败:{},jsonData:{}", customer.getPhone(), res.getInfo(),jsonData); } } catch (Exception ex) { log.error("日志编号:" + logNumber + " 手机号为:{},【助贷】进件原始返回参数转换失败,jsonData:{} ex:{}", customer.getPhone(), jsonData , ex); } return null; } /** * 加密 * @param content 输入 * @return {@code String} */ private String encrypt(String content) { byte[] originalContent = content.getBytes(StandardCharsets.UTF_8); byte[] encryptKey = secretKey.getBytes(); try { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");; SecretKeySpec skeySpec = new SecretKeySpec(encryptKey, "AES"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec); byte[] encrypted = cipher.doFinal(originalContent); return Base64.getEncoder().encodeToString(encrypted); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 要加密的对象拼接成字符串 * 如:channel=xyzxyzxyz,timestamp=1555378976238,secretKey=307C6ED8946DE351; * 拼接后的字符串:str = xyzxyzxyz&1555378976238&307C6ED8946DE351; * @param json * @return */ private String getSignContent(JSONObject json) { Map map = new HashMap<>(json.size()); json.forEach((s, v) -> { if (!"sign".equals(s)) { map.put(s, v); } }); // 按值排序 升序 Map sortMap = map .entrySet() .stream() .sorted(comparingByKey()) .collect( toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2, LinkedHashMap::new)); sortMap.put("secretKey", secretKey); StringBuilder sb = new StringBuilder(); sortMap.forEach((key, value) -> { sb.append(value).append("&"); }); String result = sb.toString(); if (result.length() > 2) { result = result.substring(0, result.length() - 1); } return result; } }