xuzhancheng 1 тиждень тому
батько
коміт
ac71a23e77
12 змінених файлів з 545 додано та 67 видалено
  1. 50 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorder/vo/TaskOrderServeSendSendVO.java
  2. 1 1
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/externalOA/ExternalOAController.java
  3. 9 4
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/synchronization/SynchronizationController.java
  4. 2 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/boilertaskorder/BoilerTaskOrderServiceImpl.java
  5. 65 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/boilertaskordersignfile/BoilerTaskOrderSignFileServiceImpl.java
  6. 243 58
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/externalOA/ExternalOAServiceImpl.java
  7. 55 1
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/externalOA/vo/ExternalOATokenRes.java
  8. 2 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/pipetaskorder/PipeTaskOrderServiceImpl.java
  9. 7 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/synchronization/SynchronizationService.java
  10. 93 1
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/synchronization/SynchronizationServiceImpl.java
  11. 16 0
      tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/synchronization/dto/SignatureDto.java
  12. 2 2
      tz-module-pressure2/tz-module-pressure2-biz/src/main/resources/mapper/equipboilerscheduling/EquipBoilerSchedulingMapper.xml

+ 50 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorder/vo/TaskOrderServeSendSendVO.java

@@ -0,0 +1,50 @@
+package cn.start.tz.module.pressure2.controller.admin.boilertaskorder.vo;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 服务单模版发送 Request VO")
+@Data
+public class TaskOrderServeSendSendVO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 任务单id
+     */
+    private String orderId;
+
+
+    /**
+     * 任务单号
+     */
+    private String orderNo;
+
+    private String equipCode;
+
+    /**
+     * 服务项目名称
+     */
+    private String serveItemName;
+
+    /**
+     * 推送时间
+     */
+    private LocalDateTime pushDateTime;
+
+    /**
+     * 详情url
+     */
+    private String detailsUrl;
+
+    /**
+     * 接收人电话
+     */
+    private String receiverPhone;
+
+    private String unitName;
+}

+ 1 - 1
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/externalOA/ExternalOAController.java

@@ -40,7 +40,7 @@ public class ExternalOAController {
     @GetMapping("/getExternalOAToken")
     @PermitAll
     public CommonResult<Object> getExternalOAToken() {
-        return success(externalOAService.getExternalOAToken(null));
+        return success(externalOAService.getExternalOAToken(null).getBindingUser().getId());
     }
 
     @PostMapping("/externalOAUploadAttachment")

+ 9 - 4
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/synchronization/SynchronizationController.java

@@ -4,14 +4,15 @@ import cn.start.tz.framework.common.pojo.CommonResult;
 import cn.start.tz.module.pressure2.framework.cron.SyncTaskBoilerEquipmentJob;
 //import cn.start.tz.module.pressure2.framework.cron.SyncTaskPipeEquipmentJob;
 import cn.start.tz.module.pressure2.service.synchronization.SynchronizationService;
+import cn.start.tz.module.pressure2.service.synchronization.dto.SignatureDto;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.annotation.security.PermitAll;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
 
 @Tag(name = "数据中台")
 @RestController
@@ -63,4 +64,8 @@ public class SynchronizationController {
 //        return CommonResult.success(null);
 //    }
 
+    @GetMapping("/authorizeSignature/{reportId}")
+    public CommonResult<List<SignatureDto>> authorizeSignature(@PathVariable String reportId,@RequestParam("name") String name) {
+        return CommonResult.success(synchronizationService.authorizeSignature(reportId, name));
+    }
 }

+ 2 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/boilertaskorder/BoilerTaskOrderServiceImpl.java

@@ -4004,6 +4004,8 @@ public class BoilerTaskOrderServiceImpl extends ServiceImpl<BoilerTaskOrderMappe
         externalOACreateFlowBodyDataReq.setFormmain_0042(formmain_0042);
         externalOACreateFlowReq.setData(externalOACreateFlowBodyDataReq);
         externalOACreateFlowReq.setThirdAttachments(thirdAttachments);
+//        String auditor = updateObj.getAuditor();
+//        AdminUserRespDTO checkedData = adminUserApi.getUserByEmployeeNo(auditor).getCheckedData();
         String approvalId = updateObj.getApprovalId();
         AdminUserRespDTO checkedData = adminUserApi.getUser(approvalId).getCheckedData();
         return externalOAService.createExternalOAFlow(externalOACreateFlowReq,checkedData.getUsername());

+ 65 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/boilertaskordersignfile/BoilerTaskOrderSignFileServiceImpl.java

@@ -2,8 +2,17 @@ package cn.start.tz.module.pressure2.service.boilertaskordersignfile;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.start.tz.framework.common.enums.TerminalEnum;
+import cn.start.tz.framework.common.exception.ErrorCode;
 import cn.start.tz.framework.common.exception.ServiceException;
+import cn.start.tz.framework.env.core.enums.EnvEnum;
 import cn.start.tz.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.start.tz.module.member.api.user.MemberUserApi;
+import cn.start.tz.module.member.api.user.dto.MemberUserReqVo;
+import cn.start.tz.module.member.api.user.dto.MemberUserRespDTO;
+import cn.start.tz.module.member.api.user.dto.SendAppMpMessageReqDTO;
+import cn.start.tz.module.member.api.user.dto.WxMpTemplateDataReqDto;
+import cn.start.tz.module.pressure2.controller.admin.boilertaskorder.vo.TaskOrderServeSendSendVO;
 import cn.start.tz.module.pressure2.controller.admin.orderreport.vo.TaskOrderSignFilePageReqVO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorder.BoilerTaskOrderDO;
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitem.BoilerTaskOrderItemDO;
@@ -19,12 +28,14 @@ import cn.start.tz.module.pressure2.dal.mysql.pipetaskorderitem.PipeTaskOrderIte
 import cn.start.tz.module.system.enums.common.IsYesOrNoEnum;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import jakarta.annotation.Resource;
 import org.springframework.validation.annotation.Validated;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 
 import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 import cn.start.tz.module.pressure2.controller.admin.boilertaskordersignfile.vo.*;
 import cn.start.tz.module.pressure2.dal.dataobject.boilertaskordersignfile.BoilerTaskOrderSignFileDO;
@@ -34,6 +45,7 @@ import cn.start.tz.framework.common.util.object.BeanUtils;
 import cn.start.tz.module.pressure2.dal.mysql.boilertaskordersignfile.BoilerTaskOrderSignFileMapper;
 
 import static cn.start.tz.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.start.tz.framework.common.util.servlet.ServletUtils.getClientIP;
 import static cn.start.tz.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 import static cn.start.tz.module.pressure2.enums.ErrorCodeConstants.*;
 
@@ -206,6 +218,9 @@ public class BoilerTaskOrderSignFileServiceImpl extends ServiceImpl<BoilerTaskOr
         return taskOrderSignFilePage;
     }
 
