跳到主要内容

🌼 最佳实践

项目结构

橙色:包,红色:类。

根据快速开始,新建好的项目,按照这个步骤可以继续详细配置:

一、补充开发时的基础类(必要的)

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里进行执行。

  1. test/java下新建CodeGenerator.class

  2. 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配置,为参数中对应的表名列表生成代码
  3. 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
  4. 最终效果: zjsj02

  5. 执行一下,就会生成相应代码:controllerserviceservice.implmodulemapper

问题:生成目录不对

多maven模块情况下,第5步可能生成代码的位置不对,可能会生成到该模块的根目录下。检查下Work directory是不是$MODULE_WORKING_DIR$,不是的话修改一下再生成一遍。

一:zjsj03

二:zjsj04

三、补充

  1. 启动类需加上注解@MapperScan("mapper包名")

  2. 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>

完成以上步骤,即可使用所有特性。