后台代码约定

### zywork-app-pro项目约定 #### 代码规范 1、zywork-app-pro套件中的Java代码遵循《阿里巴巴Java开发手册》。 #### 数据库约定 1、数据库字符集:`utf8mb4`,排序规则:`utf8mb4_general_ci`。 2、所有数据表使用`t_`前缀,表字段如果由多个单词组成,单词间使用`_`连接。 3、每个数据表都具有的字段及数据类型: ```sql ( id bigint(20) primary key auto_increment comment '**编号', version int(11) default 1 comment '**版本号', create_time datetime default CURRENT_TIMESTAMP comment '创建时间#**创建时间', update_time datetime comment '更新时间#**更新时间', is_active tinyint(4) default 1 comment '是否可用#**是否可用' ) ``` 4、表示状态、是否、有限集的数据通常是`tinyint(4)`类型,并结合数据字典使用。 5、表示是否的,如果需要默认值,1为是,0为否。 6、所有字段都必须增加注释,注释要求: 1)简洁明了 2)准确描述 3)增加必要的详细注释,如: ```sql is_active tinyint(4) default 1 comment '是否可用#**是否可用' ``` `#`前为字段说明,字段说明默认为Java属性的中文描述,最终会展现在后台前端的各类表单及表格中; `#`后为字段详细注释,用于更加充分地说明字段的含义,最终会展现在后台前端的表格中。 7、系统中已经存在的数据库表,设计理念是*尽量减少数据表的关联操作*,所以**增加了关联表的冗余字段**。 如`t_user_detail`表: ```sql ( `id` bigint(20) NOT NULL COMMENT '用户编号$t_user:id', `user_phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '手机号$t_user:phone_', `user_nickname` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '昵称$t_user:nickname', `user_headicon` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '头像地址$t_user:headicon', `user_gender` tinyint(4) NULL DEFAULT 0 COMMENT '性别$t_user:gender', `user_is_active` tinyint(4) NOT NULL COMMENT '用户是否可用$t_user:is_active', `birthday` date NULL DEFAULT NULL COMMENT '生日', `location` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '所在地', ) ``` 其中,`user_phone, user_nickname, user_headicon, user_gender, user_is_active`都是冗余字段,这些字段原本存在于`t_user`表中,在查询`t_user_detail`用户详情信息时,为了减少与`t_user`表的关联操作,则进行了重要数据的冗余。 在zywork-app-pro的整个系统的表设计中,都始终贯彻了这种设计理念。在查询数据时确实减少了表的关联操作,但是同时却带来了另外一个问题,在更新A表数据时,需要同步去更新B表、C表,甚至更多数据表中冗余的A表字段,这样才能保证表数据的一致性。 *目前系统的解决方法是在更新表数据时,同步去更新其他表中的冗余字段,以保证数据的一致性,所有数据的更新在同一个事务中。* 我们在zywork-app-pro项目的`resoures`目录中增加了一个`relation_update.json`的配置文件,该配置文件指定了在更新某个表的某些字段的时候,需要同步去更新另外的表的某些字段。 如下配置: ```json { "UserDTO": [ { "fromProps": [ "id", "phone", "nickname", "gender", "headicon", "isActive" ], "toTable": "t_user_detail", "toColumns": [ "id", "user_phone", "user_nickname", "user_gender", "user_headicon", "user_is_active" ] } } ``` 以上配置表示:当更新`t_user`表的`phone, nickname, gender, headicon, isActive`字段的时候,同步去更新`t_user_detail`表的`user_phone, user_nickname, user_gender, user_headicon, user_is_active`字段。 该方案待完善的方向(即将完善并测试): 1)加入消息队列 2)使用MySQL binlog 8、对于冗余字段的注释,我们增加了一个`$`符号,在`$`后说明冗余字段来源于哪张表的哪个字段,在某些情况下,字段的注释可能同时出现`#`和`$`。 如`t_user_detail`表: ```sql ( `id` bigint(20) NOT NULL COMMENT '用户编号$t_user:id', `user_phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '手机号$t_user:phone_', ) ``` `user_phone`字段来源于`t_user`表的`phone`字段。 为什么要使用`$`标记出来呢? 在我们使用zywork-generator-pro自动生成代码的时候,我们可以在前端界面中明确地知道冗余字段的信息,并根据此信息去生成后台前端的一些特殊界面及操作逻辑(后续会有详细说明)。 #### 代码约定 ##### 后台 1、DO, DTO, VO和Query对象 1)DO,数据对象,与数据库表对应的JavaBean 2)DTO,数据传输对象,service层使用 3)VO,值对象,与视图层对应,把数据返回给视图层 4)Query,查询对象,封装视图层查询数据的对象 *注意一:在返回分页数据时,涉及到`cn.zywork.vo.PagerVO`对象。* *注意二:其中可能会涉及到一些数据的转换,可使用自定义封装的`cn.zywork.common.BeanUtils`工具类。* 2、BaseDAO, BaseService和AbstractBaseService **cn.zywork.dao.BaseDAO** ```java public interface BaseDAO { /** * 添加数据到数据库中 * @param dataObj DO实体类 * @return 返回插入的行数 */ int save(Object dataObj); /** * 批量添加数据到数据库中 * @param dataObjList DO实体类集合 * @return 返回插入的行数 */ int saveBatch(List<Object> dataObjList); /** * 根据主键从数据库中删除数据 * @param id 主键ID * @return 返回删除的行数 */ int removeById(Serializable id); /** * 根据多个主键批量删除数据 * @param ids 多个主键组成的数组 * @return 返回删除的行数 */ int removeByIds(Serializable[] ids); /** * 根据对象更新数据库中的数据 * @param dataObj DO实体类 * @return 返回更新的行数 */ int update(Object dataObj); /** * 根据对象集合批量更新数据库中的数据 * @param dataObjList DO实体类集合 * @return 返回更新的行数 */ int updateBatch(List<Object> dataObjList); /** * 根据主键id查找数据,针对单表查询 * @param id 主键字段值 * @return 主键字段对应记录的DO对象 */ Object getById(Serializable id); /** * 根据主键获取记录版本号 * @param id 主键字段值 * @return 记录版本号 */ Integer getVersionById(Serializable id); /** * 查找所有记录数据 * @return 所有记录数据DO对象组成的List列表 */ List<Object> listAll(); /** * 根据条件查询对象查找符合条件的所有数据 * @param queryObj 条件查询对象 * @return 按照条件的所有数据DO对象组成的List列表 */ List<Object> listAllByCondition(Object queryObj); /** * 根据分页查询对象和条件查询对象查找数据 * @param queryObj 条件查询对象,包括分页数据 * @return 按照条件的分页数据DO对象所组成的List列表 */ List<Object> listPageByCondition(Object queryObj); /** * 根据条件查询对象计数 * @param queryObj 条件查询对象 * @return 按照条件查询对象的记录数 */ Long countByCondition(Object queryObj); } ``` **cn.zywork.service.BaseService** ```java public interface BaseService { /** * 添加数据到数据库中 * @param dataTransferObj DTO数据传输对象 * @return 返回插入的行数 */ int save(Object dataTransferObj); /** * 批量添加数据到数据库中 * @param dataTransferObjList DTO数据传输对象集合 * @return 返回插入的行数 */ int saveBatch(List<Object> dataTransferObjList); /** * 根据主键从数据库中删除数据 * @param id 主键ID * @return 返回删除的行数 */ int removeById(Serializable id); /** * 根据多个主键批量删除数据 * @param ids 多个主键组成的数组 * @return 返回删除的行数 */ int removeByIds(Serializable[] ids); /** * 根据对象更新数据库中的数据<br/> * 为了保证在高并发状态下的数据正确性,使用了version版本号乐观锁机制来控制数据的更新<br/> * @param dataTransferObj DTO数据传输对象 * @return 返回更新的行数。如果数据与原先的记录数据一致,则不更新,返回0;如果版本号小于等于数据库中的版本号,也不更新,返回0 */ int update(Object dataTransferObj); /** * 根据对象集合批量更新数据库中的数据 * @param dataTransferObjList DTO数据传输对象集合 * @return 返回更新的行数 */ int updateBatch(List<Object> dataTransferObjList); /** * 根据主键id查找数据 * @param id 主键字段值 * @return DTO数据传输对象 */ Object getById(Serializable id); /** * 根据主键获取记录版本号 * @param id 主键字段值 * @return 记录版本号 */ Integer getVersionById(Serializable id); /** * 查找所有记录数据 * @return DTO数据传输对象组成的PagerDTO */ PagerDTO listAll(); /** * 根据条件查询对象查找符合条件的所有数据 * @param queryObj 条件查询对象 * @return DTO数据传输对象组成的PagerDTO */ PagerDTO listAllByCondition(Object queryObj); /** * 根据分页查询对象和条件查询对象查找数据 * @param queryObj 条件查询对象,包括分页数据 * @return 按照条件的分页数据DTO对象所组成的PagerDTO */ PagerDTO listPageByCondition(Object queryObj); /** * 根据条件查询对象计数 * @param queryObj 条件查询对象 * @return 按照条件查询对象的记录数 */ Long countByCondition(Object queryObj); } ``` `cn.zywork.service.AbstractBaseService`是`cn.zywork.service.BaseService`接口的抽象实现类,完成了服务层接口的基础逻辑,业务层的CRUD代码可不需要开发人员重复编写,每个Service实现类都会继承自`cn.zywork.service.AbstractBaseService`。 3、Mapper映射文件 Mapper映射文件中的SQL语句,需要注意的如下: 1)version字段实现了乐观锁,`cn.zywork.service.AbstractBaseService`抽象实现类中已经对version字段进行了处理,在update逻辑中version会设置为原始version + 1的值。如果是自己编写update相关代码,需要注意version字段的赋值。 2)select语句条件中增加了`id in (id1, id2, id3...)`的逻辑,方便查询指定的多个id的记录。 3)select语句条件中的`order by`部分,默认会按`update_time desc, create_time desc`排序。 如未指定排序字段,则`update_time desc, create_time desc`,如指定了排序字段则`sortColumn sortOrder, update_time desc, create_time desc`,注意仅支持指定单个排序字段及其排序方式。 4)与DAO接口对应的Mapper映射文件都会自动生成两份,一份为`*DAOMapper.xml`,一份为`*DAOExMapper.xml`。以`DAOExMapper`为后缀的文件表示扩展的Mapper映射,如果我们需要在DAO接口中扩展一些自定义的接口,则可把对应的SQL写到`DAOExMapper`映射文件中,从而不影响由代码生成器生成的`DAOMapper.xml`映射文件。 4、Controller控制器 所有控制器都返回`ResponseStatusVO`实例的`JSON`对象,包含三个属性: ```json { "code": 1001, "message": "添加成功", "data": null } ``` **code**表示返回状态码,全部为自定义状态码: ```java public enum ResponseStatusEnum { OK(1001, "成功"), ERROR(1002, "系统错误"), DATA_ERROR(1003, "参数错误"), AUTHENTICATION_FAILURE(1004, "用户认证失败"), AUTHENTICATION_ERROR(1005, "未认证的用户"), AUTHENTICATION_TOKEN_ERROR(1006, "用户Token错误"), AUTHORIZATION_ERROR(1007, "未授权的用户"), REQUEST_INVALID(1008, "请求不合法"); } ``` **message**表示返回消息,中文,可由开发团队成员自定义。 **data**表示返回数据,对于添加、更新、删除操作,`data`为`null`,对于查询操作, `data`为实际返回的数据,如: ```json { "code": 1001, "message": "查询成功", "data": { "id": 1, "phone": "18888888888", "email": null, "gender": 1 } } ``` 或 ```json { "code": 1001, "message": "分页查询成功", "data": { total: 20, rows: [ { "id": 1, "phone": "18888888888", "email": null, "gender": 1 }, ...... ] } } ```