From 8504ca970c821c11e0184a8c684f62bf310ed4f6 Mon Sep 17 00:00:00 2001 From: chenxing Date: Thu, 17 Sep 2026 14:50:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(apply):=20=E5=8A=9E=E4=BB=B6=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E5=87=BA=E5=8F=82=E8=A1=A5=E5=8F=91=20formSchemaJson?= =?UTF-8?q?=E2=80=94=E2=80=94=E7=94=B3=E8=AF=B7=E5=86=85=E5=AE=B9=E6=8C=89?= =?UTF-8?q?=E8=A1=A8=E5=8D=95=20schema=20=E6=98=BE=E7=A4=BA=E4=B8=AD?= =?UTF-8?q?=E6=96=87=20label=20=E4=B8=8E=E5=88=86=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ApplyVo 新增响应期字段 formSchemaJson(仅 detail 填,不入库) - ServiceFormService.schemaAt:按办件提交时 formVersion 精确命中,缺失回落最新已发布 - 无表单/事项清理/未发布 → null,前端回落按 key 展示 - 单测 ServiceApplyDetailSchemaTest 三态冻结(详情中文化分组 2026-09-17,详见验收清单) --- .../yunhai/svchall/model/svc/SvcModels.java | 6 + .../service/impl/ServiceApplyService.java | 20 ++- .../service/impl/ServiceFormService.java | 14 +++ .../impl/ServiceApplyDetailSchemaTest.java | 115 ++++++++++++++++++ 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/yunhai/svchall/service/impl/ServiceApplyDetailSchemaTest.java diff --git a/src/main/java/com/yunhai/svchall/model/svc/SvcModels.java b/src/main/java/com/yunhai/svchall/model/svc/SvcModels.java index ebb80a5..8c18e9e 100644 --- a/src/main/java/com/yunhai/svchall/model/svc/SvcModels.java +++ b/src/main/java/com/yunhai/svchall/model/svc/SvcModels.java @@ -444,6 +444,12 @@ public final class SvcModels { * enabled=false → 真的不会翻转,直接告知未接入;enabled=true 且 flowStatus=0 → 排队中,等待轮询。 */ private Boolean flowEnabled; + /** + * 办理表单 schema(响应期补发,不入库;仅 detail 出参填): + * 前端「申请内容」以此为 key→中文 label/分组的唯一事实源(2026-09-17),版本口径= + * 办件 formVersion 精确命中、缺失回落最新已发布版本。无表单事项为 null。 + */ + private String formSchemaJson; public static ApplyVo of(com.yunhai.svchall.entity.ServiceApply a) { ApplyVo vo = new ApplyVo(); diff --git a/src/main/java/com/yunhai/svchall/service/impl/ServiceApplyService.java b/src/main/java/com/yunhai/svchall/service/impl/ServiceApplyService.java index 3da5c76..4ff1e0e 100644 --- a/src/main/java/com/yunhai/svchall/service/impl/ServiceApplyService.java +++ b/src/main/java/com/yunhai/svchall/service/impl/ServiceApplyService.java @@ -286,7 +286,25 @@ public class ServiceApplyService { .orElseThrow(() -> new BizException(ErrorCode.PARAM_ERROR, "办件不存在")); applyViewGuard.requireVisible(apply, user); } - return ApplyVo.of(apply); + ApplyVo vo = ApplyVo.of(apply); + vo.setFormSchemaJson(formSchemaJson(apply)); + return vo; + } + + /** + * 申请内容展示 schema(key→中文 label/分组的唯一事实源):办件 → 事项 formCode → + * 按提交时 formVersion 定版(缺失回落最新已发布)取 schemaJson。任何一环缺失(无表单 + * 事项/事项已清理/表单未发布过)返回 null,前端回落按 key 展示,不报错。 + */ + private String formSchemaJson(ServiceApply apply) { + if (apply.getFormVersion() == null || apply.getItemId() == null) { + return null; + } + ServiceItem item = itemRepository.findById(apply.getItemId()).orElse(null); + if (item == null || item.getFormCode() == null || item.getFormCode().isEmpty()) { + return null; + } + return formService.schemaAt(apply.getSchId(), item.getFormCode(), apply.getFormVersion()); } // ===== 管理面 ===== diff --git a/src/main/java/com/yunhai/svchall/service/impl/ServiceFormService.java b/src/main/java/com/yunhai/svchall/service/impl/ServiceFormService.java index 0cfeb85..fb17136 100644 --- a/src/main/java/com/yunhai/svchall/service/impl/ServiceFormService.java +++ b/src/main/java/com/yunhai/svchall/service/impl/ServiceFormService.java @@ -157,4 +157,18 @@ public class ServiceFormService { public Optional latestPublished(Long schId, String code) { return formRepository.findFirstBySchIdAndCodeAndStatusOrderByVersionDesc(schId, code, 1); } + + /** + * 详情展示取 schema:办件提交时的 formVersion 精确命中(快照纪律的展示侧对齐—— + * 表单后续改版不改历史办件的字段含义);该版本已不可考(如被整 code 清理)回落最新已发布版本, + * 再不行给 null(前端回落按 key 展示)。 + */ + public String schemaAt(Long schId, String code, Integer version) { + Optional form = version == null ? Optional.empty() + : formRepository.findBySchIdAndCodeAndVersion(schId, code, version); + if (!form.isPresent()) { + form = latestPublished(schId, code); + } + return form.map(ServiceForm::getSchemaJson).orElse(null); + } } diff --git a/src/test/java/com/yunhai/svchall/service/impl/ServiceApplyDetailSchemaTest.java b/src/test/java/com/yunhai/svchall/service/impl/ServiceApplyDetailSchemaTest.java new file mode 100644 index 0000000..ca67e02 --- /dev/null +++ b/src/test/java/com/yunhai/svchall/service/impl/ServiceApplyDetailSchemaTest.java @@ -0,0 +1,115 @@ +package com.yunhai.svchall.service.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.yunhai.platform.utils.model.BoaUser; +import com.yunhai.svchall.entity.ServiceApply; +import com.yunhai.svchall.entity.ServiceForm; +import com.yunhai.svchall.entity.ServiceItem; +import com.yunhai.svchall.model.svc.SvcModels.ApplyVo; +import com.yunhai.svchall.repository.ServiceApplyRepository; +import com.yunhai.svchall.repository.ServiceItemRepository; +import com.yunhai.svchall.service.FlowClient; +import com.yunhai.svchall.service.SiteDirectory; +import com.yunhai.svchall.service.SiteGuard; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * 办件详情补发表单 schema(2026-09-17):前端「申请内容」不再以英文 key 当标签, + * 改为按 schemaJson 的 groups/fields 显示中文 label 与分组;版本口径=办件提交时 + * formVersion 精确命中,缺失回落最新已发布版本(快照纪律的展示侧对齐)。 + */ +@DisplayName("办件详情 formSchemaJson:版本精确命中/回落/无表单三态") +class ServiceApplyDetailSchemaTest { + + private ServiceApplyRepository applyRepository; + private ServiceItemRepository itemRepository; + private ServiceFormService formService; + private ServiceApplyService service; + private ServiceApply apply; + + @BeforeEach + void setUp() { + applyRepository = mock(ServiceApplyRepository.class); + itemRepository = mock(ServiceItemRepository.class); + formService = mock(ServiceFormService.class); + service = new ServiceApplyService(applyRepository, itemRepository, formService, + mock(AttachmentService.class), mock(FlowClient.class), mock(SiteGuard.class), + mock(SiteDirectory.class), + new com.yunhai.svchall.service.ApplyViewGuard(mock(FlowClient.class)), + new ObjectMapper(), + new org.springframework.transaction.support.TransactionTemplate( + mock(org.springframework.transaction.PlatformTransactionManager.class))); + + apply = new ServiceApply(); + ReflectionTestUtils.setField(apply, "id", 42); + apply.setSchId(1L); + apply.setSiteCode("S1"); + apply.setItemId(7); + apply.setItemCode("ITEM-1"); + apply.setFormVersion(2); + apply.setFormDataJson("{\"name\":\"大猫咪2\"}"); + apply.setSubmitterId(9); + apply.setSubmitterName("张三"); + when(applyRepository.findByIdAndSchId(42, 1L)).thenReturn(Optional.of(apply)); + + ServiceItem item = new ServiceItem(); + ReflectionTestUtils.setField(item, "id", 7); + item.setSchId(1L); + item.setItemCode("ITEM-1"); + item.setFormCode("FORM-1"); + when(itemRepository.findById(7)).thenReturn(Optional.of(item)); + } + + private BoaUser submitter() { + BoaUser user = mock(BoaUser.class, Mockito.RETURNS_DEEP_STUBS); + when(user.getId()).thenReturn(9); + when(user.getType()).thenReturn(2); + when(user.getCurrentRole().getSchId()).thenReturn(1); + return user; + } + + private ServiceForm form(String schema) { + ServiceForm f = new ServiceForm(); + f.setSchemaJson(schema); + return f; + } + + @Test + @DisplayName("formVersion 精确命中 → 出参 formSchemaJson = 该版本 schema") + void detail_withVersionMatch_returnsSchema() { + String schema = "{\"groups\":[{\"title\":\"基本信息\",\"fields\":[{\"key\":\"name\",\"label\":\"姓名\"}]}]}"; + when(formService.schemaAt(1L, "FORM-1", 2)).thenReturn(schema); + ApplyVo vo = service.detail(submitter(), "S1", 42); + assertEquals(schema, vo.getFormSchemaJson()); + } + + @Test + @DisplayName("办件无 formVersion(无表单事项)→ formSchemaJson 为 null") + void detail_withoutFormVersion_returnsNullSchema() { + apply.setFormVersion(null); + ApplyVo vo = service.detail(submitter(), "S1", 42); + assertNull(vo.getFormSchemaJson()); + } + + @Test + @DisplayName("事项未绑定表单 → formSchemaJson 为 null(不外溢查询)") + void detail_itemWithoutFormCode_returnsNullSchema() { + ServiceItem noForm = new ServiceItem(); + ReflectionTestUtils.setField(noForm, "id", 7); + noForm.setFormCode(null); + when(itemRepository.findById(7)).thenReturn(Optional.of(noForm)); + ApplyVo vo = service.detail(submitter(), "S1", 42); + assertNull(vo.getFormSchemaJson()); + } +}