mirror of
				https://gitee.com/lab1024/smart-admin.git
				synced 2025-11-04 18:33:43 +08:00 
			
		
		
		
	添加代码规范
This commit is contained in:
		
							
								
								
									
										612
									
								
								代码规范/Java规范 V2.0.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										612
									
								
								代码规范/Java规范 V2.0.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,612 @@
 | 
			
		||||
---
 | 
			
		||||
title: 2.3、Java规范 V2.0
 | 
			
		||||
date: 2022-09-07
 | 
			
		||||
---
 | 
			
		||||
## 阅读须知
 | 
			
		||||
 | 
			
		||||
<font color="#DC143C">**在阅读《Java规范》之前,请一定先阅读上一篇《规范导读 V2.0》!!!**</font>  ,[前往阅读](./basic.md)
 | 
			
		||||
 | 
			
		||||
## 一、Java 项目规范
 | 
			
		||||
 | 
			
		||||
### 1.1、Java 项目命名规范
 | 
			
		||||
 | 
			
		||||
全部采用小写方式, 以中划线分隔。
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
正例:`mall-management-system / order-service-client / user-api`
 | 
			
		||||
 | 
			
		||||
反例:`mall_management-system / mallManagementSystem / orderServiceClient`
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 1.2、方法参数规范
 | 
			
		||||
 | 
			
		||||
无论是 `controller,service,manager,dao` 亦或是**其他 class 的**代码,每个方法最多 `5` 个参数,如果超出 `5` 个参数的话,要封装成 `javabean` 对象。
 | 
			
		||||
 | 
			
		||||
- 方便他人调用,降低出错几率。尤其是当参数是同一种类型,仅仅依靠顺序区分,稍有不慎便是灾难性后果,而且排查起来也极其恶心。
 | 
			
		||||
- 保持代码整洁、清晰度。当一个个方法里充斥着一堆堆参数的时候,再坚强的人,也会身心疲惫。
 | 
			
		||||
 | 
			
		||||
反例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
/**
 | 
			
		||||
* 使用证书加密数据工具方法
 | 
			
		||||
*
 | 
			
		||||
* @param param 参数
 | 
			
		||||
* @param password 加密密码
 | 
			
		||||
* @param priCert 私钥
 | 
			
		||||
* @param pubCert 公钥
 | 
			
		||||
* @param username 用户名
 | 
			
		||||
* @param ip ip地址
 | 
			
		||||
* @param userAgent 用户特征
 | 
			
		||||
* @return 返回加密后的字符串
 | 
			
		||||
*/
 | 
			
		||||
