🌼 最佳实践
项目结构
橙色:包,红色:类。
根据快速开始,新建好的项目,按照这个步骤可以继续详细配置:
一、补充开发时的基础类(必要的)
rest
包
响应对象
统一相应对象封装类R
/**
* 统一响应结果集R的封装【非返回对象】
* @author wangjj
*/
public abstract class R {
/** 不允许实例化该对象 */
private R() {
}
/** 根据flag返回 */
public static Result status(Boolean flag, ResultErrorCode resultErrorCode){
return flag?Result.OK():status(resultErrorCode);
}
/** 自定义错误信息 */
public static Result status(ResultErrorCode resultErrorCode){
return new Result(resultErrorCode.getCode(), resultErrorCode.getMessage(),null,null);
}
/** 自定义错误信息(带参数) */
public static Result status(ResultErrorCode resultErrorCode, Object... param){
String message = StrUtil.format(resultErrorCode.getMessage(), param);
return new Result(resultErrorCode.getCode(),message,null,null);
}
}
响应错误枚举ResultErrorCode
/**
* 响应异常状态
* @author wangjj
*/
public enum ErrorCode {
/* 统一异常 */
SYSTEM_ERROR(-1, "系统异常:{}"),
/* 成功状态码 */
SUCCESS(200,"操作成功!"),
/* 错误状态码 */
FAIL(500,"操作失败!"),
/* 系统预留,不允许使用 600-700 */
/* 业务固定错误码 1000-2000 */
CITY_IS_NOT_AVAILABLE(1001,"该城市未开通服务!"),
ROLE_INSUFFICIENT_PERMISSIONS(1002,"角色权限不足!"),
/* 参数错误:10001-19999 */
PARAM_IS_INVALID(10001, "参数无效,请参阅接口文档"),
PARAM_IS_BLANK(10002, "参数为空"),
PARAM_TYPE_BIND_ERROR(10003, "参数格式错误"),
PARAM_NOT_COMPLETE(10004, "参数缺失,请参阅接口文档。"),
/* 用户错误:20001-29999*/
USER_NOT_LOGGED_IN(20001, "用户未登录,请先登录"),
USER_LOGIN_ERROR(20002, "账号不存在或密码错误"),
USER_ACCOUNT_FORBIDDEN(20003, "账号已被冻结"),
USER_NOT_EXIST(20004, "用户不存在或注销"),
USER_HAS_EXISTED(20005, "用户已存在"),
/* 业务错误:30001-39999 */
BUSINESS_GROUP_NO_ALLOWED_DEL(30001, "应用分组已经被应用使用,不能删除"),
BUSINESS_THEME_NO_ALLOWED_DEL(30002, "主题已经被用户使用,不能删除"),
BUSINESS_THEME_NO_ALLOWED_DISABLE(30003, "主题已经被用户使用,不能停用"),
BUSINESS_THEME_DEFAULT_NO_ALLOWED_DEL(30004, "默认主题,不能删除"),
BUSINESS_THEME_NO_ALLOWED_UPDATE(30005, "主题已经被用户使用,不能修改图片信息"),
BUSINESS_IS_TOP(30040, "已经到最顶部"),
BUSINESS_IS_BOTTOM(30041, "已经到最底部"),
BUSINESS_NAME_EXISTED(30051, "名称已存在"),
SWEEP_WECHAT_APPLICATION_CODE_IS_INVALID(30052, "登录信息无效,请重新登录!"),
PHONE_NUMBER_FORMAT_IS_WRONG(30053, "手机号格式错误,请检查手机号格式!"),
ID_NUMBER_IS_INVALID(30054, "身份证无效,请检查身份证!"),
PHONE_VERIFICATION_CODE_ERROR(30055, "手机验证码错误,请重新输入!"),
/* 系统错误:40001-49999 */
SYSTEM_INNER_ERROR(40001, "系统繁忙,请稍后重试"),
UPLOAD_ERROR(40002, "文件上传异常:{}"),
REMOVE_ERROR(40003, "文件删除异常:{}"),
FILE_MAX_SIZE_OVERFLOW(40004, "上传尺寸过大"),
FILE_ACCEPT_NOT_SUPPORT(40005, "上传文件格式不支持"),
SET_UP_AT_LEAST_ONE_ADMIN(40006, "至少指定一个管理员"),
URL_INVALID(40007, "地址不合法"),
LINK_AND_LOGOUT_NO_MATCH(40008, "主页地址和注销地址IP不一致"),
IP_AND_PORT_EXISTED(40009, "当前IP和端口已经被占中"),
LINK_IS_REQUIRED(40010, "生成第三方token认证信息: 主页地址不能为空,请完善信息"),
ONLY_ROOT_DEPARTMENT(40011, "组织机构只能存在一个根机构"),
DEPART_CODE_EXISTED(40012, "组织机构编码已存在"),
DEPART_CONTAINS_USERS(40013, "该机构下是存在用户,不允许删除"),
DEPART_CONTAINS_SON(40014, "该机构下是存在子级机构,不允许删除"),
DEPART_PARENT_IS_SELF(40015, "选择的父机构不能为本身"),
DICT_EXIST_DEPEND(40016, "该字典数据存在详情依赖,不允许删除"),
DICT_DETAIL_LOCK(40017, "该字典数据被锁定,不允许修改或删除"),
DEPART_CODE_EXISTED_WITH_ARGS(40018, "组织机构编码【{}】系统已存在"),
FILE_CANNOT_BE_EMPTY(40019, "文件地址不可为空"),
SQL_SYNTAX_ERROR_EXCEPTION(40020, "SQL执行异常:{}"),
HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION(40021,"请求方法错误异常:{0}"),
ILLEGAL_ARGUMENT_EXCEPTION(40022,"请求参数错误异常:{}"),
BIND_EXCEPTION(40023,"请求参数类型转换异常:{}"),
BAD_BLOCK_EXCEPTION(40024,"数据解密异常:{}"),
/* 数据错误:50001-599999 */
RESULT_DATA_NONE(50001, "数据未找到"),
DATA_IS_WRONG(50002, "数据有误"),
DATA_ALREADY_EXISTED(50003, "数据已存在"),
ID_DOES_NOT_MATCH(50004, "身份证号不匹配,请检查重新输入!"),
IDENTITY_ERROR(50005, "调用此接口的身份错误,身份应该为[{}]!"),
/* 接口错误:60001-69999 */
INTERFACE_INNER_INVOKE_ERROR(60001, "内部系统接口调用异常"),
INTERFACE_OUTTER_INVOKE_ERROR(60002, "外部系统接口调用异常,状态码:{}"),
INTERFACE_FORBID_VISIT(60003, "该接口禁止访问"),
INTERFACE_ADDRESS_INVALID(60004, "接口地址无效"),
INTERFACE_REQUEST_TIMEOUT(60005, "接口请求超时"),
INTERFACE_EXCEED_LOAD(60006, "接口负载过高"),
THIRD_HTTP_REQUEST_NOT_OK(60007, "三方接口请求错误:{}"),
PARAMETER_IS_WRONG(60008, "参数有误"),
/* 权限错误:70001-79999 [此类错误前端需要返回登录重新获取token]*/
PERMISSION_UNAUTHENTICATED(70001,"此操作需要登陆系统!"),
PERMISSION_UNAUTHORISE(70002,"权限不足,无权操作!"),
PERMISSION_EXPIRE(70003,"登录状态过期!"),
PERMISSION_TOKEN_EXPIRED(70004, "token已过期"),
PERMISSION_LIMIT(70005, "访问次数受限制"),
PERMISSION_TOKEN_INVALID(70006, "无效token"),
PERMISSION_SIGNATURE_ERROR(70007, "签名失败"),
PERMISSION_SECRET_WRONG(70008, "JWT无效,签名本地签名不匹配"),
/** 系统级错误,影响系统运作的错误 */
GLOBAL_CONFIG_VALUES_NOT_EXIST(99999, "系统错误:配置值{}不存在!"),
UNKNOWN(99999, "未知!");
/** 操作代码 */
private final int code;
/** 提示信息 */
private String message;
ErrorCode(int code, String message){
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
exceptions
包
异常
新建类CustomException
/**
* 自定义异常
* @author wangjj
**/
public class CustomException extends RuntimeException {
/** 错误代码 */
private ResultErrorCode resultCode;
public CustomException(ResultErrorCode resultCode){
super(resultCode.getMessage());
this.resultCode = resultCode;
}
public CustomException(ResultErrorCode resultCode, Object... args){
super(StrUtil.format(resultCode.getMessage(),args));
resultCode.setMessage(StrUtil.format(resultCode.getMessage(),args));
this.resultCode = resultCode;
}
public ResultErrorCode getResultCode(){
return resultCode;
}
@Override
public String getMessage() {
return resultCode.getMessage();
}
}
config
包
bean配置
新建类GlobalExceptionHandler
可自己处理别的异常,不过不是必要的,因为框架内已经做了处理。
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义异常
*/
@ExceptionHandler(CustomException.class)
public Result handleException(CustomException e) {
log.error("### 异常信息:{} ###", e.getResultCode().getMessage());
return R.status(e.getResultCode());
}
/**
* 处理所有不可知的异常
*/
//@ExceptionHandler(Exception.class)
public Result handleOtherException(Exception e){
//打印异常堆栈信息
e.printStackTrace();
log.warn("-------------此异常未捕获-------------:{}",e.getClass().getName());
// 打印异常信息
log.error("### 不可知的异常:{} ###", e.getMessage());
return R.status(ResultErrorCode.SYSTEM_ERROR,e.getMessage());
}
}
model
包
对象模型
新建类BaseUser
这个类是token信息对象,我们生成token和解析token都用这个对象。
可按需求修改字段或字段类型,如下只是示例。
/**
* token用户信息
* @author wangjj
*/
@Data
public class BaseUser {
@ApiModelProperty("主键")
private Long id;
@ApiModelProperty("真实姓名(用户名)")
private String realname;
}
utils
包
工具类
新建类ServletUtil
getUser()
已经在DefaultServletUtil
中定义过了,建议重写一下增加返回值使用,拓展getUserId()
方便业务中使用。
/**
* 代码中常用工具
*/
public class ServletUtil extends DefaultServletUtil {
/**
* 根据token获取userId
* @return userId
*/
public static Long getUserId(){
BaseUser user = getUser();
return ObjectUtil.isEmpty(user)?null: user.getId();
}
/**
* 获取用户信息
* @return 用户基础信息
*/
public static BaseUser getUser(){
return DefaultServletUtil.getUser();
}
}
说明:
上边的这些代码照抄就行,最多看一下相关配置的东西。
二、代码生成
需要在
test
里进行执行。
-
在
test/java
下新建CodeGenerator.class
。 -
在
CodeGenerator.class
中新建方法:public static void main(String[] args) {
// 第一种方式,默认加载Mybatis-Plus.properties配置。
MybatisPlusCodeAutoGeneratorHelper.run("表名");
// 第二种方式(推荐),优先加载CodeAutoGeneratorVO配置。
CodeAutoGeneratorVO vo= CodeAutoGeneratorVO.newInstance()
.author("wangjj")
.parent("com.example")
.moduleName("demo")
.url("jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=CTT")
// .tablePrefix(new String[]{})
.userName("root")
.passWord("root");
MybatisPlusCodeAutoGeneratorHelper.run(vo,"表名");
}说明:
MybatisPlusCodeAutoGeneratorHelper中run方法有4种重载方法
方法 参数 作用 run(String...)
表名 读取 Mybatis-Plus.properties
配置,为参数中对应的表名列表生成代码run(List<String>)
表名 读取 Mybatis-Plus.properties
配置,为参数中对应的表名列表生成代码run(CodeAutoGeneratorVO,String...)
配置对象,表名 加载 CodeAutoGeneratorVO
配置,为参数中对应的表名列表生成代码run(CodeAutoGeneratorVO,List<String>)
配置对象,表名 加载 CodeAutoGeneratorVO
配置,为参数中对应的表名列表生成代码 -
在
resources
中新建配置Mybatis-Plus.properties
,写入如下内容:非必要,若run方法传入CodeAutoGeneratorVO时会默认使用该配置
#表前缀(生成类时自动删除掉文件名前缀的部分)
tablePrefix=
#设置作者
author=wangjj
#
#
#正常情况下,下面的代码无需修改!!!!!!!!!!
#
#
# 自定义包路径
parent=com.example
# 模块名(包下的子包模块)
moduleName=demo
#数据库地址
url=jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=CTT
#数据库用户名
userName=root
#数据库密码
passWord=root -
最终效果:
-
执行一下,就会生成相应代码:
controller
、service
、service.impl
、module
、mapper
问题:生成目录不对
多maven模块情况下,第5步可能生成代码的位置不对,可能会生成到该模块的根目录下。检查下Work directory
是不是$MODULE_WORKING_DIR$
,不是的话修改一下再生成一遍。
一:
二:
三、补充
-
启动类需加上注解
@MapperScan("mapper包名")
-
pom中需要写上,保证
mapper.xml
可用:<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.json</include>
<include>**/*.ftl</include>
</includes>
</resource>
</resources>
</build>
完成以上步骤,即可使用所有特性。