乐途开发规范(个人学习用)

代码规范

1.编程规约

1.1类名和方法名采用驼峰规范,不含中文信息,纯英文
1.2 抽象类命名以Abstract或Base开头,异常类使用Exception结尾
1.3 布尔类型变量定义不要用is前缀,部分框架会序列化错误
1.4 枚举类名称带Enum后缀,成员名称全大写,多个单词加_下划线
1.5 service/dao层方法命名
   1) 获取单个对象的方法用 get 做前缀。
   2) 获取多个对象的方法用 list 做前缀,复数形式结尾如:listObjects。
   3) 获取统计值的方法用 count 做前缀。
   4) 插入的方法用 save/insert 做前缀。
   5) 删除的方法用 remove/delete 做前缀。
   6) 修改的方法用 update 做前缀。(注批量更新必须清除缓存)
   7) 业务方法execute做前缀
1.6 list remove操作
	Iterator iterator = list.iterator();
	 while (iterator.hasNext()) {
	        String item = iterator.next();
	        if (删除元素的条件) {
	           iterator.remove();
	        }
	   }
1.7 每个类需加swagger注解,用做标识备注。controller,service,model dto vo都得有注解说明
1.8 vo类必须有 @JsonInclude(JsonInclude.Include.NON_NULL) 用做按需返回

2 项目结构规范

project-api: fegin共享项目(供所有项目使用)
project-biz: 业务service代码项目(私有,不能被其他项目引用)
project-controller: 业务暴露接口项目(私有,不能被其他项目引用)
project-entity: 业务实体类(供所有项目使用)
project-server: 启动服务项目(私有,不能被其他项目引用)

3 项目打包流程

1打包core-comon
2打包cloud-common
3打包cloud
注意:在引用重复jar包时,添加<scope>compile</scope> 不然可能会报错
例子:auth-api 很多地方使用,导致会出现重复引用,需要加
  <dependency>            
      <groupId>com.eastfair</groupId>            
      <artifactId>auth-api</artifactId>           
      <version>${project.version}</version>            
      <scope>compile</scope>        
   </dependency>

2.mysql规范

1 建表规约
    1 表达是否是概念的字段,必须使用is_xxx方式命名,数据类型是 tinyint 4位(1 表示是,0 表示否)。
     2 表名和字段名定义两个英文之间用_下划线
    3 禁用保留字,如 desc、range、match、delayed 等
    4 主键索引 pk_字段名, 唯一索引名为 uk_字段名,普通索引 idx_字段名
        pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称
    5 小数类型为 decimal,禁止使用 float 和 double
    6 varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长 度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索 引效率
    7  表的命名最好是加上“业务名称_表的作用”。例子alipay_task / force_project / trade_config
    8 单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表
    9 遇到text大字段的列名,需要拆表,否则影响查询效率
    10 排序字段用sequence ,默认9999
    11 类型字段需要业务标识,如 system_type,auth_type等,不能直接用type
    12 列名值为图片地址,则后缀加 _url
    13 表名和字段名小写
    14 表和字段必须有备注说明
    15 varchar 大小固定定义 8的倍数 32,64,128,256
    16 long 大小固定20
    17 int 大小 11
	18 单表字段数量尽量不超过50
	19 表定义分为基础数据表和业务表,基础数据和业务分离(建议)
	20 表枚举备注:状态 #{normal:正常;wait_init:待初始化;forbidden:禁用;waiting:待审核;refuse:拒绝;delete:已删除}
	21 数据隔离 三个级别:全局,项目,系统
	22 业务表必加字段: tenant_id   project_id   subsystem_id   is_enabled   is_deleted
	全局表:不需要 tenant_id   project_id   subsystem_id
	23 字段注释自动生成menu类
	    例子一次审核状态#{WAIT:等待审核;CHECK:审核中;CHECK_SUCC:审核成功;CHECK_FAIL:审核失败;}
