完整的 FakeRegistrationController
代碼,這讓我可以全面分析后端邏輯,特別是為什么空的字段(如 compareDate
)不返回給前端。我將詳細分析代碼的每個接口,尤其是與 list
請求和字段返回相關的部分,并解釋原因。
代碼結構總覽
FakeRegistrationController
是一個 RESTful 控制器,提供了三個接口:
POST /fakeRegistration/registration/save
:保存或修改假貨登記信息。POST /fakeRegistration/compare/save
:保存或修改比對結果。GET /fakeRegistration/list
:獲取假貨登記列表(對應前端的qlist
請求)。
我們關心的是 list
接口,因為它直接決定了前端表格接收到的數據內容,特別是為何空的 compareDate
不返回。
詳細分析 list
接口
接口定義
@GetMapping("list")
@ApiOperation("假貨登記列表")
public BaseResult list(@ApiIgnore @SessionAttribute(Constants.ADMIN_ID) Integer adminId,BasePage page) {Integer vipAdminId = copywritingApiService.getVipAdminId(adminId, 11);Page<FakeRegistration> fakeRegistrationPage = fakeRegistrationService.findByAdminId(vipAdminId, page);List<FakeRegistration> fakeRegistrations = fakeRegistrationPage.getContent();List<FakeRegistrationListDTO> fakeRegistrationListDTOS = new ArrayList<>();for (FakeRegistration fakeRegistration: fakeRegistrations) {FakeRegistrationListDTO fakeRegistrationListDTO = JSON.parseObject(JSON.toJSONString(fakeRegistration), FakeRegistrationListDTO.class);if (null != fakeRegistration.getProductId()) {Product product = productService.findById(fakeRegistration.getProductId()).orElseThrow(() -> new RuntimeException("未找到商品信息"));fakeRegistrationListDTO.setProductName(product.getName());}Admin creator = adminService.findById(fakeRegistration.getCreatorId()).orElseThrow(() -> new RuntimeException("未找到創建人信息"));fakeRegistrationListDTO.setCreatorName(StringUtils.isEmpty(creator.getNickname()) ? creator.getUsername() : creator.getNickname());fakeRegistrationListDTO.setGenuineIdentificationPoints(fakeRegistrationApiService.findIdentification(fakeRegistration.getId(), 1));fakeRegistrationListDTO.setFakeIdentificationPoints(fakeRegistrationApiService.findIdentification(fakeRegistration.getId(), 0));fakeRegistrationListDTOS.add(fakeRegistrationListDTO);}return BaseResult.success(new PageImpl<>(fakeRegistrationListDTOS, PageRequest.of(fakeRegistrationPage.getNumber(), fakeRegistrationPage.getSize()), fakeRegistrationPage.getTotalElements()));
}
數據流分析
-
數據查詢:
fakeRegistrationService.findByAdminId(vipAdminId, page)
返回一個Page<FakeRegistration>
,其中FakeRegistration
是數據庫實體類,包含所有字段(如id
,createdDate
,compareDate
,comparisonStatus
等)。fakeRegistrations
是分頁內容的List<FakeRegistration>
。
-
數據轉換:
- 每個
FakeRegistration
被轉換為FakeRegistrationListDTO
:FakeRegistrationListDTO fakeRegistrationListDTO = JSON.parseObject(JSON.toJSONString(fakeRegistration), FakeRegistrationListDTO.class);
- 這里使用了 FastJSON(
com.alibaba.fastjson.JSON
)進行序列化和反序列化:JSON.toJSONString(fakeRegistration)
將FakeRegistration
轉為 JSON 字符串。JSON.parseObject(..., FakeRegistrationListDTO.class)
將 JSON 字符串轉為FakeRegistrationListDTO
對象。
- 然后手動補充了:
productName
:從ProductService
獲取。creatorName
:從AdminService
獲取。genuineIdentificationPoints
和fakeIdentificationPoints
:從FakeRegistrationApiService
獲取。
- 每個
-
返回結果:
- 返回一個
PageImpl<FakeRegistrationListDTO>
,最終被序列化為 JSON 響應。
- 返回一個
為什么空的字段(如 compareDate
)不返回?
-
FastJSON 的默認行為:
- FastJSON 在序列化時,默認忽略
null
值字段,除非顯式配置SerializerFeature.WriteMapNullValue
。 - 如果
FakeRegistration
的compareDate
是null
,JSON.toJSONString(fakeRegistration)
生成的 JSON 字符串不會包含"compareDate"
。 - 示例:
FakeRegistration fr = new FakeRegistration(); fr.setId(34); fr.setComparisonStatus(1); fr.setCompareDate(null); // 為空 String json = JSON.toJSONString(fr); // 輸出: {"id":34,"comparisonStatus":1}
- 反序列化到
FakeRegistrationListDTO
時,由于 JSON 中沒有compareDate
,fakeRegistrationListDTO
的compareDate
字段不會被賦值,最終返回的 JSON 也不會包含它。
- FastJSON 在序列化時,默認忽略
-
業務邏輯:
- 從其他接口看,
compareDate
只有在compare/save
接口保存比對結果時才會設置:fakeRegistrationOrigin.setCompareDate(new Date());
- 在
registration/save
接口中,新建記錄時沒有設置compareDate
,它保持為null
。 - 因此,未完成比對的記錄(
comparisonStatus = 0
或1
)在數據庫中compareDate
就是NULL
,序列化后被忽略。
- 從其他接口看,
-
DTO 定義的影響:
- 如果
FakeRegistrationListDTO
中定義了compareDate
:public class FakeRegistrationListDTO {private Integer id;private Date compareDate; // 假設是這樣// 其他字段 }
- 當
JSON.parseObject
處理沒有compareDate
的 JSON 時,fakeRegistrationListDTO.compareDate
會是null
,但后續的序列化(返回給前端時)仍由 FastJSON 處理,又會被忽略。
- 如果
其他接口的補充分析
1. registration/save
@PostMapping("registration/save")
public BaseResult save(@RequestBody FakeRegistration fakeRegistration, ...) {if(null != fakeRegistration.getId()) {FakeRegistration fakeRegistrationOrigin = fakeRegistrationService.findById(fakeRegistration.getId()).orElseThrow(...);fakeRegistration = SqlUtil.mergeObject(fakeRegistration, fakeRegistrationOrigin);if(null != fakeRegistration.getCompareResult() && fakeRegistration.getCompareResult() == 1) {fakeRegistrationOrigin.setComparisonStatus(3);} else if(null != fakeRegistration.getCompareResult() && fakeRegistration.getCompareResult() == 0) {fakeRegistrationOrigin.setComparisonStatus(2);}} else {fakeRegistration.setAdminId(vipAdminId);fakeRegistration.setCreatorId(adminId);}fakeRegistration = fakeRegistrationService.save(fakeRegistration);...
}
- 關鍵點:
- 新建記錄時(
fakeRegistration.getId() == null
),只設置了adminId
和creatorId
,沒有初始化compareDate
,它默認為null
。 - 修改記錄時,如果提供了
compareResult
,會更新comparisonStatus
,但不會設置compareDate
。 - 因此,
compareDate
在這個接口中始終不會被賦值。
- 新建記錄時(
2. compare/save
@PostMapping("compare/save")
public BaseResult compareSave(@RequestBody FakeRegistrationSaveDTO fakeRegistration, ...) {FakeRegistration fakeRegistrationOrigin = fakeRegistrationService.findById(fakeRegistration.getId()).orElseThrow(...);fakeRegistrationOrigin.setCompareResult(fakeRegistration.getCompareResult());fakeRegistrationOrigin.setCompareDate(new Date());fakeRegistrationService.save(fakeRegistrationOrigin);...
}
- 關鍵點:
- 這里顯式設置了
compareDate = new Date()
,表示比對完成的時間。 - 只有通過這個接口保存比對結果時,
compareDate
才會被賦值。 - 這解釋了前端數據中,只有
comparisonStatus = 2
或更高狀態的記錄有compareDate
。
- 這里顯式設置了
綜合結論:為什么空的字段不返回?
-
數據來源:
compareDate
在數據庫中默認為NULL
,除非通過compare/save
接口保存比對結果時才會被賦值。- 未完成比對的記錄(
comparisonStatus = 0
或1
)沒有compareDate
值。
-
FastJSON 序列化:
list
接口中,JSON.toJSONString(fakeRegistration)
將FakeRegistration
轉為 JSON 時,默認忽略null
字段。- 如果
compareDate
是null
,它不會出現在 JSON 字符串中。 - 反序列化到
FakeRegistrationListDTO
后,compareDate
仍未被賦值,最終返回的 JSON 也不包含這個字段。
-
業務邏輯:
- 設計上,
compareDate
只有在比對完成時才有意義,未完成時保持null
并被忽略是合理的。
- 設計上,
-
最終返回:
- Spring Boot 的
@RestController
使用 Jackson(或其他配置)將PageImpl<FakeRegistrationListDTO>
序列化為 JSON,同樣忽略null
字段,進一步確保空的compareDate
不出現在響應中。
- Spring Boot 的
前端數據的驗證
你的前端數據:
id: 34
,33
,32
(comparisonStatus = 0
或1
):沒有compareDate
,因為未調用compare/save
。id: 31
,30
,28
(comparisonStatus = 2
):有compareDate
,因為通過compare/save
設置了值。
改進建議
如果希望空的 compareDate
也返回(例如 "compareDate": null
):
-
修改 FastJSON 配置:
FakeRegistrationListDTO fakeRegistrationListDTO = JSON.parseObject(JSON.toJSONString(fakeRegistration, SerializerFeature.WriteMapNullValue),FakeRegistrationListDTO.class );
- 添加
WriteMapNullValue
,確保null
字段被包含。
- 添加
-
調整 DTO:
- 在
FakeRegistrationListDTO
中顯式處理:fakeRegistrationListDTO.setCompareDate(fakeRegistration.getCompareDate() != null ? fakeRegistration.getCompareDate() : null);
- 在
-
全局配置:
- 在 Spring Boot 中配置 Jackson:
@Bean public ObjectMapper objectMapper() {ObjectMapper mapper = new ObjectMapper();mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);return mapper; }
- 在 Spring Boot 中配置 Jackson:
總結
空的 compareDate
不返回給前端是因為:
- 業務邏輯:未完成比對時,數據庫中
compareDate
為NULL
。 - FastJSON 默認行為:
JSON.toJSONString
忽略null
字段。 - 設計選擇:后端選擇不返回無意義的空字段。
這種行為是合理的,但如果前端需要一致性,可以通過上述方式調整后端返回。