項目開發經驗規範總結-時刻更新

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

WX20190213-180740

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)兩種類型,在默認情況整型變量都是有符號的類型

1556179030028

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 成功

ContactAuthor

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章