上一批只落了实体与 oapi 出参,管理端 save 入参与编辑回显没接上, 导致 front 录入的 6 个字段被 Jackson 静默丢弃。 - SvcModels.SvcItemSaveRequest 补 7 字段(含 @Size 校验,与实体列长一致) - SvcModels.SvcItemEditVo 补 7 字段 + of() 映射 - ServiceItemService.adminSave 补 7 行赋值(空串归一 null) - 新增 SvcItemPromiseFieldsTest:反序列化字段名对齐 + EditVo 映射(3 例) - .gitignore 排除 local-debug/(本地探针,不入库)
This commit is contained in:
@@ -3,3 +3,6 @@ target/
|
|||||||
.idea/
|
.idea/
|
||||||
*.iml
|
*.iml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# 本地调试脚本与探针输出(不入库)
|
||||||
|
local-debug/
|
||||||
|
|||||||
@@ -174,6 +174,26 @@ public final class SvcModels {
|
|||||||
private String flowGuid;
|
private String flowGuid;
|
||||||
@Size(max = 500)
|
@Size(max = 500)
|
||||||
private String applyUrl;
|
private String applyUrl;
|
||||||
|
// ===== 服务承诺(P1-1):管理端录入,全部可空 =====
|
||||||
|
/** 协办部门(顿号分隔文本快照) */
|
||||||
|
@Size(max = 500)
|
||||||
|
private String coopDepts;
|
||||||
|
/** 经办人员 */
|
||||||
|
@Size(max = 64)
|
||||||
|
private String handlerName;
|
||||||
|
/** 办理时间(办公时段,区别于办理时限) */
|
||||||
|
@Size(max = 200)
|
||||||
|
private String handleTimeDesc;
|
||||||
|
/** 跑现场次数(0=零跑动) */
|
||||||
|
private Integer runCount;
|
||||||
|
/** 是否快递送达:0 否 / 1 是(到付)/ 2 是(学校承担) */
|
||||||
|
private Integer expressFlag;
|
||||||
|
/** 收费标准 */
|
||||||
|
@Size(max = 200)
|
||||||
|
private String feeDesc;
|
||||||
|
/** 收费依据 */
|
||||||
|
@Size(max = 200)
|
||||||
|
private String feeBasis;
|
||||||
private Integer sortNo;
|
private Integer sortNo;
|
||||||
/** 指南附件 id 列表(保存时全量重绑 topic_type=svc_item_guide) */
|
/** 指南附件 id 列表(保存时全量重绑 topic_type=svc_item_guide) */
|
||||||
private List<Integer> attachmentIds;
|
private List<Integer> attachmentIds;
|
||||||
@@ -207,6 +227,14 @@ public final class SvcModels {
|
|||||||
private String formCode;
|
private String formCode;
|
||||||
private String flowGuid;
|
private String flowGuid;
|
||||||
private String applyUrl;
|
private String applyUrl;
|
||||||
|
// ===== 服务承诺(P1-1):编辑页回显,与 SvcItemSaveRequest 同名 =====
|
||||||
|
private String coopDepts;
|
||||||
|
private String handlerName;
|
||||||
|
private String handleTimeDesc;
|
||||||
|
private Integer runCount;
|
||||||
|
private Integer expressFlag;
|
||||||
|
private String feeDesc;
|
||||||
|
private String feeBasis;
|
||||||
private Integer sortNo;
|
private Integer sortNo;
|
||||||
private Integer status;
|
private Integer status;
|
||||||
private LocalDateTime publishTime;
|
private LocalDateTime publishTime;
|
||||||
@@ -241,6 +269,13 @@ public final class SvcModels {
|
|||||||
vo.setFormCode(item.getFormCode());
|
vo.setFormCode(item.getFormCode());
|
||||||
vo.setFlowGuid(item.getFlowGuid());
|
vo.setFlowGuid(item.getFlowGuid());
|
||||||
vo.setApplyUrl(item.getApplyUrl());
|
vo.setApplyUrl(item.getApplyUrl());
|
||||||
|
vo.setCoopDepts(item.getCoopDepts());
|
||||||
|
vo.setHandlerName(item.getHandlerName());
|
||||||
|
vo.setHandleTimeDesc(item.getHandleTimeDesc());
|
||||||
|
vo.setRunCount(item.getRunCount());
|
||||||
|
vo.setExpressFlag(item.getExpressFlag());
|
||||||
|
vo.setFeeDesc(item.getFeeDesc());
|
||||||
|
vo.setFeeBasis(item.getFeeBasis());
|
||||||
vo.setSortNo(item.getSortNo());
|
vo.setSortNo(item.getSortNo());
|
||||||
vo.setStatus(item.getStatus());
|
vo.setStatus(item.getStatus());
|
||||||
vo.setPublishTime(item.getPublishTime());
|
vo.setPublishTime(item.getPublishTime());
|
||||||
|
|||||||
@@ -199,6 +199,14 @@ public class ServiceItemService {
|
|||||||
item.setFormCode(emptyToNull(req.getFormCode()));
|
item.setFormCode(emptyToNull(req.getFormCode()));
|
||||||
item.setFlowGuid(emptyToNull(req.getFlowGuid()));
|
item.setFlowGuid(emptyToNull(req.getFlowGuid()));
|
||||||
item.setApplyUrl(applyUrl);
|
item.setApplyUrl(applyUrl);
|
||||||
|
// 服务承诺(P1-1):空串归一为 null,避免库里出现 "" 与 null 两种空值
|
||||||
|
item.setCoopDepts(emptyToNull(req.getCoopDepts()));
|
||||||
|
item.setHandlerName(emptyToNull(req.getHandlerName()));
|
||||||
|
item.setHandleTimeDesc(emptyToNull(req.getHandleTimeDesc()));
|
||||||
|
item.setRunCount(req.getRunCount());
|
||||||
|
item.setExpressFlag(req.getExpressFlag());
|
||||||
|
item.setFeeDesc(emptyToNull(req.getFeeDesc()));
|
||||||
|
item.setFeeBasis(emptyToNull(req.getFeeBasis()));
|
||||||
item.setSortNo(req.getSortNo() == null ? 0 : req.getSortNo());
|
item.setSortNo(req.getSortNo() == null ? 0 : req.getSortNo());
|
||||||
item = itemRepository.save(item);
|
item = itemRepository.save(item);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.yunhai.svchall.model.svc;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.yunhai.svchall.entity.ServiceItem;
|
||||||
|
import com.yunhai.svchall.model.svc.SvcModels.SvcItemEditVo;
|
||||||
|
import com.yunhai.svchall.model.svc.SvcModels.SvcItemSaveRequest;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务承诺字段(P1-1)的入参与回显契约测试。
|
||||||
|
* <p>
|
||||||
|
* 守两件事:
|
||||||
|
* ① 管理端前端用的字段名 → {@link SvcItemSaveRequest} 能反序列化进来(Jackson 默认忽略未知字段,
|
||||||
|
* 名字写错不会报错、只会静默丢数据,必须显式测);
|
||||||
|
* ② {@link SvcItemEditVo#of} 能把实体上的 7 个字段原样映射到编辑页回显。
|
||||||
|
* <p>
|
||||||
|
* 不测 adminSave 的落库:那需要 mock repository,价值低于上面两条(字段名对齐是最易错点)。
|
||||||
|
*/
|
||||||
|
class SvcItemPromiseFieldsTest {
|
||||||
|
|
||||||
|
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("SaveRequest 能接住前端传来的 7 个承诺字段(字段名不得漂移)")
|
||||||
|
void saveRequestDeserializesPromiseFields() throws Exception {
|
||||||
|
String json = "{"
|
||||||
|
+ "\"coopDepts\":\"组织部、教务处\","
|
||||||
|
+ "\"handlerName\":\"田老师\","
|
||||||
|
+ "\"handleTimeDesc\":\"工作日 08:00-11:45\","
|
||||||
|
+ "\"runCount\":1,"
|
||||||
|
+ "\"expressFlag\":2,"
|
||||||
|
+ "\"feeDesc\":\"50 元\","
|
||||||
|
+ "\"feeBasis\":\"浙价费〔2020〕1 号\""
|
||||||
|
+ "}";
|
||||||
|
|
||||||
|
SvcItemSaveRequest req = MAPPER.readValue(json, SvcItemSaveRequest.class);
|
||||||
|
|
||||||
|
assertEquals("组织部、教务处", req.getCoopDepts());
|
||||||
|
assertEquals("田老师", req.getHandlerName());
|
||||||
|
assertEquals("工作日 08:00-11:45", req.getHandleTimeDesc());
|
||||||
|
assertEquals(1, req.getRunCount());
|
||||||
|
assertEquals(2, req.getExpressFlag());
|
||||||
|
assertEquals("50 元", req.getFeeDesc());
|
||||||
|
assertEquals("浙价费〔2020〕1 号", req.getFeeBasis());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("EditVo.of 把实体 7 个承诺字段映射到回显")
|
||||||
|
void editVoMapsPromiseFields() {
|
||||||
|
ServiceItem item = new ServiceItem();
|
||||||
|
item.setId(12);
|
||||||
|
item.setSiteCode("yhms");
|
||||||
|
item.setItemCode("teacher-onboard");
|
||||||
|
item.setTitle("教师入职办理");
|
||||||
|
item.setCoopDepts("组织部、教务处");
|
||||||
|
item.setHandlerName("田老师");
|
||||||
|
item.setHandleTimeDesc("工作日 08:00-11:45");
|
||||||
|
item.setRunCount(0);
|
||||||
|
item.setExpressFlag(1);
|
||||||
|
item.setFeeDesc("无");
|
||||||
|
item.setFeeBasis(null);
|
||||||
|
|
||||||
|
SvcItemEditVo vo = SvcItemEditVo.of(item);
|
||||||
|
|
||||||
|
assertEquals("组织部、教务处", vo.getCoopDepts());
|
||||||
|
assertEquals("田老师", vo.getHandlerName());
|
||||||
|
assertEquals("工作日 08:00-11:45", vo.getHandleTimeDesc());
|
||||||
|
assertEquals(0, vo.getRunCount());
|
||||||
|
assertEquals(1, vo.getExpressFlag());
|
||||||
|
assertEquals("无", vo.getFeeDesc());
|
||||||
|
assertNull(vo.getFeeBasis());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("承诺字段全空时 SaveRequest 不报错(全部可空)")
|
||||||
|
void saveRequestAllowsNulls() throws Exception {
|
||||||
|
SvcItemSaveRequest req = MAPPER.readValue("{\"title\":\"x\"}", SvcItemSaveRequest.class);
|
||||||
|
|
||||||
|
assertNull(req.getCoopDepts());
|
||||||
|
assertNull(req.getRunCount());
|
||||||
|
assertNull(req.getExpressFlag());
|
||||||
|
assertNull(req.getFeeDesc());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user