+    @Resource
+    private MemberUserApi memberUserApi;
+
     @Override
     public void serviceFromPush(TaskOrderPushReqVO reqVO) throws Exception {
         log.info("serviceFromPush {}", reqVO);
@@ -238,6 +253,7 @@ public class BoilerTaskOrderSignFileServiceImpl extends ServiceImpl<BoilerTaskOr
             taskOrderSignFileDO.setEquipMainType(reqVO.getEquipMainType());
             taskOrderSignFileDO.setReceiver(reqVO.getReceiver());
             taskOrderSignFileDO.setReceiverPhone(reqVO.getReceiverPhone());
+            taskOrderSignFileDO.setIsSignature(IsYesOrNoEnum.UNKNOWN.getKey());
             taskOrderSignFileDO.setIsPush(IsYesOrNoEnum.YES.getKey());
             taskOrderSignFileDO.setPushDateTime(LocalDateTime.now());
             updateById(taskOrderSignFileDO);
@@ -248,6 +264,7 @@ public class BoilerTaskOrderSignFileServiceImpl extends ServiceImpl<BoilerTaskOr
             taskOrderSignFileDO.setBusinessType(reqVO.getBusinessType());
             taskOrderSignFileDO.setReceiver(reqVO.getReceiver());
             taskOrderSignFileDO.setReceiverPhone(reqVO.getReceiverPhone());
+            taskOrderSignFileDO.setIsSignature(IsYesOrNoEnum.UNKNOWN.getKey());
             taskOrderSignFileDO.setIsPush(IsYesOrNoEnum.YES.getKey());
             taskOrderSignFileDO.setPushDateTime(LocalDateTime.now());
             taskOrderSignFileDO.setOrderItemId(reqVO.getOrderItemId());
@@ -308,5 +325,53 @@ public class BoilerTaskOrderSignFileServiceImpl extends ServiceImpl<BoilerTaskOr
 
     }
 
+//    /**
+//     * 任务单-服务单模版推送
+//     *
+//     * @param vo 发送参数
+//     */
+//    public void serveOrderSend(TaskOrderServeSendSendVO vo) {
+//        if (StringUtils.isNotEmpty(vo.getReceiverPhone())) {
+//            MemberUserRespDTO memberUser = memberUserApi.getUserByMobile(vo.getReceiverPhone()).getCheckedData();
+//            if (ObjectUtil.isEmpty(memberUser)) {
+//                // 获取不到平台用户信息是创建一个平台用户账号
+//                if (ObjectUtil.isEmpty(memberUser)) {
+//                    MemberUserReqVo memberUserReqVo = new MemberUserReqVo();
+//                    memberUserReqVo.setMobile(vo.getReceiverPhone());
+//                    memberUserReqVo.setRegisterIp(getClientIP());
+//                    memberUserReqVo.setTerminal(TerminalEnum.WECHAT_MINI_PROGRAM.getTerminal());
+//                    memberUser = memberUserApi.createUser(memberUserReqVo).getCheckedData();
+//                    if (ObjectUtil.isEmpty(memberUser)) {
+//                        throw exception(new ErrorCode(1007, "创建用户失败"));
+//                    }
+//                }
+//            }
+//
+//            String date = vo.getPushDateTime().toLocalDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+//            SendAppMpMessageReqDTO param = new SendAppMpMessageReqDTO();
+//            param.setMemberId(memberUser.getId());
+//            if (EnvEnum.LOCAL.getEnvName().equals(env)) {
+//                param.setTemplateId("P8Kxrg_t4QveknyFatAlOujaTu4mwbPF0K9ZIjzSUbY");
+//            } else if (EnvEnum.UAT.getEnvName().equals(env)) {
+//                param.setTemplateId("2lzVFZpqi7zUt0curHPnJJDrd2zeTs7HLjcpXvFlHmE");
+//            }
+//            List<WxMpTemplateDataReqDto> list = new ArrayList<>();
+//
+//            list.add(new WxMpTemplateDataReqDto("character_string2", vo.getOrderNo(), null));
+//            list.add(new WxMpTemplateDataReqDto("thing6", vo.getServeItemName(), null));
+//            list.add(new WxMpTemplateDataReqDto("time10", date, null));
+//            //todo 小程序发版后 跳转至小程序缴费页面
+//            SendAppMpMessageReqDTO.MiniProgramDto mini = new SendAppMpMessageReqDTO.MiniProgramDto();
+//            mini.setPagePath("pagesSub/service-acceptance-form/index?pageNo=1&pageSize=10&total=0&isSignature=0&businessType=100");
+//            mini.setUsePath(true);
+//            param.setMiniProgramDto(mini);
+//            param.setWxMpTemplateDataList(list);
+//            param.setBusinessType(4);
+//            param.setBusinessId(vo.getOrderId());
+//            // 模板消息发送
+//            memberUserApi.sendMpMessage(param).getCheckedData();
+//        }
+//
+//    }
 
 }