2 查询规约
    1 超过三个表禁止 join
3 业务规约
表必备字段(create_by,update_by,create_time,update_time,tenant_id   project_id   subsystem_id   is_enabled   is_deleted)
表达是否是概念的字段,统一使用前缀is_,数据类型是 tinyint(长度4)。
所有的英文字段,以en_做前缀。
用于存储排序号的字段,命名统一为sequence。(建议)
单表合理规划索引,避免太多索引
定义类型字段,尽量别直接使用type字段,需要标识业务说明,如register_type 注册类型

3.工程结构

1分层领域模型规约(参考):

DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
BO(Business Object):业务对象,由 Service 层输出的封装业务逻辑的对象。
AO(Application Object):应用对象,在 Web 层与 Service 层之间抽象的复用对象模型, 极为贴近展示层,复用度不高
VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止 使用 Map 类来传输。

2 敏捷开发 = 讲故事 + 编码 + 发布。

敏捷开发是快速交付迭代可用的系统,省略多余的设计方案,摒弃传统的审批流程,但 核心关键点上的必要设计和文档沉淀是需要的

3 系统设计主要目的是明确需求、理顺逻辑、后期维护,次要目的用于指导编码(代码易读指导)。

4 文档:业务场景解决方案,系统交互,技术阐述,性能评估和使用量支撑(待评估)

4.接口请求规范

1 http请求得header头参数

1 token: Bearer +token值
2 gray-version:灰度版本
3 system-scene: 系统组织type  P:厦门智慧场馆;P_VENUE:场馆业务管理系统;P_CERTIFICATE:证件管理系统;P_COMPOSITE_VENUE:综合-场馆管理;
5 language-type: 语言 EN,CN
6 terminal-device  请求来源版本信息
7 terminal-type 请求来源类型 (web/app/mini_programs)

2http内部传输

trace 全链路id

5.项目程序规范梳理

