rawParams) {
+ if (rawParams == null) {
+ return;
+ }
+ for (String key : rawParams.keySet()) {
+ if (IDENTITY_PARAMS.contains(key)) {
+ throw new BizException(ErrorCode.PARAM_ERROR,
+ "不允许在请求参数中携带身份字段:" + key + "(身份取自登录会话)");
+ }
+ }
+ }
+
+ /** 登录态提参(沿 NoticeApprovalController 口径):登录 → 学校归属 → 姓名兜底账号 */
+ private static Operator requireOperator(BoaUser user) {
+ if (user == null || user.getId() == null) {
+ throw new BizException(ErrorCode.FORBIDDEN, "未登录");
+ }
+ if (user.getCurrentRole() == null || user.getCurrentRole().getSchId() == null) {
+ throw new BizException(ErrorCode.PARAM_ERROR, "当前身份无学校归属");
+ }
+ String name = StringUtils.hasText(user.getName()) ? user.getName() : user.getAccount();
+ return new Operator(user.getId(), user.getCurrentRole().getSchId(), name, user);
+ }
+
+ private static final class Operator {
+ final Integer userId;
+ final Integer schId;
+ final String name;
+ final BoaUser user;
+
+ Operator(Integer userId, Integer schId, String name, BoaUser user) {
+ this.userId = userId;
+ this.schId = schId;
+ this.name = name;
+ this.user = user;
+ }
+ }
+}
diff --git a/src/main/java/com/yunhai/svchall/service/SvcBizTypes.java b/src/main/java/com/yunhai/svchall/service/SvcBizTypes.java
new file mode 100644
index 0000000..b154bf9
--- /dev/null
+++ b/src/main/java/com/yunhai/svchall/service/SvcBizTypes.java
@@ -0,0 +1,29 @@
+package com.yunhai.svchall.service;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * svc-hall 流程业务类型(bizType)终值与存量兼容口径(P3.2 方案B,用户裁决 2026-10-19)。
+ *
+ * 终值以 S0-H1 实核为准:UserBase application_info appKey=100008(svc-hall 生产出站身份)
+ * 注册行 app_code = {@code svc-hall}。存量值 {@code svc_item} 是 2026-10-19 前建件的出站值;
+ * S0-H2 实测中心 flw_instance 已无 svc_item 行,读路径双查属防御性兼容(防未来存量事件重放/
+ * 重提),收敛条件=另一类型不再可能产生事件(覆盖 0 态重试/30/40 重提/积压核清),本轮不收敛单值。
+ */
+public final class SvcBizTypes {
+
+ /** 终值(新件出站 + 读路径首查键) */
+ public static final String CURRENT = "svc-hall";
+
+ /** 存量值(2026-10-19 前建件;读路径次查键,回调白名单双值之一) */
+ public static final String LEGACY = "svc_item";
+
+ private SvcBizTypes() {
+ }
+
+ /** 读路径双查序:先终值后存量,结果按序合并(方案B 双值双查补丁的统一取值点) */
+ public static List queryOrder() {
+ return Arrays.asList(CURRENT, LEGACY);
+ }
+}
diff --git a/src/main/java/com/yunhai/svchall/service/impl/SvcApprovalClient.java b/src/main/java/com/yunhai/svchall/service/impl/SvcApprovalClient.java
new file mode 100644
index 0000000..f042e69
--- /dev/null
+++ b/src/main/java/com/yunhai/svchall/service/impl/SvcApprovalClient.java
@@ -0,0 +1,190 @@
+package com.yunhai.svchall.service.impl;
+
+import com.yunhai.platform.apistd.BizException;
+import com.yunhai.platform.openapi.core.PlatformService;
+import com.yunhai.platform.openapi.core.YschOpenCall;
+import com.yunhai.platform.utils.exception.ErrorCode;
+import com.yunhai.svchall.service.FlowClient;
+import com.yunhai.svchall.service.SvcBizTypes;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 应用内审批出站客户端(P3.1,沿 NoticeApprovalClient 形态):YschOpenCall 泛化调用打
+ * flow-center {@code /oapi/flow/tasks|task|embed/**},走服务目录命名目标 flow-center
+ * (yh.call.targets > yh.open.services,与 {@link FlowCenterClient} 同一条解析链)。
+ *
+ * 与 Notice 的差异(P3.1 加严):
+ *
+ * - 读面 bizType 不固定单值——{@link SvcBizTypes#queryOrder()} 双值双查(先终值后存量,
+ * P3.2 方案B,防存量事件/重提漏查);
+ * - embed 记录页走 instanceId 形态(本地 flow_instance_id 镜像有值才可能换址成功,
+ * bizType+bizId 形态在 bizType 换值后对存量件必然"办件不存在")。
+ *
+ * 身份口径:operatorId/operatorName/schId 由调用方(controller)从 CAS 会话取,本类不猜身份;
+ * 对端按任务办理人逐字复核。
+ */
+@Slf4j
+@Service
+public class SvcApprovalClient {
+
+ private static final String TARGET = PlatformService.FLOW_CENTER.key();
+
+ private final YschOpenCall openCall;
+ private final FlowClient flowClient;
+
+ public SvcApprovalClient(ObjectProvider openCallProvider, FlowClient flowClient) {
+ this.openCall = openCallProvider.getIfAvailable();
+ this.flowClient = flowClient;
+ }
+
+ /**
+ * 我的待办分页:双值双查后合并(同一 assignee 在两个 bizType 下都可能有历史待办的过渡期口径;
+ * S0-H2 实测存量=0,合并退化为单查结果)。Map 形态即 flow-center BasePageResult JSON。
+ */
+ public Map todoTasks(Integer schId, Integer assigneeId, Integer page, Integer size) {
+ requireEnabled();
+ List