C004.23届0525怪
C004.23届0525怪
1.CompanyManageController.java 70-74
gigax 6697d36f75f4ced94545489e61f8c3781ccf5ecd
@RequestMapping("/admin/parcel/company-name.json")
public R<?> searchCompanyName() {
Map<Long, String> map = companySearchService.searchName(TenantUtil.getTenantable());
return R.okData(map);
}
// 修改后
@PostMapping("/admin/parcel/company-name.json")
public R<Map<Long, String>> searchCompanyName() {
final Map<Long, String> map = companySearchService.searchName(TenantUtil.getTenantable());
return R.okData(map);
}
明确请求 PostMapping
RequestMapping包括了 Get, Post, Put, Delete, Patch,对Swagger文档不友好,也容易造成乱用。 按wings的RestHalf约定,除了资源类和Oauht使用GET外,全部使用Post。
明确返回值 R的泛型
明确R的泛型,有利于Swagger自动推导response的示例,所以应该避免R<?>
,一般呈现以下形式。
R<Void>
- 无data,可以返回R.OK
R<DataType>
- 明确的data类型PageResult<DataType>
- 分页的,明确的Data类型
2.CompanyManageController.java 101-110
gigax 6697d36f75f4ced94545489e61f8c3781ccf5ecd
@RequestMapping(value = "/admin/company/delete.json")
public R<?> deleteInfo(@RequestParam(value = "userId") long userId) {
try {
companySearchService.deleteCustomer(userId);
return R.ok("删除成功!");
}
catch (Exception e) {
log.error("/admin/company/delete.json", e);
return R.ng("删除失败!");
}
}
统一处理异常
没有必要在Controller中处理通用异常,交给HandlerExceptionResolver统一处理。
- CodeException - 默认无栈异常,属正常业务流,自动转换I18n
- Default - 默认处理未识别异常为预设的得response
3.DashboardController.java 56-59
gigax 6697d36f75f4ced94545489e61f8c3781ccf5ecd
@PostMapping(value = "/dashboard/out/export.json")
@DoubleKill
public void export(@RequestBody DashboardService.QueryDate queryDate, HttpServletResponse response)
DoubleKill的使用注意Key
DoubleKill基于AOP,建议使用SpEL指定key,否则方法的参数需要正确实现equals, hashCode,才能正确获取锁。
4.DashboardServiceImpl.java 332-337
gigax 6697d36f75f4ced94545489e61f8c3781ccf5ecd
private void export(List<ExcelInfo> excelInfos) throws IOException {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert servletRequestAttributes != null;
HttpServletResponse response = servletRequestAttributes.getResponse();
assert response != null;
EasyExcel.write(response.getOutputStream(), ExcelInfo.class)
// 修改后
private void export(@NotNull List<ExcelInfo> excelInfos, @NotNull OutputStream output) throws IOException {
EasyExcel.write(routput, ExcelInfo.class)
严禁跨层,严禁隐式传值
- Web层对象不可以进入Service层,违反基本约定。
- 从ContextHolder取值属于方法泄露,违反基本约定。
- assert 改为前置检查,以CodeException中断处理。
- @NotNull 提供IDE检查,在编写时提供质量。
5.OrderController.java 103-106
gigax 64077da17ce4920f6c8d7088664816bb0f36a36b
@PostMapping("/parcel/truck/check-list.json")
public R<Collection<ParcelCheckQueryVo>> checkList(@RequestParam Long id) {
List<OwtLadingOrder> data = owtLadingOrderDao.fetchByTruckOrderId(id);
return R.okData(parcelHelper.transferParcelCheckQueryVo(data));
}
Controller中不可使用Dao
- 建议包装到Service,避免违反Mvc,Service/Dao的分层约定
- 若简单read,可把Dao当做Service使用,但按需select,不可泄露非必要的po信息
6.ParcelBindController.java 147-158
gigax 64077da17ce4920f6c8d7088664816bb0f36a36b
@PostMapping("/truck/truck/bindPlate.json")
public R<?> bindPlate(@PageDefault(size = 20) PageQuery pageQuery,
@RequestParam(required = false) Long supplier,
@RequestParam(required = false) String searchType,
@RequestParam(required = false) String container,
@RequestParam(required = false) Long companyId,
@RequestParam(required = false) String address,
@RequestParam(required = false) Boolean isUpdate,
@RequestParam(required = false) Integer status,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime start,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime end,
@RequestParam(required = false) LocalDate startDate,
@RequestParam(required = false) LocalDate endDate,
@RequestParam(required = false) String arriveDtStatus,
@RequestParam(required = false) Boolean signatureFileStatus,
@RequestParam(required = false) Long dismantle,
@RequestParam Boolean isTruck) {
使用对象取代长参数列表
@RequestBody BizIns ins
用于承载业务数据@PageDefault(size = 20) PageQuery pq
使用queryString接受参数,享受别名功能
以上为wings的约定,建议前端api使用TypeScript封装,如利用Axios请求及拦截处理。
@RequestParam Boolean isTruck
使用boolean
,因为必填不能为null- 使用Json包装业务数据,可以更好的支持enum,datatime的解析
7.TempSearchController.java 49-60
gigax 350877671f7ee1cbc9f444b99d822bc20257c415
// type enum
if (type == null) {
return tempSearchService.getList(pageQuery, ladingIds);
} else if (type.equals(QueryType.AirBol)) {
return tempSearchService.getByAirBol(condition, pageQuery, ladingIds);
} else if (type.equals(QueryType.SeaBol)) {
return tempSearchService.getBySeaBol(condition, pageQuery, ladingIds);
} else if (type.equals(QueryType.Mark)) {
return tempSearchService.getByMark(condition, pageQuery, ladingIds);
} else {
return R.ng("查询失败!");
}
==
表示常量比较
用enum类型使用==
比较,显示的展示常量类型,而equals一般用于引用对象的值比较。
加强版switch替换多if
// 大多数情况为null。并列条件应该按照出现概率的从高到低,从上到下写代码
if (type == null) return tempSearchService.getList(pageQuery, ladingIds);
return switch (type) {
// case null -> tempSearchService.getList(pageQuery, ladingIds); // pattern matching
case AirBol -> tempSearchService.getByAirBol(condition, pageQuery, ladingIds);
case SeaBol -> tempSearchService.getBySeaBol(condition, pageQuery, ladingIds);
case Mark -> tempSearchService.getByMark(condition, pageQuery, ladingIds);
default -> R.ng(PageResult.empty(), "查询失败!");
};
加强版switch不支持null,否则会出现一下NPE(在17的preview或21可支持pattern matching)
Cannot invoke "QueryType.ordinal()" because "type" is null
8.UserDetailCache.java 43-47
gigax 350877671f7ee1cbc9f444b99d822bc20257c415
@Cacheable(value = "userDetail")
public OwtUserDetail getByUserIdFromCache(Long id) {
Optional<OwtUserDetail> fromDb = getFromDb(id);
return fromDb.orElseThrow(() -> new CodeException(CommonErrorEnum.AssertNotFound1, id));
}
private Optional<OwtUserDetail> getFromDb(Long id) {
logger.info("find from db");
Condition condition = OwtUserDetailTable.OwtUserDetail.onlyLive(OwtUserDetailTable.OwtUserDetail.UserId.eq(id));
return Optional.ofNullable(owtUserDetailDao.fetchOne(OwtUserDetailTable.OwtUserDetail, condition));
}
// 修改后
@Cacheable(value = UserDetail.CacheNameDetail)
public OwtUserDetail getByUserIdFromCache(Long id) {
log.debug("find from db");
final OwtUserDetail detail = owtUserDetailDao.fetchOne(t -> t.onlyLive(t.UserId.eq(id)));
StateAssert.notNull(detail, CommonErrorEnum.AssertNotFound1, id);
return detail;
}
- 过度拆分方法,getFromDb 仅一处使用,可以合到一起
- 没必要使用Optional,非lambda的反应式
- Cacheable尽量使用预定义常量,使用@CacheConfig定义
- logger使用lombok的@Slf4j即可
- 使用StateAssert做CodeException的检查
- jooq不要使用table的静态实例,使用dao可正确处理别名
- 简单查询,采用Dao+lambda即可