同步操作将从 anji-plus/AJ-Gaea 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
全新的项目,代码框架初始化操作详见项目初始化
代码增量模块,基于数据模型迭代开发,自动生成前后端基础功能,详见代码生成
代码生成时可以选择国际化版本,UI国际化维护详见UI国际化配置
如果是单表的操作,通过代码模板生成后,后端不需要修改任何代码,就已经有对应的分页查询,新增,修改,删除,查询详情等接口。
@RestController
@RequestMapping("/log")
@Api(value = "/log", tags = "")
public class GaeaLogController extends GaeaBaseController<GaeaLogParam, GaeaLog, GaeaLogDTO> {
@Autowired
private GaeaLogService gaeaLogService;
@Override
public GaeaBaseService<GaeaLogParam, GaeaLog> getService() {
return gaeaLogService;
}
@Override
public GaeaLog getEntity() {
return new GaeaLog();
}
@Override
public GaeaLogDTO getDTO() {
return new GaeaLogDTO();
}
}
public interface GaeaLogService extends GaeaBaseService<GaeaLogParam, GaeaLog> {
}
@Mapper
public interface GaeaLogMapper extends GaeaBaseMapper<GaeaLog> {
}
/**
* 分页模板
*
* @param param
* @return
*/
@GetMapping("/pageList")
@Permission(code = "PAGE", name = "分页")
@GaeaAuditLog(pageTitle = "查询")
public ResponseBean pageList(P param) {
IPage iPage = getService().page(param);
List<T> records = iPage.getRecords();
List<D> list = records.stream()
.map(entity -> GaeaBeanUtils.copyAndFormatter(entity, getDTO()))
.collect(Collectors.toList());
Page<D> pageDto = new Page<>();
pageDto.setCurrent(iPage.getCurrent());
pageDto.setRecords(list);
pageDto.setPages(iPage.getPages());
pageDto.setTotal(iPage.getTotal());
pageDto.setSize(iPage.getSize());
return responseSuccessWithData(pageDto);
}
@Setter
@Getter
public class GaeaLogParam extends PageParam implements Serializable {
/**
* 用户名称
*/
@ApiModelProperty(value = "用户名称")
private String userName;
/**
* 请求路径
*/
@ApiModelProperty(value = "请求路径")
private String requestUrl;
/**
* 访问时间,最小值与最大值用逗号隔开
*/
@Query(QueryEnum.BWT)
private String requestTime;
}
@ApiModel(value = "用户表")
public class GaeaUserDTO extends GaeaBaseDTO {
@ApiModelProperty(value = "用户登录名")
@Unique
private String username;
/**
* 1:可用 0:禁用
*/
@ApiModelProperty(value = "1:可用 0:禁用")
@Formatter(dictCode = "ENABLE_FLAG", targetField = "enabledStr")
private Integer enabled;
其中的账号状态需要显示字典项值。dicCode表示的字典code,targetField表示的是返回的参数名称。
http://****/log/pageList?pageNumber=1&pageSize=10&pageTitle=&requestUrl=&userName=&requestTime=2021-03-23+10:00:00,2021-03-23+12:00:00
注:其中的requestTime是一个时间段,传给后端用逗号隔开
pageList?dictCode=ALERT_CHANNEL&pageNumber=1&pageSize=10&sort=update_time&order=DESC
注:排序字段如果有多个,可以写为sort=update_time,create_time,降序为DESC,升序为ASC
/**
* 插入
*
* @param dto
* @return
* @throws Exception
*/
@PostMapping
@Permission(code = "INSERT", name = "新增")
@GaeaAuditLog(pageTitle = "新增")
public ResponseBean insert(@Validated @RequestBody D dto) {
logger.info("{}新增服务开始,参数:{}", this.getClass().getSimpleName(), GaeaUtils.toJSONString(dto));
ResponseBean responseBean = responseSuccess();
T entity = getEntity();
//dto转为数据库实体
BeanUtils.copyProperties(dto, entity);
//插入
getService().insert(entity);
logger.info("{}新增服务结束,结果:{}", this.getClass().getSimpleName(), GaeaUtils.toJSONString(responseBean));
return responseBean;
}
@TableName("table_name")
public class GaeaUser extends GaeaBaseEntity implements Serializable {
@Unique(code = RespCommonCode.USER_CODE_ISEXIST)
private String username;
}
注:其中的code是返回给前端的提示语代码,需要国际化处理
/**
* 根据ID修改对应记录
*
* @param dto
* @return
* @throws Exception
*/
@PutMapping
@Permission(code = "UPDATE", name = "更新")
@GaeaAuditLog(pageTitle = "修改")
public ResponseBean update(@Validated @RequestBody D dto) {
String username = UserContentHolder.getContext().getUsername();
logger.info("{}更新服务开始,更新人:{},参数:{}", this.getClass().getSimpleName(), username, GaeaUtils.toJSONString(dto));
T entity = getEntity();
//dto转换entity
BeanUtils.copyProperties(dto, entity);
getService().update(entity);
logger.info("{}更新服务结束,结果:{}", this.getClass().getSimpleName(), GaeaUtils.toJSONString(entity));
return responseSuccess();
}
目前提供的删除接口都是属于物理删除
/**
* 根据ID删除指定记录,这里被删除的记录会进入删除记录表
*
* @param id
* @return
*/
@DeleteMapping("/{id}")
@Permission(code = "DELETE", name = "删除")
@GaeaAuditLog(pageTitle = "删除")
public ResponseBean deleteById(@PathVariable("id") Long id) {
logger.info("{}删除服务开始,参数ID:{}", this.getClass().getSimpleName(), id);
getService().deleteById(id);
logger.info("{}删除服务结束", this.getClass().getSimpleName());
return responseSuccess();
}
/**
* 删除批量ID对应的记录
*
* @param ids
* @return
*/
@PostMapping("/delete/batch")
@Permission(code = "BATCH_DELETE", name = "批量删除")
@GaeaAuditLog(pageTitle = "批量删除")
public ResponseBean deleteBatchIds(@RequestBody List<Serializable> ids) {
logger.info("{}批量删除服务开始,批量参数Ids:{}", this.getClass().getSimpleName(), GaeaUtils.toJSONString(ids));
boolean deleteCount = getService().deleteByIds(ids);
ResponseBean responseBean = responseSuccessWithData(deleteCount);
logger.info("{}批量删除服务结束,结果:{}", this.getClass().getSimpleName(), GaeaUtils.toJSONString(responseBean));
return responseBean;
}
/**
* 操作前处理
*
* @param entity 前端传递的对象
* @param operationEnum 操作类型
* @throws BusinessException 阻止程序继续执行或回滚事务
*/
default void processBeforeOperation(T entity, BaseOperationEnum operationEnum) throws BusinessException {
}
/**
* 操作后续处理
*
* @param entity
* @param operationEnum 操作类型
* @throws BusinessException 阻止程序继续执行或回滚事务
*/
default void processAfterOperation(T entity, BaseOperationEnum operationEnum) throws BusinessException {
}
/**
* 新增用户,需要设置默认密码
* @param entity 前端传递的对象
* @param operationEnum 操作类型
* @throws BusinessException
*/
@Override
public void processBeforeOperation(GaeaUser entity, BaseOperationEnum operationEnum) throws BusinessException {
switch (operationEnum) {
case INSERT:
setDefaultPwd(entity);
break;
default:
}
}
对于接口参数校验方面,就用Spring Boot中的注解@Validated来完成。
/**
* 用户修改密码
* @param reqParam
* @return
*/
@PostMapping("/updatePassword")
@GaeaAuditLog(pageTitle = "修改密码")
public ResponseBean updatePassword(@Validated @RequestBody GaeaUserPasswordParam reqParam){
return responseSuccessWithData(gaeaUserService.updatePassword(reqParam));
}
@Getter
@Setter
public class GaeaUserPasswordParam implements Serializable {
private String username;
/**
* 新密码
*/
@NotBlank(message = "password not empty")
@Size(min = 8,message ="Password at least 8 characters" )
private String password;
}
注解名称 | 描述 |
---|---|
@Null | 检查该字段为空 |
@NotNull | 不能为null |
@NotBlank | 不能为空,常用于检查空字符串 |
@NotEmpty | 不能为空,多用于检测list是否size是0 |
@Max | 该字段的值只能小于或等于该值 |
@Min | 该字段的值只能大于或等于该值 |
检查是否是一个有效的email地址 | |
@Pattern(regex=,flag=) | 被注释的元素必须符合指定的正则表达式 |
@Size(min=, max=) | 检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等 |
@Length(min=,max=) | 检查所属的字段的长度是否在min和max之间,只能用于字符串 |
@AssertTrue | 用于boolean字段,该字段只能为true |
@AssertFalse | 该字段的值只能为false |
file:
dist-path: /app/disk/upload
white-list: .png|.jpg|.gif|.icon|.pdf|.xlsx|.xls|.csv|.mp4|.avi
excelSuffix: .xlsx|.xls|.csv
downloadPath: http://***/business/file/download
@RestController
@RequestMapping("/file")
@Api(value = "/file", tags = "")
public class GaeaFileController {
@Autowired
private GaeaFileService gaeaFileService;
@PostMapping("/upload")
public ResponseBean upload(@RequestParam("file") MultipartFile file) {
return ResponseBean.builder().message("success").data((gaeaFileService.upload(file))).build();
}
}
接口返回的是一个完整的访问路径。返回的该url也是下载的地址。
@RestController
@RequestMapping("/file")
@Api(value = "/file", tags = "")
public class GaeaFileController {
@Autowired
private GaeaFileService gaeaFileService;
@GetMapping(value="/download/{fileId}")
public ResponseEntity<byte[]> download(HttpServletRequest request, HttpServletResponse response, @PathVariable("fileId") String fileId){
return gaeaFileService.download(request, response, fileId);
}
}
对于后端程序来说,通过Try-catch代码块来捕捉异常是常见的,为了避免程序中出现大量的try-catch代码块,可以通过全局异常对异常进行统一处理。
/**
* 全局异常处理
* @author lr
* @since 2021-01-02
*/
@RestControllerAdvice
public class ExceptionHandlerAdvice {
private Logger logger = LoggerFactory.getLogger(ExceptionHandlerAdvice.class);
@Autowired
private MessageSourceHolder messageSourceHolder;
/**
* 业务异常
* @param businessException
* @return
*/
@ExceptionHandler(BusinessException.class)
public ResponseBean handleBusinessException(BusinessException businessException) {
return ResponseBean.builder().code(businessException.getCode()).args(businessException.getArgs()).build();
}
/**
* 参数校验异常
*
* @param methodArgumentNotValidException
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseBean methodArgumentNotValidException(MethodArgumentNotValidException methodArgumentNotValidException) {
String code = methodArgumentNotValidException.getBindingResult().getFieldError().getDefaultMessage();
String message;
try {
message = messageSourceHolder.getMessage(code, null);
} catch (NoSuchMessageException exception) {
message = code;
}
return ResponseBean.builder().code(FAIL_CODE).message(message).build();
}
/**
* 业务异常
* @param exception
* @return
*/
@ExceptionHandler(Exception.class)
public ResponseBean exception(Exception exception){
//返回值构建
logger.error("系统异常", exception);
ResponseBean.Builder builder = ResponseBean.builder();
builder.code(FAIL_CODE);
builder.message(messageSourceHolder.getMessage(FAIL_CODE, null));
return builder.build();
}
}
可以手动抛出业务异常。如下:
@Override
public Boolean updatePassword(GaeaUserPasswordParam requestParam) {
//参数校验
if (!requestParam.getConfirmPassword().equals(requestParam.getPassword())) {
//密码和确认密码不一致
throw BusinessExceptionBuilder.build(RespCommonCode.AUTH_PASSWORD_NOTSAME);
}
//新密码不能与老密码一样
if (StringUtils.equals(requestParam.getOldPassword(), requestParam.getPassword())) {
throw BusinessExceptionBuilder.build(RespCommonCode.USER_PASSWORD_CONFIG_PASSWORD_CANOT_EQUAL);
}
}
详见组件文档:导出组件
详见组件文档:日志组件
详见组件文档:数据归档
详见组件文档:高级查询/自定义列
详见组件文档:高级查询/自定义列
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。