1代码自动生成规范
通过ef-generator工具自动生成代码,直接到项目里
生成代码文件有:
1controller   存在忽略
2 service     存在忽略
3serviceimpl  存在忽略
4dao           覆盖
5mapper.xml  覆盖
6entity        覆盖
7saveDto    覆盖
8updateDto  覆盖
9pageQuery  覆盖
10 DTo 业务DTO 存在忽略
涉及覆盖的:不得修改,不得污染原有项目的规范,不得追加字段以外的业务属性
存在忽略: 可修改,可添加业务属性和代码
DTO和VO类后缀都是大写
1 状态枚举尽可能用英文,这样生成枚举类方便使用
2接口名字定义规范
入参DTO,出参VO
查询接口名字:get,query,find 打头
修改接口名字:update,execute打头
添加接口名字:save,add,execute打头
删除接口名字:del,delete,remove打头
组装数据接口:assemble打头
该名字用于以后规范模块使用,如缓存策略
重要:
1尽量别在mapper.xml那写sql,需要单独新建个buiness.mapper,不然会被覆盖。这样代码生成工具能一直使用
2尽量不要自己私下创建枚举类,使用工具,在字段注释加内容即可生成
     例子一次审核状态#{WAIT:等待审核;CHECK:审核中;CHECK_SUCC:审核成功;CHECK_FAIL:审核失败;}
     ![在这里插入图片描述](https://img-blog.csdnimg.cn/c3e182674b2e44f1b5aa4ebbd47a3f2b.jpeg#pic_center)
3代码功能规范:
1 当遇到单表where查询时,可以在service写个公共方法 listOfOrganization
 
用做以后的缓存规范,大家尽可能把需要的querywrapper代码写到一个地方
2service必须有handlerSave方法
该方法里面需要有这两行,做数据分层用
model.setSubsystemId(ContextUtil.getSubSystemId());
model.setTenantId(ContextUtil.getTenantId());
3每个方法代码尽可能不超过50行,但特殊情况例外
4任何新增,修改,删除方法,不允许写自定义sql来完成,这样没法监控而且导致缓存数据不一致
查询可以用自定义sql,当总而言之,尽可能少写sql,除非业务有聚合查询和特殊需求。
5 jar包版本写在一个地方
6 数据尽量统一逻辑删除,尽量别物理删除
7 禁止feign内部调用返回结果使用IPage或Page,这样需要处理JSON转Object,
第一:改变了数据结构
第二:转Object类无法明确,只能转Jsonobject,依旧改变了数据结构
第二:枚举和localdatetime需要自行处理,目前只发现这两个数据类型需要处理,并且不能代表其他数据结构是否也存在此问题
8 controller方法需写上public,不然扫描不到,这个细节需注意

4分库分表规范

生成雪花id,使用sharding框架,对project_id定义进行分库分表规则
雪花id:64位bit。sign(1bit) |     date seconds(41bit)    | dbSuffix id(6bit)  |  tableSuffix id(6bit)  | sequence(10bit)
sign:标识位
date seconds:时间戳
workid:workid 9位数
dbSuffix:库后缀名(1-63个数字)
sequence:序列号
注:不需要分表分库的表放到pulic库,需要分库分表的放imaster_1和imaster_2库

5.token 规范

使用jwt加密,pc,app,小程序各端一份独立token,token只存放用户数据,不存放业务数据

6.dto校验

controller加自定义校验标识进行校验

7.vo按需返回

准备工作:
1 DynamicPackage 添加扫描包名
2VoGroupClass 继承BaseVoGroupClass  vo标识
3 @BaseControler自定义注解
4标签类放在demand包里
5 @JsonInclude(JsonInclude.Include.NON_NULL)
@VoInfo(groups = {VoGroupClass.all.class})
@VoInfo(groups = {VoGroupClass.findAllOrganization.class})
controller加自定义返回注解,进行按需返回。
入参属性名根据驼峰标识,第一个字母小写
6字典使用自定义注解
   @DictionaryField(type="CONTACT_GENDER")
   private String gender;

8.接口返回类R

1:成功
负数:接口错误,如入参错误,异常报错等
正数:接口正常,业务出错。如未填写姓名等
分页:IPage

9.接口角色校验规范

由于加注解麻烦,现在统一加到菜单里,只加一遍。没必要额外写一个标识,让前后端都对上

10redis 数据操作规范

定义key必须使用类,如PermissionCacheKeyBuilder,该类必须实现CacheKeyBuilder
CacheKey cacheKey = new PermissionCacheKeyBuilder().key(userId);
List<String> permissionList = cacheOps.get(cacheKey);
互动值正在开发:如点击量,游览量,top10排序等。不需要在mysql做开发

11数据字典

屬性的Text
字典c_dictionary表
@DictionaryField(type="ENABLED_LIST")
/**
* 证件类型:#{CAR:车证;PEOPLE:人证;POWER:机力车证;OTHER:其他证;}
*/
@ApiModelProperty(value = "证件类型:#{CAR:车证;PEOPLE:人证;POWER:机力车证;OTHER:其他证;}")
@JsonInclude(JsonInclude.Include.NON_NULL)
@DictionaryField(type = "CERTIFICATE_TYPE")
private CertificateCertificateTypeEnum certificateType;
用法:
1vo继承BaseVO

12代码书写规范

1 方法里需要加注释
2 每个接口必须提供接口注释
3 每个私有方法必须有注释
4 接口说明用swagger
5接口文档用apifor方便统一

13 分布式事务

目前实现:AT和半事务模式
核心流程:AT:采用seata集成,执行事务,有错误及时回滚,但长事务会锁住资源,适用于并发不高的场景
                  mq半事务:通过rocketmq集成,执行事务,出现错误则停止,迅速发邮件告知,同时快速通过链路id找到相应全量日志。(目前日志服务还在开发中)
 
使用:
    AT:发放中加自定义注解 @GlobalTransactional(name = "createExhibition", rollbackFor = Exception.class)
半事务:见point项目的例子,通过队列来完成,队列确保消息传播事务一致性,代码问题由技术自己处理。

14数据权限

通过创建用户id和组织code来实现数据权限
业务表追加org_code 字段,
角色:1仅个人
            2同组织
            3同组织及子组织
            4全部
15swagger 编写规范
controller类: @Api(value = "UserAccount", tags = "用户账户信息")
vo,dto:
@ApiModel(value = "RoleDTO", description = "")
@ApiModelProperty(value = "主键")
暂时没用到!

15 缓存策略

采用aop+cache完成,由于时间原因目前还没做参数EL精准匹配,目前参数都是hash。
搭配cache组件使用,在灯灯基础上进行开发。
注:
1 只缓存干净数据,不缓存业务代码
2 同类的方法不拦截,需要处理
((OrganizationService) AopContext.currentProxy()).queryAllOfCode(organ
3 参数在方法里不得修改,这样导致引用值变化,缓存精准失效
缓存数据:全表数据,关联关系数据,方法缓存
扫描类:
execution(* com.eastfair..service...(..))
execution( com.eastfair..handler...(..))
execution( com.eastfair..manager...*(..)
使用:
1 serviceimpl必须继承SuperCacheServiceImpl
2 类里需指正cachekey。追加这两行代码
public CacheKeyBuilder getCacheKeyBuilder() {
return cacheKeyBuilder;
}
3 service启动项目里的config加CacheConfigure类,用于启动缓存功能
4 适应cache自定义注解
@Cache(expire = 60,  key = "#hash(#args)")
@Cache( key = "#hash(#args)")
expire:缓存时间,秒。不填则24小时
key:hash值,以后在做指定参数完善
目前已经支持magic精准模式,
@Cache(key = "",
magic = @Magic(
key = "{" +
"{'languageType', #args[0]?.languageType}," +
"{'fieldName', #args[0]?.fieldName}," +
"{'fieldEnName', #args[0]?.fieldEnName}," +
"{'keyword', #args[0]?.keyword}," +
"{'fieldType', #args[0]?.fieldType}," +
"{'groupId', #args[0]?.groupId}," +
"{'fieldTypeAttribute', #args[0]?.fieldTypeAttribute}," +
"{'fieldTypeAttributeValue', #args[0]?.fieldTypeAttributeValue}," +
"{'placeholder', #args[0]?.placeholder}," +
"{'enPlaceholder', #args[0]?.enPlaceholder}," +
"{'fieldDesc', #args[0]?.fieldDesc}," +
"{'fieldEnDesc', #args[0]?.fieldEnDesc}," +
"{'inputLength', #args[0]?.inputLength}," +
"{'inputWidth', #args[0]?.inputWidth}," +
"{'sequence', #args[0]?.sequence}," +
"{'legend', #args[0]?.legend}," +
"{'legendDesc', #args[0]?.legendDesc}," +
"{'legendEnDesc', #args[0]?.legendEnDesc}," +
"{'isEdit', #args[0]?.isEdit}," +
"{'isRequired', #args[0]?.isRequired}," +
"{'isMultiple', #args[0]?.isMultiple}," +
"{'isVerification', #args[0]?.isVerification}," +
"{'verificationKeyword', #args[0]?.verificationKeyword}," +
"{'relatedId', #args[0]?.relatedId}," +
"{'id', #args[1]}" +
"}"))
对eq查询的数据进行精准缓存,这样新增数据就不会全量删除缓存
5返回类型只支持以下集中
Object
list<Object>
Ipage<list<Object>>
Page<list<Object>>
R<Ipage<list<Object>>>
6已有部分:
1数据结构和大致组件装配
2接口缓存功能
3删除缓存关系功能
4数据唯一性
7目前没有的部分:
1双写问题
2数据一致性
3穿透处理
4删除缓存失败重试
5自动加载
6 el表达式精准数据
7 缓存参数判断,避免没有覆盖的参数进入缓存

16 自定义异常

service层统一写成抛异常,不用写R。之前写的就算了。
BizException:业务异常。
直接抛。如A调用B,B失败,A感知异常,AB抛异常
NoBackBizException:不会滚业务异常,不影响调用方。(例:电商流程,生成订单并支付,金额不足,不会影响已经生成的订单)
1.A调用B,B失败,A可自行判断是否回滚。前提:B必须返回类型是R
2.前端直接调用B,B失败,则正常回滚报错

17 数据隔离规范

1系统设置隔离级别能默认系统下的表统一隔离级别
2如果系统下有表需要不同的隔离级别,在TenantIgnoreTable设置
3基础服务可设置为不限制隔离级别,由调用方来确定
如:auth属于:不检索隔离级别
订单服务:不检索隔离级别
综合服务:系统隔离级别
搭建商服务:团队隔离级别

18 rocketmq使用规范

1只关注service事件处理层
2一切tag标识写在public-cmmon里,方便大家一块使用
用法:
1设置服务的mq tag标识配置。用于快速找到属于自己的tag消息
com:
eastfair:
systemScene: false
#mq tag 标识
mqSystem: auth
2添加配置文件,来启动mq 监听
@Component
public class RocketMqConfiguration {
@Bean
@ConditionalOnMissingBean(BusinessPointListner.class)
@ConditionalOnBean({ChangeSelectorExpressionBeforeMqStartOfBusiness.class})
public BusinessPointListner getBusinessPointListner() {
    return new BusinessPointListner();
}
}
3数据的业务梳理
新增类 继承 AbstractPointService 实现BusinessPointService
@Service
public class BusinessPointServiceImpl extends AbstractPointService implements BusinessPointService {
@Autowired
private CachePlusOps cachePlusOps;
@Autowired
private OrganizationService organizationService;

@Autowired
UserAccountService userAccountService;

@Override
public void executeBusin(MySimpleMessage mySimpleMessage) {
    log.info("BusinessPointServiceImpl is execute message is " + JSONObject.toJSONString(mySimpleMessage));
    log.info("" + ContextUtil.getProjectId());

19 公共服务实现步骤

个人理解规范:
1只提供公共功能,是给所有业务使用的,而不是给指定业务使用(软件即服务)
2尽量避免重复造轮子
3业务系统能快速直接使用公共服务,在同体系下,避免或减少以接口文档的方式进行对接,导致对接工作量大(极个别除外)
4数据归属于调用方
5数据需要满足多租户数据隔离机制,不同调用方的数据需要隔离
6数据隔离需要分简单等级:团队只能看自己数据  | 系统运营人员能看该系统所有团队的数据   |   超级管理员能看所有数据
7与saas思想类似,但对特定业务需求需要单独编写
实现逻辑:通过mybatis plus多租户的方式来进行开发
准备工作:
1涉及的表加4个字段 tenant_id,  team_id, subsystem_id, project_id
2涉及的服务需要告知是系统级别,还是团队级别。如主办系统是主办自己使用,那是系统级别,搭建商系统是给公司使用,那是团队级别
做好上诉准备工作,则能自动实现上述功能,大家只需要关注业务开发即可。
场景:已在制证服务中使用
特殊说明:如遇到特殊服务需要查看所有数据,通过切面来完成。如(主场能看到制证所有数据)
特别注意提示:业务调用需要注意,不然会出现调用混乱的问题
调用链:服务A --->  服务B ----> 基础服务
逻辑:服务A去拿服务B的数据,由于数据属于服务B,所以数据归属于服务B,那么数据的subsystemid是服务B
调用问题描述1:服务A调用服务B,获取服务A的账号信息
结论:去服务B拿属于服务A的数据,这个逻辑很奇怪,毕竟服务B不是基础服务

20ToC端理解

 1接口需尽量在100毫秒内返回
 2对第三方接口规范如下
     异步接口可以放后端(如短信)
     实时同步接口除非有特别敏感数据,一般都会影响性能需要放置前端。一般第三方接口相应都在100毫秒以内,即使很快了,但这样增加了自己接口的时间,导致并发上不去
            支付接口(后端)
            图片上传(前端)
            二要素(前端)

21 如何快速搭建新服务

前置条件:
1需要在灯灯的技术体系下
2网关等基础组件需要存在
3项目的结构需要和灯灯保持一致
4公用一个用户中心
实现逻辑:通过系统标识的方式来实现快速搭建新服务
准备工作:
1需要添加系统标识,在c_data_isolation表里
2需要初始化组织和管理员角色
做好上诉准备工作,则通过调用接口的方式一键生成新服务。
接口名createAdminProject,如果已存在服务则过滤,不存在则新建服务
增值服务:
1使用基础组件:在server层写上相应的注入代码
2使用公共服务:前端直接复制页面代码即可使用(大部分情况是这样,极个别除外)

22 永久token

永久token
@Value("${com.eastfair.permanent_token}")
private String permanentToken;
需要设置系统标识
系统标识类:DataIsolationSystemSceneEnum
赋值系统
ContextUtil.setSystemScene(DataIsolationSystemSceneEnum.P_VENUE);

23 纵向越权,接口权限控制

数据格式:  项目标识+系统标识+接口url
例子: mice:ef-auth:/userAccount/getByIdLead
规则:
超级管理员:使用全量接口 mice:**
系统管理员:使用基础服务接口+该系统接口
基础服务:mice:ef-messagecore:**
系统接口:mice:mice-certificate:**
普通员工:通过菜单关联接口地址来获得接口权限
校验规则:
1feign内部调用不校验
2定时器调用不校验
3没有用户信息不校验
4网关过滤的rul不校验
5部分公共服务不校验
6第三方系统不校验
做法:
菜单填写接口地址

24 数据脱敏加密

直接设置字段属性名即可
#脱敏设置
desensitizer:
#是否脱敏
enabled: false
#身份证脱敏字段
idCarFiles:
idCard
id-card
#手机号脱敏字段
mobileFiles:
mobile
#邮箱脱敏字段
emailFiles:
email
#名字脱敏
nameFiles:
#跳过脱敏的url
excludeUrl:
./orc/.
./oauth/getClickApplicationInfo
./epUiImgSaveAndAnalysisMultipartFile
./autoKeyword,./common/defaultKaptcha
./fastdfs/.
./eempFastdfs/.
./wikipediaApi/.
.*/homepage/preview
/certificate/people/pageVo
/home/homeRegister/registerHomeUser
#/auth/userAccount/login

25 数据库加密

数据库加密:采用非对称加密方式加密,用的mybatis plus工具
做法:配置typeHandler = M9EncryptHandler.class
例子
entity类:
@TableName(value ="vb_contact", autoResultMap = true)
/**
* 手机号
*/
@ApiModelProperty(value = "手机号")
@Size(max = 16, message = "手机号长度不能超过16")
@TableField(value = "mobile", condition = LIKE, typeHandler = M9EncryptHandler.class)
@Excel(name = "手机号")
private String mobile;
xml类
<result column="mobile" jdbcType="VARCHAR" property="mobile"  typeHandler="com.eastfair.database.mybatis.typehandler.M9EncryptHandler"/>
要点:
1如果是自定义sql,mybatis plus没法拦截,需要手动解密
代码如下:RSAEncrypt.decrypt("值", M9EncryptHandler.privateKey);
2 queryWrapper 使用lambda表达式无法自动加解密
不使用lambda表达式是支持自动加密:如 Wraps.q(model, params.getExtra(), getEntityClass());这种没有lambda
https://blog.csdn.net/qq_33204709/article/details/129178188
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值