package com.malk.huagao.controller; import org.springframework.core.io.UrlResource; import org.springframework.core.io.Resource; import com.alibaba.fastjson.JSON; import com.malk.huagao.service.EqbService; import com.malk.server.common.McR; import com.malk.utils.UtilMap; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; @Slf4j @RestController @RequestMapping("/eqb") public class HgEqbController { @Autowired private EqbService eqbService; @Value(value = "${eqb.downloadFilePath}") private String fileStoragePath; @PostMapping("/sign") public McR sign(@RequestBody Map map){ eqbService.sign(map); return McR.success(); } // 使用ConcurrentHashMap保证线程安全 private final ConcurrentMap eventList = new ConcurrentHashMap<>(); // 记录最后一次清理时间 private volatile long lastCleanTime = System.currentTimeMillis(); // 清理间隔时间(毫秒) private static final long CLEAN_INTERVAL = 60_000; @PostMapping("/callback") public McR callback(@RequestBody Map map){ System.out.println(map); log.info("e签宝回调: {}", JSON.toJSONString(map)); //签署回调通知:SIGN_MISSON_COMPLETE 事件订阅-签署流程完成:SIGN_FLOW_FINISH String action = UtilMap.getString(map, "action"); String signFlowId = UtilMap.getString(map, "signFlowId");//e签宝签署流程id String info = action + "-" + signFlowId; // 定期清理过期记录 cleanExpiredEvents(); if (isCallbackProcessed(info)){ log.info("info:{},重复回调,不做处理",info); }else { eventList.put(info, System.currentTimeMillis()); //签署回调结束 if ("SIGN_MISSON_COMPLETE".equals(action)) { int signResult = UtilMap.getInt(map, "signResult");//2 - 签署完成,4 - 拒签 if (2 == signResult) { String processInstanceId = UtilMap.getString(map, "customBizNum");//宜搭审批实例id //自动同意审批节点 eqbService.autoAgree(signFlowId,processInstanceId); } } } return McR.success(); } @GetMapping("/files/{fileId}") public ResponseEntity getFileResource( @PathVariable String fileId, @RequestParam(defaultValue = "download") String option) throws IOException { // 根据fileId获取实际文件路径,这里简化处理,实际可能需要从数据库查询 Path filePath = Paths.get(fileStoragePath).resolve(fileId).normalize(); // 检查文件是否存在 if (!Files.exists(filePath)) { return ResponseEntity.notFound().build(); } // 创建Resource对象 Resource resource = new UrlResource(filePath.toUri()); // 根据选项设置响应头 HttpHeaders headers = new HttpHeaders(); if ("preview".equalsIgnoreCase(option)) { // 预览模式 - 尝试确定内容类型 String contentType = Files.probeContentType(filePath); // 强制修正 PDF 的 Content-Type if (filePath.toString().toLowerCase().endsWith(".pdf")) { contentType = "application/pdf"; } else if (contentType == null) { contentType = "application/octet-stream"; } headers.setContentType(MediaType.parseMediaType(contentType)); headers.add("Content-Disposition", "inline; filename=\"" + resource.getFilename() + "\""); headers.add("X-Content-Type-Options", "nosniff"); // 防止浏览器忽略 Content-Type } else { // 下载模式 - 默认处理 headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\""); } return ResponseEntity.ok() .headers(headers) .contentLength(resource.contentLength()) .body(resource); } /** * 检查并清理过期事件 */ private void cleanExpiredEvents() { long currentTime = System.currentTimeMillis(); // 只在达到清理间隔时执行清理 if (currentTime - lastCleanTime > CLEAN_INTERVAL) { synchronized (this) { // 双重检查,避免重复清理 if (currentTime - lastCleanTime > CLEAN_INTERVAL) { long expirationTime = currentTime - TimeUnit.MINUTES.toMillis(1); eventList.entrySet().removeIf(entry -> entry.getValue() < expirationTime); lastCleanTime = currentTime; } } } } /** * 检查该回调事件在一分钟内是否处理过 */ private boolean isCallbackProcessed(String detail) { return eventList.containsKey(detail); } }