| | |
| | | import com.ruoyi.common.utils.HttpClientKit; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.bean.DtoConversionUtils; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.project.common.FundNotifyMessageBuilder; |
| | | import com.ruoyi.project.common.IdGeneratorUtils; |
| | | import com.ruoyi.project.domain.*; |
| | | import com.ruoyi.project.domain.dto.ServiceReimbursementDto; |
| | |
| | | import com.ruoyi.project.service.*; |
| | | import com.ruoyi.system.mapper.SysUserMapper; |
| | | import com.ruoyi.system.mapper.SysUserRoleMapper; |
| | | import com.ruoyi.system.service.ISysConfigService; |
| | | import com.ruoyi.system.service.ISysPostService; |
| | | import com.ruoyi.system.service.ISysUserService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | |
| | | @Value("${spring.profiles.active}") |
| | | private String active; |
| | | |
| | | @Autowired |
| | | private ISysConfigService sysConfigService; |
| | | |
| | | /** |
| | | * 审批单是否上传医院协友(致远)系统:0=不上传(测试环境),1=上传(正式环境)。 |
| | | * |
| | | * <p>2026-10-09 新增:原来用 {@code spring.profiles.active == druid} 来判断是否推送, |
| | | * 导致测试环境与正式环境无法通过参数区分。改为系统参数控制,环境各自配置。 |
| | | */ |
| | | private static final String CFG_UPLOAD_OA = "sys.approval.upload.oa"; |
| | | |
| | | /** 参数缺省值:上传(保持正式环境原有行为) */ |
| | | private static final boolean DEFAULT_UPLOAD_OA = true; |
| | | |
| | | /** 审批单是否需要上传医院协友(致远)系统 */ |
| | | private boolean isUploadOaEnabled() { |
| | | String value = null; |
| | | try { |
| | | value = sysConfigService.selectConfigByKey(CFG_UPLOAD_OA); |
| | | } catch (Exception e) { |
| | | log.warn("读取参数 {} 失败,按默认值 {} 处理", CFG_UPLOAD_OA, DEFAULT_UPLOAD_OA, e); |
| | | } |
| | | if (value == null || value.trim().isEmpty()) { |
| | | return DEFAULT_UPLOAD_OA; |
| | | } |
| | | String v = value.trim(); |
| | | return "1".equals(v) || "true".equalsIgnoreCase(v) || "y".equalsIgnoreCase(v) || "on".equalsIgnoreCase(v); |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | log.info("上传OA文件方法的入参:{}", serviceReimbursementdetailShareds.get(i)); |
| | | try { |
| | | //如果是本地环境,就不调这个了 |
| | | if (!active.equals("druid")) uploadOAFileAndUpdateDb(serviceReimbursementdetailShareds.get(i)); |
| | | if (isUploadOaEnabled()) uploadOAFileAndUpdateDb(serviceReimbursementdetailShareds.get(i)); |
| | | } catch (Exception e) { |
| | | log.error("Exception中reimShare的入参id为:{}", serviceReimbursementShared.getId()); |
| | | log.error("差旅附件上传医院协友系统失败:分享id={},明细={}(本次报销单上传整体失败,审批不生效)", |
| | | serviceReimbursementShared.getId(), serviceReimbursementdetailShareds.get(i), e); |
| | | if (serviceReimbursementShared.getId() != null) { |
| | | Boolean aBoolean = sharedService.delResharedInfoById(serviceReimbursementShared.getId()); |
| | | log.error("文件上传失败,将分享表数据回滚:{}", aBoolean); |
| | | log.error("已回滚分享表数据:{}", aBoolean); |
| | | } |
| | | e.getMessage(); |
| | | return false; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Map<String, String> checkFund(LoginUser loginUser, CheckFundVO checkFundVO) { |
| | | Map<String, String> allMap = new HashMap<>(); |
| | | |
| | |
| | | if (newSwitch == 1) { |
| | | aBoolean1 = sendData(serviceReimbursement); |
| | | if (!aBoolean1) { |
| | | allMap.put("500", "发送失败"); |
| | | return allMap; |
| | | // 推送医院协友系统失败:整个审批不生效(事务回滚),允许下次重新审批 |
| | | log.error("差旅审批单上传医院协友系统失败,审批不生效,id={},请重新审批", serviceReimbursement.getId()); |
| | | throw new ServiceException("医院协友系统接收失败,本次审批未生效,请稍后重新审批"); |
| | | } |
| | | } |
| | | //保存审批流程表 |
| | |
| | | serviceReimbursement.setFinancechecher(user.getNickName()); |
| | | } |
| | | |
| | | } else { |
| | | } //else { |
| | | CheckFlag = 2; |
| | | |
| | | if (CheckFlag == 2) { |
| | |
| | | } |
| | | } |
| | | SysUser sysUser = sysUserService.selectUserByUserName(serviceReimbursement.getUserno()); |
| | | ArrayList<ConcurrentHashMap<String, Object>> contentList = new ArrayList<>(); |
| | | ConcurrentHashMap map = new ConcurrentHashMap(); |
| | | map.put("审批人:", loginUser.getUser().getUserName()); |
| | | map.put("审批时间:", new Date()); |
| | | map.put("意见:", "不通过"); |
| | | if (StringUtils.isNotBlank(checkFundVO.getFlowcontent())) |
| | | map.put("意见:", checkFundVO.getFlowcontent()); |
| | | contentList.add(map); |
| | | //审批未通过:单据信息 + 出差信息 + 审批人/审批时间/意见(意见 = "审批未通过。" + 驳回意见) |
| | | List<ConcurrentHashMap<String, Object>> contentList = FundNotifyMessageBuilder.newRows(); |
| | | FundNotifyMessageBuilder.addBillInfo(contentList, "0", serviceReimbursement.getUsername(), |
| | | serviceReimbursement.getAmountrequested(), null, |
| | | FundNotifyMessageBuilder.billNo(serviceReimbursement.getBh(), |
| | | serviceReimbursement.getId())); |
| | | FundNotifyMessageBuilder.addTravelInfo(contentList, serviceReimbursement.getTravelers(), |
| | | serviceReimbursement.getReason()); |
| | | FundNotifyMessageBuilder.addAuditResult(contentList, loginUser.getUser().getNickName(), new Date(), |
| | | FundNotifyMessageBuilder.opinion(checkFundVO.getFlowconclusion()+"", |
| | | checkFundVO.getFlowcontent())); |
| | | try { |
| | | DingTalkReqVo dingTalkReqVo = new DingTalkReqVo(); |
| | | dingTalkReqVo.setTitle("财务系统驳回信息"); |
| | | dingTalkReqVo.setTitle(FundNotifyMessageBuilder.TITLE); |
| | | dingTalkReqVo.setNumber(sysUser.getPhonenumber()); |
| | | dingTalkReqVo.setContents(contentList); |
| | | dingTalkService.sendNotification(dingTalkReqVo); |
| | | }catch (Exception e){ |
| | | log.error("差旅---财务系统驳回信息报错了", e); |
| | | } catch (Exception e) { |
| | | log.error("差旅---审批未通过钉钉通知失败,id={}", serviceReimbursement.getId(), e); |
| | | } |
| | | } |
| | | //} |
| | | |
| | | serviceReimbursement.setRecordstatus(RecordStatus); |
| | | serviceReimbursement.setFlowlevel(Long.valueOf(FlowLevel.toString())); |
| | |
| | | |
| | | } |
| | | //办公室主任审批通过之后,就需要把“办公室主任”的名字填上 |
| | | SysUser sysUser = null; |
| | | //SysUser sysUser = null; |
| | | if (StringUtils.isNotEmpty(user.getUserName())) { |
| | | sysUser = sysUserMapper.selectUserByUserName(user.getUserName()); |
| | | log.info("差旅sysUsers返回的值为:{}", sysUser); |
| | | } |
| | | |
| | | if (checkFundVO.getFlowconclusion() == 1 && Objects.nonNull(sysUser) && sysUser.getRoles() != null && sysUser.getRoles().stream().anyMatch(role -> "officedirector".equals(role.getRoleKey()))) { |
| | | serviceReimbursement.setOfficedirector(user.getNickName()); |
| | | // 2026-10-09:推送条件由"当前审核人有 officedirector 角色"改为"本次审批后到达最后一级"。 |
| | | // 原写法在"最后一级审核人没有该角色"时永远不会推送,而"非最后一级的 officedirector"会提前推送。 |
| | | boolean uploadAtLastLevel = checkFundVO.getFlowconclusion() == 1 |
| | | && totalLevel != null && totalLevel != 0 && totalLevel == OriginalFlowLevel + 1; |
| | | if (uploadAtLastLevel) { |
| | | // 仍只在该角色审批时记录办公室主任,保持字段语义不变 |
| | | if (Objects.nonNull(sysUser) && sysUser.getRoles() != null |
| | | && sysUser.getRoles().stream().anyMatch(role -> "officedirector".equals(role.getRoleKey()))) { |
| | | serviceReimbursement.setOfficedirector(user.getNickName()); |
| | | } |
| | | serviceReimbursement.setUploadStates(1); |
| | | |
| | | Boolean aBoolean1 = null; |
| | |
| | | log.info("差旅newSwitch返回的值为:{}", newSwitch); |
| | | aBoolean1 = sendData(serviceReimbursement); |
| | | if (!aBoolean1) { |
| | | allMap.put("500", "发送失败"); |
| | | return allMap; |
| | | // 推送医院协友系统失败:整个审批不生效(事务回滚),允许下次重新审批 |
| | | log.error("差旅审批单上传医院协友系统失败,审批不生效,id={},请重新审批", serviceReimbursement.getId()); |
| | | throw new ServiceException("医院协友系统接收失败,本次审批未生效,请稍后重新审批"); |
| | | } |
| | | } |
| | | } |
| | |
| | | return "未找到审批记录"; |
| | | } |
| | | |
| | | /** |
| | | * 审批单上传医院协友(致远)系统。 |
| | | * |
| | | * <p>2026-10-09:外层包一层兜底,保证**任何异常都不会向外抛**(原来 strRes 为 null 时 |
| | | * 会在 parseObject 后取 code 触发 NPE,把整个审批请求打成 500)。 |
| | | * 返回 false 表示推送失败,调用方按"审批不生效、允许下次重新审批"处理。 |
| | | */ |
| | | public Boolean sendData(ServiceReimbursement serviceReimbursement) { |
| | | if (!isUploadOaEnabled()) { |
| | | // 上传开关关闭(如测试环境):跳过上传并视为成功,绝不能因此阻断审批 |
| | | log.info("审批单上传医院协友系统已关闭(sys.approval.upload.oa != 1),跳过上传并视为成功,id={}", |
| | | serviceReimbursement == null ? null : serviceReimbursement.getId()); |
| | | return true; |
| | | } |
| | | try { |
| | | return doSendData(serviceReimbursement); |
| | | } catch (Throwable t) { |
| | | log.error("差旅审批单上传医院协友系统异常,审批不生效,id={}", |
| | | serviceReimbursement == null ? null : serviceReimbursement.getId(), t); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private Boolean doSendData(ServiceReimbursement serviceReimbursement) { |
| | | // fund和funddetail的数据获取 |
| | | addSharedData(serviceReimbursement.getId()); |
| | | // 2026-10-09:接住 addSharedData 的失败信号(附件上传失败时它会回滚分享表并返回 false) |
| | | Boolean sharedOk = addSharedData(serviceReimbursement.getId()); |
| | | if (sharedOk == null || !sharedOk) { |
| | | log.error("差旅报销单上传失败:分享表/附件环节失败,审批不生效,id={}", serviceReimbursement.getId()); |
| | | return false; |
| | | } |
| | | List<ServiceReimbursementShared> serviceReimbursementSharedList = sharedService.getRemShareInfoByRemId(serviceReimbursement.getId()); |
| | | if (org.springframework.util.CollectionUtils.isEmpty(serviceReimbursementSharedList)) return false; |
| | | if (org.springframework.util.CollectionUtils.isEmpty(serviceReimbursementSharedList)) { |
| | | log.error("差旅报销单上传失败:分享表为空(附件或组装环节异常),审批不生效,id={}", serviceReimbursement.getId()); |
| | | return false; |
| | | } |
| | | log.info("serviceReimbursementSharedList的数据量为:{}", serviceReimbursementSharedList.size()); |
| | | |
| | | //detail详情 |
| | |
| | | data.put("summaryId", serviceReimbursement1.getSummaryId() == null ? "" : serviceReimbursement1.getSummaryId()); |
| | | map.put("data", data); |
| | | String strRes = null; |
| | | if (!active.equals("druid")) { |
| | | if (isUploadOaEnabled()) { |
| | | //如果是本地环境,就不调这个了 |
| | | String token = getToken(); |
| | | JSONObject json1 = JSONObject.parseObject(token); |
| | |
| | | } |
| | | } |
| | | log.error("-----Reimbursement-------------strRes的值为:{}", strRes); |
| | | JSONObject jsonObject = JSONObject.parseObject(strRes); |
| | | if (strRes == null || strRes.trim().isEmpty()) { |
| | | // 医院协友系统无返回(超时/异常/未配置):推送失败,由调用方回滚整个审批 |
| | | log.error("差旅审批单上传医院协友系统失败:接口无返回内容,id={}", serviceReimbursement.getId()); |
| | | return false; |
| | | } |
| | | JSONObject jsonObject = null; |
| | | try { |
| | | jsonObject = JSONObject.parseObject(strRes); |
| | | } catch (Exception e) { |
| | | log.error("差旅审批单上传医院协友系统失败:返回内容无法解析,id={},strRes={}", |
| | | serviceReimbursement.getId(), strRes, e); |
| | | return false; |
| | | } |
| | | if (jsonObject == null) { |
| | | log.error("差旅审批单上传医院协友系统失败:返回内容为空,id={},strRes={}", |
| | | serviceReimbursement.getId(), strRes); |
| | | return false; |
| | | } |
| | | String code = jsonObject.getString("code"); |
| | | if (!code.equals("0")) { |
| | | if (code == null || !"0".equals(code)) { |
| | | log.error("差旅审批单上传医院协友系统失败:code={},id={},strRes={}", |
| | | code, serviceReimbursement.getId(), strRes); |
| | | return false; |
| | | } |
| | | |
| | |
| | | log.info("filePath的修改:{}", filePath); |
| | | String url = rbDetailFile.getUrl(); |
| | | //file用的是绝对位置 |
| | | String fileName = url.substring(url.indexOf("/profile/upload") + "/profile/upload".length()); |
| | | // 2026-10-09:附件地址解析加固,避免 url 异常/文件缺失时抛 NPE 导致看不到失败原因 |
| | | int profileIdx = url == null ? -1 : url.indexOf("/profile/upload"); |
| | | if (profileIdx < 0) { |
| | | throw new ServiceException("附件地址格式不正确(缺少 /profile/upload),无法上传协友:" + url); |
| | | } |
| | | String fileName = url.substring(profileIdx + "/profile/upload".length()); |
| | | String strFile = rbDetailFile.getUrl().replace(filePath, "/profile/upload"); |
| | | File fileUpload = new File(filePath + fileName); |
| | | if (!fileUpload.exists()) { |
| | | throw new ServiceException("附件文件在服务器上不存在,无法上传协友:" + fileUpload.getAbsolutePath()); |
| | | } |
| | | log.info("请求第三方的入参strFile : {}, strFUrl : {}", strFile, filePath); |
| | | String struploadResult = HttpClientKit.sendPostWithFile(fileUpload, strFUrl); |
| | | log.info("第三方传回的数据: {}", struploadResult); |