feat: 表单使用场景可见闭环——form/list 返回绑定事项明细 refItems
build / build-push (push) Failing after 26s

- SvcFormVo 增 refItems(ItemBrief: id/itemCode/title/siteCode/status),referenced 同源派生;仅 list 填充,versions/get 不带
- ServiceItemRepository.findBySchIdAndFormCodeIsNotNull 一次查全量按 formCode 内存分组,替代逐表单 count 的 N+1
- ServiceFormServiceTest 5 用例(未绑定/单绑/跨站点/多版本聚合/分组不串)
- 前端配套(svc-hall-front 另推):绑定明细弹窗+直达事项编辑、发布未绑定引导、schema 预览、事项编辑页草稿表单提示
This commit is contained in:
chenxing
2026-09-16 16:33:55 +08:00
parent dedb4aa436
commit 76b35b2fb3
4 changed files with 171 additions and 2 deletions
@@ -0,0 +1,133 @@
package com.yunhai.svchall.service.impl;
import com.yunhai.svchall.entity.ServiceForm;
import com.yunhai.svchall.entity.ServiceItem;
import com.yunhai.svchall.model.svc.SvcModels.SvcFormVo;
import com.yunhai.svchall.repository.ServiceFormRepository;
import com.yunhai.svchall.repository.ServiceItemRepository;
import com.yunhai.svchall.service.SiteDirectory;
import com.yunhai.svchall.service.SiteGuard;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* 表单列表使用场景可见性单测:list() 必须带回"被哪些事项绑定"的明细(refItems),
* 管理员才能从表单页反查使用位置(一期可见闭环);表单 sch 级共享,跨站点绑定如实展示。
*/
class ServiceFormServiceTest {
private ServiceFormRepository formRepository;
private ServiceItemRepository itemRepository;
private ServiceFormService service;
private SiteDirectory.SiteInfo site = new SiteDirectory.SiteInfo("S1", "站点一", 1L);
@BeforeEach
void setUp() {
formRepository = mock(ServiceFormRepository.class);
itemRepository = mock(ServiceItemRepository.class);
SiteGuard siteGuard = mock(SiteGuard.class);
when(siteGuard.requireSiteManager(null, "S1")).thenReturn(site);
service = new ServiceFormService(formRepository, itemRepository, siteGuard);
}
private ServiceForm form(String code, int version, int status) {
ServiceForm form = new ServiceForm();
form.setSchId(1L);
form.setCode(code);
form.setName("表单-" + code);
form.setVersion(version);
form.setStatus(status);
form.setSchemaJson("{\"groups\":[]}");
return form;
}
private ServiceItem item(Integer id, String siteCode, String itemCode, String title, String formCode) {
ServiceItem item = new ServiceItem();
item.setSchId(1L);
item.setSiteCode(siteCode);
item.setItemCode(itemCode);
item.setTitle(title);
item.setFormCode(formCode);
item.setStatus(1);
item.setId(id);
return item;
}
@Test
void unboundFormHasEmptyRefItemsAndNotReferenced() {
when(formRepository.findBySchIdOrderByCodeAscVersionDescIdAsc(1L))
.thenReturn(Arrays.asList(form("leave", 1, 1)));
when(itemRepository.findBySchIdAndFormCodeIsNotNull(1L)).thenReturn(Arrays.asList());
SvcFormVo vo = service.list(null, "S1").get(0);
assertFalse(vo.getReferenced());
assertTrue(vo.getRefItems().isEmpty(), "未绑定表单的 refItems 应为空列表而非 null");
}
@Test
void boundFormCarriesRefItemBrief() {
when(formRepository.findBySchIdOrderByCodeAscVersionDescIdAsc(1L))
.thenReturn(Arrays.asList(form("leave", 1, 1)));
when(itemRepository.findBySchIdAndFormCodeIsNotNull(1L))
.thenReturn(Arrays.asList(item(11, "S1", "IT-001", "请假申请", "leave")));
SvcFormVo vo = service.list(null, "S1").get(0);
assertTrue(vo.getReferenced());
assertEquals(1, vo.getRefItems().size());
SvcFormVo.ItemBrief brief = vo.getRefItems().get(0);
assertEquals(Integer.valueOf(11), brief.getId());
assertEquals("IT-001", brief.getItemCode());
assertEquals("请假申请", brief.getTitle());
assertEquals("S1", brief.getSiteCode());
assertEquals(Integer.valueOf(1), brief.getStatus());
}
@Test
void crossSiteBindingsAreAllListed() {
// 表单 sch 级共享:同 sch 下另一站点的事项绑了同一张表单,也要在明细里可见
when(formRepository.findBySchIdOrderByCodeAscVersionDescIdAsc(1L))
.thenReturn(Arrays.asList(form("leave", 1, 1)));
when(itemRepository.findBySchIdAndFormCodeIsNotNull(1L))
.thenReturn(Arrays.asList(
item(11, "S1", "IT-001", "请假申请", "leave"),
item(12, "S2", "IT-002", "教职工请假", "leave")));
SvcFormVo vo = service.list(null, "S1").get(0);
assertEquals(2, vo.getRefItems().size());
}
@Test
void versionAggregationKeepsRefItemsOnLatestRow() {
// 同 code 多版本聚合出最新版一行;绑定关系挂在 code 上,明细归到该行
when(formRepository.findBySchIdOrderByCodeAscVersionDescIdAsc(1L))
.thenReturn(Arrays.asList(form("leave", 2, 0), form("leave", 1, 1)));
when(itemRepository.findBySchIdAndFormCodeIsNotNull(1L))
.thenReturn(Arrays.asList(item(11, "S1", "IT-001", "请假申请", "leave")));
List<SvcFormVo> list = service.list(null, "S1");
assertEquals(1, list.size());
assertEquals(Integer.valueOf(2), list.get(0).getVersion());
assertEquals(1, list.get(0).getRefItems().size());
}
@Test
void refItemsAreGroupedToTheirOwnForm() {
when(formRepository.findBySchIdOrderByCodeAscVersionDescIdAsc(1L))
.thenReturn(Arrays.asList(form("leave", 1, 1), form("seal", 1, 1)));
when(itemRepository.findBySchIdAndFormCodeIsNotNull(1L))
.thenReturn(Arrays.asList(item(11, "S1", "IT-001", "请假申请", "leave")));
List<SvcFormVo> list = service.list(null, "S1");
assertEquals(0, list.get(1).getRefItems().size(), "seal 表单不应带上 leave 的事项明细");
assertEquals("seal", list.get(1).getCode());
}
}