【开发规范】持续更新中......

目录

一、编码规范

接口规范

命名规范

并发处理

常量定义

事务规范

其他规范

二、异常日志

日志规范


XT开发规范

一、编码规范

接口规范

1、RPC‘接口返回值’以及‘接口入参值’,禁止使用枚举;枚举在你的系统内部使用即可。推荐在这个字段上注释一个枚举类型。【强制】

正例:

 /**  * 类型 {@link com.atta.infra.workorder.api.constrants.WorkOrderType}  */ 
private String type;

2、非查询接口,必须是幂等的。【强制】(幂等定义说明)(幂等返回值方式:告知单号、告知重复,都是成功)幂等ID由上游提供,保证不同业务不同幂等ID。

不考虑并发情况,update本来技术幂等的。根据业务场景,如果是对status的修改,那么修改之前是需要对数据库status进行判断校验的。

3、大数据量查询必须提供分页操作,每页size不超过200条(dba强制:in语句在100条以内)。【强制】(框架解决)

4、浮点数(金额)必须使用BigDecimal,必须使用String构造方法。【强制】(增加案例)

说明:

1)防止精度丢失

5、日元、韩元、台币不允许有小数点。【强制】(补充其他币种:越南币...)

说明:

1)日元、韩元、台币最小单位是元,人民币、美元最小单位是分。

2)日元、韩元、台币,银行系统不支持小数点,会报错。

6、webapp接口,必须做水平权限检查。【强制】

说明:

1)例如:接口是根据id查询业务信息,必须校验id是当前用户的id,否则会出现水平权限漏洞。

正例:
@GetMapping("bankcard/detail")
public bankcardModel getBankcardDetail(@RequestParam String bankcardId){
  BankcardVO bankcardVO = bankcardClient.queryBankcardProfile(bankcardId);
  if(!UserUtils.getFirmId().equals(bankcardVO.getFirmId()){
    //throw 403 异常
  }
  if(UserUtils.getFirmId().equals(bankcardVO.getFirmId())
  return convertVO2Model(bankcardVO);
}
反例:
@GetMapping("bankcard/detail")
public bankcardModel getBankcardDetail(@RequestParam String bankcardId){
  BankcardVO bankcardVO = bankcardClient.queryBankcardProfile(bankcardId);
  return convertVO2Model(bankcardVO);
}

7、敏感数据(手机号、邮箱、企业名称、身份证号、地址等)必须脱敏返回 【强制】(增加安全规范连接)日志里面不能打印用户的信息。打印id即可,然后自己去数据库里面查。手机号、邮箱这种定位到人的,属于敏感;你可以加*来脱敏打印。

说明:可以使用脱敏注解。

8、增删改查,可以提供批量接口的,尽量提供批量接口(批量的size也得有限制)。【推荐】

命名规范

1、数据库表名,必须加数据库缩写前缀。【强制】

说明:可以避免数仓同步数据,表重名问题。

正例:ul_audit_request boss_audit_request 反例:audit_request

2、【强制】抽象类命名使用Abstract或Base开头;异常类命名使用Exception结尾;测试类命名以 它要测试的类的名称开始,以Test结尾。

3、【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。

正例:应用工具类包名为com.alibaba.open.util、类名为MessageUtils(此规则参考spring的框架结构)

4、【推荐】子程序的名字尽可能的表达出所实现的功能,建议是单子程序只实现单功能(单一原则); 而接口的名字需要站在更高的位置,表达出高度的抽象,其名称只需要表达出主业务功能即可。

  • 子程序实现单一功能时,其名称需要具体到所实现的某种功能:如果子程序只是参数组装(填充):子程序名字可以是assem、fill甚至modify这些动词开头;
  • 子程序实现多个功能时/定义接口时,其名称需要抽象表达主业务功能:如果子程序/接口既需要调用api接口,又需要插入数据库,又需要调用第三方接口:子程序名字可以是handle、process等动词开头,以表达更高的抽象。

并发处理

1、线程池队列如果使用LinkedBlockingQueue,必须设置队列大小。【强制】

2、数据库单条更新操作,必须使用version做乐观锁,modified_time也必须更新;批量更新用status做乐观锁,modified_time也必须更新。【强制】

3、日期格式化必须使用XT自己的DateFormatter工具类。SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁。【强制】

4、【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的 处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors返回的线程池对象的弊端如下: 1)FixedThreadPool和SingleThreadPool: 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。 2)CachedThreadPool和ScheduledThreadPool: 允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

常量定义

1、禁止用内部类初始化对象。【强制】

反例:
user.setAddress(new Address(){{
  setStreet("荆州路");
  setCity("上海");
  setProvince("上海");
}})

事务规范

1、本地事务一律使用编程式事务。【强制】

声明式事务不容易引起开发人员的关注;

2、update方法要检查返回值是否是期望的,也必须放在DAO层检查。【强制】

其他规范

1、禁止使用BeanUtils.copyProperties(A,B)或其他类似工具,手写转换方法就好【强制】

2、定时任务的job接口,必须是异步执行。【强制】

二、异常日志

日志规范

1、日志中禁止打印客户的敏感信息(密码、秘钥、手机号、邮箱、身份证号、企业名称、住址等)。【强制】

  1. 推荐:不要打印完整对象
  2. 推荐:按需打印对象中的信息,如:id。

2、禁止使用e.printStackTrace()。【强制】

说明:直接这么用,线上异常栈打不到日志中。

3、system.out.print禁止使用(增)【强制】

4、【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么往上抛。

正例:logger.error(各类参数toString + "_" + e.getMessage(), e);

5、【强制】对trace/debug/info级别的日志输出,必须使用条件输出形式或者使用占位符的方 式。

说明:logger.debug(“Processing trade with id: “ + id + “ symbol: “ + symbol);如果日志级别是warn,上述日志不会打印,但是会执行字符串拼接操作,如果symbol是对象,会执行toString()方法,浪费了系统资源,执行了上述操作,最终日志却没有打印。

正例:(条件)
if (logger.isDebugEnabled()) {
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}
正例:(占位符)
logger.debug("Processing trade with id: {} symbol : {} ", id, symbol);

6、日志一定要加入关键输入/输出参数,不要打印大量无关信息。【推荐】


版权声明:本文为qq_43783527原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。