public String signEnvelop(JdRequestParam param, String password, String priCert, String pubCert, String username, String ip, String userAgent){
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 1.3、代码目录结构
 | 
			
		||||
 | 
			
		||||
统一的目录结构是所有项目的基础。
 | 
			
		||||
 | 
			
		||||
```xml
 | 
			
		||||
src                               源码目录
 | 
			
		||||
|-- common                            各个项目的通用类库
 | 
			
		||||
|-- config                            项目的配置信息
 | 
			
		||||
|-- constant                          全局公共常量
 | 
			
		||||
|-- handler                           全局处理器
 | 
			
		||||
|-- interceptor                       全局连接器
 | 
			
		||||
|-- listener                          全局监听器
 | 
			
		||||
|-- module                            各个业务(方便将来拆成微服务)
 | 
			
		||||
|-- |--- employee                         员工模块
 | 
			
		||||
|-- |--- role                             角色模块
 | 
			
		||||
|-- |--- login                            登录模块
 | 
			
		||||
|-- third                             三方服务,比如redis, oss,微信sdk等等
 | 
			
		||||
|-- util                              全局工具类
 | 
			
		||||
|-- Application.java                  启动类
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 1.4、common 目录规范
 | 
			
		||||
 | 
			
		||||
common 目录用于存放各个项目通用的项目,但是又可以依照项目进行特定的修改。
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
src 源码目录
 | 
			
		||||
|-- common 各个项目的通用类库
 | 
			
		||||
|-- |--- anno          通用注解,比如权限,登录等等
 | 
			
		||||
|-- |--- constant      通用常量,比如 ResponseCodeConst
 | 
			
		||||
|-- |--- domain        全局的 javabean,比如 BaseEntity,PageParamDTO 等
 | 
			
		||||
|-- |--- exception     全局异常,如 BusinessException
 | 
			
		||||
|-- |--- json          json 类库,如 LongJsonDeserializer,LongJsonSerializer
 | 
			
		||||
|-- |--- swagger       swagger 文档
 | 
			
		||||
|-- |--- validator     适合各个项目的通用 validator,如 CheckEnum,CheckBigDecimal 等
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 1.5、module 目录规范
 | 
			
		||||
 | 
			
		||||
module 目录里写项目的各个业务,每个业务一个独立的顶级文件夹,在文件里进行 mvc 的相关划分。
 | 
			
		||||
其中,domain 包里存放 entity, dto, vo,bo 等 javabean 对象
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
src
 | 
			
		||||
|-- module                         所有业务模块
 | 
			
		||||
|-- |-- role                          角色模块
 | 
			
		||||
|-- |-- |--RoleController.java              controller
 | 
			
		||||
|-- |-- |--RoleConst.java                   role相关的常量
 | 
			
		||||
|-- |-- |--RoleService.java                 service
 | 
			
		||||
|-- |-- |--RoleDao.java                     dao
 | 
			
		||||
|-- |-- |--domain                           domain
 | 
			
		||||
|-- |-- |-- |-- RoleEntity.java                  表对应实体
 | 
			
		||||
|-- |-- |-- |-- RoleForm.java                     请求Form对象
 | 
			
		||||
|-- |-- |-- |-- RoleVO.java                      返回对象
 | 
			
		||||
|-- |-- employee                      员工模块
 | 
			
		||||
|-- |-- login                         登录模块
 | 
			
		||||
|-- |-- email                         邮件模块
 | 
			
		||||
|-- |-- ....                          其他
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 二、MVC 规范
 | 
			
		||||
 | 
			
		||||
### 2.1、整体分层
 | 
			
		||||
 | 
			
		||||
- controller 层
 | 
			
		||||
- service 层
 | 
			
		||||
- manager 层
 | 
			
		||||
- dao 层
 | 
			
		||||
 | 
			
		||||
### 2.2、 `controller` 层规范
 | 
			
		||||
 | 
			
		||||
#### 1) 只允许在 method 上添加 `RequestMapping` 注解
 | 
			
		||||
 | 
			
		||||
只允许在 method 上添加 `RequestMapping` 注解,不允许加在 `class` 上(为了方便的查找 url,放到 class 上 url 不能一次性查找出来)
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
@RestController
 | 
			
		||||
public class DepartmentController {
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/department/list")
 | 
			
		||||
    public ResponseDTO<List<DepartmentVO>> listDepartment() {
 | 
			
		||||
        return departmentService.listDepartment();
 | 
			
		||||
    }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
反例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
@RequestMapping ("/department")
 | 
			
		||||
public class DepartmentController {
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/list")
 | 
			
		||||
    public ResponseDTO<List<DepartmentVO>> listDepartment() {
 | 
			
		||||
        return departmentService.listDepartment();
 | 
			
		||||
    }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 2)不推荐使用 restful 命名 url
 | 
			
		||||
 | 
			
		||||
不推荐使用 restful 命名 url, 只能使用 `get/post` 方法。url 命名遵循:**`/业务模块/子模块/动作`** ;  
 | 
			
		||||
其中 `业务模块和子模块` 使用 名字, `动作` 使用动词;
 | 
			
		||||
原因:
 | 
			
		||||
 | 
			
		||||
> **虽然 restful 大法好,但是有时并不能一眼根据 url 看出来是什么操作,所以我们选择了后者,这个没有对与错,只有哪个更适合我们的团队**
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
GET  /department/get/{id}      查询某个部门详细信息
 | 
			
		||||
POST /department/query         复杂查询
 | 
			
		||||
POST /department/add           添加部门
 | 
			
		||||
POST /department/update        更新部门
 | 
			
		||||
GET  /department/delete/{id}   删除部门
 | 
			
		||||
 | 
			
		||||
GET  /department/employee/delete/{id}   删除部门员工
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 3)swagger 接口注释必须加上后端作者
 | 
			
		||||
 | 
			
		||||
每个方法必须添加 `swagger` 文档注解 `@ApiOperation` ,并填写接口描述信息,描述最后必须加上接口的作者信息,格式如下: `@author 卓大`
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
    @ApiOperation("更新部门信息 @author 卓大")
 | 
			
		||||
    @PostMapping("/department/update")
 | 
			
		||||
    public ResponseDTO<String> updateDepartment(@Valid @RequestBody DepartmentUpdateForm departmentUpdateForm) {
 | 
			
		||||
        return departmentService.updateDepartment(departmentUpdateForm);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 4)controller 每个方法要保持简洁
 | 
			
		||||
 | 
			
		||||
controller 在mvc中负责协同和委派业务,充当路由的角色,所以要保持代码少量和清晰,要做到如下要求:
 | 
			
		||||
 | 
			
		||||
- 不做任何的业务逻辑操作
 | 
			
		||||
- 不做任何的参数、业务校验,参数校验只允许使用@Valid 注解做简单的校验
 | 
			
		||||
- 不做任何的数据组合、拼装、赋值等操作
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
    @ApiOperation("添加部门 @author 卓大")
 | 
			
		||||
    @PostMapping("/department/add")
 | 
			
		||||
    public ResponseDTO<String> addDepartment(@Valid @RequestBody DepartmentAddForm departmentAddForm) {
 | 
			
		||||
        return departmentService.addDepartment(departmentAddForm);
 | 
			
		||||
    }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 5)只能在 `controller` 层获取当前请求用户  
 | 
			
		||||
 | 
			
		||||
只能在 `controller` 层获取当前请求用户,并传递给 `service` 层。
 | 
			
		||||
> **因为获取当前请求用户是从 ThreadLocal 里获取取的,在 service、manager、dao 层极有可能是其他非 request 线程调用,会出现 null 的情况,尽量避免**
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
    @ApiOperation("添加员工 @author 卓大")
 | 
			
		||||
    @PostMapping("/employee/add")
 | 
			
		||||
    public ResponseDTO<String> addEmployee(@Valid @RequestBody EmployeeAddForm employeeAddForm) {
 | 
			
		||||
        RequestUser requestUser = SmartRequestUtil.getRequestUser();
 | 
			
		||||
        return employeeService.addEmployee(employeeAddForm, requestUser);
 | 
			
		||||
    }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 2.3、 `service` 层规范
 | 
			
		||||
 | 
			
		||||
#### 1) 合理拆分 service 业务
 | 
			
		||||
 | 
			
		||||
我们不建议service文件行数太大 ,如果业务较大,请拆分为多个 service;  
 | 
			
		||||
 | 
			
		||||
如订单业务,所有业务都写到 OrderService 中会导致文件过大,故需要进行拆分如下:
 | 
			
		||||
 | 
			
		||||
- `OrderQueryService` 订单查询业务
 | 
			
		||||
- `OrderCreateService` 订单新建业务
 | 
			
		||||
- `OrderDeliverService` 订单发货业务
 | 
			
		||||
- `OrderValidatorService` 订单验证业务
 | 
			
		||||
 | 
			
		||||
#### 2) 谨慎使用 `@Transactional` 事务注解  
 | 
			
		||||
 | 
			
		||||
谨慎使用 `@Transactional` 事务注解的使用,不要简单对 `service` 的方法添加个 `@Transactional` 注解就觉得万事大吉了。  
 | 
			
		||||
应当合并对数据库的操作,尽量减少添加了`@Transactional`方法内的业务逻辑。  
 | 
			
		||||
`@Transactional` 注解内的 `rollbackFor` 值必须使用异常 `Exception.class`
 | 
			
		||||
 | 
			
		||||
> _对于@Transactional 注解,当 spring 遇到该注解时,会自动从数据库连接池中获取 connection,并开启事务然后绑定到 ThreadLocal 上,如果业务并没有进入到最终的 操作数据库环节,那么就没有必要获取连接并开启事务,应该直接将 connection 返回给数据库连接池,供其他使用(比较难以讲解清楚,如果不懂的话就主动去问)。_
 | 
			
		||||
 | 
			
		||||
反例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
    @Transactional(rollbackFor = Exception.class)
 | 
			
		||||
    public ResponseDTO<String> upOrDown(Long departmentId, Long swapId) {
 | 
			
		||||
        // 验证 1
 | 
			
		||||
        DepartmentEntity departmentEntity = departmentDao.selectById(departmentId);
 | 
			
		||||
        if (departmentEntity == null) {
 | 
			
		||||
            return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
 | 
			
		||||
        }
 | 
			
		||||
        // 验证 2
 | 
			
		||||
        DepartmentEntity swapEntity = departmentDao.selectById(swapId);
 | 
			
		||||
        if (swapEntity == null) {
 | 
			
		||||
            return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
 | 
			
		||||
        }
 | 
			
		||||
        // 验证 3
 | 
			
		||||
        Long count = employeeDao.countByDepartmentId(departmentId)
 | 
			
		||||
        if (count != null && count > 0) {
 | 
			
		||||
            return ResponseDTO.wrap(DepartmentResponseCodeConst.EXIST_EMPLOYEE);
 | 
			
		||||
        }
 | 
			
		||||
        // 操作数据库 4
 | 
			
		||||
        Long departmentSort = departmentEntity.getSort();
 | 
			
		||||
        departmentEntity.setSort(swapEntity.getSort());
 | 
			
		||||
        departmentDao.updateById(departmentEntity);
 | 
			
		||||
        swapEntity.setSort(departmentSort);
 | 
			
		||||
        departmentDao.updateById(swapEntity);
 | 
			
		||||
        return ResponseDTO.succ();
 | 
			
		||||
    }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
以上代码前三步都是使用 connection 进行验证操作,由于方法上有@Transactional 注解,所以这三个验证都是使用的同一个 connection。
 | 
			
		||||
 | 
			
		||||
若对于复杂业务、复杂的验证逻辑,会导致整个验证过程始终占用该 connection 连接,占用时间可能会很长,直至方法结束,connection 才会交还给数据库连接池。
 | 
			
		||||
 | 
			
		||||
对于复杂业务的不可预计的情况,长时间占用同一个 connection 连接不是好的事情,应该尽量缩短占用时间。
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
    DepartmentService.java
 | 
			
		||||
 | 
			
		||||
    public ResponseDTO<String> upOrDown(Long departmentId, Long swapId) {
 | 
			
		||||
        DepartmentEntity departmentEntity = departmentDao.selectById(departmentId);
 | 
			
		||||
        if (departmentEntity == null) {
 | 
			
		||||
            return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
 | 
			
		||||
        }
 | 
			
		||||
        DepartmentEntity swapEntity = departmentDao.selectById(swapId);
 | 
			
		||||
        if (swapEntity == null) {
 | 
			
		||||
            return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
 | 
			
		||||
        }
 | 
			
		||||
        Long count = employeeDao.countByDepartmentId(departmentId)
 | 
			
		||||
        if (count != null && count > 0) {
 | 
			
		||||
            return ResponseDTO.wrap(DepartmentResponseCodeConst.EXIST_EMPLOYEE);
 | 
			
		||||
        }
 | 
			
		||||
        departmentManager.upOrDown(departmentSort,swapEntity);
 | 
			
		||||
        return ResponseDTO.succ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    DepartmentManager.java
 | 
			
		||||
 | 
			
		||||
    @Transactional(rollbackFor = Throwable.class)
 | 
			
		||||
    public void upOrDown(DepartmentEntity departmentEntity ,DepartmentEntity swapEntity){
 | 
			
		||||
        Long departmentSort = departmentEntity.getSort();
 | 
			
		||||
        departmentEntity.setSort(swapEntity.getSort());
 | 
			
		||||
        departmentDao.updateById(departmentEntity);
 | 
			
		||||
        swapEntity.setSort(departmentSort);
 | 
			
		||||
        departmentDao.updateById(swapEntity);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
将数据在 service 层准备好,然后传递给 manager 层,由 manager 层添加@Transactional 进行数据库操作。
 | 
			
		||||
 | 
			
		||||
**以上是使用`manager`去处理解决的,其实也可以是使用spring的 `TransactionTemplate ` 事务模板解决**
 | 
			
		||||
 | 
			
		||||
#### 3)需要注意的是:注解 `@Transactional` 事务在类的内部方法调用是不会生效的
 | 
			
		||||
 | 
			
		||||
反例:如果发生异常,saveData 方法上的事务注解并不会起作用
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
@Service
 | 
			
		||||
public class OrderService{
 | 
			
		||||
 | 
			
		||||
    public void createOrder(OrderAddForm addForm){
 | 
			
		||||
        this.saveData(addForm);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Transactional(rollbackFor = Exception.class)
 | 
			
		||||
    public void saveData(OrderAddForm addForm){
 | 
			
		||||
        orderDao.insert(addForm);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
> _Spring 采用动态代理(AOP)实现对 bean 的管理和切片,它为我们的每个 class 生成一个代理对象。只有在代理对象之间进行调用时,可以触发切面逻辑。而在同一个 class 中,方法 A 调用方法 B,调用的是原对象的方法,而不通过代理对象。所以 Spring 无法拦截到这次调用,也就无法通过注解保证事务了。简单来说,在同一个类中的方法调用,不会被方法拦截器拦截到,因此事务不会起作用。_
 | 
			
		||||
 | 
			
		||||
解决方案:
 | 
			
		||||
 | 
			
		||||
1. 可以将方法放入另一个类,如新增 `manager层`,通过 spring 注入,这样符合了在对象之间调用的条件。
 | 
			
		||||
2. 启动类添加` @EnableAspectJAutoProxy(exposeProxy = true)`,方法内使用`AopContext.currentProxy()`获得代理类,使用事务。
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
SpringBootApplication.java
 | 
			
		||||
 | 
			
		||||
@EnableAspectJAutoProxy(exposeProxy = true)
 | 
			
		||||
@SpringBootApplication
 | 
			
		||||
public class SpringBootApplication {}
 | 
			
		||||
 | 
			
		||||
OrderService.java
 | 
			
		||||
 | 
			
		||||
public void createOrder(OrderCreateDTO createDTO){
 | 
			
		||||
    OrderService orderService = (OrderService)AopContext.currentProxy();
 | 
			
		||||
    orderService.saveData(createDTO);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 2.4、 manager 层规范
 | 
			
		||||
 | 
			
		||||
manager 层的作用(引自《阿里 java 手册》):
 | 
			
		||||
 | 
			
		||||
- 对第三方平台封装的层,预处理返回结果及转化异常信息;
 | 
			
		||||
- 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理;
 | 
			
		||||
- 与 DAO 层交互,对多个 DAO 的组合复用。
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 2.5、 dao 层规范
 | 
			
		||||
 | 
			
		||||
#### 1)持久层框架选择
 | 
			
		||||
优先使用 mybatis-plus 框架。如果需要多个数据源操作的,可以选择使用 我们实验室的 [SmartDb](https://gitee.com/lab1024/smartdb.git) 框架。
 | 
			
		||||
 | 
			
		||||
#### 2)使用mybatis-plus的要求
 | 
			
		||||
 | 
			
		||||
- 所有 Dao 继承自 BaseMapper
 | 
			
		||||
- 禁止使用 Mybatis-plus 的 Wrapper 条件构建器
 | 
			
		||||
- 禁止直接在 mybatis xml 中写死常量,应从 dao 中传入到 xml 中
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
NoticeDao.java  常量在参数中传入到xml
 | 
			
		||||
```java
 | 
			
		||||
public interface NoticeDao{
 | 
			
		||||
 | 
			
		||||
    Integer noticeCount(@Param("sendStatus") Integer sendStatus);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
NoticeMapper.xml
 | 
			
		||||
```xml
 | 
			
		||||
    <select id="noticeCount" resultType="integer">
 | 
			
		||||
        select
 | 
			
		||||
        count(1)
 | 
			
		||||
        from t_notice
 | 
			
		||||
        where
 | 
			
		||||
        send_status = #{sendStatus}
 | 
			
		||||
    </select>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
反例:常量直接写死到 xml 中
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
public interface NoticeDao{
 | 
			
		||||
 | 
			
		||||
    Integer noticeCount();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
NoticeMapper.xml
 | 
			
		||||
```xml
 | 
			
		||||
    <select id="noticeCount" resultType="integer">
 | 
			
		||||
        select
 | 
			
		||||
        count(1)
 | 
			
		||||
        from t_notice
 | 
			
		||||
        where
 | 
			
		||||
        send_status = 0
 | 
			
		||||
    </select>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 4)连接join写法   
 | 
			
		||||
建议在xml中的 join 关联写法使用表名的全称,而不是用别名,对于关联表太多的话,在xml格式中,其实很难记住 别名是什么意思!
 | 
			
		||||
 | 
			
		||||
反例: t_notice 别名 tn,t_employee别名 e, 在xml中已经很难区分是什么意思了,索性不如使用全称
 | 
			
		||||
```xml
 | 
			
		||||
<select id="queryPage" resultType="net.lab1024.sa.admin.module.business.oa.notice.domain.vo.NoticeVO">
 | 
			
		||||
        SELECT tn.*,
 | 
			
		||||
        e.actual_name AS createUserName
 | 
			
		||||
        FROM t_notice tn
 | 
			
		||||
        LEFT JOIN t_employee e ON tn.create_user_id = e.employee_id
 | 
			
		||||
        <where>
 | 
			
		||||
            tn.deleted_flag = #{queryForm.deletedFlag}
 | 
			
		||||
            <if test="queryForm.keywords != null and queryForm.keywords != ''">
 | 
			
		||||
                AND (INSTR(tn.notice_title,#{queryForm.keywords}) OR INSTR(e.actual_name,#{queryForm.keywords}))
 | 
			
		||||
            </if>
 | 
			
		||||
            <if test="queryForm.noticeType != null">
 | 
			
		||||
                AND tn.notice_type = #{queryForm.noticeType}
 | 
			
		||||
            </if>
 | 
			
		||||
            <if test="queryForm.noticeBelongType != null">
 | 
			
		||||
                AND tn.notice_belong_type = #{queryForm.noticeBelongType}
 | 
			
		||||
            </if>
 | 
			
		||||
            <if test="queryForm.startTime != null">
 | 
			
		||||
                AND DATE_FORMAT(tn.publish_time, '%Y-%m-%d') >= #{queryForm.startTime}
 | 
			
		||||
            </if>
 | 
			
		||||
            <if test="queryForm.endTime != null">
 | 
			
		||||
                AND DATE_FORMAT(tn.publish_time, '%Y-%m-%d') <= #{queryForm.endTime}
 | 
			
		||||
            </if>
 | 
			
		||||
            <if test="queryForm.disabledFlag != null">
 | 
			
		||||
                AND tn.disabled_flag = #{queryForm.disabledFlag}
 | 
			
		||||
            </if>
 | 
			
		||||
        </where>
 | 
			
		||||
        <if test="queryForm.sortItemList == null or queryForm.sortItemList.size == 0">
 | 
			
		||||
            ORDER BY tn.top_flag DESC,tn.publish_time DESC
 | 
			
		||||
        </if>
 | 
			
		||||
    </select>
 | 
			
		||||
```
 | 
			
		||||
正确:使用全称
 | 
			
		||||
```xml
 | 
			
		||||
    <select id="queryPage" resultType="net.lab1024.sa.admin.module.business.oa.notice.domain.vo.NoticeVO">
 | 
			
		||||
        SELECT t_notice.*,
 | 
			
		||||
        t_employee.actual_name AS createUserName
 | 
			
		||||
        FROM t_notice
 | 
			
		||||
        LEFT JOIN t_employee  ON t_notice.create_user_id = t_employee.employee_id
 | 
			
		||||
        <where>
 | 
			
		||||
            t_notice.deleted_flag = #{queryForm.deletedFlag}
 | 
			
		||||
            <if test="queryForm.keywords != null and queryForm.keywords != ''">
 | 
			
		||||
                AND (INSTR(t_notice.notice_title,#{queryForm.keywords}) OR INSTR(t_employee.actual_name,#{queryForm.keywords}))
 | 
			
		||||
            </if>
 | 
			
		||||
            <if test="queryForm.noticeType != null">
 | 
			
		||||
                AND t_notice.notice_type = #{queryForm.noticeType}
 | 
			
		||||
            </if>
 | 
			
		||||
            <if test="queryForm.noticeBelongType != null">
 | 
			
		||||
                AND t_notice.notice_belong_type = #{queryForm.noticeBelongType}
 | 
			
		||||
            </if>
 | 
			
		||||
            <if test="queryForm.startTime != null">
 | 
			
		||||
                AND DATE_FORMAT(t_notice.publish_time, '%Y-%m-%d') >= #{queryForm.startTime}
 | 
			
		||||
            </if>
 | 
			
		||||
            <if test="queryForm.endTime != null">
 | 
			
		||||
                AND DATE_FORMAT(t_notice.publish_time, '%Y-%m-%d') <= #{queryForm.endTime}
 | 
			
		||||
            </if>
 | 
			
		||||
            <if test="queryForm.disabledFlag != null">
 | 
			
		||||
                AND t_notice.disabled_flag = #{queryForm.disabledFlag}
 | 
			
		||||
            </if>
 | 
			
		||||
        </where>
 | 
			
		||||
        <if test="queryForm.sortItemList == null or queryForm.sortItemList.size == 0">
 | 
			
		||||
            ORDER BY t_notice.top_flag DESC,t_notice.publish_time DESC
 | 
			
		||||
        </if>
 | 
			
		||||
    </select>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 2.6、 javabean 命名规范
 | 
			
		||||
 | 
			
		||||
1) `javabean` 的整体要求:
 | 
			
		||||
- 不得有任何的业务逻辑或者计算
 | 
			
		||||
- 基本数据类型必须使用包装类型`(Integer, Double、Boolean 等)`
 | 
			
		||||
- 不允许有任何的默认值
 | 
			
		||||
- 每个属性必须添加注释,并且必须使用多行注释。
 | 
			
		||||
- 必须使用 `lombok` 简化 `getter/setter` 方法
 | 
			
		||||
- 建议对象使用 `lombok` 的 `@Builder ,@NoArgsConstructor`,同时使用这两个注解,简化对象构造方法以及 set 方法。
 | 
			
		||||
 | 
			
		||||
2)javabean 名字划分:
 | 
			
		||||
- XxxEntity 数据库持久对象
 | 
			
		||||
- XxxVO 返回前端对象 (一些大厂用 Resp结尾,比如 XxxxResp)
 | 
			
		||||
- XxxForm 前端请求对象 (一些大厂用 Req结尾,比如 XxxxReq)
 | 
			
		||||
- XxxDTO 数据传输对象
 | 
			
		||||
- XxxBO 内部处理对象
 | 
			
		||||
 | 
			
		||||
3)数据对象;`XxxxEntity`,要求:
 | 
			
		||||
 | 
			
		||||
- 以 `Entity` 为结尾(阿里是为 DO 为结尾)
 | 
			
		||||
- Xxxx 与数据库表名保持一致
 | 
			
		||||
- 类中字段要与数据库字段保持一致,不能缺失或者多余
 | 
			
		||||
- 类中的每个字段添加注释,并与数据库注释保持一致
 | 
			
		||||
- 不允许有组合
 | 
			
		||||
- 项目内的日期类型必须统一,使用 `java.time.LocalDateTime` 或者 `java.time.LocalDate`
 | 
			
		||||
 | 
			
		||||
4)请求对象;`XxxxForm`,要求:
 | 
			
		||||
 | 
			
		||||
- 不可以继承自 `Entity`
 | 
			
		||||
- `Form` 可以继承、组合其他 `DTO,VO,BO` 等对象
 | 
			
		||||
- `Form` 只能用于前端、RPC 的请求参数
 | 
			
		||||
 | 
			
		||||
3)返回对象;`XxxxVO`,要求:
 | 
			
		||||
 | 
			
		||||
- 不可继承自 `Entity`
 | 
			
		||||
- `VO` 可以继承、组合其他 `DTO,VO,BO` 等对象
 | 
			
		||||
- `VO` 只能用于返回前端、rpc 的业务数据封装对象
 | 
			
		||||
 | 
			
		||||
4)业务对象 `BO`,要求:
 | 
			
		||||
 | 
			
		||||
- 不可以继承自 `Entity`
 | 
			
		||||
- `BO` 对象只能用于 `service,manager,dao` 层,不得用于 `controller` 层
 | 
			
		||||
 | 
			
		||||
### 2.7、boolean 类型的属性命名规范
 | 
			
		||||
 | 
			
		||||
> 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。反例:定义为基本数据类型 Boolean isDeleted;的属性,它的方法也是 isDeleted(),RPC 在反向解析的时候,“以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。
 | 
			
		||||
 | 
			
		||||
这是阿里巴巴开发手册中的原文,我们团队的规定是:`boolean` 类型的类属性和数据表字段都统一使用 `flag` 结尾。虽然使用 `isDeleted,is_deleted` 从字面语义上更直观,但是比起可能出现的潜在错误,这点牺牲还是值得的。
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
deletedFlag,deleted_flag,onlineFlag,online_flag
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 三、数据库 规范
 | 
			
		||||
 | 
			
		||||
### 3.1、数据库命名   
 | 
			
		||||
 | 
			
		||||
全部采用小写方式, 以下划线分隔,并且区分是什么环境的数据库  
 | 
			
		||||
 | 
			
		||||
```sql
 | 
			
		||||
正例:smart_admin_v2_dev / smart_admin_v2_prod / smart_admin_v2_test
 | 
			
		||||
 | 
			
		||||
反例:mall_management-system / mallManagementSystem / orderServiceClient
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3.2、表命名
 | 
			
		||||
全部采用小写方式, 以下划线分隔,并且并且以 `t_` 开头   
 | 
			
		||||
 | 
			
		||||
比如:员工表:`t_employee`、部门表:`t_department`、配置表:`t_config`
 | 
			
		||||
 | 
			
		||||
### 3.3、建表规范
 | 
			
		||||
 | 
			
		||||
表必备三字段:id, create_time, update_time
 | 
			
		||||
 | 
			
		||||
- id 字段 Long 类型,单表自增,自增长度为 1
 | 
			
		||||
- create_time 字段 datetime 类型,默认值 CURRENT_TIMESTAMP
 | 
			
		||||
- update_time 字段 datetime 类型,默认值 CURRENT_TIMESTAMP, On update CURRENT_TIMESTAMP
 | 
			
		||||
 | 
			
		||||
**#### 枚举类表字段注释需要将所有枚举含义进行注释**
 | 
			
		||||
 | 
			
		||||
修改或增加字段的状态描述,必须要及时同步更新注释。如下表的 `sync_status` 字段 `同步状态 0 未开始 1同步中 2同步成功 3失败`。
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
```sql
 | 
			
		||||
CREATE TABLE `t_change_data` (
 | 
			
		||||
	`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
 | 
			
		||||
	`sync_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT '同步状态 0 未开始 1同步中 2同步成功 3失败',
 | 
			
		||||
	`sync_time` DATETIME NULL DEFAULT NULL COMMENT '同步时间',
 | 
			
		||||
	`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
	`update_time` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
 | 
			
		||||
	PRIMARY KEY (`change_data_id`)
 | 
			
		||||
)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
反例:
 | 
			
		||||
