diff --git a/src/main/java/com/yunhai/notice/controller/api/NoticeFlowController.java b/src/main/java/com/yunhai/notice/controller/api/NoticeFlowController.java
index f4205b9..5d60b2e 100644
--- a/src/main/java/com/yunhai/notice/controller/api/NoticeFlowController.java
+++ b/src/main/java/com/yunhai/notice/controller/api/NoticeFlowController.java
@@ -22,6 +22,7 @@ import java.util.Map;
*
POST Flow/Binding/Save:保存/解除应用级绑定(仅本校管理员);
* POST Flow/TrySubmit:提交/重提审核(幂等,bizId=notice.id;apply 失败保持待提交可重推);
* GET Flow/Trace:审批进度(后端 getInstanceByBiz 组装,前端不直调 oapi——放行判定必须收在后端)。
+ * GET Flow/Embed:办件进度嵌入页地址(Drawer iframe 直嵌,可见性与 Trace 同口径)。
*
*/
@RestController
@@ -30,6 +31,7 @@ import java.util.Map;
public class NoticeFlowController {
private final NoticeFlowService noticeFlowService;
+ private final com.yunhai.notice.service.flow.NoticeFlowEmbedService noticeFlowEmbedService;
/** 审核流程下拉选项(流程中心未启用时返回空列表,前端隐藏选择器) */
@GetMapping("Options")
@@ -81,6 +83,15 @@ public class NoticeFlowController {
return noticeFlowService.diagramImage(serid, user);
}
+ /**
+ * 办件进度嵌入页地址(详情页 Drawer 内 iframe 直嵌 flow-center 统一进度页)。
+ * 可见性与 Trace/Diagram 同口径;pageUrl 含短时 token,现取现发不落前端缓存。
+ */
+ @GetMapping("Embed")
+ public Map embed(@RequestParam("serid") Integer serid, BoaUser user) {
+ return noticeFlowEmbedService.embedUrl(serid, user);
+ }
+
private Integer currentSchId(BoaUser user) {
if (user == null || user.getCurrentRole() == null) {
return null;
diff --git a/src/main/java/com/yunhai/notice/service/flow/NoticeFlowEmbedService.java b/src/main/java/com/yunhai/notice/service/flow/NoticeFlowEmbedService.java
new file mode 100644
index 0000000..c3ba7e9
--- /dev/null
+++ b/src/main/java/com/yunhai/notice/service/flow/NoticeFlowEmbedService.java
@@ -0,0 +1,52 @@
+package com.yunhai.notice.service.flow;
+
+import com.yunhai.notice.entity.Notice;
+import com.yunhai.notice.repository.NoticeRepository;
+import com.yunhai.platform.apistd.BizException;
+import com.yunhai.platform.openapi.flow.YschFlowOpenApi;
+import com.yunhai.platform.openapi.flow.model.FlowEmbedUrl;
+import com.yunhai.platform.utils.exception.ErrorCode;
+import com.yunhai.platform.utils.model.BoaUser;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * 办件进度嵌入页换址(2026-10-03 嵌入页方案):子应用 Drawer 内 iframe 直嵌 flow-center
+ * 统一进度页,零样式复制。与 Diagram 同口径的用户级守卫(未发布仅提交人/管理员/办理人
+ * /被抄送人可见);oapi 层只认 schId——"谁能看"必须落在本端点,应用签名不替代身份。
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class NoticeFlowEmbedService {
+
+ private final NoticeRepository noticeRepository;
+ private final NoticeFlowGuard noticeFlowGuard;
+
+ /**
+ * 取嵌入页地址(pageUrl 含 10 分钟短时 token):现取现发、不缓存、不落库、
+ * 不打进应用日志(token 泄露窗口即有效期,纪律见方案 §6.1/§7)。
+ */
+ public Map embedUrl(Integer noticeId, BoaUser user) {
+ Notice notice = noticeRepository.findById(noticeId)
+ .orElseThrow(() -> new BizException(ErrorCode.FORBIDDEN, "通知不存在: " + noticeId));
+ if (notice.getStatus() == null || notice.getStatus() != 1) {
+ noticeFlowGuard.requireUnpublishedVisible(notice, user);
+ }
+ if (notice.getFlowInstanceId() == null || notice.getSchId() == null) {
+ throw new BizException(ErrorCode.PARAM_ERROR, "该通知尚未进入流程");
+ }
+ FlowEmbedUrl embed = YschFlowOpenApi.embedUrl(
+ null, FlowCenterClient.BIZ_TYPE, String.valueOf(notice.getId()),
+ notice.getSchId(), FlowEmbedUrl.class);
+ if (embed == null || !StringUtils.hasText(embed.getPageUrl())) {
+ throw new BizException(ErrorCode.INTERNAL_SERVER_ERROR, "嵌入页地址获取失败,请稍后重试");
+ }
+ return Collections.singletonMap("pageUrl", embed.getPageUrl());
+ }
+}