前置条件
要求版本 2.1.0.0
及以上
<parent>
<groupId>cn.flyrise</groupId>
<artifactId>pai</artifactId>
<version>2.1.0.0</version>
<relativePath/>
</parent>
引用依赖
<parent>
<groupId>cn.flyrise</groupId>
<artifactId>pai-common-i18nkit</artifactId>
</parent>
定义 i18n 资源
储存目录:src\main\resources\i18n
文件夹里包含多个资源文件:
- messages.properties
- messages_en_US.properties
- messages_zh_CN.properties
- messages_zh_TW.properties
注意事项
1.其中 messages.properties 是个默认文件,即使为空也要有,否则MessageSource加会变成[Empty MessageSource]
2.切记切记所有文件编码必需是UTF-8
格式
配置application.yaml
spring:
messages:
#相对路径 开头请勿添加斜杠, 注意要放一个messages.properties 否则会加载失败,建议直接把这个当成中文来用
basename: i18n/messages
encoding: UTF-8
定义错误常量类
src\main\java\cn\flyrise\pai\demo\exception\BusinessErrors.java
划重点:
示例1:public static final Message ERR_1001 = Message.create(“walking”, “1001”, “{}数据已存在”)
示例2:public static final Message ERR_1001 = Message.create(“1001”, “{}数据已存在”)
- 1001 :错误代码,取值范围 1001-2999 (与 VO的InvalidCode 3001-9999校验区分开,如业务特别复杂可以考虑升至5位各自独立编码范围段)
- 解析到资源文件,
- 示例1:message.walking.1001={}数据已存在
- 示例2:message.code.1001={}数据已存在
- {}数据已存在 :即是描述信息也是默认信息,当 message.walking.1001 在相应的资源文件中找不到时使用
- biz.data.exist :对比应 messages.properties 中定义的Key
import cn.flyrise.common.core.domain.Message;
/**
* 业务错误代码定义枚举类,错误代码 1001-2999
*
* @author Joe
* @date 2020/5/8 星期五 16:30
*/
public class BusinessErrors {
private BusinessErrors() {
}
private static final String WALKING = "walking";
private static final String EXCEL = "excel";
// 通用编码示例(取值全局1001~2999)
// message.code.1001
public static final Message ERR_1001 = Message.create("1001", "{}数据不存在");
// message.walking.1002
public static final Message ERR_1002 = Message.create(WALKING, "1002", "车牌号格式不符");
// 按模块编码(取值全局1001~2999)
// message.walking.1001...2999
public static final Message ERR_WALKING_1001 = Message.create(WALKING, "1001", "{}数据已存在");
public static final Message ERR_WALKING_1002 = Message.create(WALKING, "1002", "车牌号格式不符");
// message.excel.1001...2999
public static final Message ERR_WALKING_1001 = Message.create(EXCEL, "1001", "{}数据已存在");
public static final Message ERR_WALKING_1002 = Message.create(EXCEL, "1002", "格式不符");
//... 其他错误码的定义
}
定义业务异常类
要求每个业务工程有一个独立的业务异常类,继承于BizException
主要用于Feign调用时返回异常信息有助于开发人员识别错误来源
/**
* 演示模块异常类
*
* @author Joe
* @date 2020/5/8 星期五 16:30
*/
public class DemoBizException extends BizException {
public DemoBizException(Message error, Object... params) {
super(error.getCode(), error.format(params));
}
}
业务逻辑中使用常量
@Service
public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserPO, UserVO> implements IUserService {
@Autowired
UserMapper userMapper;
@Override
public boolean insert(UserPO userPO) {
if (StrUtil.isNotEmpty(userPO.getId()) && userMapper.selectById(userPO.getId()) != null) {
// 使用常量抛异常
new DemoBizException(BusinessErrors.ERR_1001, userPO.getId());
}
return SqlHelper.retBool(userMapper.insert(userPO));
}
}
数据对象(VO)中使用
划重点:
通过注解 @InvalidCode 实现错误消息使用
value: 错误编码,取值范围 3001-9999 (建议取3001-9999开始,与 常量类1001-2999 区分开)
message: 错误消息表达式,用 {} 包起来,Key为 messages.properties 定义(弃用)
更新说明:
2021/01/04 为了简化编Key的过程,message 默认为空,即得默认值为 invalid.code.xxxx(xxxx为value的数值型错误代码), properties中对应的配置为 invalid.code.xxxx=内容desc: 错误消息的描述,当没有在messages.properties中定义时,会作为默认消息
UserVO.java
@ApiModel
public class UserVO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("主键")
private String id;
@ApiModelProperty("账号")
@Mapping("name")
@NotBlank
@InvalidCode(value = "3001", message = "{view.user.3001}", desc = "账号")
public String name;
@ApiModelProperty("密码")
@NotNull
@Size(min = 6, max = 16)
@InvalidCode(value = "3002", desc = "密码")
protected String password;
}
解释一下:
@InvalidCode(value = “3001”, message = “{view.user.3001}”, desc = “账号”)
@InvalidCode(value = “3002”, desc = “密码”)
对应的分别是:
view.user.3001=账号
invalid.code.3002=密码
意思就是:开发者定义了message属性就以定义的为准,没有则默认为 invalid.code.编号
控制层(Controller)
@ApiOperation("修改用户信息")
@PostMapping("/user/info/update")
public Reply<?> updateUserInfo(@RequestBody @Valid UserVO userVO)
开发工具包 I18nKit
String packageName = "cn.flyrise.pai.xxx";
// 1.针对所有使用了@ApiModelProperty 或 @InvalidCode 且有使用@NotNull等验证规则的源码类按全路径名称升序排序后按指定的起始编号(不含)重新编码
I18nKit.renewAllWithAnnotation(ApiModelProperty.class, "cn.flyrise.xxx.model.vo");
I18nKit.renewAllWithAnnotation(ApiModelProperty.class, "cn.flyrise.xxx.model.vo", 3000);
I18nKit.renewAllWithAnnotation(InvalidCode.class, "cn.flyrise.xxx.model.vo");
I18nKit.renewAllWithAnnotation(InvalidCode.class, "cn.flyrise.xxx.model.vo", 3000);
// 2.针对所有使用了@ApiModelProperty 或 @InvalidCode 且有使用@NotNull等验证规则的源码类按全路径名称升序排序后按最大的编号(不含)编码
I18nKit.fixAddWithAnnotation(ApiModelProperty.class, "cn.flyrise.xxx.model.vo");
I18nKit.fixAddWithAnnotation(InvalidCode.class, "cn.flyrise.xxx.model.vo");
// 3. 扫描生成国际化配置(使用了@InvalidCode的源代码生成配置)
for (String s : genI18nForInvalidCode(packageName)) {
System.out.println(s);
}
// 4. 扫描生成国际化配置(常量类的源代码生成配置,如: BusinessErrors.class)
for (String s : genI18nForConstants(BusinessErrors.class)) {
System.out.println(s);
}
注: 以上示例中 3 不能与 1或2 同时执行,同时要求VP和常量类按以上文规则方可实现扫描生成