feat(site): /oapi/site/list 按校查询只下发主站——院系子站不接入办事大厅
build / build-push (push) Successful in 50s

- 带 schId 过滤 parentSiteId=0(办事大厅按校接入,一校一主站,SiteOapiIT 冻结契约)
- 不传 schId 全量仍含子站:svc-hall resolveSite 反解 siteCode→schId 依赖,历史办件可继续定位
This commit is contained in:
chenxing
2026-09-17 14:51:07 +08:00
parent 89a5a346d3
commit 8839ef8a5f
2 changed files with 18 additions and 10 deletions
@@ -16,8 +16,10 @@ import java.util.List;
/**
* 站点目录开放接口(/oapi/site/list,appKey 签名访问;V2.0 Part C.3)。
* 消费方=办事大厅 svc-hall(CmsSiteDirectoryImpl:resolveSite 反解 siteCode→schId、
* 站点选择器按校拉取;本地缓存 5min)。schId 可选:不传=全部启用站点(跨校全量,
* 供 siteCode 反解);传了=按校过滤(P0-1 契约对齐,两侧注释口径一致)。
* 站点选择器按校拉取;本地缓存 5min)。schId 可选:
* 不传=全部启用站点(跨校全量,供 siteCode 反解 siteCode→schId,旧办件仍可定位);
* 传了=按校过滤且<b>仅主站</b>(parentSiteId=0):院系子站不接入办事大厅(2026-09-17 口径收窄,
* 办事大厅按校接入一间学校只对应主站一个站点),SiteOapiIT 冻结该契约。
*/
@RestController
@RequestMapping("/oapi/site")
@@ -38,6 +40,8 @@ public class SiteOapiController {
}
return siteRepository.findBySchId(schId).stream()
.filter(site -> site.getStatus() != null && site.getStatus() == 1)
// 仅主站:院系子站不接入办事大厅(反解全量不受影响)
.filter(site -> site.getParentSiteId() == null || site.getParentSiteId() == 0)
.map(SiteOapiController::toVo)
.collect(java.util.stream.Collectors.toList());
}
@@ -41,8 +41,10 @@ class SiteOapiIT extends CmsIntegrationTestBase {
@BeforeEach
void seed() {
purgeTables();
persistSite("demo1", 1, "演示站点一");
persistSite("schx", 1, "主站X子站");
CmsSite demo1 = persistSite("demo1", 1, "演示站点一");
// schx 为 demo1 的院系子站:《院系子站方案》接入口径——子站不进办事大厅,
// 按校下发(svc-hall 站点选择器 listSites(schId))只含主站;反解全量仍含子站。
persistSite("schx", 1, "主站X子站", demo1.getId());
persistSite("demo2", 2, "演示站点二");
// 停用站点:任何形态(全量/按校)都不得下发
CmsSite disabled = persistSite("off1", 1, "停用站点");
@@ -73,26 +75,28 @@ class SiteOapiIT extends CmsIntegrationTestBase {
}
@Test
@DisplayName("带签名不传 schId → 全部启用站点(跨校全量,供 resolveSite 反解 siteCode)")
@DisplayName("带签名不传 schId → 全部启用站点(含子站,供 resolveSite 反解 siteCode→schId)")
void signed_withoutSchId_returnsAllEnabledSites() throws Exception {
MvcResult result = mockMvc.perform(get("/oapi/site/list").headers(signHeaders("/oapi/site/list")))
.andExpect(status().isOk())
.andReturn();
String body = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
assertThat(body).contains("\"siteCode\":\"demo1\"").contains("\"siteCode\":\"demo2\"");
assertThat(body).contains("\"siteCode\":\"demo1\"").contains("\"siteCode\":\"demo2\"")
.contains("\"siteCode\":\"schx\"");
assertThat(body).doesNotContain("off1").doesNotContain("停用站点");
}
@Test
@DisplayName("带签名传 schId → 仅该校启用站点")
void signed_withSchId_filtersBySchool() throws Exception {
@DisplayName("带签名传 schId → 仅该校启用主站(院系子站不接入办事大厅,不下发)")
void signed_withSchId_returnsMainSitesOnly() throws Exception {
MvcResult result = mockMvc.perform(get("/oapi/site/list?schId=1")
.headers(signHeaders("/oapi/site/list?schId=1")))
.andExpect(status().isOk())
.andReturn();
String body = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
assertThat(body).contains("\"siteCode\":\"demo1\"").contains("\"siteCode\":\"schx\"");
assertThat(body).doesNotContain("demo2");
assertThat(body).contains("\"siteCode\":\"demo1\"");
assertThat(body).doesNotContain("schx").doesNotContain("主站X子站");
assertThat(body).doesNotContain("demo2").doesNotContain("off1");
}
@Test