|
|
@@ -687,11 +687,11 @@ public class HuaNongCrawlerRequestImpl implements HuaNongCrawlerRequest {
|
|
|
String ciPolicyNumber = huaNongCrawlerOrder.getCiPolicyNumber();
|
|
|
String biPolicyNumber = huaNongCrawlerOrder.getBiPolicyNumber();
|
|
|
|
|
|
- // 获取支付链接
|
|
|
+ /* // 获取支付链接
|
|
|
String payUrl = huaNongCrawlerRequestComponent.getPayUrl(ciPolicyNumber, biPolicyNumber, username);
|
|
|
- log.info("支付链接获取完成: {}", payUrl);
|
|
|
+ log.info("支付链接获取完成: {}", payUrl);*/
|
|
|
// 返回转人工结果
|
|
|
- return convertToUnderwritingResultsVo(manualSubmit, payUrl);
|
|
|
+ return convertToUnderwritingResultsVo(submit, "");
|
|
|
}
|
|
|
|
|
|
// 正常核保:获取支付链接
|
|
|
@@ -782,7 +782,9 @@ public class HuaNongCrawlerRequestImpl implements HuaNongCrawlerRequest {
|
|
|
underwritingResultsVo.setSyApplyNo(contractNo);
|
|
|
}
|
|
|
// 状态转换:以第一个为准
|
|
|
- if (underwritingResultsVo.getUnderwritingStatus() == null) {
|
|
|
+ if ("9".equals(status)) {
|
|
|
+ underwritingResultsVo.setUnderwritingStatus(InsOrderStatusEnum.STAFF_PROCESS);
|
|
|
+ }else if("8".equals(status)){
|
|
|
underwritingResultsVo.setUnderwritingStatus(InsOrderStatusEnum.PAY_PENDING);
|
|
|
}
|
|
|
// 拼接核保意见
|
|
|
@@ -966,20 +968,309 @@ public class HuaNongCrawlerRequestImpl implements HuaNongCrawlerRequest {
|
|
|
return downloadPolicyResultVo;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 查询华农订单状态。
|
|
|
+ *
|
|
|
+ * <p>先通过 FindBusinesses 定位当前投保单;待缴费状态直接生成支付链接,
|
|
|
+ * 其他状态继续查询核保意见,并转换为系统统一订单状态。</p>
|
|
|
+ *
|
|
|
+ * @param orderStatusVo 订单状态查询参数
|
|
|
+ * @return 系统统一订单状态、投保单号、保单号及审核信息
|
|
|
+ */
|
|
|
@Override
|
|
|
public OrderStatusResultVo queryOrderState(OrderStatusVo orderStatusVo) {
|
|
|
- LoginInfoBO loginInfoDto = getLoginInfoDto(orderStatusVo.getOrder().getAttributionInformationVo());
|
|
|
- HttpHeaders httpHeaders = new HttpHeaders();
|
|
|
+ // 查询依赖订单号和归属账号,缺少任一关键参数都无法调用华农接口
|
|
|
+ if (orderStatusVo == null || orderStatusVo.getOrder() == null
|
|
|
+ || orderStatusVo.getOrder().getAttributionInformationVo() == null
|
|
|
+ || !StringUtils.hasText(orderStatusVo.getOrder().getOrdersNo())) {
|
|
|
+ throw new SystemException("华农订单状态查询参数不完整");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取华农登录信息和本地持久化的投保单、保单数据
|
|
|
+ AttributionInformationVo attributionInformationVo = orderStatusVo.getOrder().getAttributionInformationVo();
|
|
|
+ LoginInfoBO loginInfoDto = getLoginInfoDto(attributionInformationVo);
|
|
|
HuaNongCrawlerOrder order = huaNongCrawlerOrderService.getById(orderStatusVo.getOrder().getOrdersNo());
|
|
|
- String policyNo = StringUtils.hasText(order.getBiPolicyNumber()) ? order.getBiPolicyNumber() : order.getCiPolicyNumber();
|
|
|
- HuaNongCrawlerFindBusinessesRequestRequest huaNongCrawlerFindBusinessesRequest
|
|
|
- = new HuaNongCrawlerFindBusinessesRequestRequest(loginInfoDto.getToken(), loginInfoDto.getLoginComCode(), policyNo, order.getRecordingTime());
|
|
|
- HuaNongCrawlerFindBusinessesResponse businesses = huaNongCrawlerRequestComponent.findBusinesses(huaNongCrawlerFindBusinessesRequest, httpHeaders);
|
|
|
- OrderStatusResultVo vo = new OrderStatusResultVo();
|
|
|
- vo.setOrderStatus(InsOrderStatusEnum.UNDERWRITED);
|
|
|
- vo.setJqPolicyno(order.getCiPolicyNumber());
|
|
|
- vo.setJyPolicyno(order.getNiInfos().get(0).getNiPolicyNumber());
|
|
|
- return vo;
|
|
|
+ if (order == null) {
|
|
|
+ throw new SystemException("未找到华农本地订单信息");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 先按字段真实语义回填三类投保单号和已存在的保单号
|
|
|
+ OrderStatusResultVo result = buildOrderStatusResult(order);
|
|
|
+
|
|
|
+ // FindBusinesses 每次按一个投保单号查询,优先商业险,其次交强险和非车险
|
|
|
+ String niPolicyNumber = getFirstNiNumber(order.getNiInfos(), false);
|
|
|
+ String proposalNo = firstNonBlank(order.getBiPolicyNumber(), order.getCiPolicyNumber(), niPolicyNumber);
|
|
|
+ if (!StringUtils.hasText(proposalNo)) {
|
|
|
+ setOrderStatus(result, InsOrderStatusEnum.AUDIT_ING);
|
|
|
+ result.setErrorMsg("华农订单状态查询失败:本地未保存投保单号");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // comCode 使用归属机构编码;未配置时回退到登录机构编码
|
|
|
+ String comCode = loginInfoDto.getParameters() != null
|
|
|
+ ? loginInfoDto.getParameters().getComCode() : null;
|
|
|
+ if (!StringUtils.hasText(comCode)) {
|
|
|
+ comCode = loginInfoDto.getLoginComCode();
|
|
|
+ }
|
|
|
+ // 按 Python 逻辑使用投保单模式查询未完成业务:businessType=2、queryStatus=0
|
|
|
+ HuaNongCrawlerFindBusinessesRequestRequest findRequest =
|
|
|
+ HuaNongCrawlerFindBusinessesRequestRequest.forProposalStatus(
|
|
|
+ loginInfoDto.getToken(), loginInfoDto.getLoginComCode(), comCode,
|
|
|
+ proposalNo, order.getRecordingTime());
|
|
|
+ HuaNongCrawlerFindBusinessesResponse businesses =
|
|
|
+ huaNongCrawlerRequestComponent.findBusinesses(findRequest, new HttpHeaders());
|
|
|
+
|
|
|
+ // 响应可能包含多条业务,必须用本地交强、商业、非车投保单号精确匹配当前订单
|
|
|
+ Set<String> proposalNumbers = collectProposalNumbers(order);
|
|
|
+ HuaNongCrawlerFindBusinessesResponse.BusinessDTO matchedBusiness =
|
|
|
+ findMatchedBusiness(businesses, proposalNumbers);
|
|
|
+ if (matchedBusiness == null) {
|
|
|
+ setOrderStatus(result, InsOrderStatusEnum.AUDIT_ING);
|
|
|
+ result.setErrorMsg(businesses == null || businesses.getBusiness() == null
|
|
|
+ || businesses.getBusiness().isEmpty()
|
|
|
+ ? "华农订单状态查询失败:未返回业务记录"
|
|
|
+ : "华农订单状态查询失败:返回记录未匹配本地投保单号");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 华农业务状态8表示核保已通过、可以缴费,不代表已经生成正式保单
|
|
|
+ if ("8".equals(matchedBusiness.getStatus())) {
|
|
|
+ setOrderStatus(result, InsOrderStatusEnum.PAY_PENDING);
|
|
|
+ result.setPaymentLink(buildPayUrl(order, attributionInformationVo.getUsername()));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 非待缴费状态继续查询核保意见;与 Python 一致,仅提交交强和商业投保单号
|
|
|
+ HuaNongCrawlerQueryReviveOpinionRequest opinionRequest =
|
|
|
+ new HuaNongCrawlerQueryReviveOpinionRequest(
|
|
|
+ loginInfoDto.getToken(), loginInfoDto.getLoginComCode(),
|
|
|
+ order.getCiPolicyNumber(), order.getBiPolicyNumber());
|
|
|
+ if (opinionRequest.getContractMain().isEmpty()) {
|
|
|
+ setOrderStatus(result, InsOrderStatusEnum.STAFF_PROCESS);
|
|
|
+ result.setErrorMsg("审核中:缺少可用于核保意见查询的车险投保单号");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ HuaNongCrawlerQueryReviveOpinionResponse opinionResponse =
|
|
|
+ huaNongCrawlerRequestComponent.queryReviveOpinion(opinionRequest, new HttpHeaders());
|
|
|
+
|
|
|
+ // flag=1 表示核保退回或失败,优先级高于其他审核结果
|
|
|
+ Optional<HuaNongCrawlerQueryReviveOpinionResponse.OpinionDTO> rejectedOpinion =
|
|
|
+ findOpinion(opinionResponse, "1");
|
|
|
+ if (rejectedOpinion.isPresent()) {
|
|
|
+ setOrderStatus(result, InsOrderStatusEnum.AUDIT_FAIL);
|
|
|
+ result.setErrorMsg(formatOpinion(rejectedOpinion.get(), true));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // flag=0 表示需要人工处理,保留审核人员返回的详细信息
|
|
|
+ Optional<HuaNongCrawlerQueryReviveOpinionResponse.OpinionDTO> manualOpinion =
|
|
|
+ findOpinion(opinionResponse, "0");
|
|
|
+ if (manualOpinion.isPresent()) {
|
|
|
+ setOrderStatus(result, InsOrderStatusEnum.STAFF_PROCESS);
|
|
|
+ result.setErrorMsg(formatOpinion(manualOpinion.get(), false));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 未返回 flag 时,按 Python 规则同时检查 nodeStatus=0 和“同意承保”
|
|
|
+ if (isUnderwritingApproved(opinionResponse)) {
|
|
|
+ setOrderStatus(result, InsOrderStatusEnum.UNDERWRITED);
|
|
|
+ result.setPaymentLink(buildPayUrl(order, attributionInformationVo.getUsername()));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 没有明确失败、人工或通过标记时,订单仍处于核保处理中
|
|
|
+ setOrderStatus(result, InsOrderStatusEnum.AUDIT_ING);
|
|
|
+ result.setErrorMsg("审核中");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用本地订单初始化状态查询结果,严格区分投保单号和正式保单号。
|
|
|
+ */
|
|
|
+ private OrderStatusResultVo buildOrderStatusResult(HuaNongCrawlerOrder order) {
|
|
|
+ OrderStatusResultVo result = new OrderStatusResultVo();
|
|
|
+ setOrderStatus(result, InsOrderStatusEnum.AUDIT_ING);
|
|
|
+ result.setJqApplyNo(normalize(order.getCiPolicyNumber()));
|
|
|
+ result.setSyApplyNo(normalize(order.getBiPolicyNumber()));
|
|
|
+ result.setJyApplyNo(getFirstNiNumber(order.getNiInfos(), false));
|
|
|
+ result.setJqPolicyno(normalize(order.getCiPolicyNo()));
|
|
|
+ result.setSyPolicyno(normalize(order.getBiPolicyNo()));
|
|
|
+ result.setJyPolicyno(getFirstNiNumber(order.getNiInfos(), true));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取第一条有效的非车险号码。
|
|
|
+ *
|
|
|
+ * @param niInfos 非车险信息列表
|
|
|
+ * @param policyNo true 获取正式保单号,false 获取投保单号
|
|
|
+ */
|
|
|
+ private String getFirstNiNumber(List<HuaNongCrawlerOrder.NiInfo> niInfos, boolean policyNo) {
|
|
|
+ if (niInfos == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return niInfos.stream()
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .map(niInfo -> policyNo ? niInfo.getNiPolicyNo() : niInfo.getNiPolicyNumber())
|
|
|
+ .map(this::normalize)
|
|
|
+ .filter(StringUtils::hasText)
|
|
|
+ .findFirst()
|
|
|
+ .orElse(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 收集当前订单的全部投保单号,用于从华农业务列表中精确定位订单。
|
|
|
+ */
|
|
|
+ private Set<String> collectProposalNumbers(HuaNongCrawlerOrder order) {
|
|
|
+ Set<String> proposalNumbers = new LinkedHashSet<>();
|
|
|
+ addIfHasText(proposalNumbers, order.getCiPolicyNumber());
|
|
|
+ addIfHasText(proposalNumbers, order.getBiPolicyNumber());
|
|
|
+ if (order.getNiInfos() != null) {
|
|
|
+ order.getNiInfos().stream()
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .map(HuaNongCrawlerOrder.NiInfo::getNiPolicyNumber)
|
|
|
+ .forEach(number -> addIfHasText(proposalNumbers, number));
|
|
|
+ }
|
|
|
+ return proposalNumbers;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 标准化号码并加入集合,统一过滤 null、空串和重复号码。
|
|
|
+ */
|
|
|
+ private void addIfHasText(Set<String> numbers, String number) {
|
|
|
+ String normalized = normalize(number);
|
|
|
+ if (StringUtils.hasText(normalized)) {
|
|
|
+ numbers.add(normalized);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按华农响应顺序查找第一条与本地投保单号匹配的业务记录。
|
|
|
+ */
|
|
|
+ private HuaNongCrawlerFindBusinessesResponse.BusinessDTO findMatchedBusiness(
|
|
|
+ HuaNongCrawlerFindBusinessesResponse response, Set<String> proposalNumbers) {
|
|
|
+ if (response == null || response.getBusiness() == null || proposalNumbers.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ for (HuaNongCrawlerFindBusinessesResponse.BusinessDTO business : response.getBusiness()) {
|
|
|
+ if (business != null && isBusinessMatched(business, proposalNumbers)) {
|
|
|
+ return business;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查华农业务记录的 contractNo1 至 contractNo6 是否命中本地投保单号。
|
|
|
+ */
|
|
|
+ private boolean isBusinessMatched(HuaNongCrawlerFindBusinessesResponse.BusinessDTO business,
|
|
|
+ Set<String> proposalNumbers) {
|
|
|
+ return Arrays.asList(
|
|
|
+ business.getContractNo1(), business.getContractNo2(), business.getContractNo3(),
|
|
|
+ business.getContractNo4(), business.getContractNo5(), business.getContractNo6())
|
|
|
+ .stream()
|
|
|
+ .map(this::normalize)
|
|
|
+ .filter(StringUtils::hasText)
|
|
|
+ .anyMatch(proposalNumbers::contains);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据华农审核标志查找第一条意见:1 为失败,0 为人工处理。
|
|
|
+ */
|
|
|
+ private Optional<HuaNongCrawlerQueryReviveOpinionResponse.OpinionDTO> findOpinion(
|
|
|
+ HuaNongCrawlerQueryReviveOpinionResponse response, String flag) {
|
|
|
+ if (response == null) {
|
|
|
+ return Optional.empty();
|
|
|
+ }
|
|
|
+ return response.getOpinions().stream()
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .filter(opinion -> flag.equals(opinion.getFlag()))
|
|
|
+ .findFirst();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将核保意见转换为前端可直接展示的多行中文信息。
|
|
|
+ */
|
|
|
+ private String formatOpinion(HuaNongCrawlerQueryReviveOpinionResponse.OpinionDTO opinion,
|
|
|
+ boolean includeGrade) {
|
|
|
+ StringBuilder message = new StringBuilder();
|
|
|
+ appendOpinionLine(message, "审核时间", opinion.getSHTime());
|
|
|
+ appendOpinionLine(message, "审核信息", opinion.getNotionCode());
|
|
|
+ if (includeGrade) {
|
|
|
+ appendOpinionLine(message, "审核级别名称", opinion.getCurrentGradeName());
|
|
|
+ }
|
|
|
+ appendOpinionLine(message, "操作员名称", opinion.getOperateUserName());
|
|
|
+ appendOpinionLine(message, "详细审核信息", opinion.getHandleText());
|
|
|
+ return message.length() > 0 ? message.toString() : "审核状态已更新";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 仅追加有值的审核字段,避免结果中出现空行或 null 文本。
|
|
|
+ */
|
|
|
+ private void appendOpinionLine(StringBuilder message, String label, String value) {
|
|
|
+ if (!StringUtils.hasText(value)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (message.length() > 0) {
|
|
|
+ message.append('\n');
|
|
|
+ }
|
|
|
+ message.append(label).append(':').append(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按 Python 判断规则确认核保通过:响应必须同时包含 nodeStatus=0 和“同意承保”。
|
|
|
+ */
|
|
|
+ private boolean isUnderwritingApproved(HuaNongCrawlerQueryReviveOpinionResponse response) {
|
|
|
+ if (response == null || !StringUtils.hasText(response.getRawResponseBody())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ String responseBody = response.getRawResponseBody();
|
|
|
+ boolean approvedNode = Pattern.compile("\\\"nodeStatus\\\"\\s*:\\s*\\\"?0\\\"?")
|
|
|
+ .matcher(responseBody)
|
|
|
+ .find();
|
|
|
+ return approvedNode && responseBody.contains("同意承保");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验支付链接必要参数后,复用组件生成与 Python 一致的 Base64 支付地址。
|
|
|
+ */
|
|
|
+ private String buildPayUrl(HuaNongCrawlerOrder order, String username) {
|
|
|
+ if (!StringUtils.hasText(username)) {
|
|
|
+ throw new SystemException("华农支付链接生成失败:操作员账号为空");
|
|
|
+ }
|
|
|
+ if (!StringUtils.hasText(order.getCiPolicyNumber())
|
|
|
+ && !StringUtils.hasText(order.getBiPolicyNumber())) {
|
|
|
+ throw new SystemException("华农支付链接生成失败:交强险和商业险投保单号均为空");
|
|
|
+ }
|
|
|
+ return huaNongCrawlerRequestComponent.getPayUrl(
|
|
|
+ order.getCiPolicyNumber(), order.getBiPolicyNumber(), username);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 同步设置枚举状态及其字符串编码,避免两个返回字段不一致。
|
|
|
+ */
|
|
|
+ private void setOrderStatus(OrderStatusResultVo result, InsOrderStatusEnum status) {
|
|
|
+ result.setOrderStatus(status);
|
|
|
+ result.setOrderStatusStr(status.getCode());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按业务优先级返回第一个非空号码。
|
|
|
+ */
|
|
|
+ private String firstNonBlank(String... values) {
|
|
|
+ return Arrays.stream(values)
|
|
|
+ .map(this::normalize)
|
|
|
+ .filter(StringUtils::hasText)
|
|
|
+ .findFirst()
|
|
|
+ .orElse(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 去除号码首尾空白,保留 null 语义供上层统一判空。
|
|
|
+ */
|
|
|
+ private String normalize(String value) {
|
|
|
+ return value == null ? null : value.trim();
|
|
|
}
|
|
|
|
|
|
/**
|