feat(oapi): 办件进度/流程图token出图/托管撤回 + CAS直嵌示意图(两会话产物合入)
build / build-push (push) Successful in 1m29s

This commit is contained in:
chenxing
2026-09-17 01:03:57 +08:00
parent b8441065d0
commit 6afc7d7b92
10 changed files with 978 additions and 1 deletions
@@ -0,0 +1,78 @@
package com.yunhai.flowcenter.controller.open;
import com.yunhai.flowcenter.entity.FlwInstance;
import com.yunhai.flowcenter.model.FlowModels;
import com.yunhai.flowcenter.repository.FlwDefRepository;
import com.yunhai.flowcenter.repository.FlwInstanceRepository;
import com.yunhai.flowcenter.service.DiagramImageService;
import com.yunhai.flowcenter.service.DiagramTokenService;
import com.yunhai.flowcenter.service.FlowDiagramService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 免签图片流端点(/open/flow/**,用户拍板 2026-09-16,"CAS 验证码式"嵌入)。
* <p>
* 故意不落 /api/**(CAS 会话)也不落 /oapi/**(app-sign):&lt;img src&gt; 两者都带不了鉴权头;
* 免签资格由 10 分钟短时 token 承担(DiagramTokenService,token 由 app-sign 的
* GET /oapi/flow/diagram/url 签发)。token 分发点之外,本端点不接受任何业务参数直接定位——
* 免签面收窄到"只认 token",定位与可见性全在签发侧(OapiFlowService.diagramUrl)完成。
*/
@Slf4j
@RestController
@RequestMapping("/open/flow")
@RequiredArgsConstructor
public class OpenDiagramImageController {
private final DiagramTokenService tokenService;
private final DiagramImageService imageService;
private final FlwInstanceRepository instanceRepository;
private final FlwDefRepository defRepository;
private final FlowDiagramService diagramService;
@GetMapping("/diagram/img")
public ResponseEntity<byte[]> diagramImg(@RequestParam("token") String token) {
DiagramTokenService.TokenEntry entry = tokenService.resolve(token);
if (entry == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.contentType(MediaType.TEXT_PLAIN)
.body("流程图已过期,请刷新页面重新获取".getBytes());
}
// 办件口径:节点带运行时状态(token 已收 schId,出图前再二次比对防漂移)
if (entry.getInstanceId() != null) {
FlwInstance instance = instanceRepository.findById(entry.getInstanceId()).orElse(null);
if (instance == null || !entry.getSchId().equals(instance.getSchId())) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.contentType(MediaType.TEXT_PLAIN)
.body("办件不存在".getBytes());
}
return png(diagramService.byInstance(instance, entry.getMode()));
}
// 定义口径(flowGuid 预览)
return defRepository.findById(entry.getDefId())
.map(def -> entry.getSchId().equals(def.getSchId())
? png(diagramService.build(def, null, entry.getMode()))
: notFound("流程不存在"))
.orElseGet(() -> notFound("流程不存在"));
}
private ResponseEntity<byte[]> png(FlowModels.DiagramVo vo) {
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
.cacheControl(org.springframework.http.CacheControl.noStore())
.body(imageService.renderPng(vo));
}
private ResponseEntity<byte[]> notFound(String msg) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.contentType(MediaType.TEXT_PLAIN)
.body(msg.getBytes());
}
}