一、后端开发规范的意义
提高团队的协作能力
提高代码的复用利用率
写出质量更高,效率更好的代码
为后期维护提供更好的支持
二、项目介绍
该项目主要负责的是某软件的数据管理
该项目的开发语言:Java8
-
该项目所用到的技术:
数据库:Mysql
数据库框架:MyBatis Plus
应用框架:Spring Boot
权限框架:Spring Security + JWT
接口文档 :Swagger2
项目构建工具:Maven
-
该项目所用到的开发工具:
开发工具:IntelliJ IDEA
代码同步工具:Git
接口测试工具:Postman
-
该项目的设计原则:SOLID
SRP 单一原则
OCP 开闭原则
LSP 里氏替换原则
ISP 接口隔离原则
DIP 依赖倒置原则
该项目的接口风格是Resful
该项目的代码规范遵循阿里巴巴Java开发手册V1.5( 下载点我)
三、项目下载及导入所需要的SDK
1.项目下载
git clone (git@git.labs.qqtowns.com:alanchen/next-web-server.git)(括号里的可以换成git项目地址)
2.导入SDK
打开wz-out-files文件,运行readme.sh脚本即可。
3.运行程序
运行程序,测试依赖是否成功导入
四、开发规范
1.层级结构:entity(实体层)、dao(持久层)、service(业务层)、controller(控制层)
entity
实体层
1) 命名:类名以及属性名使用驼峰式命名首字母大写
2) 统一使用Lombok插件,在开头加上@Data注释,不需要写get、set、toString方法
3) 统一继承BaseEntity类,因此实体类中一般不需要加上id、createTime、updateTime、deleted字段
4)需要备注的字段前面要加上@ApiModelProperty("注释内容")
@Data
public class UserProfession extends BaseEntity {
@NotNull
private String userId;
@ApiModelProperty("单位全称")
private String company;
@ApiModelProperty("部门")
private String department;
@ApiModelProperty("最高职位")
private String topProfession;
@ApiModelProperty("入职年份")
private String year;
@ApiModelProperty("位置序号")
private Integer position;
private String createUserId;
private String updateUserId;
@ApiModelProperty("授权方id")
private String sourceId;
}
dao
持久层
1)命名:现在基本用到的是mybatisplus框架,现在的接口命名还是与业务逻辑层的接口命名保持一致以‘I’开头以‘Dao’结尾,其中接口继承了BaseMapper<T>。
- 在开头加上@Mapper 和@Repository注解
3)dao中的方法都需要加以备注说明此方法的功能
4)为了框架的灵活性,该项目采取写sql语句来实现增删改查的方法,sql写在resources中的mapper包里对应的...Mapper(注意:一般删除用的是update,逻辑删除)
例子(上面为接口,下面是用sql实现):
@Mapper
@Repository
public interface IRoleDao extends BaseMapper<Role> {
/**
* 获取角色列表
* @param sourceId
* @return
*/
List<Node> getUserRole(@Param("sourceId") String sourceId);
<select id="getUserRole" resultType="com.netx.web.vo.common.Node">
select id as mapkey, role_name as mapvalue from role where source_id = #{sourceId} and deleted = 0 order by update_time desc
</select>
5)特别注意的是:插入新数据使用mybatisplus自带的insert方法,插入一个对象,这时,由于我们的实体类是继承了BaseEntity的,所以在用insert插入时,插入的对象不需要设置BaseEntity的字段
UserPayAccount newAccount = new UserPayAccount();
newAccount.setAccountIdentity(wzMobileCodeRequest.getAccountIdentity());
newAccount.setPriority(888);
newAccount.setAccountType("2");
newAccount.setUserId(wzMobileCodeRequest.getId());
newAccount.setSourceId(wzMobileCodeRequest.getSourceId());
userPayAccountDao.insert(newAccount);
service
业务层包括接口层和实现层 主要负责业务逻辑
接口层
1)命名:驼峰式命名,以‘I’开头以‘Service’结尾,中间放实体类的名字。
2)每个接口都需要加上注释,说明接口的目的
/**
* 根据授权方id获取角色列表
* @param sourceId
* @param adminId
* @return
*/
List<Node> getRole(String sourceId,String adminId);
实现层
1)命名:驼峰式命名,开头实体类的名字首字母大写以‘ServiceImpl’结尾。
2)前面加上@Service注解
3)在需要注入的Bean前面加上@Autowired注解
4)实现接口方法前需要加上@Override注解
@Service
public class RoleServiceImpl implements IRoleService {
@Autowired
IRoleDao roleDao;
@Autowired
IRoleMenuRelationDao roleMenuDao;
@Autowired
IMenuDao menuDao;
@Autowired
IUserAdminDao userAdminDao;
@Override
public List<Node> getRole(String sourceId,String agentId) {
List<Node> agent = roleMenuDao.getAllAgentRole("代理");
List<Node> allRole = roleDao.getUserRole(sourceId);
//如果不是代理则不显示有关代理的角色
if(StringUtils.isBlank(agentId)){
allRole.removeAll(agent);
}
return allRole;
}
}
controller
控制层
1. 接口风格
遵循Resful风格接口(详情)。
1. 请求方式 : post:新增 、 get 获取 、 put 与 patch是修改 、 delete 删除
2. 拒绝动词 : 在请求路径里面不要存在动词,基本是名词并且一看就能明白你要做什么!!!
2. 统一返回机制
该项目有做统一返回参数的处理,查询的接口需要定义返回类型,增删改统一返回void就行
3. 注解
1)在smm里已经把该用到的注解包装成@TRestController("/自定义url")
2)开头需要加上@Api(tags = "总接口介绍暴露给前端")注解
3)在需要注入的Bean前面加上@Autowired注解
4)每个接口前加上@ApiOperation("接口功能描述")注解
5)根据需求在每个接口前加上**@Post/Get/Put/DeleteMapping(/自定义url)****注解
6)根据传参方式,在传参的地方加上@RequestBody或@RequestBody注解,如果是需要进行数据校验则需要加上@Valid
4. 自定义url
定义url需要注意的是全小写,不带任何动词,不可避免时用 '-' 来隔开;
5. 拒绝逻辑
在控制层不需要做任何逻辑判断,只有调用业务逻辑层并返回的数据。所有的业务逻辑必须放到业务逻辑层。
@Api(tags = "角色管理")
@TRestController(value = "/user-roles")
public class RoleContrller {
@Autowired
IRoleService userRoleService;
@Autowired
IMenuService menuService;
@ApiOperation("根据授权方id分页查询角色列表,也可以通过角色名称查询(角色列表用的)")
@GetMapping("/list")
public IPage<Role> pageUserRole(UserRoleSearch search){
return userRoleService.pageUserRole(search);
}
@ApiOperation("新增角色,角色名字、授权方id、可以查看的菜单名字不能为空")
@PostMapping
public void addUserRole (@RequestBody @Valid UserRoleVO userRole){
userRoleService.addUserRole(userRole);
}
}
2.VO
该项目对vo的理解:表现层对象(View Object),主要对应展示界面显示的数据对象,用一个VO对象来封装整个界面展示所需要的对象数据。
运用场景:一般在查询的时候会需要定义一个vo来返回所需要的字段给前端
注解:和实体类一样,需要用到@Data、@ApiModelProperty("描述")注释,若需要进行数据校验,还需要加上校验的注解,例如:@NotBlank(message = "....")
特别注意:id也是要传的,id也是要传的,id也是要传的;前端操作需要的参数也是要传的,前端操作需要的参数也是要传的,前端操作需要的参数也是要传的
@Data
public class MenuVO {
@ApiModelProperty("菜单id")
private String id;
@ApiModelProperty("菜单名字")
@NotBlank(message = "菜单名字不能为空")
private String menuName;
@ApiModelProperty("上级菜单id")
private String parentId;
@ApiModelProperty("菜单url(标识符)")
@NotBlank(message = "菜单url不能为空")
private String menuUrl;
@ApiModelProperty("菜单优先级(排序不用传)")
private Integer priority;
@ApiModelProperty("是否独有,0不是1是")
@NotBlank(message = "标识不能为空")
private String isJinhui;
}
3.分页查询
该项目所用到的是MyBatis Plus的分页插件
参考文档:https://mp.baomidou.com/guide/page.html
在该项目使用分页查询时需要注意的地方:
统一写sql来实现分页查询,返回类型为IPage<T>,传参一般为(Page page,@Param("search") ...Search search),到这里你可能会问何为search?
search是继承了page的类放在search包中,在这个类中,你可以放要求筛选的字段,并且在sql中进行筛选,注解和vo、entity的注解类似
/**
* 分页查询角色列表
*
* @param page
* @param search
* @return
*/
IPage<Role> pageUserRole(@Param("page") Page page, @Param("search") UserRoleSearch search);
@Data
public class UserRoleSearch extends Page {
@ApiModelProperty("授权方id")
@NotBlank(message = "授权方id不能为空")
private String sourceId;
@ApiModelProperty("角色名称")
private String roleName;
}
<select id="pageUserRole" resultType="com.netx.web.entity.Role">
select id,role_name from role
<where>
source_id = #{search.sourceId} and deleted = 0
<if test="search.roleName != null and search.roleName != '' ">
and role_name like concat('%',#{search.roleName},'%')
</if>
</where>
</select>
执行sql后的结果:
分页查询测试结果:
{
"code": 200,
"data": {
"current": 1,
"orders": [],
"pages": 1,
"records": [
{
"id": "1a8bd785c6b84be3a5da2b670a417c44",
"roleName": "厂库管理员"
},
{
"id": "599b9f54004f4731a17af279420a1491",
"roleName": "代理管理员"
}
],
"searchCount": true,
"size": 10,
"sourceId": "a79745b785fc4a928a8fbbb86111e859",
"total": 2
},
"msg": ""
}
4.枚举
应用场景:当要用到数字甚至字母来表示某一状态的时候
意义:枚举可以限定参数的个数,对调用者的行为能更加严格地进行控制;它的对象在一个枚举类生成的时候已经确定。
用法(直接上例子):
public enum UserAdminTypeEnum implements IEnum {
/**
* 0:普通用户
* 1:管理员
*/
USER(0,"普通用户"),
ADMIN(1,"管理员"),
IT_ADMIN(2,"IT管理员");
private Integer code;
private String msg;
UserAdminTypeEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
@Override
public Serializable getValue() {
return this.code;
}
@JsonValue
public String getMsg(){
return this.msg;
}
}
//其他类调用时:
Boolean isITAdmin = userAdmin.getAdminType() == UserAdminTypeEnum.IT_ADMIN.getValue();
Boolean isLianzhi = userAdmin.getSourceId().equals(sourceProperty.getLianzhiSourceId());
5.config、authority以及utils
config用于系统全局环境的设置
现用来进行全局异常、全局返回参数、跨域请求、swagger配置、Mybatis Plus配置处理
authority用于权限管理
现用来进行密码加密、登陆后的个人信息返回以及权限的放行
utils用于存放辅助工具
现有的有支付宝提现、图片上传、创建报表、生成随机码、金钱转换、商品落地等等
6.application.yml
用于存放端口号、连接数据库的参数以及全局变量
开发时特需注意的是
冒号后要加空格,冒号后要加空格,冒号后要加空格
换行要对齐,换行要对齐,换行要对齐
端口号以及数据库参数不要随意改,端口号以及数据库参数不要随意改,端口号以及数据库参数不要随意改
7.阿里巴巴代码规约扫描
五、接口测试
测试工具:postman
测试路径:localhost:端口号(一般是8080)/comtroller开头自定义的路径名/接口自定义的路径名
请求方式:post、get、put、delete
测试用例:针对自己的接口想出有针对性的用例,避免重复改bug
六、GIT操作
参考:https://www.jianshu.com/p/1d9067b044ab
七、开发插件推荐
mybatiscodehelperpro
阿里巴巴代码规约扫描 alibaba
lombok