欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

项目开发经验规范总结-时刻更新

程序员文章站 2022-03-08 15:57:17
...

1、常用规范类

1.1、异常相关

1.1.1、业务异常类

package com.healerjean.proj.exception;


import com.healerjean.proj.enums.ResponseEnum;

/**
 * 系统业务异常
 */
public class BusinessException extends RuntimeException {

    private int code;

    public BusinessException(int code) {
        this.code = code;
    }

    public BusinessException(String message) {
        super(message);
        this.code = ResponseEnum.逻辑错误.code;
    }

    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }

    public BusinessException(ResponseEnum responseEnum) {
        super(responseEnum.msg);
        this.code = responseEnum.code ;
    }

    public BusinessException(ResponseEnum responseEnum,String message) {
        super(message);
        this.code = responseEnum.code ;
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
        this.code = ResponseEnum.逻辑错误.code;
    }

    public BusinessException(int code ,Throwable e) {
        super(e);
        this.code = code;
    }

    public BusinessException(ResponseEnum responseEnum, Throwable t) {
        super(responseEnum.msg, t);
        this.code = responseEnum.code;
    }


    public void setCode(int code) {
        this.code = code;
    }

    public int getCode() {
        return code;
    }

}

1.1.2、参数异常类

package com.healerjean.proj.exception;

import com.healerjean.proj.enums.ResponseEnum;

/**
 * @author HealerJean
 * @ClassName ParameterErrorException
 * @date 2019/10/17  16:19.
 * @Description 参数错误
 */
public class ParameterErrorException extends com.healerjean.proj.exception.BusinessException {

    public ParameterErrorException() {
        super(ResponseEnum.参数错误);
    }

    public ParameterErrorException(ResponseEnum responseEnum) {
        super(ResponseEnum.参数错误, responseEnum.msg);
    }

    public ParameterErrorException(String msg) {
        super(ResponseEnum.参数错误, msg);
    }


}

1.1.3、接口异常类

package com.healerjean.proj.exception;

import com.healerjean.proj.enums.ResponseEnum;

/**
 * @author HealerJean
 * @ClassName HaoDanKuApiException
 * @date 2019/10/15  20:08.
 * @Description
 */
public class HaoDanKuApiException extends BusinessException {


    public HaoDanKuApiException( ) {
        super(ResponseEnum.好单库口请求异常);
    }

    public HaoDanKuApiException(String msg) {
        super(ResponseEnum.好单库接口数据异常, msg);
    }

    public HaoDanKuApiException(Throwable e) {
        super(ResponseEnum.好单库口请求异常, e);
    }

}

1.1.4、异常全局处理

package com.healerjean.proj.config;

import com.healerjean.proj.dto.ResponseBean;
import com.healerjean.proj.enums.ResponseEnum;
import com.healerjean.proj.exception.BusinessException;
import com.healerjean.proj.exception.ParameterErrorException;
import com.healerjean.proj.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletResponse;
import javax.validation.UnexpectedTypeException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName ControllerHandleExceptionConfig
 * @date 2019/5/31  20:19.
 * @Description
 */
@Slf4j
@ControllerAdvice
public class ControllerHandleConfig {