+ 243 - 58
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/externalOA/ExternalOAServiceImpl.java

@@ -24,10 +24,12 @@ import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Lazy;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Service;
 
 import java.io.InputStream;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 
 import static cn.start.tz.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
 
@@ -70,31 +72,158 @@ public class ExternalOAServiceImpl implements ExternalOAService {
     @Lazy
     private PipeTaskOrderItemReportService pipeTaskOrderItemReportService;
 
+    @Resource
+    private StringRedisTemplate stringRedisTemplate;
+
     @Value("${tz.fonts-folder-path:}")
     private String fontsFolderPath;
 
+    /** Redis缓存key前缀:OA token */
+    private static final String OA_TOKEN_CACHE_KEY = "OA_TOKEN:";
+    /** Token缓存过期时间:30分钟 */
+    private static final long TOKEN_CACHE_TTL_SECONDS = 30 * 60;
+
     /**
-     * 外部接口调用获取OA系统token
+     * 外部接口调用获取OA系统token(带Redis缓存)
      *
+     * @param oaLoginName 登录用户名,为null时使用默认配置
      * @return
      */
     @Override
-    public ExternalOATokenRes getExternalOAToken( String oaLoginName ) {
+    public ExternalOATokenRes getExternalOAToken(String oaLoginName) {
+        // 确定实际使用的登录名
+        String effectiveLoginName = oaLoginName != null ? oaLoginName : this.oaLoginName;
+        String cacheKey = OA_TOKEN_CACHE_KEY + effectiveLoginName;
+
+        // 1. 先从Redis缓存获取token
+        String cachedTokenJson = stringRedisTemplate.opsForValue().get(cacheKey);
+        if (StringUtils.isNotBlank(cachedTokenJson)) {
+            log.debug("从Redis缓存获取OA token,loginName={}", effectiveLoginName);
+            return JSON.parseObject(cachedTokenJson, ExternalOATokenRes.class);
+        }
+
+        // 2. 缓存未命中,调用OA接口获取新token
         Map<String, Object> params = new HashMap<>();
         params.put("userName", oaUserName);
         params.put("password", oaUserPassword);
-        if (oaLoginName != null){
-            params.put("loginName", oaLoginName);
-        }else {
-            params.put("loginName",this.oaLoginName);
-        }
+        params.put("loginName", effectiveLoginName);
 
         String res = HttpRequest.post(this.oaHost + "/seeyon/rest/token").body(JSON.toJSONString(params)).execute().body();
         ExternalOATokenRes externalOATokenRes = JSON.parseObject(res, ExternalOATokenRes.class);
         log.info("获取外部OA token结果:{}", externalOATokenRes);
+
+        // 3. 存入Redis缓存(仅当token有效时缓存)
+        if (externalOATokenRes != null && StringUtils.isNotBlank(externalOATokenRes.getId())) {
+            stringRedisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(externalOATokenRes),
+                    TOKEN_CACHE_TTL_SECONDS, TimeUnit.SECONDS);
+        }
         return externalOATokenRes;
     }
 
+    /**
+     * 强制刷新token缓存(清除缓存后重新获取)
+     *
+     * @param oaLoginName 登录用户名
+     * @return 新的token响应
+     */
+    private ExternalOATokenRes refreshToken(String oaLoginName) {
+        String effectiveLoginName = oaLoginName != null ? oaLoginName : this.oaLoginName;
+        stringRedisTemplate.delete(OA_TOKEN_CACHE_KEY + effectiveLoginName);
+        log.info("已清除OA token缓存并重新获取,loginName={}", effectiveLoginName);
+        return getExternalOAToken(oaLoginName);
+    }
+
+    /**
+     * 判断OA接口响应是否表示token已失效
+     * 当响应为null、空字符串、或返回 {"code": 401, "success": false, "message": "Invalid token,..."} 格式时,认为token失效
+     *
+     * @param responseBody 接口响应体
+     * @return true表示token失效
+     */
+    private boolean isTokenInvalidResponse(String responseBody) {
+        // 响应为空,视为token失效
+        if (StringUtils.isBlank(responseBody)) {
+            return true;
+        }
+        try {
+            JSONObject json = JSON.parseObject(responseBody);
+            if (json.getInteger("code") != null && (json.getInteger("code") == 401 || json.getInteger("code") == 1010)) {
+                return true;
+            }
+        } catch (Exception e) {
+            // 解析异常不算token失效,正常返回
+        }
+        return false;
+    }
+
+    /**
+     * 执行OA POST请求(带token过期自动重试)
+     *
+     * @param oaLoginName 登录用户名
+     * @param apiPathWithToken API路径(含token=前缀),如 "/seeyon/rest/myflow/sendflow?token="
+     * @param requestBody POST请求体
+     * @param tokenExtractor 从ExternalOATokenRes中提取token值的方式,如 ExternalOATokenRes::getId 或 r -> String.valueOf(r.getBindingUser().getId())
+     * @return 响应体字符串
+     */
+    private String doPostWithTokenRetry(String oaLoginName, String apiPathWithToken, String requestBody,
+                                        java.util.function.Function<ExternalOATokenRes, String> tokenExtractor) {
+        ExternalOATokenRes tokenRes = getExternalOAToken(oaLoginName);
+        String token = tokenExtractor.apply(tokenRes);
+        String result = HttpUtil.createPost(this.oaHost + apiPathWithToken + token)
+                .body(requestBody)
+                .execute()
+                .body();
+
+        // token失效时,刷新token后重试一次
+        if (isTokenInvalidResponse(result)) {
+            log.warn("OA token失效,刷新token后重试POST请求");
+            tokenRes = refreshToken(oaLoginName);
+            token = tokenExtractor.apply(tokenRes);
+            log.info("url:{}", this.oaHost + apiPathWithToken + token);
+            result = HttpUtil.createPost(this.oaHost + apiPathWithToken + token)
+                    .body(requestBody)
+                    .execute()
+                    .body();
+        }
+        if (isTokenInvalidResponse(result)) {
+            log.warn("OA token失效,刷新token后重试POST请求");
+            token = tokenRes.getId();
+            log.info("url:{}", this.oaHost + apiPathWithToken + token);
+            result = HttpUtil.createPost(this.oaHost + apiPathWithToken + token)
+                    .body(requestBody)
+                    .execute()
+                    .body();
+        }
+        return result;
+    }
+
+    /**
+     * 执行OA GET请求(带token过期自动重试)
+     *
+     * @param oaLoginName 登录用户名
+     * @param apiPathWithToken API路径(含token=前缀),如 "/seeyon/rest/myflow/usercomment?token="
+     * @param extraParams 额外的URL参数,如 "&summaryId=123"
+     * @param tokenExtractor 从ExternalOATokenRes中提取token值的方式
+     * @return 响应体字符串
+     */
+    private String doGetWithTokenRetry(String oaLoginName, String apiPathWithToken, String extraParams,
+                                       java.util.function.Function<ExternalOATokenRes, String> tokenExtractor) {
+        ExternalOATokenRes tokenRes = getExternalOAToken(oaLoginName);
+        String token = tokenExtractor.apply(tokenRes);
+        String url = this.oaHost + apiPathWithToken + token + extraParams;
+        String result = HttpUtil.createGet(url).execute().body();
+
+        // token失效时,刷新token后重试一次
+        if (isTokenInvalidResponse(result)) {
+            log.warn("OA token失效,刷新token后重试GET请求");
+            tokenRes = refreshToken(oaLoginName);
+            token = tokenExtractor.apply(tokenRes);
+            url = this.oaHost + apiPathWithToken + token + extraParams;
+            result = HttpUtil.createGet(url).execute().body();
+        }
+        return result;
+    }
+
     /**
      * 发起OA流程
      *
@@ -111,10 +240,12 @@ public class ExternalOAServiceImpl implements ExternalOAService {
         req.setTemplateCode("jianwureport");
         req.setDraft("0");
         log.info("发起OA流程参数:{}", JSON.toJSONString(req));
-        String result = HttpUtil.createPost(this.oaHost + "/seeyon/rest/myflow/sendflow?token=" + this.getExternalOAToken(oaLoginName).getBindingUser().getId())
-                .body(JSON.toJSONString(req))
-                .execute()
-                .body();
+
+        // 调用OA接口,token失效时自动刷新重试一次(使用bindingUser.id作为token)
+        String result = doPostWithTokenRetry(oaLoginName,
+                "/seeyon/rest/myflow/sendflow?token=",
+                JSON.toJSONString(req),
+                r -> String.valueOf(r.getBindingUser().getId()));
         log.info("发起OA流程结果:{}", result);
         return JSON.parseObject(result, ExternalOACreateFlowRes.class);
     }
@@ -127,9 +258,11 @@ public class ExternalOAServiceImpl implements ExternalOAService {
      */
     @Override
     public ExternalOACommentRes getExternalOAComment(String summaryid) {
-        String url = this.oaHost + "/seeyon/rest/myflow/usercomment?token=" + this.getExternalOAToken(null).getBindingUser().getId() + "&summaryId=" + summaryid;
-        String result = HttpUtil.createGet(url)
-                .execute().body();
+        // 调用OA接口,token失效时自动刷新重试一次(使用token.id)
+        String result = doGetWithTokenRetry(null,
+                "/seeyon/rest/myflow/usercomment?token=",
+                "&summaryId=" + summaryid,
+                ExternalOATokenRes::getId);
         log.info("获取OA流程状态结果:{}", result);
         return JSON.parseObject(result, ExternalOACommentRes.class);
     }
@@ -142,55 +275,79 @@ public class ExternalOAServiceImpl implements ExternalOAService {
      */
     @Override
     public ExternalOAUploadAttachmentRes externalOAUploadAttachment(InputStream inputStream, String fileName) {
-        // 1. 获取token(保留你的原有逻辑)
-        String token = this.getExternalOAToken(null).getId();
+        try {
+            // 1. 先读取文件字节数组(只能读一次,重试时复用)
+            byte[] fileBytes = IoUtil.readBytes(inputStream);
+            if (fileBytes.length == 0) {
+                log.error("上传文件为空");
+                return null;
+            }
+
+            // 2. 上传附件,token失效时自动刷新重试一次
+            return doUploadFileWithRetry(null, fileName, fileBytes);
+        } finally {
+            IoUtil.close(inputStream);
+        }
+    }
+
+    /**
+     * 执行文件上传(带token过期重试)
+     */
+    private ExternalOAUploadAttachmentRes doUploadFileWithRetry(String oaLoginName, String fileName, byte[] fileBytes) {
+        ExternalOATokenRes tokenRes = getExternalOAToken(oaLoginName);
+        String token = tokenRes != null ? tokenRes.getId() : null;
         if (StringUtils.isBlank(token)) {
             log.error("获取外部OA token失败");
             return null;
         }
 
-        // 2. 构建带token参数的URL(保留原有逻辑)
         String url = this.oaHost + "/seeyon/rest/attachment?token=" + token;
         log.info("OA上传附件地址:{},文件名:{}", url, fileName);
 
-        try {
-            // 3. 关键修复:用 Hutool 支持的 byte[] 代替 InputStreamResource(避免流关闭问题)
-            byte[] fileBytes = IoUtil.readBytes(inputStream); // Hutool 的 IoUtil,安全读取流为字节数组
-            if (fileBytes.length == 0) {
-                log.error("上传文件为空");
-                return null;
+        ExternalOAUploadAttachmentRes result = executeUpload(url, fileBytes, fileName);
+        if (result == null) {
+            // token可能失效,刷新后重试一次
+            log.warn("OA上传附件失败,刷新token后重试");
+            tokenRes = refreshToken(oaLoginName);
+            token = tokenRes != null ? tokenRes.getId() : null;
+            if (StringUtils.isNotBlank(token)) {
+                url = this.oaHost + "/seeyon/rest/attachment?token=" + token;
+                result = executeUpload(url, fileBytes, fileName);
             }
+        }
+        return result;
+    }
 
-            // 4. 发起请求(核心修复:移除手动Content-Type,用form传byte[]+文件名)
-            try (HttpResponse response = HttpUtil.createPost(url)
-                    // 移除手动设置的 Content-Type!Hutool 会自动生成 multipart/form-data + boundary
-                    .header("Accept", "application/json") // 告诉接口期望返回JSON(可选,根据对方接口调整)
-                    // 关键:用 byte[] 传文件,第三个参数是文件名(Hutool会自动封装为正确的表单文件格式)
-                    .form("file", fileBytes, fileName)
-                    .timeout(60000) // 延长超时(文件上传建议60秒)
-                    .execute()) {
-
-                // 5. 新增:打印响应状态码和响应体(排查关键)
-                int statusCode = response.getStatus();
-                String result = response.body();
-                log.info("OA上传附件响应状态码:{},响应体:{}", statusCode, result);
-
-                // 6. 校验响应状态(非200直接返回失败)
-                if (statusCode != HttpStatus.HTTP_OK) {
-                    log.error("上传失败,状态码:{},响应:{}", statusCode, result);
-                    return null;
-                }
+    /**
+     * 执行单次文件上传请求
+     * 返回null表示请求失败(含token失效),触发调用方重试
+     */
+    private ExternalOAUploadAttachmentRes executeUpload(String url, byte[] fileBytes, String fileName) {
+        try (HttpResponse response = HttpUtil.createPost(url)
+                .header("Accept", "application/json")
+                .form("file", fileBytes, fileName)
+                .timeout(60000)
+                .execute()) {
+
+            int statusCode = response.getStatus();
+            String result = response.body();
+            log.info("OA上传附件响应状态码:{},响应体:{}", statusCode, result);
+
+            // 检查响应体是否指示token失效(如 {"code": 401})
+            if (isTokenInvalidResponse(result)) {
+                log.warn("OA上传附件响应指示token失效");
+                return null;
+            }
 
-                // 7. 解析响应(保留原有逻辑)
-                ExternalOAUploadAttachmentRes uploadRes = JSON.parseObject(result, ExternalOAUploadAttachmentRes.class);
-                return uploadRes;
-            } catch (Exception e) {
-                log.error("OA上传附件请求异常", e);
+            if (statusCode != HttpStatus.HTTP_OK) {
+                log.error("上传失败,状态码:{},响应:{}", statusCode, result);
                 return null;
             }
-        } finally {
-            // 关闭输入流(避免资源泄漏)
-            IoUtil.close(inputStream);
+
+            return JSON.parseObject(result, ExternalOAUploadAttachmentRes.class);
+        } catch (Exception e) {
+            log.error("OA上传附件请求异常", e);
+            return null;
         }
     }
 
@@ -198,12 +355,13 @@ public class ExternalOAServiceImpl implements ExternalOAService {
     @Override
     public CommonResult<Boolean> cancleflow(String summaryId) {
         log.info("撤销OA流程,summaryId={}", summaryId);
-        // 调用OA系统撤销流程接口
-        String url = this.oaHost + "/seeyon/rest/myflow/cancleflow?token=" + this.getExternalOAToken("300801").getBindingUser().getId();
+        // 调用OA系统撤销流程接口(POST,summaryId放body,使用bindingUser.id作为token)
         JSONObject params = new JSONObject();
         params.put("summaryId", summaryId);
-        String result = HttpUtil.createPost(url).body(params.toJSONString())
-                .execute().body();
+        String result = doPostWithTokenRetry("300801",
+                "/seeyon/rest/myflow/cancleflow?token=",
+                params.toJSONString(),
+                r -> String.valueOf(r.getBindingUser().getId()));
         log.info("撤销OA流程结果:{}", result);
         JSONObject jsonObject = JSON.parseObject(result);
         if (jsonObject.getString("data") != null && jsonObject.getString("data").contains("撤销成功!")) {
@@ -262,7 +420,7 @@ public class ExternalOAServiceImpl implements ExternalOAService {
         }
 
         // 构建请求并调用OA获取待办事项列表
-        String url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getBindingUser().getId();
+        String url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getId();
         Map<String, String> requestBody = new HashMap<>();
         requestBody.put("memberCode", checkedData.getUsername());
         requestBody.put("templateCode", "jianwureport");
@@ -278,6 +436,19 @@ public class ExternalOAServiceImpl implements ExternalOAService {
                     .body();
             log.info("获取OA待办事项响应:{}", result);
 
+            // token失效时,刷新后重试一次
+            if (isTokenInvalidResponse(result)) {
+                log.warn("OA token失效,刷新token后重试获取待办事项");
+                tokenRes = refreshToken(checkedData.getUsername());
+                url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getId();
+                result = HttpUtil.createPost(url)
+                        .body(JSON.toJSONString(requestBody))
+                        .timeout(30000)
+                        .execute()
+                        .body();
+                log.info("重试获取OA待办事项响应:{}", result);
+            }
+
             // 解析响应并校验格式
             OAGetAffairsResVO getAffairsRes = JSON.parseObject(result, OAGetAffairsResVO.class);
             if (getAffairsRes == null) {
@@ -311,6 +482,7 @@ public class ExternalOAServiceImpl implements ExternalOAService {
             throw new RuntimeException("获取OA待办事项链接失败:" + e.getMessage(), e);
         }
     }
+
     @Override
     public List<Long> getAffairSummaryList() {
         // 获取OA系统token
@@ -328,7 +500,7 @@ public class ExternalOAServiceImpl implements ExternalOAService {
         }
 
         // 构建请求并调用OA获取待办事项列表
-        String url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getBindingUser().getId();
+        String url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getId();
         Map<String, String> requestBody = new HashMap<>();
         requestBody.put("memberCode", checkedData.getUsername());
         requestBody.put("templateCode", "jianwureport");
@@ -344,6 +516,19 @@ public class ExternalOAServiceImpl implements ExternalOAService {
                     .body();
             log.info("获取OA待办事项响应:{}", result);
 
+            // token失效时,刷新后重试一次
+            if (isTokenInvalidResponse(result)) {
+                log.warn("OA token失效,刷新token后重试获取待办事项列表");
+                tokenRes = refreshToken(checkedData.getUsername());
+                url = this.oaHost + "/seeyon/rest/myflow/getAffairs?token=" + tokenRes.getId();
+                result = HttpUtil.createPost(url)
+                        .body(JSON.toJSONString(requestBody))
+                        .timeout(30000)
+                        .execute()
+                        .body();
+                log.info("重试获取OA待办事项列表响应:{}", result);
+            }
+
             // 解析响应并校验格式
             OAGetAffairsResVO getAffairsRes = JSON.parseObject(result, OAGetAffairsResVO.class);
             if (getAffairsRes == null) {
@@ -363,8 +548,8 @@ public class ExternalOAServiceImpl implements ExternalOAService {
             // 筛选SUMMARYID匹配的待办事项
             return data.getList().stream().map(OAAffairItemVO::getSummaryId).toList();
         } catch (Exception e) {
-            log.error("获取OA待办事项链接异常,", e);
-            throw new RuntimeException("获取OA待办事项链接失败:" + e.getMessage(), e);
+            log.error("获取OA待办事项列表异常", e);
+            throw new RuntimeException("获取OA待办事项列表失败:" + e.getMessage(), e);
         }
     }
 }

+ 55 - 1
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/externalOA/vo/ExternalOATokenRes.java

@@ -4,9 +4,63 @@ import lombok.Data;
 
 @Data
 public class ExternalOATokenRes {
-    private String bindingUser;
+    private BindingUser bindingUser;
+    private String userName;
     /**
      * token
      */
     private String id;
+
+    @Data
+    public static class BindingUser {
+        private String loginState;
+        private String sessionId;
+        private Long id;
+        private Integer securityKey;
+        private String loginName;
+        private String name;
+        private Long loginAccount;
+        private String loginAccountName;
+        private String loginAccountShortName;
+        private String province;
+        private String city;
+        private String rectangle;
+        private String nodeIndex;
+        private Long departmentId;
+        private Long levelId;
+        private Long postId;
+        private Long accountId;
+        private String remoteAddr;
+        private String userAgentFrom;
+        private Integer externalType;
+        private String locale;
+        private Long loginTimestamp;
+        private Object loginLogId;
+        private String skin;
+        private Object userSSOFrom;
+        private Object browser;
+        private String timeZone;
+        private Boolean canSendSMS;
+        private String etagRandom;
+        private Long changeRoleTimestamp;
+        private Boolean auditAdmin;
+        private Boolean fromM1;
+        private Boolean admin;
+        private Boolean guest;
+        private Boolean internal;
+        private Boolean visitor;
+        private Boolean systemAdmin;
+        private Integer loginSign;
+        private Boolean v5Member;
+        private Boolean groupSystemAdmin;
+        private Boolean unitSystemAdmin;
+        private Boolean administrator;
+        private Boolean groupAdmin;
+        private Boolean vjoinMember;
+        private Boolean v5External;
+        private Boolean screenGuest;
+        private Boolean platformAdmin;
+        private Boolean superAdmin;
+        private Boolean defaultGuest;
+    }
 }

+ 2 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/pipetaskorder/PipeTaskOrderServiceImpl.java

@@ -3217,6 +3217,8 @@ public class PipeTaskOrderServiceImpl extends ServiceImpl<PipeTaskOrderMapper, P
         externalOACreateFlowBodyDataReq.setFormmain_0042(formmain_0042);
         externalOACreateFlowReq.setData(externalOACreateFlowBodyDataReq);
         externalOACreateFlowReq.setThirdAttachments(thirdAttachments);
+//        String auditor = updateObj.getAuditor();
+//        AdminUserRespDTO checkedData = adminUserApi.getUserByEmployeeNo(auditor).getCheckedData();
         String approvalId = updateObj.getApprovalId();
         AdminUserRespDTO checkedData = adminUserApi.getUser(approvalId).getCheckedData();
         return externalOAService.createExternalOAFlow(externalOACreateFlowReq,checkedData.getUsername());

+ 7 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/synchronization/SynchronizationService.java

@@ -2,6 +2,9 @@ package cn.start.tz.module.pressure2.service.synchronization;
 
 import cn.start.tz.module.pressure2.service.synchronization.dto.CheckDto;
 import cn.start.tz.module.pressure2.service.synchronization.dto.ReportDto;
+import cn.start.tz.module.pressure2.service.synchronization.dto.SignatureDto;
+
+import java.util.List;
 
 public interface SynchronizationService {
 //    /**
@@ -28,4 +31,8 @@ public interface SynchronizationService {
      */
     boolean uploadReport(ReportDto reportDto);
 
+    /**
+     * 授权签字人
+     */
+    List<SignatureDto> authorizeSignature(String reportId, String name);
 }

+ 93 - 1
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/synchronization/SynchronizationServiceImpl.java

@@ -10,9 +10,24 @@ import cn.start.tz.framework.ip.core.Area;
 import cn.start.tz.framework.ip.core.utils.AreaUtils;
 import cn.start.tz.module.pressure2.controller.admin.equippipe.vo.EquipPipeSaveReqVO;
 import cn.start.tz.module.pressure2.controller.admin.equippipedetail.vo.EquipPipeDetailSaveReqVO;
+import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitem.BoilerTaskOrderItemDO;
+import cn.start.tz.module.pressure2.dal.dataobject.boilertaskorderitemreport.BoilerTaskOrderItemReportDO;
 import cn.start.tz.module.pressure2.dal.dataobject.equipboiler.EquipBoilerDO;
+import cn.start.tz.module.pressure2.dal.dataobject.equippipedetail.EquipPipeDetailDO;
+import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorder.PipeTaskOrderDO;
+import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitem.PipeTaskOrderItemDO;
+import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemdetail.PipeTaskOrderItemDetailDO;
+import cn.start.tz.module.pressure2.dal.dataobject.pipetaskorderitemreport.PipeTaskOrderItemReportDO;
+import cn.start.tz.module.pressure2.dal.mysql.boilertaskorderitem.BoilerTaskOrderItemMapper;
+import cn.start.tz.module.pressure2.dal.mysql.equipboiler.EquipBoilerMapper;
+import cn.start.tz.module.pressure2.dal.mysql.equippipedetail.EquipPipeDetailMapper;
+import cn.start.tz.module.pressure2.dal.mysql.pipetaskorder.PipeTaskOrderMapper;
+import cn.start.tz.module.pressure2.dal.mysql.pipetaskorderitem.PipeTaskOrderItemMapper;
+import cn.start.tz.module.pressure2.dal.mysql.pipetaskorderitemdetail.PipeTaskOrderItemDetailMapper;
+import cn.start.tz.module.pressure2.service.boilertaskorderitemreport.BoilerTaskOrderItemReportService;
 import cn.start.tz.module.pressure2.service.equipboiler.EquipBoilerService;
 import cn.start.tz.module.pressure2.service.equippipe.EquipPipeService;
+import cn.start.tz.module.pressure2.service.pipetaskorderitemreport.PipeTaskOrderItemReportService;
 import cn.start.tz.module.pressure2.service.synchronization.dto.*;
 import cn.start.tz.module.system.api.clientunit.ClientUnitApi;
 import cn.start.tz.module.system.api.clientunit.dto.ClientUnitDTO;
@@ -20,10 +35,12 @@ import cn.start.tz.module.system.api.dept.DeptApi;
 import cn.start.tz.module.system.api.dept.dto.DeptRespDTO;
 import cn.start.tz.module.system.api.dict.DictDataApi;
 import cn.start.tz.module.system.api.dict.dto.DictDataRespDTO;
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
@@ -125,7 +142,6 @@ public class SynchronizationServiceImpl implements SynchronizationService {
                         addList.add(equipBoilerDO);
                     }
                 }
-                log.info("同步,添加锅炉:{},更新锅炉:{}", addList.size(), updateList.size());
                 equipBoilerService.saveBatch(addList);
 
                 updateList.forEach(equipBoilerDO -> {
@@ -136,6 +152,7 @@ public class SynchronizationServiceImpl implements SynchronizationService {
                             }
                         }
                 );
+                log.info("同步,添加锅炉:{},更新锅炉:{}", addList.size(), updateList.size());
             }
         }
     }
@@ -191,6 +208,7 @@ public class SynchronizationServiceImpl implements SynchronizationService {
                     log.error("保存管道数据失败:{}", equipPipeDO.getProjectNo(), e);
                 }
             });
+            log.info("同步,添加管道:{}", list.size());
         }
     }
 
@@ -256,6 +274,80 @@ public class SynchronizationServiceImpl implements SynchronizationService {
         return false;
     }
 
+    @Resource
+    @Lazy
+    private BoilerTaskOrderItemReportService boilerTaskOrderItemReportService;
+
+    @Resource
+    @Lazy
+    private EquipBoilerMapper equipBoilerMapper;
+
+    @Resource
+    @Lazy
+    private BoilerTaskOrderItemMapper boilerTaskOrderItemMapper;
+
+    @Resource
+    @Lazy
+    private PipeTaskOrderItemReportService pipeTaskOrderItemReportService;
+
+    @Resource
+    @Lazy
+    private EquipPipeDetailMapper equipPipeDetailMapper;
+
+    @Resource
+    @Lazy
+    private PipeTaskOrderMapper pipeTaskOrderMapper;
+
+    @Resource
+    @Lazy
+    private PipeTaskOrderItemMapper pipeTaskOrderItemMapper;
+
+    @Resource
+    @Lazy
+    private PipeTaskOrderItemDetailMapper pipeTaskOrderItemDetailMapper;
+
+
+    @Override
+    public List<SignatureDto> authorizeSignature(String reportId, String name) {
+        BoilerTaskOrderItemReportDO boilerTaskOrderItemReportDO = boilerTaskOrderItemReportService.getById(reportId);
+        if (boilerTaskOrderItemReportDO != null) {
+            Map<String, Object> map = new HashMap<>();
+            map.put("NAME", name);
+            map.put("FIELD0001", boilerTaskOrderItemReportDO.getReportNo().substring(2, 4));
+            BoilerTaskOrderItemDO boilerTaskOrderItemDO = boilerTaskOrderItemMapper.selectById(boilerTaskOrderItemReportDO.getOrderItemId());
+            String equipId = boilerTaskOrderItemDO.getEquipId();
+            EquipBoilerDO equipBoilerDO = equipBoilerMapper.selectById(equipId);
+            DeptRespDTO data = deptApi.getDept(equipBoilerDO.getRelatedDepartment()).getCheckedData();
+            map.put("FIELD0003", !equipBoilerDO.getType().equals("1") && data.getName().equals("锅炉检验部") ? "1" : "0");
+            String result = getRequest("formmain_1650", map);
+            JSONObject jsonObject = JSONObject.parseObject(result);
+            return JSONArray.parseArray(jsonObject.getString("data"), SignatureDto.class);
+        }
+        PipeTaskOrderItemReportDO pipeTaskOrderItemReportDO = pipeTaskOrderItemReportService.getById(reportId);
+        if (pipeTaskOrderItemReportDO != null) {
+            Map<String, Object> map = new HashMap<>();
+            map.put("NAME", name);
+            map.put("FIELD0001", pipeTaskOrderItemReportDO.getReportNo().substring(2, 4));
+            PipeTaskOrderDO pipeTaskOrderDO = pipeTaskOrderMapper.selectById(pipeTaskOrderItemReportDO.getOrderId());
+            List<PipeTaskOrderItemDO> pipeTaskOrderItemDOS = pipeTaskOrderItemMapper.selectList(PipeTaskOrderItemDO::getOrderId, pipeTaskOrderDO.getId());
+            if (pipeTaskOrderItemDOS.isEmpty()){
+                return new ArrayList<>();
+            }
+            PipeTaskOrderItemDO pipeTaskOrderItemDO = pipeTaskOrderItemDOS.get(0);
+            List<PipeTaskOrderItemDetailDO> pipeTaskOrderItemDetailDOS = pipeTaskOrderItemDetailMapper.selectList(PipeTaskOrderItemDetailDO::getOrderId, pipeTaskOrderItemDO.getOrderId());
+            if (pipeTaskOrderItemDetailDOS.isEmpty()){
+                return new ArrayList<>();
+            }
+            String equipId = pipeTaskOrderItemDetailDOS.get(0).getEquipDetailId();
+            EquipPipeDetailDO equipPipeDetailDO = equipPipeDetailMapper.selectById(equipId);
+            map.put("FIELD0003", equipPipeDetailDO.getPipeRegCode().startsWith("81") ? "1" : "0");
+            String data = getRequest("formmain_1650", map);
+            JSONObject jsonObject = JSONObject.parseObject(data);
+            return JSONArray.parseArray(jsonObject.getString("data"), SignatureDto.class);
+        }
+        return new ArrayList<>();
+    }
+
 
     /**
      * 发起get请求到数据中台

+ 16 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/synchronization/dto/SignatureDto.java

@@ -0,0 +1,16 @@
+package cn.start.tz.module.pressure2.service.synchronization.dto;
+
+import lombok.Data;
+
+@Data
+public class SignatureDto {
+    private String NAME;
+    private String CODE;
+    private String CREATE_TIME;
+    private String FIELD0001;
+    private String FIELD0003;
+    private String FIELD0004;
+    private String FIELD0005;
+    private String FIELD0007;
+    private String MODIFY_DATE;
+}

+ 2 - 2
tz-module-pressure2/tz-module-pressure2-biz/src/main/resources/mapper/equipboilerscheduling/EquipBoilerSchedulingMapper.xml

@@ -73,8 +73,8 @@
         MAX(CASE WHEN s.type = 300 THEN s.PLAN_DATE END) AS plan_pressure_check_date
         FROM PRESSURE2_EQUIP_BOILER_SCHEDULING s
         INNER JOIN PRESSURE2_EQUIP_BOILER_SCHEDULING_ITEM b ON s.ID = b.SCHEDULING_ID
-        left join PRESSURE_TASK_ORDER pto on s.id = pto.SCHEDULING_ID
-        WHERE s.DELETED = 0 AND b.DELETED = 0 AND s.type IN (100, 200, 300) and pto.task_status != 800
+        left join PRESSURE_TASK_ORDER pto on s.id = pto.SCHEDULING_ID AND pto.task_status != 800
+        WHERE s.DELETED = 0 AND b.DELETED = 0 AND s.type IN (100, 200, 300)
         GROUP BY b.equip_id
         ) scheduling_info ON eb.ID = scheduling_info.equip_id
         <where>