```sql
 | 
			
		||||
CREATE TABLE `t_change_data` (
 | 
			
		||||
	`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
 | 
			
		||||
	`sync_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT '同步状态 ',
 | 
			
		||||
	`sync_time` DATETIME NULL DEFAULT NULL COMMENT '同步时间',
 | 
			
		||||
	`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
	`update_time` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
 | 
			
		||||
	PRIMARY KEY (`change_data_id`)
 | 
			
		||||
)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3.4、索引规范
 | 
			
		||||
 | 
			
		||||
具体索引规范请参照《阿里巴巴 Java 开发手册》索引规约
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 联系我们
 | 
			
		||||
 | 
			
		||||
[1024创新实验室-主任:卓大](https://zhuoda.vip),混迹于各个技术圈,研究过计算机,熟悉点 java,略懂点前端。  
 | 
			
		||||
[1024创新实验室(河南·洛阳)](https://1024lab.net) 致力于成为中原领先、国内一流的技术团队,以技术创新为驱动,合作各类项目。
 | 
			
		||||
<table>
 | 
			
		||||
<tr>
 | 
			
		||||
  <td><img src="http://img.zhuoluodada.cn/wechat/zhuoda-wechat.jpg"/></td>
 | 
			
		||||
  <td><img src="http://img.zhuoluodada.cn/wechat/xiaozhen-gzh.jpg"/></td>
 | 
			
		||||
  <td><img src="http://img.zhuoluodada.cn/wechat/zhuoda-wechat-money-v1.jpg"/></td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
  <td style="text-align:center">加 主任 “卓大” 微信 <br> 拉你入群,一起学习</td>
 | 
			
		||||
  <td style="text-align:center">关注 “小镇程序员” <br> 分享代码与生活、技术与赚钱</td>
 | 
			
		||||
  <td style="text-align:center">请 “1024创新实验室” 喝咖啡 <br> 支持我们的开源与分享 </td>
 | 
			
		||||
</tr>
 | 
			
		||||
</table>
 | 
			
		||||
							
								
								
									
										704
									
								
								代码规范/Vue3规范 V2.0.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										704
									
								
								代码规范/Vue3规范 V2.0.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,704 @@
 | 
			
		||||
---
 | 
			
		||||
title: 2.2、Vue3规范 V2.0
 | 
			
		||||
date: 2022-09-07
 | 
			
		||||
---
 | 
			
		||||
## 阅读须知
 | 
			
		||||
 | 
			
		||||
<font color="#DC143C">**在阅读《Java规范》之前,请一定先阅读上一篇《规范导读 V2.0》!!!**</font>  ,[前往阅读](./basic.md)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 一、Vue3 基础规范
 | 
			
		||||
 | 
			
		||||
### 1.1、项目命名
 | 
			
		||||
 | 
			
		||||
全部采用小写方式, 以中划线分隔。
 | 
			
		||||
```javascript
 | 
			
		||||
正例:`smart-admin`
 | 
			
		||||
反例:`mall_management-system / mallManagementSystem`
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 1.2、目录、文件命名
 | 
			
		||||
 | 
			
		||||
目录、文件名 均以 小写方式, 以中划线分隔。
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
正例:`/head-search/`、`/shopping-car/`、`smart-logo.png`、`role-form.vue`
 | 
			
		||||
反例:`/headSearch/`、 `smartLogo.png`、 `RoleForm.vue`
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 1.3、单引号、双引号、分号   
 | 
			
		||||
 | 
			
		||||
- html中、vue的template 中 标签属性 使用 **双引号**
 | 
			
		||||
- 所有js中的 字符串 使用 **单引号**
 | 
			
		||||
- 所有js中的代码行换行要用 **分号**
 | 
			
		||||
 | 
			
		||||
## 二、Vue3 组合式 API规范
 | 
			
		||||
 | 
			
		||||
### 2.1、 使用setup语法糖
 | 
			
		||||
 | 
			
		||||
- 组件必须使用 `setup` 语法糖
 | 
			
		||||
- setup 大法方便简洁
 | 
			
		||||
- 全局都要使用setup语法糖
 | 
			
		||||
 | 
			
		||||
### 2.2、组合式Composition API 规范
 | 
			
		||||
 | 
			
		||||
组件内必须使用模块化思想,把代码 进行 拆分;   
 | 
			
		||||
参照 vue3官方文档对于 Composition Api的理解: [更灵活的代码组织](https://cn.vuejs.org/guide/extras/composition-api-faq.html#better-logic-reuse) ,组合式Api,即 Composition API 解决的是让 相互关联的代码在一起,以更方便的组织代码,故我们的代码格式如下:   
 | 
			
		||||
 | 
			
		||||
<font color="#DC143C">**即:将相关的 变量和代码 写到一起,并使用 行注释 进行分块**</font>
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
<script setup>
 | 
			
		||||
// 各种需要导入
 | 
			
		||||
import xxxxx;
 | 
			
		||||
import xxxxx;
 | 
			
		||||
import xxxxx;
 | 
			
		||||
 | 
			
		||||
// -------- 定义组件属性和对外暴露的方法、以及抛出的事件 --------
 | 
			
		||||
 | 
			
		||||
// -------- 表格查询的 变量和方法 --------
 | 
			
		||||
 | 
			
		||||
// -------- 批量操作的 变量和方法 --------
 | 
			
		||||
 | 
			
		||||
// -------- 表单的 变量和方法 --------
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
```
 | 
			
		||||
举例,分成了两个模块,即:
 | 
			
		||||
- 模块1:显示 、隐藏操作的 变量和方法
 | 
			
		||||
- 模块2:表单的  变量和方法
 | 
			
		||||
 | 
			
		||||
<font color="#DC143C">**【以下是正确的例子】**</font>   
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
<script setup>
 | 
			
		||||
  import { ref, reactive } from 'vue';
 | 
			
		||||
  import { message } from 'ant-design-vue';
 | 
			
		||||
  import { SmartLoading } from '/@/components/framework/smart-loading';
 | 
			
		||||
  import _ from 'lodash';
 | 
			
		||||
  import { categoryApi } from '/@/api/business/category/category-api';
 | 
			
		||||
  import { smartSentry } from '/@/lib/smart-sentry';
 | 
			
		||||
 | 
			
		||||
  // emit
 | 
			
		||||
  const emit = defineEmits('reloadList');
 | 
			
		||||
 | 
			
		||||
  //  组件
 | 
			
		||||
  const formRef = ref();
 | 
			
		||||
 | 
			
		||||
  // ------------------------------ 显示 、隐藏操作的 变量和方法------------------------------
 | 
			
		||||
 | 
			
		||||
  // 是否展示抽屉
 | 
			
		||||
  const visible = ref(false);
 | 
			
		||||
  // 显示
 | 
			
		||||
  function showModal(categoryType, parentId, rowData) {
 | 
			
		||||
    Object.assign(form, formDefault);
 | 
			
		||||
    form.categoryType = categoryType;
 | 
			
		||||
    form.parentId = parentId;
 | 
			
		||||
    if (rowData && !_.isEmpty(rowData)) {
 | 
			
		||||
      Object.assign(form, rowData);
 | 
			
		||||
    }
 | 
			
		||||
    visible.value = true;
 | 
			
		||||
  }
 | 
			
		||||
  // 隐藏
 | 
			
		||||
  function onClose() {
 | 
			
		||||
    Object.assign(form, formDefault);
 | 
			
		||||
    visible.value = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ------------------------------ 表单的  变量和方法 ------------------------------
 | 
			
		||||
  // 查询表单默认值
 | 
			
		||||
  const formDefault = {
 | 
			
		||||
    categoryId: undefined, //分类id
 | 
			
		||||
    categoryName: '', //分类名字
 | 
			
		||||
    categoryType: 1, // 分类类型
 | 
			
		||||
    parentId: undefined, // 父级id
 | 
			
		||||
    disabledFlag: false, //是否禁用
 | 
			
		||||
  };
 | 
			
		||||
  // 查询表单
 | 
			
		||||
  let form = reactive({ ...formDefault });
 | 
			
		||||
  // 表单校验规则
 | 
			
		||||
  const rules = {
 | 
			
		||||
    categoryName: [{ required: true, message: '请输入分类名称' }],
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  function onSubmit() {
 | 
			
		||||
    formRef.value
 | 
			
		||||
      .validate()
 | 
			
		||||
      .then(async () => {
 | 
			
		||||
        SmartLoading.show();
 | 
			
		||||
        try {
 | 
			
		||||
          if (form.categoryId) {
 | 
			
		||||
            await categoryApi.updateCategory(form);
 | 
			
		||||
          } else {
 | 
			
		||||
            await categoryApi.addCategory(form);
 | 
			
		||||
          }
 | 
			
		||||
          message.success(`${form.categoryId ? '修改' : '添加'}成功`);
 | 
			
		||||
          emit('reloadList', form.parentId);
 | 
			
		||||
          onClose();
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
          smartSentry.captureError(error);
 | 
			
		||||
        } finally {
 | 
			
		||||
          SmartLoading.hide();
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      .catch((error) => {
 | 
			
		||||
        console.log('error', error);
 | 
			
		||||
        message.error('参数验证错误,请仔细填写表单数据!');
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  defineExpose({
 | 
			
		||||
    showModal,
 | 
			
		||||
  });
 | 
			
		||||
</script>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<font color="#DC143C">**【错误的例子】**</font>   
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
<script setup>
 | 
			
		||||
  import { ref, reactive } from 'vue';
 | 
			
		||||
  import { message } from 'ant-design-vue';
 | 
			
		||||
  import { SmartLoading } from '/@/components/framework/smart-loading';
 | 
			
		||||
  import _ from 'lodash';
 | 
			
		||||
  import { categoryApi } from '/@/api/business/category/category-api';
 | 
			
		||||
  import { smartSentry } from '/@/lib/smart-sentry';
 | 
			
		||||
 | 
			
		||||
  // -----------------  定义 所有变量  -----------------
 | 
			
		||||
 | 
			
		||||
  // emit
 | 
			
		||||
  const emit = defineEmits('reloadList');
 | 
			
		||||
  //  组件
 | 
			
		||||
  const formRef = ref();
 | 
			
		||||
  // 是否展示抽屉
 | 
			
		||||
  const visible = ref(false);
 | 
			
		||||
  // 查询表单默认值
 | 
			
		||||
  const formDefault = {
 | 
			
		||||
    categoryId: undefined, //分类id
 | 
			
		||||
    categoryName: '', //分类名字
 | 
			
		||||
    categoryType: 1, // 分类类型
 | 
			
		||||
    parentId: undefined, // 父级id
 | 
			
		||||
    disabledFlag: false, //是否禁用
 | 
			
		||||
  };
 | 
			
		||||
  // 查询表单
 | 
			
		||||
  let form = reactive({ ...formDefault });
 | 
			
		||||
  // 表单校验规则
 | 
			
		||||
  const rules = {
 | 
			
		||||
    categoryName: [{ required: true, message: '请输入分类名称' }],
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // -----------------  定义 所有方法 -----------------
 | 
			
		||||
  // 显示
 | 
			
		||||
  function showModal(categoryType, parentId, rowData) {
 | 
			
		||||
    Object.assign(form, formDefault);
 | 
			
		||||
    form.categoryType = categoryType;
 | 
			
		||||
    form.parentId = parentId;
 | 
			
		||||
    if (rowData && !_.isEmpty(rowData)) {
 | 
			
		||||
      Object.assign(form, rowData);
 | 
			
		||||
    }
 | 
			
		||||
    visible.value = true;
 | 
			
		||||
  }
 | 
			
		||||
  // 隐藏
 | 
			
		||||
  function onClose() {
 | 
			
		||||
    Object.assign(form, formDefault);
 | 
			
		||||
    visible.value = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function onSubmit() {
 | 
			
		||||
    formRef.value
 | 
			
		||||
      .validate()
 | 
			
		||||
      .then(async () => {
 | 
			
		||||
        SmartLoading.show();
 | 
			
		||||
        try {
 | 
			
		||||
          if (form.categoryId) {
 | 
			
		||||
            await categoryApi.updateCategory(form);
 | 
			
		||||
          } else {
 | 
			
		||||
            await categoryApi.addCategory(form);
 | 
			
		||||
          }
 | 
			
		||||
          message.success(`${form.categoryId ? '修改' : '添加'}成功`);
 | 
			
		||||
          emit('reloadList', form.parentId);
 | 
			
		||||
          onClose();
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
          smartSentry.captureError(error);
 | 
			
		||||
        } finally {
 | 
			
		||||
          SmartLoading.hide();
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      .catch((error) => {
 | 
			
		||||
        console.log('error', error);
 | 
			
		||||
        message.error('参数验证错误,请仔细填写表单数据!');
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  defineExpose({
 | 
			
		||||
    showModal,
 | 
			
		||||
  });
 | 
			
		||||
</script>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<font color="#DC143C">** vue 官方希望是 模块化的组织代码,你看记得官方这个图吗?**</font>   
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<table>
 | 
			
		||||
<tr>
 | 
			
		||||
  <td><img src="http://img.zhuoluodada.cn/smart-admin-v2/standard/composition-api.png"/></td>
 | 
			
		||||
</tr>
 | 
			
		||||
</table>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
**第一种写法,所有变量写到了一起,就像vue2的 data区域,所有方法写到一起,就像vue2中的 methods区域,这样下来其实和vue2没什么区别,也就语法稍微变了一下,那么就与composition api的初衷违背了**,下面引用官方的话
 | 
			
		||||
>处理相同逻辑关注点的代码被强制拆分在了不同的选项中,位于文件的不同部分。在一个几百行的大组件中,要读懂代码中的一个逻辑关注点,需要在文件中反复上下滚动,这并不理想。另外,如果我们想要将一个逻辑关注点抽取重构到一个可复用的工具函数中,需要从文件的多个不同部分找到所需的正确片段。
 | 
			
		||||
 | 
			
		||||
**SmartAdmin中的写法才真正做到了vue3组合式composition api的意图,每一块业务的变量与方法写到一起,比如上面代码中的`显示与隐藏`的变量和方法放到了一起,`表单`的变量和方法放到了一起**,下面引用官方的话
 | 
			
		||||
> 现在与同一个逻辑关注点相关的代码被归为了一组:我们无需再为了一个逻辑关注点在不同的选项块间来回滚动切换。此外,我们现在可以很轻松地将这一组代码移动到一个外部文件中,不再需要为了抽象而重新组织代码,大大降低了重构成本,这在长期维护的大型项目中非常关键。
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 2.3 模板引用变量Ref
 | 
			
		||||
 | 
			
		||||
对于vue3中的模板引用ref,即 ref 是作为一个特殊的 attribute
 | 
			
		||||
```html
 | 
			
		||||
<input ref="inputRef">
 | 
			
		||||
```
 | 
			
		||||
我们要求:
 | 
			
		||||
- 使用 ref方法,参数为空 进行声明变量
 | 
			
		||||
- 变量必须以 `Ref`为结尾
 | 
			
		||||
- template中的ref 也必须以 `Ref` 为结尾
 | 
			
		||||
 | 
			
		||||
比如上面的例子,我们声明如下
 | 
			
		||||
```js
 | 
			
		||||
const inputRef = ref();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 2.4 变量和方法的注释
 | 
			
		||||
在使用Composition Api进行代码编写时,我们有效的组织了代码,但是由于Composition Api变量和方法会写到一起,这时候注释就变得很有必要
 | 
			
		||||
要求:
 | 
			
		||||
- 变量必须都加上注释
 | 
			
		||||
- 方法必须加上注释
 | 
			
		||||
比如
 | 
			
		||||
```js
 | 
			
		||||
  // 查询 公告 默认值
 | 
			
		||||
  const queryFormState = {
 | 
			
		||||
    noticeTypeId: undefined, //分类
 | 
			
		||||
    keywords: '', //标题、作者、来源
 | 
			
		||||
    documentNumber: '', //文号
 | 
			
		||||
    createUserId: undefined, //创建人
 | 
			
		||||
    deletedFlag: undefined, //删除标识
 | 
			
		||||
    createTimeBegin: null, //创建-开始时间
 | 
			
		||||
    createTimeEnd: null, //创建-截止时间
 | 
			
		||||
    publishTimeBegin: null, //发布-开始时间
 | 
			
		||||
    publishTimeEnd: null, //发布-截止时间
 | 
			
		||||
    pageNum: 1,
 | 
			
		||||
    pageSize: PAGE_SIZE,
 | 
			
		||||
  };
 | 
			
		||||
  // 查询 公告 请求表单
 | 
			
		||||
  const queryForm = reactive({ ...queryFormState });
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 三、Vue3 组件规范
 | 
			
		||||
 | 
			
		||||
### 3.1、 组件文件名
 | 
			
		||||
组件文件名应该为 pascal-case 格式
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
components
 | 
			
		||||
|- my-component.vue
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
反例:
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
components
 | 
			
		||||
|- myComponent.vue
 | 
			
		||||
|- MyComponent.vue
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 3.2、 父子组件文件名
 | 
			
		||||
和父组件紧密耦合的子组件应该以父组件名作为前缀命名
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
components
 | 
			
		||||
|- todo-list.vue
 | 
			
		||||
|- todo-list-item.vue
 | 
			
		||||
|- todo-list-item-button.vue
 | 
			
		||||
|- user-profile-options.vue (完整单词)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
反例:
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
components
 | 
			
		||||
|- TodoList.vue
 | 
			
		||||
|- TodoItem.vue
 | 
			
		||||
|- TodoButton.vue
 | 
			
		||||
|- UProfOpts.vue (使用了缩写)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 3.3、 组件属性
 | 
			
		||||
组件属性较多,应该主动换行。
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
```html
 | 
			
		||||
<MyComponent foo="a" bar="b" baz="c"
 | 
			
		||||
    foo="a" bar="b" baz="c"
 | 
			
		||||
    foo="a" bar="b" baz="c"
 | 
			
		||||
 />
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
反例:
 | 
			
		||||
```html
 | 
			
		||||
<MyComponent foo="a" bar="b" baz="c" foo="a" bar="b" baz="c" foo="a" bar="b" baz="c" foo="a" bar="b" baz="c"/>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3.4、 模板中表达式
 | 
			
		||||
 | 
			
		||||
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
<template>
 | 
			
		||||
  <p>{{ normalizedFullName }}</p>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
// 复杂表达式已经移入一个计算属性
 | 
			
		||||
computed: {
 | 
			
		||||
  normalizedFullName: function () {
 | 
			
		||||
    return this.fullName.split(' ').map(function (word) {
 | 
			
		||||
      return word[0].toUpperCase() + word.slice(1)
 | 
			
		||||
    }).join(' ')
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
反例:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
<template>
 | 
			
		||||
  <p>
 | 
			
		||||
       {{
 | 
			
		||||
          fullName.split(' ').map(function (word) {
 | 
			
		||||
             return word[0].toUpperCase() + word.slice(1)
 | 
			
		||||
           }).join(' ')
 | 
			
		||||
        }}
 | 
			
		||||
  </p>
 | 
			
		||||
</template>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3.5、 标签顺序
 | 
			
		||||
 | 
			
		||||
单文件组件应该总是让标签顺序保持为 `<template> 、<script>、 <style>`
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
<template>...</template>
 | 
			
		||||
<script>...</script>
 | 
			
		||||
<style>...</style>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
反例:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
<template>...</template>
 | 
			
		||||
<style>...</style>
 | 
			
		||||
<script>...</script>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 四、Vue Router 规范
 | 
			
		||||
 | 
			
		||||
### 4.1、 页面传参
 | 
			
		||||
 | 
			
		||||
页面跳转,例如 A 页面跳转到 B 页面,需要将 A 页面的数据传递到 B 页面,推荐使用 路由参数进行传参,即 `{query:param}`
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let id = ' 123';
 | 
			
		||||
this.$router.push({ name: 'userCenter', query: { id: id } });
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 4.2、 path 和 name 命名规范
 | 
			
		||||
- path`kebab-case`命名规范(尽量与vue文件的目录结构保持一致,因为目录、文件名都是`kebab-case`,这样很方便找到对应的文件)
 | 
			
		||||
- path 必须以 / 开头,即使是children里的path也要以 / 开头。如下示例
 | 
			
		||||
- 经常有这样的场景:某个页面有问题,要立刻找到这个vue文件,如果不用以/开头,path为parent和children组成的,可能经常需要在router文件里搜索多次才能找到,而如果以/开头,则能立刻搜索到对应的组件
 | 
			
		||||
- name 命名规范采用`KebabCase`命名规范且和component组件名保持一致!(因为要保持keep-alive特性,keep-alive按照component的name进行缓存,所以两者必须高度保持一致)
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
// 动态加载
 | 
			
		||||
export const reload = [
 | 
			
		||||
  {
 | 
			
		||||
    path: '/reload',
 | 
			
		||||
    name: 'reload',
 | 
			
		||||
    component: Main,
 | 
			
		||||
    meta: {
 | 
			
		||||
      title: '动态加载',
 | 
			
		||||
      icon: 'icon iconfont'
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        path: '/reload/smart-reload-list',
 | 
			
		||||
        name: 'SmartReloadList',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: 'SmartReload',
 | 
			
		||||
          childrenPoints: [
 | 
			
		||||
            {
 | 
			
		||||
              title: '查询',
 | 
			
		||||
              name: 'smart-reload-search'
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              title: '执行reload',
 | 
			
		||||
              name: 'smart-reload-update'
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              title: '查看执行结果',
 | 
			
		||||
              name: 'smart-reload-result'
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
        },
 | 
			
		||||
        component: () =>
 | 
			
		||||
          import('@/views/reload/smart-reload/smart-reload-list.vue')
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 五、 Vue 项目规范
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 5.1、 目录规范
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
src                               源码目录
 | 
			
		||||
|-- api                              所有api接口
 | 
			
		||||
|-- assets                           静态资源,images, icons, styles等
 | 
			
		||||
|-- components                       公用组件
 | 
			
		||||
|-- config                           配置信息
 | 
			
		||||
|-- constants                        常量信息,项目所有Enum, 全局常量等
 | 
			
		||||
|-- directives                       自定义指令
 | 
			
		||||
|-- i18n                             国际化
 | 
			
		||||
|-- lib                              外部引用的插件存放及修改文件
 | 
			
		||||
|-- mock                             模拟接口,临时存放
 | 
			
		||||
|-- plugins                          插件,全局使用
 | 
			
		||||
|-- router                           路由,统一管理
 | 
			
		||||
|-- store                            vuex, 统一管理
 | 
			
		||||
|-- theme                            自定义样式主题
 | 
			
		||||
|-- utils                            工具类
 | 
			
		||||
|-- views                            视图目录
 | 
			
		||||
|   |-- role                             role模块名
 | 
			
		||||
|   |-- |-- role-list.vue                    role列表页面
 | 
			
		||||
|   |-- |-- role-add.vue                     role新建页面
 | 
			
		||||
|   |-- |-- role-update.vue                  role更新页面
 | 
			
		||||
|   |-- |-- index.less                      role模块样式
 | 
			
		||||
|   |-- |-- components                      role模块通用组件文件夹
 | 
			
		||||
|   |-- employee                         employee模块
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 5.2、 api 目录
 | 
			
		||||
 | 
			
		||||
- api文件要以api为结尾,比如 `employee-api.js`、`login-api.js`,方便查找
 | 
			
		||||
- api文件必须导出对象必须以`Api`为结尾,如:`employeeApi`、`noticeApi`
 | 
			
		||||
- api中以一个对象将方法包裹
 | 
			
		||||
- api中的注释,必须和后端 swagger 文档保持一致,同时保留后端作者
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
前端: `department-api.js`
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
import { getRequest, postRequest } from '/@/lib/axios';
 | 
			
		||||
 | 
			
		||||
export const departmentApi = {
 | 
			
		||||
  /**
 | 
			
		||||
   * @description: 查询部门列表 @author 卓大
 | 
			
		||||
   * @param {*}
 | 
			
		||||
   * @return {*}
 | 
			
		||||
   */
 | 
			
		||||
  queryAllDepartment: () => {
 | 
			
		||||
    return getRequest('/department/listAll');
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @description: 查询部门树形列表 @author 卓大
 | 
			
		||||
   * @param {*}
 | 
			
		||||
   * @return {*}
 | 
			
		||||
   */
 | 
			
		||||
   queryDepartmentTree: () => {
 | 
			
		||||
    return getRequest('/department/treeList');
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @description: 添加部门 @author 卓大
 | 
			
		||||
   * @param {*}
 | 
			
		||||
   * @return {*}
 | 
			
		||||
   */
 | 
			
		||||
  addDepartment: (param) => {
 | 
			
		||||
    return postRequest('/department/add', param);
 | 
			
		||||
  },
 | 
			
		||||
  /**
 | 
			
		||||
   * @description: 更新部门信息 @author 卓大
 | 
			
		||||
   * @param {*}
 | 
			
		||||
   * @return {*}
 | 
			
		||||
   */
 | 
			
		||||
  updateDepartment: (param) => {
 | 
			
		||||
    return postRequest('/department/update', param);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 5.3、 assets 目录
 | 
			
		||||
 | 
			
		||||
assets 为静态资源,里面存放 images, styles, icons 等静态资源,静态资源命名格式为 kebab-case
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
|assets
 | 
			
		||||
|-- icons
 | 
			
		||||
|-- images
 | 
			
		||||
|   |-- background-color.png
 | 
			
		||||
|   |-- upload-header.png
 | 
			
		||||
|-- styles
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 5.4、 components 目录
 | 
			
		||||
 | 
			
		||||
此目录应按照组件进行目录划分,目录命名为 kebab-case,一个组件必须一个单独的目录  ;  
 | 
			
		||||
目的:  
 | 
			
		||||
- 一个组件一个目录是为了将来组件的扩展,因为这是整个项目公用的组件
 | 
			
		||||
- 组件入口必须为 index.vue,原因也是因为这是整个项目公用的组件
 | 
			
		||||
 | 
			
		||||
举例如下:
 | 
			
		||||
```
 | 
			
		||||
|components
 | 
			
		||||
|-- error-log
 | 
			
		||||
|   |-- index.vue
 | 
			
		||||
|   |-- index.less
 | 
			
		||||
|-- markdown-editor
 | 
			
		||||
|   |-- index.vue
 | 
			
		||||
|   |-- index.js
 | 
			
		||||
|-- kebab-case
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 5.5、 constants 目录
 | 
			
		||||
 | 
			
		||||
此目录存放项目所有常量和枚举,如果常量在 vue 中使用,请使用 `src/plugin/smart-enums-plugin.js`插件,也可以使用我们写的第三方库: [vue-enum 插件](https://www.npmjs.com/package/vue-enum),但是还不支持vue3,等待更新吧
 | 
			
		||||
 | 
			
		||||
具体要求:
 | 
			
		||||
- 常量文件要以 `const` 为结尾,比如`login-const.js`、`file-const.js`
 | 
			
		||||
- 变量要:大写下划线,比如 `LOGIN_RESULT_ENUM`、`LOGIN_SUCCESS`、`LOGIN_FAIL`
 | 
			
		||||
- 如果是 枚举,变量必须以 `ENUM`为结尾,如:`LOGIN_RESULT_ENUM`、`CODE_FRONT_COMPONENT_ENUM`
 | 
			
		||||
 | 
			
		||||
目录结构:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
|constants
 | 
			
		||||
|-- index-const.js
 | 
			
		||||
|-- role-const.js
 | 
			
		||||
|-- employee-const.js
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
例子: employee-const.js
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
export const EMPLOYEE_STATUS = {
 | 
			
		||||
  NORMAL: {
 | 
			
		||||
    value: 1,
 | 
			
		||||
    desc: '正常'
 | 
			
		||||
  },
 | 
			
		||||
  DISABLED: {
 | 
			
		||||
    value: 1,
 | 
			
		||||
    desc: '禁用'
 | 
			
		||||
  },
 | 
			
		||||
  DELETED: {
 | 
			
		||||
    value: 2,
 | 
			
		||||
    desc: '已删除'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const EMPLOYEE_ACCOUNT_TYPE = {
 | 
			
		||||
  QQ: {
 | 
			
		||||
    value: 1,
 | 
			
		||||
    desc: 'QQ登录'
 | 
			
		||||
  },
 | 
			
		||||
  WECHAT: {
 | 
			
		||||
    value: 2,
 | 
			
		||||
    desc: '微信登录'
 | 
			
		||||
  },
 | 
			
		||||
  DINGDING: {
 | 
			
		||||
    value: 3,
 | 
			
		||||
    desc: '钉钉登录'
 | 
			
		||||
  },
 | 
			
		||||
  USERNAME: {
 | 
			
		||||
    value: 4,
 | 
			
		||||
    desc: '用户名密码登录'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  EMPLOYEE_STATUS,
 | 
			
		||||
  EMPLOYEE_ACCOUNT_TYPE
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 5.6、 router 与 store 目录
 | 
			
		||||
 | 
			
		||||
这两个目录一定要将业务进行拆分,不能放到一个 js 文件里。
 | 
			
		||||
 | 
			
		||||
`router` 尽量按照 views 中的结构保持一致
 | 
			
		||||
 | 
			
		||||
`store` 按照业务进行拆分不同的 js 文件
 | 
			
		||||
 | 
			
		||||
### 5.7、 views 目录
 | 
			
		||||
 | 
			
		||||
目录要求,按照模块划分,其中具体文件名要求如下:
 | 
			
		||||
 | 
			
		||||
- 如果是列表页面,要以list为结尾,如`role-list.vue`、`cache-list.vue`
 | 
			
		||||
- 如果是 表单页面,要以 form为结尾,如 `role-form.vue`、`notice-add-form.vue`
 | 
			
		||||
- 如果是 modal弹窗,要以 modal为结尾,如 表单弹窗 `role-form-modal.vue`,详情 `role-detail-modal.vue`
 | 
			
		||||
- 如果是 drawer 抽屉页面,要同上以 `Drawer`为结尾
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
|-- views                                        视图目录
 | 
			
		||||
|   |-- role                                     role模块名
 | 
			
		||||
|   |   |-- role-list.vue                        role列表页面
 | 
			
		||||
|   |   |-- role-add-form.vue                    role新建页面
 | 
			
		||||
|   |   |-- role-update-form-modal.vue           role更新页面
 | 
			
		||||
|   |   |-- index.less                           role模块样式
 | 
			
		||||
|   |   |-- components                           role模块通用组件文件夹
 | 
			
		||||
|   |   |   |-- role-title-modal.vue             role弹出框组件
 | 
			
		||||
|   |-- employee                                 employee模块
 | 
			
		||||
|   |-- behavior-log                             行为日志log模块
 | 
			
		||||
|   |-- code-generator                           代码生成器模块
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 联系我们
 | 
			
		||||
 | 
			
		||||
[1024创新实验室-主任:卓大](https://zhuoda.vip),混迹于各个技术圈,研究过计算机,熟悉点 java,略懂点前端。  
 | 
			
		||||
[1024创新实验室(河南·洛阳)](https://1024lab.net) 致力于成为中原领先、国内一流的技术团队,以技术创新为驱动,合作各类项目。
 | 
			
		||||
<table>
 | 
			
		||||
<tr>
 | 
			
		||||
  <td><img src="http://img.zhuoluodada.cn/wechat/zhuoda-wechat.jpg"/></td>
 | 
			
		||||
  <td><img src="http://img.zhuoluodada.cn/wechat/xiaozhen-gzh.jpg"/></td>
 | 
			
		||||
  <td><img src="http://img.zhuoluodada.cn/wechat/zhuoda-wechat-money-v1.jpg"/></td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
  <td style="text-align:center">加 主任 “卓大” 微信 <br> 拉你入群,一起学习</td>
 | 
			
		||||
  <td style="text-align:center">关注 “小镇程序员” <br> 分享代码与生活、技术与赚钱</td>
 | 
			
		||||
  <td style="text-align:center">请 “1024创新实验室” 喝咖啡 <br> 支持我们的开源与分享 </td>
 | 
			
		||||
</tr>
 | 
			
		||||
</table>
 | 
			
		||||
							
								
								
									
										219
									
								
								代码规范/规范导读 V2.0.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								代码规范/规范导读 V2.0.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,219 @@
 | 
			
		||||
---
 | 
			
		||||
title: 2.1、规范导读 V2.0
 | 
			
		||||
date: 2022-09-07
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## 序
 | 
			
		||||
 | 
			
		||||
2019 年,我们 **1024 创新实验室** 发布《1024 创新实验室-Java 规范 V1.0 和 前端规范 V1.0》,在业内收到了很多好评,时至今日过去了三年,我们实验室在与百余家企业沟通和交流中,以及在我们实验室团队的不断成长和不断的摸索中,我们又在 v1.0 的基础上做了更好的改进。规范的目的是为了编写高质量的代码,让你的团队成员每天得心情都是愉悦的,大家在一起是快乐的。
 | 
			
		||||
 | 
			
		||||
引自《阿里规约》的开头片段:
 | 
			
		||||
 | 
			
		||||
> _----现代软件架构的复杂性需要协同开发完成,如何高效地协同呢?无规矩不成方圆,无规范难以协同,比如,制订交通法规表面上是要限制行车权,实际上是保障公众的人身安全,试想如果没有限速,没有红绿灯,谁还敢上路行驶。对软件来说,适当的规范和标准绝不是消灭代码内容的创造性、优雅性,而是限制过度个性化,以一种普遍认可的统一方式一起做事,提升协作效率,降低沟通成本。代码的字里行间流淌的是软件系统的血液,质量的提升是尽可能少踩坑,杜绝踩重复的坑,切实提升系统稳定性,码出质量。_
 | 
			
		||||
 | 
			
		||||
## 前言
 | 
			
		||||
 | 
			
		||||
我们开源的代码规范基于阿里巴巴、华为的开发手册,添加了我们团队的风格和规范,补充了一些细节。感谢前人的经验和付出,让我们可以有机会站在巨人的肩膀上眺望星辰大海。
 | 
			
		||||
 | 
			
		||||
- 规范不是为了约束和禁锢大家的创造力,而是为了帮助大家能够在正确的道路上,尽可能的避免踩坑和跑偏。  
 | 
			
		||||
- 规范可以让我们无论单枪匹马还是与众人同行的时候都能得心应手。  
 | 
			
		||||
- 规范可以让我们在面对日益变态的需求和做代码接盘侠的时候,更优雅从容。
 | 
			
		||||
- 规则并不是完美的,通过约束和禁止在特定情况下的特性,可能会对代码实现造成影响。
 | 
			
		||||
 | 
			
		||||
**我们制定规则的目的:为了大多数程序员小伙伴可以得到更多的好处,如果在团队实际运作中认为某个规则无法遵循或有更好的做法,希望大家可以共同改进该规范。**
 | 
			
		||||
 | 
			
		||||
## 一、基础规范
 | 
			
		||||
 | 
			
		||||
### 1.1、什么是好的代码?
 | 
			
		||||
 | 
			
		||||
在 V1.0 版本中,我们对好的代码引用了 Kent Beck 的简单设计四原则,大家反馈比较抽象。V2.0 我们决定用白话来简单讲讲好的代码原则:
 | 
			
		||||
 | 
			
		||||
- **满足业务需要:代码是来实现业务的,如果业务都实现不了,代码也就没什么价值了**
 | 
			
		||||
- **代码尽可能的清晰明了:就是让小白也能看懂你的代码**
 | 
			
		||||
- **代码尽可能的少:在保证清晰明了的前提下,能少一行少一行,能少一个类少一个类,能少一行注释少一行注释**
 | 
			
		||||
- **代码尽可能复用性和模块化:在保证清晰明了和尽可能少的前提下,能复用的代码尽量复用,能模块的尽量模块**
 | 
			
		||||
 | 
			
		||||
以上四个原则的重要程度依次降低, 这是我们**1024 创新实验室**认为好代码的原则,即:简单的、好的、代码
 | 
			
		||||
 | 
			
		||||
### 1.2、英文单词命名规范
 | 
			
		||||
 | 
			
		||||
无论前端代码还是后端代码、异或其他代码,都是由一个个单词组成的,所以一个好的单词影响着代码的本身,所以我们定义如下:
 | 
			
		||||
#### 1)合理使用正确的英文单词
 | 
			
		||||
很多人认为自己英语不好就命名比较随意,但我们看来,一个有道词典或者百度翻译就能看好的解决这件事情,所以单词的命名必须使用正确。我们曾经遇到过这种起名字的,比如有一个双11业务,要求第一天业务、第二天、第三天业务的区别,有些小伙伴这样起名字:
 | 
			
		||||
```java
 | 
			
		||||
第一天: di1day
 | 
			
		||||
第二天: di2day
 | 
			
		||||
第三天: treeday  (三应该是 three,笑喷)
 | 
			
		||||
```
 | 
			
		||||
以上是真实发生的例子,故我们定义如下:  
 | 
			
		||||
- 一定要用英文,且单词正确,不要用汉语拼音(特殊情况除外,比如某些税务系统的特殊科目命名)
 | 
			
		||||
- 英文单词一定要使用常用词
 | 
			
		||||
- 英文单词要符合业务
 | 
			
		||||
 | 
			
		||||
#### 2)合理区分名词和动词
 | 
			
		||||
我们知道在大部分编程语言中, 项目、类Class、数据库、表名、url中的前半部分 等等 一个比较大的范围都是应该用名词,比如java中的`class`、`interface`、`enum` 等等都是名词,比如 `OrderService`。   
 | 
			
		||||
而具体的方法名应该是**动词**异或**动名词**,比如方法:创建订单:`createOrder`,查询订单`queryOrder`
 | 
			
		||||
 | 
			
		||||
#### 3)各个端、数据库、等命名要统一
 | 
			
		||||
无论团队里的前端、后端、移动端、数据库、服务器、redis、docker等等一切对于 某个业务或者某个业务单元的命名必须高度保持一致。  
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
比如之前遇到过这么一个功能,OA办公系统的`通知`功能,各个端定义为:   
 | 
			
		||||
- 后端:`notice`, (NoticeController, 表:t_notice)
 | 
			
		||||
- 前端用成了`news`, (news-list.vue, /news/news-list)
 | 
			
		||||
- 移动端用成了`message`,(MessageFragement)
 | 
			
		||||
- 最后再对接的时候,懵逼了,懵了;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 1.3、注释规范
 | 
			
		||||
 | 
			
		||||
#### 1)注释和代码一样重要
 | 
			
		||||
 | 
			
		||||
注释是我们披荆斩棘历经磨难翻越需求这座大山时,留下的踪迹和收获的经验教训,这些宝贵的知识除了证明我们曾经存在过,也提醒着后来的人们殷鉴不远、继往开来。
 | 
			
		||||
 | 
			
		||||
注释除了说明作用、逻辑之外。还有一个很重要的原因:当业务逻辑过于复杂,代码过于庞大的时候,注释就变成了一道道美化环境、分离与整理逻辑思路的路标。这是很重要的一点,它能有效得帮助我们免于陷入代码与业务逻辑的泥沼之中。
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
/**
 | 
			
		||||
* 开始抽奖方法
 | 
			
		||||
* 保存中奖信息、奖励用户积分等
 | 
			
		||||
* @param luckDrawDTO
 | 
			
		||||
* @return ResponseDTO 返回中奖信息
 | 
			
		||||
*/
 | 
			
		||||
public ResponseDTO<String> startLuckDraw(LuckDrawDTO luckDrawDTO) {
 | 
			
		||||
 | 
			
		||||
    // -------------- 1、校验抽奖活动基本信息 ------------------------
 | 
			
		||||
    xxx伪代码一顿操作
 | 
			
		||||
 | 
			
		||||
    // -------------- 2、新增抽奖记录 -------------------------------
 | 
			
		||||
    xxx伪代码一顿操作
 | 
			
		||||
 | 
			
		||||
    // -------------- 3、如果需要消耗积分,则扣除钢镚积分 -------------
 | 
			
		||||
    xxx伪代码一顿操作
 | 
			
		||||
 | 
			
		||||
    // -------------- 4、获取奖品信息,开始翻滚吧 --------------------
 | 
			
		||||
    xxx伪代码一顿操作
 | 
			
		||||
 | 
			
		||||
    return ResponseDTO.succ(luckDrawPrizeVO);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 2)注释和代码的一致性
 | 
			
		||||
 | 
			
		||||
注释并不是越多越好,当注释过多,维护代码的同时,还需要维护注释,不仅变成了一种负担,也与我们当初添加注释的初衷背道而驰。
 | 
			
		||||
 | 
			
		||||
首先:大家应该通过清晰的逻辑架构,好的变量命名来提高代码可读性;需要的时候,才辅以注释说明。注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,按需注释。注释内容要简洁、明了、无二义性,信息全面且不冗余。
 | 
			
		||||
 | 
			
		||||
其次:无论是修改、复制代码时,都要仔细核对注释内容是否正确。只改代码,不改注释是一种不文明行为,破坏了代码与注释的一致性,会让阅读者迷惑、费解,甚至误解。
 | 
			
		||||
 | 
			
		||||
反例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
// 查询部门
 | 
			
		||||
EmployeeVO employee = employeeDao.listByDepartmenttId(deptId);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 3)方法注释
 | 
			
		||||
 | 
			
		||||
方法要尽量通过方法名自解释,不要写无用、信息冗余的方法头,不要写空有格式的方法头注释。
 | 
			
		||||
 | 
			
		||||
方法头注释内容可选,但不限于:功能说明、返回值,用法、算法实现等等。尤其是对外的方法接口声明,其注释,应当将重要、有用的信息表达清楚。
 | 
			
		||||
 | 
			
		||||
正例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
/**
 | 
			
		||||
 * 解析转换时间字符串为 LocalDate 时间类
 | 
			
		||||
 * 调用前必须校验字符串格式 否则可能造成解析失败的错误异常
 | 
			
		||||
 *
 | 
			
		||||
 * @param dateStr 必须是 yyyy-MM-dd 格式的字符串
 | 
			
		||||
 * @return LocalDate
 | 
			
		||||
 */
 | 
			
		||||
public static LocalDate parseYMD(String dateStr){}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
反例:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
/**
 | 
			
		||||
 * 校验对象
 | 
			
		||||
 *
 | 
			
		||||
 * @param t
 | 
			
		||||
 * @return String
 | 
			
		||||
 */
 | 
			
		||||
public static <T> String checkObj(T t);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
反例中出现的问题:
 | 
			
		||||
 | 
			
		||||
- 方法注释没有说明具体的作用、使用事项。
 | 
			
		||||
- 参数、返回值,空有格式没内容。这是非常重要一点,任何人调用任何方法之前都需要知道方法对参数的要求,以及返回值是什么。
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 1.4、TODO FIX规范
 | 
			
		||||
 | 
			
		||||
`TODO/TBD(to be determined)` 注释一般用来描述已知待改进、待补充的修改点,并且加上作者名称。  
 | 
			
		||||
`FIXME` 注释一般用来描述已知缺陷,它们都应该有统一风格,方便文本搜索统一处理。如:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
// TODO <author-name>: 补充XX处理
 | 
			
		||||
// FIXME <author-name>: XX缺陷
 | 
			
		||||
```
 | 
			
		||||
举例:
 | 
			
		||||
```js
 | 
			
		||||
// TODO <卓大> 为了数据安全,此处应该对手机号进行加*处理
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 1.5、 无用代码:删!
 | 
			
		||||
因为现在所有的项目都使用了代码管理工具,比如 git、svn 、ts等等,所以对于无用的代码,让我们尽情的删除掉吧!   
 | 
			
		||||
重要的说三遍:   
 | 
			
		||||
不要注释,不要注释,不要注释!   
 | 
			
		||||
要删除代码,要删除代码,要删除代码!
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 1.6、 代码Git提交规范
 | 
			
		||||
 | 
			
		||||
- 提交前应该冷静、仔细检查一下,确保没有忘记加入版本控制或不应该提交的文件。
 | 
			
		||||
- 提交前应该先编译一次(idea 里 ctrl+F9),防止出现编译都报错的情况。
 | 
			
		||||
- 提交前先更新 pull 一次代码,提交前发生冲突要比提交后发生冲突容易解决的多。
 | 
			
		||||
- 提交前检查代码是否格式化,是否符合代码规范,无用的包引入、变量是否清除等等。
 | 
			
		||||
- 提交时检查注释是否准确简洁的表达出了本次提交的内容。
 | 
			
		||||
- 提交代码时必须填写详细备注,如完成功能,注释为“新增 XX 功能”;
 | 
			
		||||
- 若此次提交代码对应禅道中的任务或者 bug,格式如下:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
task#[任务id] [任务标题] [具体事项]
 | 
			
		||||
bug#[bug id] [bug标题] [具体事项]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- 例子如下:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
git commit -m 'task#1101 开发smartreload功能 完成线程池的编码'
 | 
			
		||||
git commit -m 'bug#1102 smartreload时间不正确 线程池的大小问题'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 1.7、保持项目整洁
 | 
			
		||||
 | 
			
		||||
使用 git,必须添加 .gitignore 忽略配置文件。  
 | 
			
		||||
不要提交与项目无关的内容文件:idea 配置、target 包等。
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 联系我们
 | 
			
		||||
 | 
			
		||||
[1024创新实验室-主任:卓大](https://zhuoda.vip),混迹于各个技术圈,研究过计算机,熟悉点 java,略懂点前端。  
 | 
			
		||||
[1024创新实验室(河南·洛阳)](https://1024lab.net) 致力于成为中原领先、国内一流的技术团队,以技术创新为驱动,合作各类项目。
 | 
			
		||||
<table>
 | 
			
		||||
<tr>
 | 
			
		||||
  <td><img src="http://img.zhuoluodada.cn/wechat/zhuoda-wechat.jpg"/></td>
 | 
			
		||||
  <td><img src="http://img.zhuoluodada.cn/wechat/xiaozhen-gzh.jpg"/></td>
 | 
			
		||||
  <td><img src="http://img.zhuoluodada.cn/wechat/zhuoda-wechat-money-v1.jpg"/></td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
  <td style="text-align:center">加 主任 “卓大” 微信 <br> 拉你入群,一起学习</td>
 | 
			
		||||
  <td style="text-align:center">关注 “小镇程序员” <br> 分享代码与生活、技术与赚钱</td>
 | 
			
		||||
  <td style="text-align:center">请 “1024创新实验室” 喝咖啡 <br> 支持我们的开源与分享 </td>
 | 
			
		||||
</tr>
 | 
			
		||||
</table>
 | 
			
		||||
		Reference in New Issue
	
	Block a user