    /**
     * 不支持的请求方始
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(value = HttpStatus.METHOD_NOT_ALLOWED)
    public ResponseBean methodNotSupportExceptionHandler(HttpRequestMethodNotSupportedException e) {
        log.error("不支持的请求方式", e);
        return ResponseBean.buildFailure(ResponseEnum.不支持的请求方式.code, e.getMessage());
    }


    /**
     * 参数类型错误
     * 1、(BindException : 比如 Integer 传入String  )
     * Field error in object 'demoDTO' on field 'age': rejected value [fasdf]; codes [typeMismatch.demoDTO.age,typeMismatch.age,typeMismatch.java.lang.Integer,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [demoDTO.age,age]; arguments []; default message [age]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Integer' for property 'age'; nested exception is java.lang.NumberFormatException: For input string: "fasdf"]
     */
    @ExceptionHandler(value = {BindException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ResponseBean bindExceptionHandler(BindException e) {
        log.error("====参数类型错误===", e);
        return ResponseBean.buildFailure(ResponseEnum.参数类型错误.code, e.getMessage());
    }


    /**
     * 参数格式问题
     */
    @ExceptionHandler(value = {MethodArgumentTypeMismatchException.class, HttpMessageConversionException.class, UnexpectedTypeException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ResponseBean httpMessageConversionExceptionHandler(Exception e) {
        log.error("====参数格式异常===", e);
        return ResponseBean.buildFailure(ResponseEnum.参数格式异常.code, e.getMessage());
    }



    /**
     * 参数错误
     */
    @ExceptionHandler(value = ParameterErrorException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ResponseBean parameterErrorExceptionHandler(ParameterErrorException e) {
        log.error("参数异常------------参数错误:code:{},message:{}", e.getCode(), e.getMessage());
        return ResponseBean.buildFailure(e.getCode(), e.getMessage());
    }


    /**
     * 业务异常,给前台返回异常数据
     */
    @ExceptionHandler(value = BusinessException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ResponseBean businessExceptionHandler(BusinessException e) {
        log.error("业务异常------------异常信息:code:{},message{}" ,e.getCode(), e.getMessage());
        return ResponseBean.buildFailure(e.getCode(),e.getMessage());
    }




    /**
     * 所有异常报错
     */
    @ExceptionHandler
    @ResponseBody
    public HttpEntity<ResponseBean> allExceptionHandler(HttpServletResponse response, Exception e) {
        log.error("====系统错误===", e);
        response.setStatus(ResponseEnum.系统错误.code);
        return returnMessage(ResponseBean.buildFailure(ResponseEnum.系统错误));
    }

    private HttpEntity<ResponseBean> returnMessage(ResponseBean responseBean) {
        HttpHeaders header = new HttpHeaders();
        header.add("Content-Type", "application/json");
        header.add("Charset", "UTF-8");
        return new HttpEntity<>(responseBean, header);
    }



    /**
     * 参数非法
     * 1、(BindException : 比如 Integer 传入abc  )
     */
    // @ExceptionHandler(value = {MethodArgumentTypeMismatchException.class, HttpRequestMethodNotSupportedException.class, HttpMessageConversionException.class, BindException.class, UnexpectedTypeException.class})
    // @ResponseBody
    // public HttpEntity<ResponseBean> httpMessageConversionExceptionHandler(HttpServletResponse response, Exception e) {
    //     log.error("====参数格式异常===", e);
    //     // 等同于 @ResponseStatus(HttpStatus.BAD_REQUEST)
    //     // 但是setStatus 不能比随便设置,最好一般情况下不要和HttpStatus 有重复的,这样有可能会造成没有输出Response body
    //     response.setStatus(ResponseEnum.参数格式异常.code);
    //     return returnMessage(ResponseBean.buildFailure(ResponseEnum.参数格式异常));
    // }
    // @ExceptionHandler(value ={HttpMessageConversionException.class, BindException.class} )
    // @ResponseBody
    // public HttpEntity<ResponseBean> httpMessageConversionExceptionHandler(Exception e) {
    //     log.error("====参数格式异常===", e);
    //     return new ResponseEntity<>(ResponseBean.buildFailure(ResponseEnum.参数格式异常),HttpStatus.BAD_REQUEST);
    // }

}

1.2、枚举

1.2.1、响应枚举

package com.healerjean.proj.enums;

import java.util.Arrays;
import java.util.List;

/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName ResponseEnum
 * @date 2019/6/13  20:45.
 * @msgcription
 */
public enum ResponseEnum {


    正常(200, "访问正常"),

    参数错误(301, "参数错误"),
    参数格式异常(302, "参数格式异常"),
    不支持的请求方式(303, "不支持的请求方式"),
    参数类型错误(304, "参数类型错误"),
    逻辑错误(305, "逻辑错误"),
    未登陆(306, "未登陆"),
    登陆成功(307, "登陆成功"),
    重复操作(308, "重复操作"),
    非法操作(309, "非法操作"),

    请求无法被服务器理解(400, "请求无法被服务器理解"),
    未授权(401, "未授权"),
    访问禁止(403, "访问禁止"),
    页面丢失(404, "页面丢失"),
    系统错误(500, "系统错误"),
    未知错误(999, "未知错误"),


    用户已经存在(1000, "用户已经存在"),
    用户不存在(1001, "用户不存在"),

    微信接口请求异常(2001,"微信接口请求异常"),
    淘宝接口请求异常(2002,"淘宝接口请求异常"),
    淘宝接口数据异常(2003,"淘宝接口数据异常"),
    好单库口请求异常(2004,"好单库口请求异常"),
    好单库接口数据异常(2005,"好单库接口数据异常"),

    ;


    public int code;
    public String msg;


    ResponseEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }


    public static boolean checkExist( Integer code){
        for (ResponseEnum value : ResponseEnum.values()){
            if (value.code == code){
                return true;
            }
        }
        return false;
    }


    public static ResponseEnum toEnum(int code){
        for (ResponseEnum value : ResponseEnum.values()){
            if (value.code == code){
                return value;
            }
        }
        return ResponseEnum.未知错误;
    }

    public static String getMsg(int code){
        for (ResponseEnum value : ResponseEnum.values()){
            if (value.code == code){
                return value.msg;
            }
        }
        return ResponseEnum.未知错误.msg;
    }


    public ResponseEnum value(String enumName){
        return valueOf( enumName ) ;
    }

    public static List<ResponseEnum> getList(){
        return Arrays.asList(values());
    }


}

1.2.2、业务枚举

package com.healerjean.proj.enums;

/**
 * @author HealerJean
 * @ClassName BusinessEnum
 * @date 2019/9/30  14:39.
 * @Description
 */
public interface BusinessEnum {


    /**
     * 验证码枚举
     */
    enum VerifyCodeTypeEnum {

        图片验证码("captcha", "图片验证码"),
        注册邮箱验证码("RegistEmail", "注册邮箱验证码"),
        找回密码邮箱验证码("RetrievePasswordEmail", "找回密码邮箱验证码"),
        ;

        VerifyCodeTypeEnum(String code, String desc) {
            this.code = code;
            this.desc = desc;
        }

        public String code;
        public String desc;

        public static VerifyCodeTypeEnum toEnum(String code) {
            for (VerifyCodeTypeEnum item : VerifyCodeTypeEnum.values()) {
                if (item.code.equals(code)) {
                    return item;
                }
            }
            return null;
        }
    }


    /**
     * 模板类型
     */
    enum TemplateTypeEnum {

        邮件("Email", "邮件"),
        ;
        public String code;
        public String desc;

        TemplateTypeEnum(String code, String desc) {
            this.code = code;
            this.desc = desc;
        }

    }



    /**
     * 模板名字
     */
    enum TempleNameEnum  {
        邮箱验证("VerifyEmail", "邮箱验证"),
        找回密码邮箱验证("PasswordVerifyEmail", "找回密码邮箱验证"),
        手机号验证("VerifyPhone", "手机号验证"),
        ;
        TempleNameEnum(String code, String desc) {
            this.code = code;
            this.desc = desc;
        }

        public String code;
        public String desc;


        public static TempleNameEnum toEnum(String code) {
            for (TempleNameEnum item : TempleNameEnum.values()) {
                if (item.code.equals(code)) {
                    return item;
                }
            }
            return null;
        }
    }


    /**
     * 菜单类型
     */
    enum MenuTypeEnum {

        后端菜单("0", "后端菜单"),
        前端菜单("1", "前端菜单");

        MenuTypeEnum(String code, String desc) {
            this.code = code;
            this.desc = desc;
        }

        public String code;
        public String desc;


        public static MenuTypeEnum toEnum(String code) {
            for (MenuTypeEnum value : MenuTypeEnum.values()) {
                if (value.code .equals( code)) {
                    return value;
                }
            }
            return null;
        }
    }



    /**
     * 用户类型
     */
    enum UserTypeEnum {

        管理人员("manager", "管理人员"),
        网站用户("webuser", "网站用户");

        UserTypeEnum(String code, String desc) {
            this.code = code;
            this.desc = desc;
        }

        public String code;
        public String desc;


        public static MenuTypeEnum toEnum(String code) {
            for (MenuTypeEnum value : MenuTypeEnum.values()) {
                if (value.code .equals( code)) {
                    return value;
                }
            }
            return null;
        }
    }


}


1.2.3、状态枚举

package com.healerjean.proj.enums;

/**
 * @Description
 * @Author HealerJean
 * @Date 2019-06-16  01:58.
 */
public enum  StatusEnum {

    生效("10", "生效"),
    废弃("99", "废弃");

    StatusEnum(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String code;
    public String desc;


}

1.2.4、下拉菜单枚举

package com.duodian.admore.data;

import java.io.Serializable;

/**
 * 下拉列表用
 */
public class LabelValueBean implements Serializable{
    private static final long serialVersionUID = -1211726511402154326L;

    private String label;
    private String value;

    private Boolean checked = false;

    public LabelValueBean() {
    }

    public LabelValueBean(String label, String value) {
        this.label = label;
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public Boolean getChecked() {
        return checked;
    }

    public void setChecked(Boolean checked) {
        this.checked = checked;
    }
}

1.3、响应Bean

package com.healerjean.proj.dto;


import com.healerjean.proj.enums.ResponseEnum;
import com.healerjean.proj.utils.JsonUtils;

/**
 * 返回对象
 */
public class ResponseBean {

    private ResponseBean() {
    }

    public static ResponseBean buildSuccess() {
        ResponseBean responseBean = new ResponseBean();
        responseBean.setSuccess(true);
        responseBean.setCode(ResponseEnum.正常.code);
        responseBean.setDate(System.currentTimeMillis() + "");
        return responseBean;
    }

    public static ResponseBean buildSuccess(String msg) {
        ResponseBean responseBean = new ResponseBean();
        responseBean.setSuccess(true);
        responseBean.setCode(ResponseEnum.正常.code);
        responseBean.setResult(msg);
        responseBean.setDate(System.currentTimeMillis() + "");
        return responseBean;
    }

    public static ResponseBean buildSuccess(Object result) {
        ResponseBean responseBean = new ResponseBean();
        responseBean.setSuccess(true);
        responseBean.setCode(ResponseEnum.正常.code);
        responseBean.setResult(result);
        responseBean.setDate(System.currentTimeMillis() + "");
        return responseBean;
    }

    public static ResponseBean buildSuccess(String msg, Object result) {
        ResponseBean responseBean = new ResponseBean();
        responseBean.setSuccess(true);
        responseBean.setCode(ResponseEnum.正常.code);
        responseBean.setMsg(msg);
        responseBean.setResult(result);
        responseBean.setDate(System.currentTimeMillis() + "");
        return responseBean;
    }

    public static String buildSensitivitySuccess(String msg, Object result) {
        return JsonUtils.toJsonStringWithSensitivity(buildSuccess(msg, result));
    }

    public static String buildSensitivitySuccess(Object result) {
        return JsonUtils.toJsonStringWithSensitivity(buildSuccess(result));
    }


    public static ResponseBean buildFailure() {
        ResponseBean responseBean = new ResponseBean();
        responseBean.setSuccess(false);
        responseBean.setCode(ResponseEnum.系统错误.code);
        responseBean.setDate(System.currentTimeMillis() + "");
        return responseBean;
    }

    public static ResponseBean buildFailure(String msg) {
        ResponseBean responseBean = new ResponseBean();
        responseBean.setSuccess(false);
        responseBean.setCode(ResponseEnum.系统错误.code);
        responseBean.setMsg(msg);
        responseBean.setDate(System.currentTimeMillis() + "");
        return responseBean;
    }

    public static ResponseBean buildFailure(ResponseEnum responseEnum) {
        ResponseBean responseBean = new ResponseBean();
        responseBean.setSuccess(false);
        responseBean.setCode(responseEnum.code);
        responseBean.setMsg(responseEnum.msg);
        responseBean.setDate(System.currentTimeMillis() + "");
        return responseBean;
    }


    public static ResponseBean buildFailure(int code, String msg) {
        ResponseBean responseBean = new ResponseBean();
        responseBean.setSuccess(false);
        responseBean.setCode(code);
        responseBean.setMsg(msg);
        responseBean.setDate(System.currentTimeMillis() + "");
        return responseBean;
    }


    public static ResponseBean buildFailure(ResponseEnum responseEnum, String msg) {
        ResponseBean responseBean = new ResponseBean();
        responseBean.setSuccess(false);
        responseBean.setCode(responseEnum.code);
        responseBean.setMsg(msg);
        responseBean.setDate(System.currentTimeMillis() + "");
        return responseBean;
    }


    private boolean success;
    private Object result = "{}";
    private String msg = "";
    private int code;
    private String date;

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }
}

2、代码规范

2.1、方法、实体名字:

小米

manager dao 层

queryBeanSingle
queryBeanPage
queryBeanList
queryBeanPageLike
queryBeanListLike

2.1.1、查询

  • 单个记录查询
根据某几个简单属性查询
查询数据库  findBeanNameBy
不查询数据库 getBeanNameBy


查询数据库  findBeanName
不查询数据库 getBeanName
  • 多个记录查询
根据某几个简单属性查询
查询数据库  qureyBeanNameBy
不查询数据库 dataBeanNameBy


查询数据库分页  qureyBeanNamePage
查询数据库集合  qureyBeanNameList
不查询数据库分页 dataBeanNamePage
不查询数据库集合  dataBeanNameList

2.1.2、删除

更新status字段
deleteBean 

直接删除库里的数据
deleteDBeanName 

2.1.4、接口请求 rquest/response

请求data
请求参数的data为		ReqRecordData

返回单个实体
返回结果的data为		RspRecordModel

返回集合
返回结果的data为		RspRecordList

项目开发经验规范总结-时刻更新

2.2、注释、日志规范

2.2.1、controller日志

  • -------8个-
  • 头部是controller说明,日志内容写上头部以及功能日志说明
/**
 * @author HealerJean
 * @ClassName UserController
 * @date 2019/10/18  14:10.
 * @Description 用户管理
 */
public class UserController extends BaseController {
   
public ResponseBean register(@RequestBody UserDTO userDTO) {
   log.info("用户管理---------用户注册---------请求参数:{}", userDTO);
     
   log.info("用户管理---------用户注册---------请求参数:{},响应结果:{}",userDTO, userDTO);

     }
     
     
/**
 * @author HealerJean
 * @ClassName UserController
 * @date 2019/10/18  14:10.
 * @Description 系统管理-字典管理
 */ 

public ResponseBean getDictType(@PathVariable Long id) {
  log.info("系统管理-字典管理--------字典类型查询--------字典类型Id:{}", id);

  log.info("系统管理-字典管理--------字典类型查询--------字典类型Id:{},响应结果:{}", id,dict);
}

     

2.2.2、 方法注释和日志

 /**
     * 根据合同模板生成合同初始pdf文件,讲合同状态设置为待确认状态
     * 1、数据校验
     *  1.1、基本数据校验
     *  1.2、校验合同模板是否存在
     *  1.3、校验签署人是否完整
     *  1.4、校验签署方是否真实有效
     * 2、把合同签署各方的信息和模板取出,将变量更替,生成word和pdf
     * 4、保存签署人信息
     * 5、合同初始化日志保存
     * 6、删除临时文件
     */
    void createContractByTeamplate(ContractDTO contractDTO)

2.3、异常问题

2.3.1 、工具类异常问题

public class UrlEncodeUtil {

    public String encode(String text){
        try {
            return URLEncoder.encode(text,"gbk" );
        }catch (UnsupportedEncodingException e) {
            throw new RuntimeException("{}加密失败", text,e);
        }catch (Exception e){
            throw new RuntimeException("{}加密失败”,text, e);
        }
    }
}

3、数据库规范

3.1、demo

3.1.1、建表语句

  • 是否 nuLl 必须not null ,这样不方便建立索引,为了防止为null,我们可以给赋予初始值,今后建议 default ‘’,给一个空串,空串不占内存空间,NULL是占内存空间的
create table test
(
    `id`        bigint(20) unsigned not null auto_increment comment '主键',
    uk_name     bigint(20) unsigned not null comment 'uk',
    idx_name    bigint(20) unsigned not null comment 'idx',
    ref_item_id  bigint(20) unsigned not null comment 'item表主键',
    status      varchar(32)         not null comment '产品状态 字典表 productstatus',
    create_user bigint(20) unsigned null     default 0 comment '创建人',
    create_name varchar(64)         null     default '' comment '创建人名称',
    create_time datetime            not null default current_timestamp comment '创建时间',
    update_user bigint(20) unsigned null     default 0 comment '更新人',
    update_name varchar(64)         null     default '' comment '更新人名称',
    update_time datetime            not null default current_timestamp on update current_timestamp comment '更新时间',
    unique index uk_name (uk_name) using btree comment '唯一索引',
    index idx_name (idx_name) using btree comment '索引',
    primary key (`id`) using btree
) engine = innodb comment '测试表'


3.1.2、建表说明

类型 名称 长度 解释
bigint 主键 bigint(20)
varchar 地址 varchar(128)
varchar 状态 varchar(8)
varchar 附件,逗号相隔 varchar(128)
varchar 备注、描述 varchar(128/256)
varchar 手机号 varchar(20)
varchar 名字 varchar(64)
varchar 邮件 varchar(64)
decimal 金额 decimal(20,0) 以分为单位
decimal 百分比 decimal(7,6) 80% 0.800000
int 数字 int(10)
tinyint 布尔 tinyint(1)

3.2、基础规范

3.2.1、统一说明

  • MySQL字符集统一使用utf8,默认排序规则:utf8_general_ci

  • 使用InnoDB存储引擎,默认事务隔离级别REPEATABLE-READ(可重复读)

  • 不要使用MySQL存储过程,视图,触发器,Event, InnoDB外键约束

  • 每个数据表都添加注释 comment, 每个字段也添加comment

  • 不要在数据库中存储大图片或大文件,尽量使用简单的数据类型,避免使用blob和text类型

  • 单表数据量控制在1000W行以内 ,采用合适的分库分表策略,例如十库百表

3.2.2、字段设计

  • 表示状态字段(0-255)的使用TINYINT UNSINGED ; 0避免成为有效状态值,非负的数字类型字段,都添加上UNSINGED,
  • 时间字段使用时间日期类型,避免使用字符串类型存储,日期使用DATE类型,年使用YEAR类型,日期时间可使用DATETIME和TIMESTAMP
  • 字符串VARCHAR(N), 其中N表示字符个数,请尽量减少N的大小
  • 字段都设置为NOT NULL, 为字段提供默认值,如’’和’0’ ;
  • 主键尽量保持增长趋势,建议使用id的生成器,避免使用表的自增列

3.2.3、sql使用规范

  • 避免使用join,子查询等SQL
1.对于mysql,不推荐使用子查询和join是因为本身join的效率就是硬伤,一旦数据量很大效率就很难保证,强烈推荐分别根据索引单表取数据,然后在程序里面做join,merge数据,导致性能下降

2.子查询就更别用了,效率太差,执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,所以,子查询的速度会受到一定的影响,这里多了一个创建和销毁临时表的过程。

3.如果是JOIN的话,它是走嵌套查询的。小表驱动大表,且通过索引字段进行关联。如果表记录比较少的话,还是OK的。大的话业务逻辑中可以控制处理
  • 在线业务的update和delete的where中是唯一索引或者主键,避免一次修改多条语句的情况,而且这样锁住的是一行数据
  • 避免在MySQL数据库中进行计算操作,尽量由业务来处理运算,数据库,就应该让它做存储数据,查询数据的事情,
  • 避免使用select * , 只返回自己需要的字段,枚举出要返回的字段名称
  • SQL过滤的where条件尽量不使用OR, NOT IN , NOT EXIST
  • 使用where IN()过滤时,IN集合个数必须小于500,因为in的数据少的时候,mysql优化器会可能会使用索引,但是当数据太多以后就不一定了,可以让MySQL按照ID顺序进行查询,这可能比随机的关联要更高效

3.3、表名设计

尽量使用 项目名(scf)_模块名_表名

项目名,因为我们可能一个数据库对应多个项目,这样容易区分

模块名,能够清晰明了的知道是哪个模块的表

3.4、字段设计

使用下划线,不要使用大小写组合,原因自己理解吧,兄弟

3.4.1、长度说明

3.4.1.1、数字型
类型 字节 范围(有符号) 范围(无符号) 用途
tintint 1 (-128,127) (0,255) 小整数值
smallint 2 (-32 768,32 767) (0,65 535) 大整数值
mediumint 3 (-8 388 608,8 388 607) (0,16 777 215) 大整数值
int/integer 4 (-2 147 483 648,2 147 483 647) (0,4 294 967 295) 大整数值
bigint 8 (-9 233 372 036 854 775 808,9 223 372 036 854 775 807) (0,18 446 744 073 709 551 615) 极大整数值
float 4 (-3.402 823 466 E+38,1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38) 0,(1.175 494 351 E-38,3.402 823 466 E+38) 单精度/浮点数值
double 8 (1.797 693 134 862 315 7 E+308,2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) 0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) 双精度/浮点数值
decimal M>D,为M+2否则为D+2 decimal(M,D)依赖于M和D的值 依赖于M和D的值 小数值
3.4.1.2、字符类型
类型 字节 用途
char 0-255字节 变长字符串
varchar 0-255字节 变长字符串
tinyblog 0-255字节 不超过 255 个字符的二进制字符串
tinytext 0-255字节 短文本字符串
blog 0-65 535字节 二进制形式的长文本数据
text 0-65 535字节 长文本数据
mediumblog 0-16 777 215字节 16M 二进制形式的中等长度文本数据
mediumtext 0-16 777 215字节 16M 中等长度文本数据
longblog 0-4 294 967 295字节 4G 二进制形式的极大文本数据
longtext 0-4 294 967 295字节 4G 极大文本数据
3.4.1.3、时间类型
类型 字节 取值范围 用途
year 1 1901——2155 YYYY 日期值
date 4 1000-01-01——9999-12-31 YYYY-MM-DD 时间值或持续时间
time 3 -838:59:59——838:59:59 HH:MM:SS 年份值
datetime 8 1000-01-01 00:00:00——9999-12-31 23:59:59 YYYY-MM-DD HH:MM:SS 混合日期和时间值
timestamp 4 19700101080001——20380119111407 YYYYMMDD HHMMSSsss 混合日期和时间值,时间戳

3.4.2、字段详解

3.4.2.1、字符 char(M),varcahr(M)

char是一种固定长度的类型,varchar则是一种可变长度的类型,它们的区别是:

  • char(M)类型的数据列里,每个值都占用M个字节,如果某个长度小于M,MySQL就会在它的右边用空格字符补足.(在检索操作中那些填补出来的空格字符将被去掉)

  • varchar(M)类型的数据列里,每个值只占用刚好够用的字节再加上一个用来记录其长度的字节(即总长度为L+1字节

注意:一般千万不要使用text ,这样从mybatis取出来看似是String类型的,但是在实际使用中却或出现字符问题

3.4.2.3、日期
1、Local类型的日期

LocalTime 对应 **time **只包括时间

LocalDate 对应 **date ** 只包括日期

LocalDateTime 对应 **timestamp datetime **包括日期和时间

2、timestamp

timestamp 多个日期,如果可能为空,则建议使用datetime(默认值建议设置为)
0001-01-01 00:00:00,因为0000-00-00 00:00:00mysql不能保存,而且会报错,

普通字段不要设置为timestamp,timestamp列必须有默认值,默认值可以为“0000-00-00 00:00:00”,但不能为null。如果我们在save实体的时候,没有给相关timestamp设置值,那么他就会自动由mysql将当前时间设置进去

3、注解使用
@Column(insertable = true,updatable = false)
@ApiModelProperty(hidden = true)
private Date cdate;

@UpdateTimestamp
@ApiModelProperty(hidden = true)
private Date udate;
3.4.2.3、小数设计 decimal

mysql中的decimal字段,声明语法为DECIMAL(M,D) D是小数点右侧数位0-30,M最大精度数位,1-65

  • D:是小数部分的位数,若插入的值未指定小数部分或者小数部分不足D位则会自动补到D位小数,若插入的值小数部分超过了D为则会发生截断,截取前D位小数(四舍五入截取)。

  • M:是整数部分+小数部分=总长度,也即插入的数字整数部分不能超过M-D位,否则不能成功插入,会报超出范围的错误。

规则:先保证小数点,再保证整数,  

举例说明,11615.23653234568这个数存你说的三个格式
decimal:11615
decimal(3)999
decimal(3,2)9.99
decimal(10,5)11615.23653
超出精度范围的数会被强制进位并只显示数据类型定义的格式


3.4.2.4、数字
1、mysql 类型有符号范围和无符号范围

带符号和无符号,顾名思义,就是是否有正负之分:

比如8位的二进制,

如果带符号,需要用1位表示符号(1表示负数,0表示正),剩下7位表示数据.
那么表示范围是-128—127(包括-0和+0).

如果不带符号,8位全部表示数据,
那么表示范围是 0–256

最小负数二进制是1000 0000 → 减一: 0111 1111 取反: 1000 0000 = 128 所以应该为 - 128
最大负数二进制是1111 1111 → 减一: 1111 1110 取反: 0000 0001 = 1 所以应该为 - 1
如果带符号,需要用1位表示符号(1表示负数,0表示正),剩下7位表示数据. 那么表示范围是-128—127(包括-0和+0).

理解下了的话,就是无符号都是正数 ,所以主键自增Id我们一般都设计为无符号的

`id` bigint(16) unsigned NOT NULL AUTO_INCREMENT,
2、各个数据类型的长度以及默认

整型的每一种都分无符号(unsigned)和有符号(signed)两种类型,在默认情况整型变量都是有符号的类型

项目开发经验规范总结-时刻更新

3、int(M) (用于提示开发者长度)

这个长度M并不代表允许存储的宽度,int(M),也就是int(3)和int(11)能够存储的数据是一样的

只有联合zerofill参数才能有意义,否则int(3)int(11)没有任何区别。

  • 不加zeroffill没有区别
create table test_int
    (
      id   int(3)  unsigned  not null,
      uid  int(11) unsigned  not null,
      uuid int     unsigned  not null
    );
#插入数据 
insert into test_int
values (4294967295, 4294967295, 4294967295);

#查询数据,发现没有什么区别
select * from test_int;
+------------+------------+------------+
| id         | uid        | uuid       |
+------------+------------+------------+
| 4294967295 | 4294967295 | 4294967295 |
+------------+------------+------------+
1 row in set (0.00 sec)


  • 有了zeroffill 不足会自动补0
create table test_int1
(
      id   int(3) unsigned zerofill  not null,
      uid  int(11) unsigned zerofill not null,
      uuid int unsigned zerofill     not null
);
#插入数据
insert into test_int1
values (4294967295, 4294967295, 4294967295);

insert into test_int1
values (1, 4294967295, 110000);

#查询数据 发现前面的不足长度的右0了,当然不能使用idea测试,idea没有显示0
mysql> select * from test_int1;
+------------+-------------+------------+
| id         | uid         | uuid       |
+------------+-------------+------------+
| 4294967295 | 04294967295 | 4294967295 |
|        001 | 04294967295 | 0000110000 |
+------------+-------------+------------+
2 rows in set (0.02 sec)

  • 当使用zerofill 时,默认会自动加unsigned(无符号),zerofill默认为int(10)
create table test_int2
(
      id   int(3) unsigned zerofill  not null,
      uid  int  zerofill not null,
      uuid int unsigned zerofill     not null
);

# 下面的不能执行成功,以为无符号的都是正数
insert into test_int2
values (1, -4294967295, 110000);

insert into test_int2
values (1, 12345678, 110000);

mysql> select * from test_int2;
+-----+------------+------------+
| id  | uid        | uuid       |
+-----+------------+------------+
| 001 | 0012345678 | 0000110000 |
+-----+------------+------------+


3.4.2.5、boolean

boolean值用1代表TRUE,0代表FALSE。boolean在mysql里的类型为tinyint(1)。mysql里有四个常量:true,false,TRUE,FALSE分别代表1,0,1,0。

private Boolean loan;
	
tinyint(1) NOT NULL COMMENT '是否借款 true/false 1/0',

3.5、索引设计

  • 普通索引前缀:idx_索引字段名,唯一索引前缀:uk_索引字段名
  • 每个表必须显示指定主键,主键尽量为一个字段,且为数字类型,避免使用字符串
  • 主键尽量保持增长趋势,建议使用id的生成器,而不使用数据库自增(这个很难,我这里还是自增的)
  • 重要的SQL或调用频率高的SQL
update/select/delete的where条件列字段都要添加索引
order by , group by, distinct的字段都要添加索引

  • 组合索引创建时,把区分度(选择性)高的字段放在前面;根据SQL的特性,调整组合索引的顺序
  • 禁止对索引列进行函数运算和数学运算
  • 每个表的索引个数尽量少于5个,避免创建重复冗余索引;每个组合索引尽量避免超过3个字段

**唯一索引添加之后,如果是逻辑删除的,如果有可能恢复,记得还原id,没有添加唯一索引,则按照正常的删除即可 **

**不过一般情况下,我们这种下面这种没有业务的是可以恢复的,如果是设计到用户名,一般情况下我个人的理解是注销的用户,数据进行迁移,当前数据库中没有,然后就可以恢复了 **

1、没有索引删除的
/**
     * 添加字典类型
     *
     * @return
     */
    @Override
    public void addDictType(DictionaryTypeDTO typeDTO, LoginUserDTO loginUserDTO) {
        SysDictionaryTypeQuery query = new SysDictionaryTypeQuery();
        query.setTypeKey(typeDTO.getTypeKey());
        query.setStatus(StatusEnum.生效.code);
        SysDictionaryType type = sysDictionaryTypeManager.findByQueryContion(query);
        if (type != null) {
            throw new BusinessException(ResponseEnum.字典类型已存在);
        }
        type = new SysDictionaryType();
        type.setCreateUser(loginUserDTO.getUserId());
        type.setCreateName(loginUserDTO.getRealName());
        type.setTypeKey(typeDTO.getTypeKey());
        type.setTypeDesc(typeDTO.getTypeDesc());
        type.setStatus(StatusEnum.生效.code);
        type.setUpdateUser(loginUserDTO.getUserId());
        type.setUpdateName(loginUserDTO.getRealName());
        sysDictionaryTypeManager.insertSelective(type);
    }

    /**
     * 删除字典类型
     */
    @Override
    public void deleteDictType(Long id, LoginUserDTO loginUserDTO) {
        SysDictionaryType type = sysDictionaryTypeManager.findById(id);
        if (type == null) {
            throw new BusinessException(ResponseEnum.字典类型不存在);
        }
        type.setStatus(StatusEnum.废弃.code);
        type.setUpdateUser(loginUserDTO.getUserId());
        type.setUpdateName(loginUserDTO.getRealName());
        sysDictionaryTypeManager.updateSelective(type);
    }


    /**
     * 修改字典类型
     */
    @Override
    public void updateDictType(DictionaryTypeDTO typeDTO, LoginUserDTO loginUserDTO) {

        SysDictionaryTypeQuery query = new SysDictionaryTypeQuery();
        query.setTypeKey(typeDTO.getTypeKey());
        query.setStatus(StatusEnum.生效.code);
        SysDictionaryType typeExist = sysDictionaryTypeManager.findByQueryContion(query);
        //判断是是否已经存在数据
        if (typeExist != null && !typeExist.getId().equals(typeDTO.getId())  ) {
            throw new BusinessException(ResponseEnum.字典类型已存在);
        }
        SysDictionaryType type = BeanUtils.dtoToDictionaryType(typeDTO);
        type.setUpdateUser(loginUserDTO.getUserId());
        type.setUpdateName(loginUserDTO.getRealName());
        sysDictionaryTypeManager.updateSelective(type);
    }
    

2、有索引删除的代码
    /**
     * 添加域名
     */
    @Override
    public DomainDTO addDomain(DomainDTO domainDTO, LoginUserDTO loginUserDTO) {
        AlimamaInfoDTO alimamaInfoDTO = loginUserDTO.getAlimamaInfo();
        SysDomainQuery domainQuery = new SysDomainQuery();
        domainQuery.setRefAlimamaInfoId(alimamaInfoDTO.getAlimamaInfoId());
        domainQuery.setType(domainDTO.getType());
        SysDomain domain = sysDomainManager.findByQueryContion(domainQuery);
        if (domain != null) {
            if (domain.getStatus().equals(StatusEnum.生效.code)) {
                throw new BusinessException(ResponseEnum.域名已存在);
            } else {
                domain.setRefAlimamaInfoId(alimamaInfoDTO.getAlimamaInfoId());
                domain.setStatus(StatusEnum.生效.code);
                domain.setValue(domainDTO.getValue());
                domain.setCreateUser(loginUserDTO.getUserId());
                domain.setCreateName(loginUserDTO.getRealName());
                domain.setUpdateUser(loginUserDTO.getUserId());
                domain.setUpdateName(loginUserDTO.getRealName());
                sysDomainManager.updateSelective(domain);
            }
        } else {
            domain = new SysDomain();
            domain.setRefAlimamaInfoId(alimamaInfoDTO.getAlimamaInfoId());
            domain.setType(domainDTO.getType());
            domain.setValue(domainDTO.getValue());
            domain.setStatus(StatusEnum.生效.code);
            domain.setCreateUser(loginUserDTO.getUserId());
            domain.setCreateName(loginUserDTO.getRealName());
            domain.setUpdateUser(loginUserDTO.getUserId());
            domain.setUpdateName(loginUserDTO.getRealName());
            sysDomainManager.save(domain);
        }
        domainDTO.setDomainId(domain.getId());
        return domainDTO;
    }

    /**
     * 修改域名
     */
    @Override
    public void updateDomain(DomainDTO domainDTO, LoginUserDTO loginUserDTO) {
        AlimamaInfoDTO alimamaInfoDTO = loginUserDTO.getAlimamaInfo();
        SysDomainQuery domainQuery = new SysDomainQuery();
        domainQuery.setRefAlimamaInfoId(alimamaInfoDTO.getAlimamaInfoId());
        domainQuery.setType(domainDTO.getType());
        SysDomain domain = sysDomainManager.findByQueryContion(domainQuery);
        if(domain != null ){
            if(!domain.getId().equals(domainDTO.getDomainId())){
                if(domain.getStatus().equals(StatusEnum.生效.code)){
                    throw new BusinessException(ResponseEnum.域名已存在);
                }else {//前提必须是status,否则会出问题
                    domain.setStatus(StatusEnum.生效.code);
                    domain.setValue(domainDTO.getValue());
                    domain.setUpdateUser(loginUserDTO.getUserId());
                    domain.setUpdateName(loginUserDTO.getRealName());
                    sysDomainManager.updateSelective(domain);
                }
            }else {
                domain.setValue(domainDTO.getValue());
                domain.setUpdateUser(loginUserDTO.getUserId());
                domain.setUpdateName(loginUserDTO.getRealName());
                sysDomainManager.updateSelective(domain);
            }
        }else {
            throw new BusinessException(ResponseEnum.域名不存在);
        }
    }



    /**
     * 删除域名
     */
    @Override
    public void deleteDomain(DomainDTO domainDTO, LoginUserDTO loginUserDTO) {
        AlimamaInfoDTO alimamaInfoDTO = loginUserDTO.getAlimamaInfo();
        SysDomain domain = sysDomainManager.findByIdAndAlimamaId(domainDTO.getDomainId(), alimamaInfoDTO.getAlimamaInfoId());
        if(domain == null ){
            throw new BusinessException(ResponseEnum.域名不存在);
        }
        if(domain.getStatus().equals(StatusEnum.废弃.code)){
            throw new BusinessException(ResponseEnum.重复操作);
        }
        domain.setStatus(StatusEnum.废弃.code);
        domain.setUpdateUser(loginUserDTO.getUserId());
        domain.setUpdateName(loginUserDTO.getRealName());
        sysDomainManager.updateSelective(domain);
    }

3.5、sql规范


select o.createTime,
       o.clickTime,
       c.itemTitle,
       o.itemId,
       o.orderNo,
       c.shopTitle,
       o.estimateAmount,
       o.payAmount,
       c.commissionRatio,
       o.orderStatus
from user_order o left  join
     coupon_taoke_data c on c.id = o.taokeId
where 1=1

4、接口文档规范

4.1、多点

4.1.1、 查询排重接口
接口详情
地址 http://www.baidu.com (正式环境)
请求方式 GET
参数 是否必填 说明
idfa 广告标识符,只支持单个查询
source 渠道来源,具体值在接入时再进行分配

返回结果 格式 JSON
状态码 10000 success(调用成功)
10001 param error(参数错误)
10002 query failed(查询失败)
10010 access prohibited(访问拒绝)

具体返回结果举例:

1、查询成功

{
  "state": 10000,
  "message": "success",
  "data": {
    "BD239708-2874-417C-8292-7E335A537FAD": 1 //已经存在
  }
}

{
  "state": 10000,
  "message": "success",
  "data": {
    "BD239708-2874-417C-8292-7E335A537FAD": 0 //不存在
  }
}
  1. 接口调用失败
{
  "state": 10010,
  "message": "access prohibited",
  "data": [
    
  ]
}

4.2、小米

4.2.1、角色列表查询

说明

  • 测试调用地址:/api/roles
  • 调用方式:GET

请求参数

参数名称 参数类型 参数长度 是否必需 说明 备注
pageSize 整数 每页显示数量 默认10
pageNo 整数 当前查看页码 默认1
roleName 字符串 64 角色名称
systemCode 字符串 32 系统CODE
isPage 布尔 4 是否分页 true/false

请求报文样例

{
    "pageSize": 1,
    "pageNo": 1,
    "roleName": "",
    "systemCode": "scf-manager",
    "isPage": true
}

响应参数

参数名称 参数类型 参数长度 是否必需 说明 备注
msg 字符串 255 返回结果
total 数字 总数
pageNo 数字 页数
totalPage 数字 总页数
pageSize 数字 每页数量
datas Role数组 返回的数据信息

Role 数据结构

参数名称 参数类型 参数长度 是否必需 说明 备注
id 数字 16 id
roleName 字符串 64 角色名称
systemCode 字符串 64 系统CODE
status 字符串 8 状态
desc 字符串 255 描述

响应报文样例

{
    "msg": "角色列表查询成功",
    "total": 2,
    "pageNo": 1,
    "totalPage": 1,
    "datas": [
        {
            "id": 1,
            "roleName": "后台管理员",
            "systemCode": "scf-manager",
            "status": "10"
        },
        {
            "id": 4,
            "roleName": "测试角色哦",
            "systemCode": "scf-manager",
            "status": "10",
            "desc": "真的是测试"
        }
    ],
    "pageSize": 10
}

返回码解析

返回码 含义 备注
200 成功

感兴趣的,欢迎添加博主微信,

哈,博主很乐意和各路好友交流,如果满意,请打赏博主任意金额,感兴趣的在微信转账的时候,备注您的微信或者其他联系方式。添加博主微信哦。

请下方留言吧。可与博主*讨论哦

支付包 微信 微信公众号
项目开发经验规范总结-时刻更新 项目开发经验规范总结-时刻更新 项目开发经验规范总结-时刻更新
相关标签: 经验