refactor(sdk)!: 适配 SDK 领域分包——流程/班级/用户中心门面切新包
build / build-push (push) Successful in 55s

- FlowModels / FlowCenterClient / FlowCenterClientTest:
  openapi.dto.FlowDto → openapi.flow.model.*(按需引入 FlowApplyBody/Result、FlowCallbackRegBody/Result、
  FlowInstance/FlowSummary);TargetSpec/YschCallProperties/YschOpenCall → openapi.core.*
- ClassService(+IClassService):切新班级域门面 YschClassOpenApi + clazz.model.SchTea,
  替换原先用 openapi.client.YschBaseOpenApi 直连基址的写法
- ContactsService / NoticeService / UserCenterService:YschBaseOpenApi → openapi.base.*
- application.yml:随服务目录兜底去掉 open.enabled / user-center-url / userclass 三个 legacy 键
  (userclass 已由 PlatformService 枚举兜底 = base-host + /userclass)

验证:clean test 全绿 64 个非 IT 用例(含本次改动的 FlowCenterClientTest 5/5)。
12 个 *IT 在本机因无 MySQL(jdbc:mysql://localhost:3306/bsch_notice_test)在
entityManagerFactory 阶段 Connection refused,属环境性失败,与本次迁移无关。

说明:这批改动是工作区在途内容(适配 SDK 领域分包所必需),不落库则 Notice 无法对
当前 nexus 上的 SDK 构建通过——即"消费方提交态编译不过"的根因。
This commit is contained in:
chenxing
2026-09-16 19:26:27 +08:00
parent 01ce87ed7a
commit 8852c7ab02
9 changed files with 64 additions and 69 deletions
@@ -1,7 +1,12 @@
package com.yunhai.notice.model.flow; package com.yunhai.notice.model.flow;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.yunhai.platform.openapi.dto.FlowDto; import com.yunhai.platform.openapi.flow.model.FlowSummary;
import com.yunhai.platform.openapi.flow.model.FlowApplyBody;
import com.yunhai.platform.openapi.flow.model.FlowApplyResult;
import com.yunhai.platform.openapi.flow.model.FlowInstance;
import com.yunhai.platform.openapi.flow.model.FlowCallbackRegBody;
import com.yunhai.platform.openapi.flow.model.FlowCallbackRegResult;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -21,39 +26,39 @@ public final class FlowModels {
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public static class FlowSummaryVo extends FlowDto.FlowSummary { public static class FlowSummaryVo extends FlowSummary {
} }
/** 代发起请求体(POST /oapi/flow/apply) */ /** 代发起请求体(POST /oapi/flow/apply) */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public static class ApplyBody extends FlowDto.ApplyBody { public static class ApplyBody extends FlowApplyBody {
} }
/** 代发起响应(biz uk 幂等:重复 apply 返回既有办件 duplicated=true;30/40 态 R4 重启推进) */ /** 代发起响应(biz uk 幂等:重复 apply 返回既有办件 duplicated=true;30/40 态 R4 重启推进) */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public static class ApplyResultVo extends FlowDto.ApplyResult { public static class ApplyResultVo extends FlowApplyResult {
} }
/** 按业务键查办件响应(GET /oapi/flow/instance;不存在为 null) */ /** 按业务键查办件响应(GET /oapi/flow/instance;不存在为 null) */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public static class InstanceVo extends FlowDto.Instance { public static class InstanceVo extends FlowInstance {
} }
/** 回调注册请求体(POST /oapi/flow/callback/reg) */ /** 回调注册请求体(POST /oapi/flow/callback/reg) */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public static class CallbackRegBody extends FlowDto.CallbackRegBody { public static class CallbackRegBody extends FlowCallbackRegBody {
} }
/** 回调注册响应(secret 注册时生成下发;重复注册会轮换 secret,绝不重复调用) */ /** 回调注册响应(secret 注册时生成下发;重复注册会轮换 secret,绝不重复调用) */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public static class CallbackRegResultVo extends FlowDto.CallbackRegResult { public static class CallbackRegResultVo extends FlowCallbackRegResult {
} }
} }
@@ -6,7 +6,7 @@ import java.util.List;
/** /**
* 班级管理(userclass)开放接口客户端。 * 班级管理(userclass)开放接口客户端。
* 基址取配置 {@code yh.open.userclass},当前仅提供教师查询,后续班级/年级等接口按需追加。 * 2026-09-16 起委托 SDK 类型化门面 {@code YschClassOpenApi}(服务目录 userclass),当前仅提供教师查询。
*/ */
public interface IClassService { public interface IClassService {
@@ -4,8 +4,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.yunhai.notice.model.flow.FlowModels; import com.yunhai.notice.model.flow.FlowModels;
import com.yunhai.platform.appauth.AppSignInterceptor; import com.yunhai.platform.appauth.AppSignInterceptor;
import com.yunhai.platform.appauth.LocalAppIdentityProvider; import com.yunhai.platform.appauth.LocalAppIdentityProvider;
import com.yunhai.platform.openapi.TargetSpec; import com.yunhai.platform.openapi.core.TargetSpec;
import com.yunhai.platform.openapi.YschOpenCall; import com.yunhai.platform.openapi.core.YschOpenCall;
import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.apistd.BizException;
import com.yunhai.platform.utils.exception.ErrorCode; import com.yunhai.platform.utils.exception.ErrorCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -47,14 +47,16 @@ public class FlowCenterClient implements FlowClient {
if (call == null) { if (call == null) {
// 兜底自建(正常路径由 yh-openapi-starter 装配 YschOpenCall bean);identity 缺失时 // 兜底自建(正常路径由 yh-openapi-starter 装配 YschOpenCall bean);identity 缺失时
// AppSignInterceptor 自动跳过签名(请求裸发,enforce 端会 401——配置问题尽早暴露) // AppSignInterceptor 自动跳过签名(请求裸发,enforce 端会 401——配置问题尽早暴露)
call = new YschOpenCall(new com.yunhai.platform.openapi.YschCallProperties(), objectMapper, call = new YschOpenCall(new com.yunhai.platform.openapi.core.YschCallProperties(), objectMapper,
(host, basePath) -> new AppSignInterceptor(identityProvider.getIfAvailable(), host, basePath)); (host, basePath) -> java.util.Collections.singletonList(
(org.springframework.http.client.ClientHttpRequestInterceptor)
new AppSignInterceptor(identityProvider.getIfAvailable(), host, basePath)));
} }
this.openCall = call; this.openCall = call;
// 服务目录显式配置(targets/services)则走门面命名目标;否则回落 legacy base-url ad-hoc // 服务目录显式配置(targets/services)则走门面命名目标;否则回落 legacy base-url ad-hoc
this.directoryResolved = call.hasTarget(com.yunhai.platform.openapi.PlatformService.FLOWCENTER.key()); this.directoryResolved = call.hasTarget(com.yunhai.platform.openapi.core.PlatformService.FLOWCENTER.key());
if (directoryResolved) { if (directoryResolved) {
com.yunhai.platform.openapi.client.YschFlowOpenApi.setCall(call); com.yunhai.platform.openapi.flow.YschFlowOpenApi.setCall(call);
} }
} }
@@ -62,7 +64,7 @@ public class FlowCenterClient implements FlowClient {
public List<FlowModels.FlowSummaryVo> listFlows(Integer schId) { public List<FlowModels.FlowSummaryVo> listFlows(Integer schId) {
requireEnabled(); requireEnabled();
if (directoryResolved) { if (directoryResolved) {
return com.yunhai.platform.openapi.client.YschFlowOpenApi.listFlows( return com.yunhai.platform.openapi.flow.YschFlowOpenApi.listFlows(
schId, FlowModels.FlowSummaryVo.class); schId, FlowModels.FlowSummaryVo.class);
} }
return openCall.getList(target(), "/oapi/flow/list", params("schId", schId), return openCall.getList(target(), "/oapi/flow/list", params("schId", schId),
@@ -73,7 +75,7 @@ public class FlowCenterClient implements FlowClient {
public FlowModels.ApplyResultVo apply(Integer schId, FlowModels.ApplyBody body) { public FlowModels.ApplyResultVo apply(Integer schId, FlowModels.ApplyBody body) {
requireEnabled(); requireEnabled();
if (directoryResolved) { if (directoryResolved) {
return com.yunhai.platform.openapi.client.YschFlowOpenApi.apply( return com.yunhai.platform.openapi.flow.YschFlowOpenApi.apply(
schId, body, FlowModels.ApplyResultVo.class); schId, body, FlowModels.ApplyResultVo.class);
} }
return openCall.post(target(), "/oapi/flow/apply", params("schId", schId), body, return openCall.post(target(), "/oapi/flow/apply", params("schId", schId), body,
@@ -84,7 +86,7 @@ public class FlowCenterClient implements FlowClient {
public FlowModels.InstanceVo getInstanceByBiz(Integer schId, String bizType, String bizId) { public FlowModels.InstanceVo getInstanceByBiz(Integer schId, String bizType, String bizId) {
requireEnabled(); requireEnabled();
if (directoryResolved) { if (directoryResolved) {
return com.yunhai.platform.openapi.client.YschFlowOpenApi.instance( return com.yunhai.platform.openapi.flow.YschFlowOpenApi.instance(
bizType, bizId, schId, FlowModels.InstanceVo.class); bizType, bizId, schId, FlowModels.InstanceVo.class);
} }
Map<String, String> query = new HashMap<>(); Map<String, String> query = new HashMap<>();
@@ -98,7 +100,7 @@ public class FlowCenterClient implements FlowClient {
public FlowModels.CallbackRegResultVo registerCallback(Integer schId, FlowModels.CallbackRegBody body) { public FlowModels.CallbackRegResultVo registerCallback(Integer schId, FlowModels.CallbackRegBody body) {
requireEnabled(); requireEnabled();
if (directoryResolved) { if (directoryResolved) {
return com.yunhai.platform.openapi.client.YschFlowOpenApi.registerCallback( return com.yunhai.platform.openapi.flow.YschFlowOpenApi.registerCallback(
schId, body, FlowModels.CallbackRegResultVo.class); schId, body, FlowModels.CallbackRegResultVo.class);
} }
return openCall.post(target(), "/oapi/flow/callback/reg", params("schId", schId), body, return openCall.post(target(), "/oapi/flow/callback/reg", params("schId", schId), body,
@@ -2,63 +2,52 @@ package com.yunhai.notice.service.impl;
import com.yunhai.notice.model.SchTeaList; import com.yunhai.notice.model.SchTeaList;
import com.yunhai.notice.service.IClassService; import com.yunhai.notice.service.IClassService;
import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.openapi.clazz.YschClassOpenApi;
import com.yunhai.platform.utils.exception.ErrorCode; import com.yunhai.platform.openapi.clazz.model.SchTea;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
* 班级管理(userclass)开放接口实现。 * 班级管理(userclass)开放接口实现。<br>
* userclass 为开放接口(无需应用签名),使用独立 RestTemplate 直连。 * 2026-09-16 SDK 重构起改为委托类型化门面 {@link YschClassOpenApi}(服务目录 key=userclass:
* services/枚举兜底统一解析,签名/信封/错误语义由 SDK 承载),替代原手搓 RestTemplate +
* legacy 键 {@code yh.open.userclass} 直连(无签名旁路)。Notice 侧模型 {@link SchTeaList}
* 保留(消费方签名零改动),此处仅做字段映射。
*/ */
@Service @Service
public class ClassService implements IClassService { public class ClassService implements IClassService {
private static final Logger log = LoggerFactory.getLogger(ClassService.class); private static final Logger log = LoggerFactory.getLogger(ClassService.class);
/** 班级管理服务基址,如 http://base.yunhaixingtong.com/userclass */
@Value("${yh.open.userclass:}")
private String userClassHost;
private final RestTemplate restTemplate;
public ClassService() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(5000);
factory.setReadTimeout(10000);
this.restTemplate = new RestTemplate(factory);
}
@Override @Override
public List<SchTeaList> getSchTeaList(int schId) { public List<SchTeaList> getSchTeaList(int schId) {
if (!StringUtils.hasText(userClassHost)) { List<SchTea> teachers = YschClassOpenApi.getSchTeaList(schId);
throw new BizException(ErrorCode.INTERNAL_SERVER_ERROR, "班级管理接口未配置(yh.open.userclass)"); log.info("[getSchTeaList] schId={}, 获取教师 {} 人", schId, teachers == null ? 0 : teachers.size());
if (teachers == null || teachers.isEmpty()) {
return Collections.emptyList();
} }
List<SchTeaList> result = new ArrayList<>(teachers.size());
for (SchTea tea : teachers) {
result.add(map(tea));
}
return result;
}
String url = userClassHost + "/oapi/DbClass/GetSchteaList?Group=" + schId; private static SchTeaList map(SchTea tea) {
try { SchTeaList t = new SchTeaList();
ResponseEntity<List<SchTeaList>> resp = restTemplate.exchange( t.setSchSerID(tea.getSchSerId());
url, HttpMethod.GET, null, t.setTeaTel(tea.getTeaTel());
new ParameterizedTypeReference<List<SchTeaList>>() { t.setTeaName(tea.getTeaName());
}); t.setUserTeaSerID(tea.getUserTeaSerId());
List<SchTeaList> list = resp.getBody(); t.setTeaIDCard(tea.getTeaIdCard());
log.info("[getSchTeaList] schId={}, 获取教师 {} 人", schId, list == null ? 0 : list.size()); t.setTeaNo(tea.getTeaNo());
return list != null ? list : Collections.emptyList(); t.setGroupIds(tea.getGroupIds());
} catch (RestClientException e) { t.setGroupId(tea.getGroupId());
log.error("[getSchTeaList] 请求班级管理接口失败, schId={}, url={}", schId, url, e); return t;
throw new BizException(ErrorCode.INTERNAL_SERVER_ERROR, "获取教师数据失败");
}
} }
} }
@@ -8,7 +8,7 @@ import com.yunhai.notice.service.IContactsService;
import com.yunhai.platform.auth.LoginUser; import com.yunhai.platform.auth.LoginUser;
import com.yunhai.platform.auth.UserContextHolder; import com.yunhai.platform.auth.UserContextHolder;
import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.apistd.BizException;
import com.yunhai.platform.openapi.client.YschBaseOpenApi; import com.yunhai.platform.openapi.base.YschBaseOpenApi;
import com.yunhai.platform.utils.exception.ErrorCode; import com.yunhai.platform.utils.exception.ErrorCode;
import com.yunhai.platform.utils.model.BoaUser; import com.yunhai.platform.utils.model.BoaUser;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -21,7 +21,7 @@ import com.yunhai.platform.auth.LoginUser;
import com.yunhai.platform.auth.UserContextHolder; import com.yunhai.platform.auth.UserContextHolder;
import com.yunhai.platform.auth.spring.CurrentBoaUser; import com.yunhai.platform.auth.spring.CurrentBoaUser;
import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.apistd.BizException;
import com.yunhai.platform.openapi.client.YschBaseOpenApi; import com.yunhai.platform.openapi.base.YschBaseOpenApi;
import com.yunhai.platform.utils.exception.ErrorCode; import com.yunhai.platform.utils.exception.ErrorCode;
import com.yunhai.platform.utils.model.BoaGroup; import com.yunhai.platform.utils.model.BoaGroup;
import com.yunhai.platform.utils.model.BoaUser; import com.yunhai.platform.utils.model.BoaUser;
@@ -3,7 +3,7 @@ package com.yunhai.notice.service.impl;
import com.yunhai.notice.model.SchTeaList; import com.yunhai.notice.model.SchTeaList;
import com.yunhai.notice.service.IClassService; import com.yunhai.notice.service.IClassService;
import com.yunhai.notice.service.IUserCenterService; import com.yunhai.notice.service.IUserCenterService;
import com.yunhai.platform.openapi.client.YschBaseOpenApi; import com.yunhai.platform.openapi.base.YschBaseOpenApi;
import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.apistd.BizException;
import com.yunhai.platform.utils.exception.ErrorCode; import com.yunhai.platform.utils.exception.ErrorCode;
import com.yunhai.platform.utils.model.BoaGroup; import com.yunhai.platform.utils.model.BoaGroup;
+1 -4
View File
@@ -93,11 +93,8 @@ yh:
# "100009": "dcc36c98299a4d36bb0ec39d42008e8c" # "100009": "dcc36c98299a4d36bb0ec39d42008e8c"
trust-registry: true trust-registry: true
open: open:
enabled: true # 四域服务目录零配置:userbase/userclass/usermsg/flowcenter 枚举兜底 = base-host + 默认 ctx
base-host: http://base.yunhaixingtong.com base-host: http://base.yunhaixingtong.com
user-center-url: http://base.yunhaixingtong.com/userbase
# 班级管理(userclass)服务基址(原 bschclass 路由已下线)
userclass: http://base.yunhaixingtong.com/userclass
# env-config 前端运行时配置(免登录 GET /api/env-config?target=front,数据源见 # env-config 前端运行时配置(免登录 GET /api/env-config?target=front,数据源见
# config/PropertyEnvConfigSource:聚合 starter 自动装配,有 bean 即注册端点)。 # config/PropertyEnvConfigSource:聚合 starter 自动装配,有 bean 即注册端点)。
# 本地无需配值(前端 dev 走 .env.dev);线上值经环境变量 YH_ENV_CONFIG_SOURCE_FRONT # 本地无需配值(前端 dev 走 .env.dev);线上值经环境变量 YH_ENV_CONFIG_SOURCE_FRONT
@@ -3,9 +3,9 @@ package com.yunhai.notice.service.flow;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.yunhai.notice.model.flow.FlowModels; import com.yunhai.notice.model.flow.FlowModels;
import com.yunhai.platform.openapi.TargetSpec; import com.yunhai.platform.openapi.core.TargetSpec;
import com.yunhai.platform.openapi.YschOpenCall; import com.yunhai.platform.openapi.core.YschOpenCall;
import com.yunhai.platform.openapi.YschCallProperties; import com.yunhai.platform.openapi.core.YschCallProperties;
import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.apistd.BizException;
import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpServer;
@@ -135,7 +135,9 @@ class FlowCenterClientTest {
props.setEnabled(enabled); props.setEnabled(enabled);
props.setBaseUrl("http://localhost:" + port); props.setBaseUrl("http://localhost:" + port);
YschOpenCall openCall = new YschOpenCall(new YschCallProperties(), objectMapper, YschOpenCall openCall = new YschOpenCall(new YschCallProperties(), objectMapper,
(host, basePath) -> (request, body, execution) -> execution.execute(request, body)); (host, basePath) -> java.util.Collections.singletonList(
(org.springframework.http.client.ClientHttpRequestInterceptor)
(request, body, execution) -> execution.execute(request, body)));
return new FlowCenterClient(props, new FixedProvider<>(openCall), null, objectMapper); return new FlowCenterClient(props, new FixedProvider<>(openCall), null, objectMapper);
} }