开发规范
使用本框架作为开发项目,必须遵守以下规范.
开发工具强制统一
前端: VS Code/WebStorm
后端: IntelliJ IDEA
数据库设计: CHINER
数据库客户端: DBeaver
IDEA 强制安装插件
- Lombok:自动生成get set等方法
IDEA 推荐安装插件
- Maven Helper: maven 依赖排查
- Free Mybatis plugin: mybatis xml 快速切换
- RestfulTool: 接口查找
- Alibaba Java Coding Guidelines : 阿里代码编码指南
- Git commit Template: git 提交消息模板
- GitToolBox: Git工具
VS Code 推荐安装插件
- Iconify IntelliSense- Iconify 图标插件
- windicss IntelliSense- windicss 提示插件
- I18n-ally- i18n 插件
- Vetur- vue 开发必备 (也可以选择 Volar)
- ESLint- 脚本代码检查
- Prettier- 代码格式化
- Stylelint- css 格式化
- DotENV- .env 文件 高亮
约定
源码、jdk、mysql、redis、rabbitmq、nacos、seata等存放路径禁止包含中文、空格、特殊字符等。
# 正例: D:/projects/lamp-cloud D:/projects/lamp-boot D:/projects/lamp-util D:/projects/tools/nacos_121 D:/projects/tools/redis D:/projects/tools/seata # 反例: D:/Program Files/lamp-cloud D:/Program Files/lamp-boot D:/Program Files/lamp-util D:/Program Files/视频配套项目软件_window版(配置文件被群主优化过)/nacos_121 D:/Program Files/视频配套项目软件_window版(配置文件被群主优化过)/redis D:/Program Files/视频配套项目软件_window版(配置文件被群主优化过)/seata
数据库设计
设计表结构时尽量参考项目中已经创建好的表结构。
使用PDManer来设计表结构
必须显式指定主键, 勿用复合主键. 主键的命名统一为:
id
任何表至少包含3个字段: bigint id、 datetime created_time、bigint created_by
关于状态字段根据业务含义命名为: state、status
- 如(隐藏、显示)、(是、否)、(可用、不可用)、(启用、禁用) 等字段用 state
- 如 OrderedStatus、ShippingStatus、ReceiptedStatus 等字段用 status
界面上要显示成树形结构的表,至少需要3个字段: id、parent_id、sort_value
表中有以下含义的字段, 尽可能的采用同样的命名规则
- 名称: name
- 编码: code
- 描述: describe_
- 创建用户ID: created_by
- 创建时间: created_time
- 最后修改数据的用户ID: updated_by
- 最后修改数据的时间: updated_time
- 是否内置数据: readonly_
字段以
_
结尾没有什么特殊意义,仅仅是为了避免数据库关键字。POJO 类的布尔属性不能加 is,而数据库字段必须加 is_,要求在 resultMap 中进行字段与属性之间的映射。
主键索引名为 pk_字段名
唯一索引名为 uk_字段名
普通索引名则为 idx_字段名。
说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称
不得使用外键与级联,一切外键概念必须在应用层解决
不用存储过程
数据库名、表名、字段名统一使用小写字母,
_
分割数据库字段名取名为关键字时,可以在字段后面加上
_
如: type_、code_、like_ 等字段
varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
表和字段必须加注释!
表名注释支持换行,第一行会被视为表名,表的介绍请换行填写。
字段的第一行视为字段简介,详细介绍和枚举类型请换行。
所有字段尽量根据业务设置合理的缺省值,尽量避免表中出现NULL值
当字段为外键时,字段名为:关联表_id, 注释需要在字段注释基础上,换行加上
#关联表表名
来说明关联的哪张表。当字段为枚举时,按照下面的格式给字段加上注释,使用代码生成器时,可以自动生成枚举类
注释模板1: 注释内容;#枚举类名{枚举值英文名:"枚举值英文注释"; ...} 注释模板2: 注释内容;#枚举类名{枚举值英文名:val,"枚举值英文注释"; ...} 注释模板4: 注释内容;#{枚举值英文名:"枚举值英文注释"; ...} 其中枚举类名可以没有,如果没有,则生成的枚举值名为:表对应的实体名 + 当前字段对应的属性名(首字母大写) + Enum 枚举值例子: 注释模板1:文件类型;#FileType{PAN:云盘数据;API:接口数据} 注释模板2:数据类型;#DataTypeEnum{SYSTEM:10,系统值;BUSINESS:20,业务值} 注释模板3:数据类型;#{DIR:目录;IMAGE:图片;VIDEO:视频;AUDIO:音频;DOC:文档;OTHER:其他}
示例:
/** * 枚举类型正则 解析#Xxxx{} 格式 * <p> * 注释模板1: 注释内容;#枚举类名{枚举值英文名:"枚举值英文注释"; ...} * 注释模板2: 注释内容;#枚举类名{枚举值英文名:val,"枚举值英文注释"; ...} * 注释模板3: 注释内容;#枚举类名{枚举值英文名:val,"枚举值英文注释",val2; ...} * 注释模板4: 注释内容;#{枚举值英文名:"枚举值英文注释"; ...} */ public final static Pattern ENUM_FIELD_PATTERN = Pattern.compile("(#([a-zA-Z\\d\"._]+)?[{](.*?)?[}])"); /** * 枚举类型正则 解析{}内的内容 * 匹配 xx:xx; 形式的注释 */ public final static Pattern ENUM_KEY_VALUE_PATTERN = Pattern.compile("([A-Za-z1-9_-]+):(.*?)?;");
/** * 注释模板1: * #ProductType{ordinary:普通;gift:赠品} * * @author 阿汤哥 * @date 2023-03-22 20:26:31 */ @Getter @AllArgsConstructor @NoArgsConstructor @ApiModel(value = "ProductType", description = "商品类型-枚举") public enum ProductType implements BaseEnum { /** * ORDINARY */ ORDINARY("普通"), /** * GIFT */ GIFT("赠品"), ; @ApiModelProperty(value = "描述") private String desc; /** * 根据当前枚举的name匹配 */ public static ProductType match(String val, ProductType def) { return Stream.of(values()).parallel().filter(item -> item.name().equalsIgnoreCase(val)).findAny().orElse(def); } public static ProductType get(String val) { return match(val, null); } public boolean eq(ProductType val) { return val != null && eq(val.name()); } @Override @ApiModelProperty(value = "name", allowableValues = "ORDINARY,GIFT", example = "ORDINARY") public String getCode() { return this.name(); } @Override @ApiModelProperty(value = "数据库中的值") public String getValue() { return this.name(); } }
/** * 注释模板2: * #DataTypeEnum{SYSTEM:10,系统值;BUSINESS:20,业务值} * * @author tangyh * @date 2021/3/15 3:34 下午 */ @Getter @ApiModel(value = "DataTypeEnum", description = "数据类型-枚举") public enum DataTypeEnum implements BaseEnum { /** * 应用授权 */ SYSTEM("10", "系统值"), /** * 应用续期 */ BUSINESS("20", "业务值"), ; private final String code; private final String desc; DataTypeEnum(String code, String desc) { this.code = code; this.desc = desc; } /** * 根据当前枚举的name匹配 */ public static DataTypeEnum match(String val, DataTypeEnum def) { return Stream.of(values()).parallel().filter(item -> item.name().equalsIgnoreCase(val)).findAny().orElse(def); } public static DataTypeEnum get(String val) { return match(val, null); } public boolean eq(DataTypeEnum val) { return val != null && eq(val.name()); } @Override @ApiModelProperty(value = "编码", allowableValues = "10,20", example = "20") public String getCode() { return this.code; } }
/** * 注释模板3: * #{ordinary:01,普通;gift:02,赠品;} * * @author 阿汤哥 * @date 2023-03-22 20:26:31 */ @Getter @AllArgsConstructor @NoArgsConstructor @ApiModel(value = "TestSimple22Type2Enum", description = "商品类型2-枚举") public enum TestSimple22Type2Enum implements BaseEnum { /** * ORDINARY */ ORDINARY("01","普通"), /** * GIFT */ GIFT("02","赠品"), ; @ApiModelProperty(value = "数据库存储值") private String value; @ApiModelProperty(value = "描述") private String desc; /** * 根据当前枚举的name匹配 */ public static TestSimple22Type2Enum match(String val, TestSimple22Type2Enum def) { return Stream.of(values()).parallel().filter(item -> item.name().equalsIgnoreCase(val)).findAny().orElse(def); } public static TestSimple22Type2Enum get(String val) { return match(val, null); } public boolean eq(TestSimple22Type2Enum val) { return val != null && eq(val.name()); } @Override @ApiModelProperty(value = "name", allowableValues = "ORDINARY,GIFT", example = "ORDINARY") public String getCode() { return this.name(); } @Override @ApiModelProperty(value = "数据库中的值") public String getValue() { return this.value; } }
当字段需要使用Echo模块回显功能时, 需要按照下面当格式添加注释
/** * Echo 注解解析 正则 * * <p> * * @Echo() 内部的字段编写顺序一定是: api、ref、beanClass、dictType, 除了api必填,其他都可以不填 * api、ref、dictType 可以直接写字符串,也能写常量,但 * api 的常量只能存放在 EchoApi * ref 的常量只能存放在 EchoRef * dictType 的常量只能存放在 EchoDictType * <p> * api、ref、beanClass、dictType 中使用的类需要提前在 lamp.generator.constantsPackage 中配置 * <p> * 如: * 匹配: @Echo(api="") * 匹配: @Echo(api="", dictType = "") * 匹配: @Echo(api="", beanClass=Xxx.class) * 匹配: @Echo(api="", ref="", beanClass=Xxx.class) * 匹配: @Echo(api="orgApi", ref="" beanClass=Org.class, dictType="") */ public final static Pattern ECHO_FIELD_PATTERN = Pattern.compile("(@Echo[(](api|feign)? *= *([a-zA-Z\\d\"._]+)(, *ref *= *([a-zA-Z\\d\"._]+))?(, *beanClass *= *([a-zA-Z\\d\"._]+))?(, *dictType *= *([a-zA-Z\\d\"._]+))?[)])");
public class User { // 模版1: @Echo(api = ORG_ID_CLASS, beanClass = Org.class) private Long orgId; // 模版2: @Echo(api = DICTIONARY_ITEM_CLASS, dictType = DictionaryType.NATION) private String nation; // 模版3: @Echo(api = STATION_ID_CLASS) private Long stationId; }
其他更多约束尽量遵守阿里规范
后端
详细的代码注释: 类、字段、方法、参数上必须加上doc注释, 方法内部适当的位置加上行注释和块注释. doc注释标明 作者(@author)、创建时间(@date)等.
使用Mybatis-plus 相关查询接口时, 使用系统封装的 Wraps , 而非mp官方的 Wrappers
在xml写sql 需要使用模糊查询时, 使用自定义类型处理器:
fullLike
, 会自动给你拼接%select * from user where name like #{name, typeHandler=fullLike} -- 传递的name = 参数时,生成如下SQL -- select * from user where name like '%参数%'
尽量使用jdk8+新特性
必须显示的使用泛型
暴露给前端的接口参数不宜过多, 使用VO类将必要的字段暴露给前端,避免前端大海捞针!
如: 保存接口 id、createdTime、createdBy、status 等字段不应该显示在swagger文档的入参里面, 而是由后台自动赋值.
暴露给前端的接口,一定要在swagger文档上体现合理的注释!
其他更多约束尽量遵守阿里规范
常量的复用层次有五层:跨服务共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量。
跨服务共享常量:放置在lamp-commom模块中,constant 目录下。
应服务共享常量:放置在lamp-xxx-entity中, constant 目录下。
子模块共享常量:即在当前模块的 constant 目录下。
包内共享常量:即在当前包下单独的 constant 目录下。
类内共享常量:直接在类内部 private static final 定义。
集合初始化时,指定集合初始值大小。
说明:HashMap 使用 HashMap(int initialCapacity) 初始化,如果暂时无法确定集合大小,那么指定默 认值(16)即可。 正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认 为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。
前端
基础平台的业务代码都写在
src/views/basic
中,开发运营系统的业务代码都写在src/views/devOperation
中,其他系统在views目录新建一个文件夹,建文件夹规则跟项目的菜单层级保持一致。文件夹名跟菜单的路由uri保持一致(大小写也要一致)如: 用户管理的路由为:
#/user/user
, 代码就放在src/views/basic/user/user/Index.vue
其他更多约束尽量遵守阿里规范
提交代码规范
- 每次提交尽量按功能点或bug提交代码,哪怕是只修改了一行代码,一个字母,尽量不要一次性提交过多的功能和bug等
- 及时拉取、及时提交、及时推送、及时合并;
- 提交代码前,记得勾选IDEA提交框中的
Reformat code
、Rearrage code
、Optimize imports
选项 - 提交代码时按照以下模版进行注释
- type: 用于说明 commit的类别,只允许使用下面几个标识:
- fix:修补bug
- hotfix:紧急修复bug
- chore:构建过程或辅助工具的变动
- docs:文档(documentation)
- feat:新功能(feature)
- refactor:重构(即不是新增功能,也不是修改bug的代码变动)
- style: 仅仅修改了空格、缩进等,不改变代码逻辑(不影响代码运行的变动)
- test:增加测试
- revert:回滚到上一个版本;
- perf:改善性能和体现的修改
- build:改变构建流程,新增依赖库、工具等(例如webpack修改);
- scope of this change : 本次变更范围。用于描述改动的范围,格式为项目名/模块名,例如: node-pc/common rrd-h5/activity,而we-sdk不需指定模块名。如果一次commit修改多个模块,建议拆分成多次commit,以便更好追踪和维护。
- short description : 简要说明
- Long description : 详细说明
- breaking changes : 不兼容变动
- break changes指明是否产生了破坏性修改,涉及break changes的改动必须指明该项,类似版本升级、接口参数减少、接口删除、迁移等。
- close issue : 关闭指定Issue
❗️❗️❗️若评论区无法显示,请使用"手机热点"或"科学上网"。