[新增功能](dev): 初始化提交
This commit is contained in:
parent
f358bf9e5b
commit
5e34c0c0b9
102
.gitignore
vendored
102
.gitignore
vendored
@ -1,3 +1,102 @@
|
||||
# ---> Java
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
# ---> JetBrains
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
# ---> Maven
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
@ -9,3 +108,6 @@ buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
/.idea/
|
||||
/.fastRequest/
|
394
README.md
Normal file
394
README.md
Normal file
@ -0,0 +1,394 @@
|
||||
# dict-trans
|
||||
|
||||
## 前言
|
||||
|
||||
简单的字典翻译组件
|
||||
|
||||
## 功能
|
||||
|
||||
* [x] 字典翻译
|
||||
* [x] 枚举翻译
|
||||
* [x] 数据库翻译
|
||||
* [x] 自定义翻译
|
||||
* [x] 翻译结果脱敏
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
EasyTranslate
|
||||
└── src
|
||||
├── main
|
||||
│ └── java
|
||||
│ └── com
|
||||
│ └── aizuda
|
||||
│ └── trans
|
||||
│ ├── annotation 翻译注解
|
||||
│ │ ├── Dictionary.java 字典注解,标识在字典数据类上(自动生成查询 SQL)
|
||||
│ │ ├── Translate.java 翻译字段注解,标识在需要翻译的字段上
|
||||
│ │ └── Translator.java 翻译方法注解,对方法返回值进行翻译
|
||||
│ ├── aspect
|
||||
│ │ └── TranslateAspect.java 翻译切面
|
||||
│ ├── config
|
||||
│ │ └── TranslatorConfig.java 默认翻译方法注入配置
|
||||
│ ├── constants 常量配置
|
||||
│ │ └── DesensitizedTypeConstants.java
|
||||
│ ├── desensitized 脱敏相关
|
||||
│ │ ├── Desensitized.java
|
||||
│ │ └── IDesensitized.java
|
||||
│ ├── dict 数据字典相关
|
||||
│ │ └── DictTranslate.java
|
||||
│ ├── enums 枚举相关
|
||||
│ │ ├── EnumPool.java
|
||||
│ │ ├── FormatType.java
|
||||
│ │ └── IEnum.java
|
||||
│ ├── service
|
||||
│ │ ├── DictTranslateService.java 字典翻译接口
|
||||
│ │ ├── impl
|
||||
│ │ │ ├── DefaultDictTranslateServiceImpl.java 默认数据字典翻译实现(实现字典翻译接口。仿照该方法,实现自己的业务)
|
||||
│ │ │ ├── DictCacheTranslator.java 数据字典翻译实现(调用 字典翻译接口实现)
|
||||
│ │ │ ├── DataBaseTranslator.java 数据库翻译服务
|
||||
│ │ │ ├── DesensitizedTranslator.java 脱敏实现(没啥操作,就返回原值)
|
||||
│ │ │ ├── EnumTranslator.java 枚举翻译实现
|
||||
│ │ │ └── TranslatorHandle.java 翻译操作类
|
||||
│ │ └── Translatable.java 翻译接口(字典、枚举、....接实现该接口)
|
||||
│ ├── TranslatorBootApplication.java
|
||||
│ └── util 一些工具类
|
||||
│ ├── LambdaUtil.java
|
||||
│ └── NameUtil.java
|
||||
└── test demo演示
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 引入jar包
|
||||
|
||||
```xml
|
||||
<!-- 自行编译 -->
|
||||
<dependency>
|
||||
<groupId>com.aizuda</groupId>
|
||||
<artifactId>dict-trans</artifactId>
|
||||
<version>${latestVersion}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool工具类 -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据库 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 配置
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowMultiQueries=true&useSSL=false&rewriteBatchedStatements=true
|
||||
username: root
|
||||
password: 123456
|
||||
```
|
||||
|
||||
### 注解说明
|
||||
|
||||
#### @Dictionary
|
||||
|
||||
> 需配合 `@Translate` 、`@Translator` 食用
|
||||
|
||||
说明:标识在字典数据类上(自动生成查询 SQL),**只能用在类上**
|
||||
|
||||
参数:
|
||||
* table:字典表名,为空时取 `TableName` ,取不到则取 类名(驼峰转大写下划线)
|
||||
* codeColumn:字典编码的属性,对应 查询条件的列字段(需要的是表字段)
|
||||
* textColumn:字典值的列名,对应 要查询的字段(需要的是表字段)
|
||||
* groupColumn:字典组别属性,对应 要查询的字段(需要的是表字段,某些字典可能会需要根据某个类别划分,再进行翻译,如静态字典中的DICT_ID)
|
||||
* translator:自定义翻译方法,遇到特殊的翻译场景可自定义翻译实现,需要自行编写实现类实现Translatable接口并实现翻译方法,程序将使用该方法进行翻译,该注解中的所有配置信息将传递到实现方法中
|
||||
|
||||
示例:
|
||||
```java
|
||||
// =========================== 示例1 ===========================
|
||||
/**
|
||||
* 用户(数据库)
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 016 14:07:27
|
||||
*/
|
||||
@Dictionary(codeColumn = "id", textColumn = {"user_name"})
|
||||
@TableName("sys_user")
|
||||
public class UserDB {
|
||||
|
||||
@TableId(value = "id", type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
|
||||
@TableField(value = "user_name")
|
||||
private String name;
|
||||
|
||||
}
|
||||
|
||||
// =========================== 示例2 ===========================
|
||||
/**
|
||||
* 人
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 016 11:40:30
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class People {
|
||||
|
||||
@Translate(dictClass = Dict.class, groupValue = "sex", translateField = "sexName")
|
||||
private String sex;
|
||||
|
||||
private String sexName;
|
||||
|
||||
@Translate(dictClass = Desensitized.class, translateField = "phone", desensitizedModel = DesensitizedTypeConstants.MOBILE_PHONE)
|
||||
private String phone;
|
||||
|
||||
@Translate(dictionary = @Dictionary(translator = CustomerTranslateServiceImpl.class), translateField = "name")
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### @Translate
|
||||
|
||||
> 需配合 `@Dictionary` 、`@Translator` 食用
|
||||
|
||||
说明:标识在需要翻译的字段上,**只能用在字段上**
|
||||
|
||||
参数:
|
||||
* dictClass:字典配置类,指定的 class 上必须是 `DictTranslate.class` 实现类 、 `IEnum` 接口的实现类 、 `Desensitized` 或者是 `@Dictionary` 注解;
|
||||
* translateField:翻译后的属性名,注意使用驼峰命名,默认为原属性名去除末尾的 "Id" 和 "Code" 再接上 "Name";
|
||||
* groupValue:组属性值,在静态字典表这种拥有组属性的字典中需要手动传入一个定值(即:字典分组的 code);
|
||||
* dictionary:指定 `Dictionary` 并设置其属性,将覆盖 `dictClass` 上的 `Dictionary` 注解的配置,指定了该属性后也可不指定 `dictClass` ,一般情况下不会使用;
|
||||
* conditionField:指定判断条件字段(仅自定义翻译实现时用来进行判断);
|
||||
* desensitizedModel:脱敏模型,用来给数据脱敏打 `*` 使用。常见模型在 `DesensitizedTypeConstants` 常量中。也可自定义,格式:`{含开始位置,含结束位置}` ,举例:`{1,2}` ;
|
||||
|
||||
*注:字段自身脱敏时,需将 `dictClass` 设置为 `Desensitized.class`(此时字段仅返回原值后脱敏。可与翻译共用,那样先翻译后脱敏。)*
|
||||
|
||||
* value:dictClass的别名,当只需要配置 `dictClass` 时,可以简写成 `@Translate(XX.class)` ;
|
||||
|
||||
示例:
|
||||
```java
|
||||
// =========================== 示例1 字典 ===========================
|
||||
|
||||
// 1.1 字典 code(需要在实现字典翻译服务时自己实现)
|
||||
|
||||
@Translate(dictClass = Dict.class, groupValue = "sex", translateField = "sexName")
|
||||
private String sex;
|
||||
|
||||
private String sexName;
|
||||
|
||||
// 1.2 自定义完整字典(需要在实现字典翻译服务时自己实现)
|
||||
|
||||
@Translate(dictClass = Dict.class, groupValue = "{男:1;女:2;}", translateField = "sexName")
|
||||
private String sex;
|
||||
|
||||
private String sexName;
|
||||
|
||||
// =========================== 示例2 数据库 ===========================
|
||||
|
||||
// 2.1. 字典定义
|
||||
@Dictionary(codeColumn = "id", textColumn = {"user_name"})
|
||||
@TableName("sys_user")
|
||||
// 省略 @Data 等
|
||||
// ....
|
||||
public class UserDB {
|
||||
}
|
||||
|
||||
// 2.2. 字段翻译
|
||||
@Translate(dictClass = UserDB.class, translateField = "name")
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
// =========================== 示例3 枚举 ===========================
|
||||
|
||||
// 3.1. 枚举定义
|
||||
public interface MyDict {
|
||||
/**
|
||||
* 示例1:性别枚举
|
||||
*/
|
||||
enum SexDict implements IDict {
|
||||
//
|
||||
MALE("0", "男"),
|
||||
FEMALE("1", "女");
|
||||
|
||||
SexDict(String code, String text) {
|
||||
// 构造方法中只需要调用接口的init方法即可,省略了属性的定义和赋值,也不用定义累赘的get方法
|
||||
init(code, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3.2. 枚举翻译
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
@Translate(MyDict.SexDict.class)
|
||||
private String sex;
|
||||
|
||||
private String sexName;
|
||||
|
||||
// =========================== 示例4 脱敏 ===========================
|
||||
|
||||
// 4.1 字段给自身脱敏
|
||||
@ApiModelProperty("联系电话")
|
||||
@Translate(dictClass = Desensitized.class, desensitizedModel = DesensitizedTypeConstants.MOBILE_PHONE, translateField = "phone")
|
||||
private String phone;
|
||||
|
||||
// 4.2 字段翻译并脱敏
|
||||
@ApiModelProperty("用户等级")
|
||||
@Translate(dictClass = Dict.class, groupValue = DictConstants.APP_USER_LEVEL, translateField = "userLevelName", desensitizedModel = "{0,1}")
|
||||
private String userLevel;
|
||||
|
||||
@ApiModelProperty("用户等级中文")
|
||||
private String userLevelName;
|
||||
|
||||
// =========================== 示例5 自定义 ===========================
|
||||
|
||||
// 5.1 翻译字段
|
||||
@Translate(dictionary = @Dictionary(translator = CustomerTranslateServiceImpl.class), translateField = "name")
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
// 5.2 自定义翻译方法
|
||||
@Component
|
||||
public class CustomerTranslateServiceImpl implements Translatable {
|
||||
|
||||
@Override
|
||||
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig,
|
||||
Class dictClass) {
|
||||
List<String> rList = new ArrayList<String>(1);
|
||||
if (StrUtil.equals(origin, "1")) {
|
||||
rList.add("结果1");
|
||||
} else {
|
||||
rList.add("结果2");
|
||||
}
|
||||
return rList;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// =========================== 示例6 数据库查询多字段进行字段映射 ===========================
|
||||
|
||||
// 6.1 数据库字典类
|
||||
@Dictionary(codeColumn = "id", textColumn = {"user_name", "real_name"})
|
||||
@TableName("sys_user")
|
||||
// 省略 @Data 等
|
||||
// ....
|
||||
public class UserDB {
|
||||
}
|
||||
|
||||
|
||||
// 6.2 翻译字段
|
||||
// 注意:translateField 需要跟 textColumn 的下标一一对应!!!
|
||||
@Translate(dictClass = UserDB.class, translateField = {"zh", "zsxm"})
|
||||
private String id;
|
||||
|
||||
private String zh;
|
||||
|
||||
private String zsxm;
|
||||
|
||||
```
|
||||
|
||||
#### @Translator
|
||||
|
||||
> 需配合 `@Dictionary` 、`@Translate` 食用
|
||||
>
|
||||
> 方法返回类型支持 `Map` 、 `Entity` 、 `List` 、 `IPage`
|
||||
|
||||
|
||||
说明:对方法返回值进行翻译,**只能用在方法上**
|
||||
|
||||
参数:无
|
||||
|
||||
示例:
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class DemoServiceImpl implements DemoService {
|
||||
|
||||
@Translator
|
||||
@Override
|
||||
public List<People> dictDemo() {
|
||||
People man = People.builder().sex("1").id("1").phone("18612345678").build();
|
||||
People woman = People.builder().sex("2").id("2").phone("18612345678").build();
|
||||
return CollUtil.newArrayList(man, woman);
|
||||
}
|
||||
|
||||
@Translator
|
||||
@Override
|
||||
public List<Device> enumDemo() {
|
||||
Device man = Device.builder().status("1").build();
|
||||
Device woman = Device.builder().status("2").build();
|
||||
return CollUtil.newArrayList(man, woman);
|
||||
}
|
||||
|
||||
@Translator
|
||||
@Override
|
||||
public List<People2> dbDemo() {
|
||||
People2 man = People2.builder().id("1").build();
|
||||
People2 woman = People2.builder().id("17ed02e3f05c629385371ce561f2dc50").build();
|
||||
return CollUtil.newArrayList(man, woman);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## 演示
|
||||
|
||||
```java
|
||||
@SpringBootTest
|
||||
@RunWith(SpringRunner.class)
|
||||
public class TranslatorTest {
|
||||
|
||||
@Autowired
|
||||
private DemoService demoService;
|
||||
|
||||
@Test
|
||||
public void demo1() {
|
||||
List<People> peopleList = demoService.dictDemo();
|
||||
Console.log("---> 翻译结果:{}", peopleList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demo2() {
|
||||
List<Device> deviceList = demoService.enumDemo();
|
||||
Console.log("---> 翻译结果:{}", deviceList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demo3() {
|
||||
List<People2> peopleList = demoService.dbDemo();
|
||||
Console.log("---> 翻译结果:{}", peopleList);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## 附注
|
||||
|
||||
该项目得益于 [Transformer v1 版本](https://github.com/luo-zhan/Transformer) 增加了 脱敏,完善了 数据库翻译 等功能。(Transformer可能是最简单,但最强大的字段转换插件,一个注解搞定任意转换,让开发变得更加丝滑),基本上就是在此项目上增加功能。
|
BIN
doc/imgs/demo.png
Normal file
BIN
doc/imgs/demo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
38
doc/t_test.sql
Normal file
38
doc/t_test.sql
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
Navicat Premium Data Transfer
|
||||
|
||||
Source Server : 公司mysql-新
|
||||
Source Server Type : MySQL
|
||||
Source Server Version : 50739
|
||||
Source Host : 192.168.0.44:3306
|
||||
Source Schema : t_test
|
||||
|
||||
Target Server Type : MySQL
|
||||
Target Server Version : 50739
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 18/04/2023 11:36:32
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_user
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user`;
|
||||
CREATE TABLE `sys_user` (
|
||||
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
|
||||
`user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_user
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_user` VALUES ('1', '张三');
|
||||
INSERT INTO `sys_user` VALUES ('2', '李四');
|
||||
INSERT INTO `sys_user` VALUES ('3', '王五');
|
||||
INSERT INTO `sys_user` VALUES ('4', '赵六');
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
73
pom.xml
Normal file
73
pom.xml
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.13</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.aizuda</groupId>
|
||||
<artifactId>dict-trans</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.1</version>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<properties>
|
||||
<!-- 数据库操作 -->
|
||||
<mybatis-plus.version>3.5.2</mybatis-plus.version>
|
||||
<!-- hutool -->
|
||||
<hutool.version>5.8.8</hutool.version>
|
||||
<!-- mysql -->
|
||||
<mysql.version>5.1.49</mysql.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- AOP拦截 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool工具类 -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,22 @@
|
||||
package com.aizuda.trans;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
|
||||
/**
|
||||
* 转换器引导应用程序
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 11:30:52
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@Import(cn.hutool.extra.spring.SpringUtil.class)
|
||||
public class TranslatorBootApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TranslatorBootApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
60
src/main/java/com/aizuda/trans/annotation/Dictionary.java
Normal file
60
src/main/java/com/aizuda/trans/annotation/Dictionary.java
Normal file
@ -0,0 +1,60 @@
|
||||
package com.aizuda.trans.annotation;
|
||||
|
||||
import com.aizuda.trans.service.Translatable;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 【字典翻译注解】标识在字典数据类上
|
||||
*
|
||||
* @author luozhan
|
||||
* @date 2019-03
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Dictionary {
|
||||
|
||||
/**
|
||||
* 字典表名
|
||||
* <p>
|
||||
* 为空时取 {@link TableName#value()},取不到则取 类名(驼峰转大写下划线)
|
||||
* </p>
|
||||
*/
|
||||
String table() default "";
|
||||
|
||||
/**
|
||||
* 字典编码的属性
|
||||
* <p>
|
||||
* 查询条件的列字段(需要的是表字段)
|
||||
* </p>
|
||||
*/
|
||||
String codeColumn() default "";
|
||||
|
||||
/**
|
||||
* 字典值的列名
|
||||
* <p>
|
||||
* 要查询的字段(需要的是表字段)
|
||||
* </p>
|
||||
*/
|
||||
String[] textColumn() default {};
|
||||
|
||||
/**
|
||||
* 字典组别属性
|
||||
* (某些字典可能会需要根据某个类别划分,再进行翻译,如静态字典中的DICT_ID)
|
||||
*/
|
||||
String groupColumn() default "";
|
||||
|
||||
/**
|
||||
* 自定义翻译方法
|
||||
* <p>
|
||||
* 1.不配置默认使用DataBaseTranslator翻译
|
||||
* 2.遇到特殊的翻译场景可自定义翻译实现,需要自行编写实现类实现Translatable接口并实现翻译方法,
|
||||
* 程序将使用该方法进行翻译,该注解中的所有配置信息将传递到实现方法中
|
||||
*
|
||||
* @see Translatable
|
||||
*/
|
||||
Class<? extends Translatable> translator() default Translatable.class;
|
||||
|
||||
}
|
67
src/main/java/com/aizuda/trans/annotation/Translate.java
Normal file
67
src/main/java/com/aizuda/trans/annotation/Translate.java
Normal file
@ -0,0 +1,67 @@
|
||||
package com.aizuda.trans.annotation;
|
||||
|
||||
import com.aizuda.trans.constants.DesensitizedTypeConstants;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 【字典翻译注解】标识在需要翻译的字段上
|
||||
*
|
||||
* @author luozhan
|
||||
* @date 2019-03
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Translate {
|
||||
|
||||
/**
|
||||
* 字典配置类 指定的class上必须有@Dictionary注解或者是IDictEnum接口的实现类 注:一般情况下,本属性必须指定(或者使用别名value属性)
|
||||
*
|
||||
* <ul>
|
||||
* <li>DictTranslate 实现类:字典翻译</li>
|
||||
* <li>IEnum 实现类:枚举翻译</li>
|
||||
* <li>Desensitized: 脱敏翻译</li>
|
||||
* <li>其他: 数据库翻译</li>
|
||||
* </ul>
|
||||
*/
|
||||
Class<?> dictClass() default void.class;
|
||||
|
||||
/**
|
||||
* 翻译后的属性名,注意使用驼峰命名 默认为原属性名去除末尾的"Id"和"Code"再接上"Name"
|
||||
*/
|
||||
String[] translateField() default {};
|
||||
|
||||
/**
|
||||
* 组属性值 在静态字典表这种拥有组属性的字典中需要手动传入一个定值
|
||||
* <p>
|
||||
* 即:字典分组的 code
|
||||
* </p>
|
||||
*/
|
||||
String groupValue() default "";
|
||||
|
||||
/**
|
||||
* 指定Dictionary并设置其属性,将覆盖dictClass上的Dictionary注解的配置 指定了该属性后也可不指定dictClass 一般情况下不会使用
|
||||
*/
|
||||
Dictionary dictionary() default @Dictionary;
|
||||
|
||||
/**
|
||||
* dictClass的别名 当只需要配置dictClass时,可以简写成@Translate(XX.class)
|
||||
*/
|
||||
Class<?> value() default void.class;
|
||||
|
||||
/**
|
||||
* 判断条件字段(仅自定义 dictionary 时有效)
|
||||
*/
|
||||
String conditionField() default "";
|
||||
|
||||
/**
|
||||
* 脱敏模型
|
||||
* <ul>
|
||||
* <li>常见格式:使用常量类 DesensitizedTypeConstants </li>
|
||||
* <li>自定义格式:{含开始位置,含结束位置}。举例:{1,2}</li>
|
||||
* </ul>
|
||||
*/
|
||||
String desensitizedModel() default DesensitizedTypeConstants.EMPTY;
|
||||
|
||||
}
|
21
src/main/java/com/aizuda/trans/annotation/Translator.java
Normal file
21
src/main/java/com/aizuda/trans/annotation/Translator.java
Normal file
@ -0,0 +1,21 @@
|
||||
package com.aizuda.trans.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 【字典翻译注解】指定在方法上,对方法返回值进行翻译
|
||||
*
|
||||
* @author luozhan
|
||||
* @date 2020-04
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Translator {
|
||||
|
||||
/**
|
||||
* 指定翻译配置类,可传多个,一般为实体class
|
||||
*/
|
||||
Class<?>[] value() default void.class;
|
||||
|
||||
}
|
40
src/main/java/com/aizuda/trans/aspect/TranslateAspect.java
Normal file
40
src/main/java/com/aizuda/trans/aspect/TranslateAspect.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.aizuda.trans.aspect;
|
||||
|
||||
import com.aizuda.trans.annotation.Translator;
|
||||
import com.aizuda.trans.service.impl.TranslatorHandle;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 字典翻译
|
||||
* 在方法上定义@Tanslator注解,对方法返回值进行翻译
|
||||
*
|
||||
* @author luozhan
|
||||
* @create 2020-04
|
||||
* @see Translator
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
public class TranslateAspect {
|
||||
|
||||
@Pointcut("@annotation(com.aizuda.trans.annotation.Translator)")
|
||||
public void pointCut() {
|
||||
}
|
||||
|
||||
@AfterReturning(pointcut = "pointCut()", returning = "object")
|
||||
public void doAfter(JoinPoint joinPoint, Object object) {
|
||||
if (null == object) {
|
||||
return;
|
||||
}
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Translator config = signature.getMethod().getAnnotation(Translator.class);
|
||||
TranslatorHandle.parse(object, config.value());
|
||||
}
|
||||
|
||||
}
|
24
src/main/java/com/aizuda/trans/config/TranslatorConfig.java
Normal file
24
src/main/java/com/aizuda/trans/config/TranslatorConfig.java
Normal file
@ -0,0 +1,24 @@
|
||||
package com.aizuda.trans.config;
|
||||
|
||||
import com.aizuda.trans.service.DictTranslateService;
|
||||
import com.aizuda.trans.service.impl.DefaultDictTranslateServiceImpl;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 翻译配置
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-08-18 018 16:37:42
|
||||
*/
|
||||
@Configuration
|
||||
public class TranslatorConfig {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DictTranslateService dictTranslateService() {
|
||||
return new DefaultDictTranslateServiceImpl();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.aizuda.trans.constants;
|
||||
|
||||
/**
|
||||
* 脱敏型常数
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-08 008 10:08:26
|
||||
*/
|
||||
public class DesensitizedTypeConstants {
|
||||
|
||||
/** 不启用 */
|
||||
public static final String EMPTY = "";
|
||||
/** 用户id */
|
||||
public static final String USER_ID = "USER_ID";
|
||||
/** 中文名 */
|
||||
public static final String CHINESE_NAME = "CHINESE_NAME";
|
||||
/** 身份证号 */
|
||||
public static final String ID_CARD = "ID_CARD";
|
||||
/** 座机号 */
|
||||
public static final String FIXED_PHONE = "FIXED_PHONE";
|
||||
/** 手机号 */
|
||||
public static final String MOBILE_PHONE = "MOBILE_PHONE";
|
||||
/** 地址 */
|
||||
public static final String ADDRESS = "ADDRESS";
|
||||
/** 电子邮件 */
|
||||
public static final String EMAIL = "EMAIL";
|
||||
/** 密码 */
|
||||
public static final String PASSWORD = "PASSWORD";
|
||||
/** 中国大陆车牌,包含普通车辆、新能源车辆 */
|
||||
public static final String CAR_LICENSE = "CAR_LICENSE";
|
||||
/** 银行卡 */
|
||||
public static final String BANK_CARD = "BANK_CARD";
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.aizuda.trans.desensitized;
|
||||
|
||||
/**
|
||||
* 脱敏
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-08 008 10:50:37
|
||||
*/
|
||||
public class Desensitized implements IDesensitized {
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.aizuda.trans.desensitized;
|
||||
|
||||
/**
|
||||
* 脱敏接口
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-08 008 10:47:22
|
||||
*/
|
||||
public interface IDesensitized {
|
||||
}
|
11
src/main/java/com/aizuda/trans/dict/DictTranslate.java
Normal file
11
src/main/java/com/aizuda/trans/dict/DictTranslate.java
Normal file
@ -0,0 +1,11 @@
|
||||
package com.aizuda.trans.dict;
|
||||
|
||||
/**
|
||||
* 字典翻译
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-08-18 018 16:30:47
|
||||
*/
|
||||
public interface DictTranslate {
|
||||
|
||||
}
|
44
src/main/java/com/aizuda/trans/enums/EnumPool.java
Normal file
44
src/main/java/com/aizuda/trans/enums/EnumPool.java
Normal file
@ -0,0 +1,44 @@
|
||||
package com.aizuda.trans.enums;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 枚举字典池
|
||||
*
|
||||
* @author luozhan
|
||||
* @create 2020-04
|
||||
*/
|
||||
class EnumPool {
|
||||
|
||||
private static final Map<IEnum, EnumBean> DICT_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
static void putDict(IEnum dict, String code, String text) {
|
||||
DICT_MAP.put(dict, new EnumBean(code, text));
|
||||
}
|
||||
|
||||
static EnumBean getDict(IEnum dict) {
|
||||
return DICT_MAP.get(dict);
|
||||
}
|
||||
|
||||
static class EnumBean implements IEnum {
|
||||
private String code;
|
||||
private String text;
|
||||
|
||||
EnumBean(String code, String text) {
|
||||
this.code = code;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
27
src/main/java/com/aizuda/trans/enums/FormatType.java
Normal file
27
src/main/java/com/aizuda/trans/enums/FormatType.java
Normal file
@ -0,0 +1,27 @@
|
||||
package com.aizuda.trans.enums;
|
||||
|
||||
/**
|
||||
* 格式类型
|
||||
*
|
||||
* @author luozhan
|
||||
* @create 2020-04
|
||||
* @date 2022-12-16 01:52:02
|
||||
*/
|
||||
public enum FormatType {
|
||||
|
||||
/**
|
||||
* 大写下划线
|
||||
*/
|
||||
UPPERCASE_UNDERLINE,
|
||||
|
||||
/**
|
||||
* 小写下划线
|
||||
*/
|
||||
LOWERCASE_UNDERLINE,
|
||||
|
||||
/**
|
||||
* 驼峰
|
||||
*/
|
||||
CAMEL
|
||||
|
||||
}
|
67
src/main/java/com/aizuda/trans/enums/IEnum.java
Normal file
67
src/main/java/com/aizuda/trans/enums/IEnum.java
Normal file
@ -0,0 +1,67 @@
|
||||
package com.aizuda.trans.enums;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 枚举接口
|
||||
* <p>
|
||||
* 自定义的枚举类实现本接口后在构造方法中只需调用init方法即可初始化
|
||||
*
|
||||
* @author luozhan
|
||||
* @date 2019-03
|
||||
*/
|
||||
public interface IEnum {
|
||||
|
||||
/**
|
||||
* 通过code获取value
|
||||
*
|
||||
* @param clazz 枚举class
|
||||
* @param code code
|
||||
* @return
|
||||
*/
|
||||
static String getTextByCode(Class<? extends IEnum> clazz, String code) {
|
||||
return Stream.of(clazz.getEnumConstants())
|
||||
.filter((IEnum e) -> e.getCode().equals(code))
|
||||
.map(IEnum::getText)
|
||||
.findAny().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过text获取code
|
||||
*
|
||||
* @param clazz 枚举class
|
||||
* @param text text
|
||||
* @return
|
||||
*/
|
||||
static String getCodeByText(Class<? extends IEnum> clazz, String text) {
|
||||
return Stream.of(clazz.getEnumConstants())
|
||||
.filter((IEnum e) -> e.getText().equals(text))
|
||||
.map(IEnum::getCode)
|
||||
.findAny().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param code 字典编码
|
||||
* @param text 字典文本
|
||||
*/
|
||||
default void init(String code, String text) {
|
||||
EnumPool.putDict(this, code, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取code
|
||||
*/
|
||||
default String getCode() {
|
||||
return EnumPool.getDict(this).getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取text
|
||||
*/
|
||||
default String getText() {
|
||||
return EnumPool.getDict(this).getText();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.aizuda.trans.service;
|
||||
|
||||
/**
|
||||
* 字典翻译服务
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-08-18 018 16:31:43
|
||||
*/
|
||||
public interface DictTranslateService {
|
||||
|
||||
/**
|
||||
* 获取字典标签
|
||||
*
|
||||
* @param dictCode 分组
|
||||
* @param dictValue 值
|
||||
* @return {@link String }
|
||||
* @author nn200433
|
||||
*/
|
||||
public String findDictLabel(String dictCode, String dictValue);
|
||||
|
||||
}
|
29
src/main/java/com/aizuda/trans/service/Translatable.java
Normal file
29
src/main/java/com/aizuda/trans/service/Translatable.java
Normal file
@ -0,0 +1,29 @@
|
||||
package com.aizuda.trans.service;
|
||||
|
||||
import com.aizuda.trans.annotation.Dictionary;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字典接口
|
||||
* <p>
|
||||
* 实现本接口后,可在@Dictionary注解的method属性中指定实现类,以实现特殊的翻译
|
||||
*
|
||||
* @author luozhan
|
||||
* @create 2020-04
|
||||
*/
|
||||
public interface Translatable {
|
||||
|
||||
/**
|
||||
* 自定义翻译方法,需要自己实现
|
||||
*
|
||||
* @param groupValue 字典group值,不用可忽略(如静态字典需要先通过group值确定范围,再根据code值得到对应的value)
|
||||
* @param conditionValue 条件字段值(仅限用户自定义时生效)
|
||||
* @param origin 待翻译的原始值(对应字典code属性)
|
||||
* @param dictConfig 字典注解,可获取属性配置
|
||||
* @param dictClass 字典class
|
||||
* @return 字典value,可以返回null值,翻译时会处理(如果为null则显示原始值)
|
||||
*/
|
||||
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig, Class dictClass);
|
||||
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package com.aizuda.trans.service.impl;
|
||||
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.Db;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.hutool.db.handler.EntityHandler;
|
||||
import cn.hutool.db.handler.EntityListHandler;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.aizuda.trans.annotation.Dictionary;
|
||||
import com.aizuda.trans.enums.FormatType;
|
||||
import com.aizuda.trans.service.Translatable;
|
||||
import com.aizuda.trans.util.NameUtil;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 翻译默认实现
|
||||
* <p>
|
||||
* 适用于字典为单表,使用sql查询
|
||||
*
|
||||
* @author luozhan
|
||||
* @create 2020-04
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DataBaseTranslator implements Translatable {
|
||||
|
||||
/**
|
||||
* 获取数据源
|
||||
*/
|
||||
private static DataSource dataSource = SpringUtil.getBean(DataSource.class);
|
||||
|
||||
@Override
|
||||
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig, Class dictClass) {
|
||||
// 获取参数
|
||||
String codeColumn = dictConfig.codeColumn();
|
||||
String[] textColumnArray = dictConfig.textColumn();
|
||||
String groupColumn = dictConfig.groupColumn();
|
||||
String tableName = getTableName(dictConfig, dictClass);
|
||||
|
||||
Assert.isTrue(StrUtil.isNotEmpty(codeColumn), "@Dictionary注解codeColumn配置有误,找不到指定的属性名,class:" + dictClass.getSimpleName());
|
||||
Assert.isTrue(ArrayUtil.isNotEmpty(textColumnArray), "@Dictionary注解textColumn配置有误,找不到指定的属性名,class:" + dictClass.getSimpleName());
|
||||
|
||||
List<String> rsList = new ArrayList<String>(textColumnArray.length);
|
||||
try {
|
||||
log.debug("---> 触发字典翻译:查询表 {} 中的字段 {} ,查询条件 {} = {}", tableName, textColumnArray, codeColumn, origin);
|
||||
|
||||
// 查询条件为空时直接返回相对应个数的空数组
|
||||
if (ObjectUtil.isNull(origin) || StrUtil.isBlank(origin)) {
|
||||
log.debug("---> 触发字典翻译:查询条件为空,直接返回对应个数的空数组");
|
||||
return Opt.ofNullable(textColumnArray).stream().map(s -> StrUtil.EMPTY).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
Entity where = Entity.create(tableName);
|
||||
if (StrUtil.isNotEmpty(groupColumn)) {
|
||||
where.set(groupColumn, groupValue);
|
||||
}
|
||||
|
||||
if (StrUtil.contains(origin, StrUtil.COMMA)) {
|
||||
// 多条记录取单列(查询结果为多条记录,循环后转为单条)
|
||||
// 传入数据:1,2,3
|
||||
// 查询结果:["张三","李四","王五"]
|
||||
// 返回结果:张三、李四、王五
|
||||
final String field = textColumnArray[0];
|
||||
where.set(codeColumn, StrUtil.split(origin, StrUtil.COMMA));
|
||||
List<Entity> entityList = Db.use(dataSource).find(where, EntityListHandler.create(), textColumnArray);
|
||||
rsList.add(entityList.stream().map(e -> e.getStr(field)).collect(Collectors.joining("、")));
|
||||
} else {
|
||||
// 单条记录多列(查询结果为单条多列,循环后为多条,根据传入的字段顺序返回)
|
||||
// 传入数据:1
|
||||
// 查询结果:{ "列1": "aa", "列2": "bb"}
|
||||
// 返回结果:["aa","bb"]
|
||||
where.set(codeColumn, origin);
|
||||
Entity entity = Db.use(dataSource).find(where, EntityHandler.create(), textColumnArray);
|
||||
for (String column : textColumnArray) {
|
||||
rsList.add(entity.getStr(column));
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
// 空指针异常时,可能查询条件为空。返回空数组
|
||||
log.error("---> DataBaseTranslator 字典翻译,空指针异常", e);
|
||||
} catch (SQLException e) {
|
||||
log.error("---> DataBaseTranslator 字典翻译,SQL查询异常", e);
|
||||
}
|
||||
|
||||
return rsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表名
|
||||
*
|
||||
* @param dictConfig dict配置
|
||||
* @param dictClass dict类
|
||||
* @return {@link String }
|
||||
* @author nn200433
|
||||
*/
|
||||
private String getTableName(Dictionary dictConfig, Class dictClass) {
|
||||
if (StrUtil.isNotEmpty(dictConfig.table())) {
|
||||
return dictConfig.table();
|
||||
}
|
||||
|
||||
// 类名转表名
|
||||
final String className = dictClass.getSimpleName();
|
||||
String tName = className.substring(0, 1) + NameUtil.parseCamelTo(className.substring(1), FormatType.UPPERCASE_UNDERLINE);
|
||||
|
||||
// 获取mp注解上的表名
|
||||
TableName tableName = (TableName) dictClass.getAnnotation(TableName.class);
|
||||
if (null != tableName) {
|
||||
tName = tableName.value();
|
||||
}
|
||||
|
||||
return tName;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.aizuda.trans.service.impl;
|
||||
|
||||
import com.aizuda.trans.service.DictTranslateService;
|
||||
|
||||
/**
|
||||
* 默认字典翻译服务实现
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-08-18 018 16:32:24
|
||||
*/
|
||||
public class DefaultDictTranslateServiceImpl implements DictTranslateService {
|
||||
|
||||
@Override
|
||||
public String findDictLabel(String dictCode, String dictValue) {
|
||||
return dictValue;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.aizuda.trans.service.impl;
|
||||
|
||||
import com.aizuda.trans.annotation.Dictionary;
|
||||
import com.aizuda.trans.service.Translatable;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 脱敏翻译器
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-08 10:38:56
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DesensitizedTranslator implements Translatable {
|
||||
|
||||
@Override
|
||||
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig, Class dictClass) {
|
||||
// 直接返回原值
|
||||
return Collections.singletonList(origin);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.aizuda.trans.service.impl;
|
||||
|
||||
import com.aizuda.trans.annotation.Dictionary;
|
||||
import com.aizuda.trans.service.DictTranslateService;
|
||||
import com.aizuda.trans.service.Translatable;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据字典翻译
|
||||
*
|
||||
* @author luozhan
|
||||
* @create 2020-04
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DictCacheTranslator implements Translatable {
|
||||
|
||||
@Autowired
|
||||
private DictTranslateService dictTranslateService;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig, Class dictClass) {
|
||||
return Collections.singletonList(dictTranslateService.findDictLabel(groupValue, origin));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.aizuda.trans.service.impl;
|
||||
|
||||
import com.aizuda.trans.annotation.Dictionary;
|
||||
import com.aizuda.trans.enums.IEnum;
|
||||
import com.aizuda.trans.service.Translatable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 枚举翻译
|
||||
*
|
||||
* @author luozhan
|
||||
* @create 2020-04
|
||||
*/
|
||||
@Component
|
||||
public class EnumTranslator implements Translatable {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig, Class dictClass) {
|
||||
Assert.isTrue(IEnum.class.isAssignableFrom(dictClass), dictClass.getSimpleName() + "不是IDictEnum的实现类,无法使用EnumTranslator进行翻译");
|
||||
final String s = Stream.of(((Class<IEnum>) dictClass).getEnumConstants())
|
||||
.filter((IEnum e) -> e.getCode().equals(origin))
|
||||
.map(IEnum::getText)
|
||||
.findAny().orElse(null);
|
||||
return Collections.singletonList(s);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,394 @@
|
||||
package com.aizuda.trans.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.aizuda.trans.annotation.Dictionary;
|
||||
import com.aizuda.trans.annotation.Translate;
|
||||
import com.aizuda.trans.desensitized.IDesensitized;
|
||||
import com.aizuda.trans.dict.DictTranslate;
|
||||
import com.aizuda.trans.enums.FormatType;
|
||||
import com.aizuda.trans.enums.IEnum;
|
||||
import com.aizuda.trans.service.Translatable;
|
||||
import com.aizuda.trans.util.LambdaUtil;
|
||||
import com.aizuda.trans.util.NameUtil;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import sun.reflect.annotation.AnnotationType;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 翻译工具 结合注解使用
|
||||
*
|
||||
* @author luozhan
|
||||
* @see Translate
|
||||
* @see com.aizuda.trans.annotation.Dictionary
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TranslatorHandle {
|
||||
|
||||
/**
|
||||
* 翻译Map或Entity或Page
|
||||
*
|
||||
* @param origin 需要翻译的数据
|
||||
* @param clazz 指定翻译的模板class,可传多个
|
||||
* @param <T> Map或Entity
|
||||
* @return origin
|
||||
*/
|
||||
public static <T> T parse(T origin, Class<?>... clazz) {
|
||||
if (origin == null) {
|
||||
return null;
|
||||
}
|
||||
if (origin instanceof List) {
|
||||
return (T) parse((List) origin, clazz);
|
||||
} else if (origin instanceof Map) {
|
||||
Map<String, Object> m = (Map<String, Object>) origin;
|
||||
Collection<Object> values = m.values();
|
||||
// 查询不为基本类型的数量
|
||||
long count = values.stream().filter(v -> !ClassUtil.isSimpleTypeOrArray(ClassUtil.getClass(v))).count();
|
||||
if (values.size() == count) {
|
||||
for (Map.Entry<String, Object> entry : m.entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
// 跳过空对象
|
||||
if (ObjectUtil.isNull(value)) {
|
||||
continue;
|
||||
}
|
||||
Class<Object> classType = ClassUtil.getClass(value);
|
||||
value = parse(Collections.singletonList(value), classType).get(0);
|
||||
}
|
||||
return (T) m;
|
||||
}
|
||||
} else if (origin instanceof IPage) {
|
||||
Page p = (Page) origin;
|
||||
p.setRecords(parse(p.getRecords(), clazz));
|
||||
return (T) p;
|
||||
}
|
||||
return parse(Collections.singletonList(origin), clazz).get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译集合 集合元素为Map或Entity
|
||||
* <p>
|
||||
* 注意: 如果不指定class,默认使用List中元素的class上配置的翻译规则进行翻译 当List集合元素不为Entity类型时,class参数至少需要指定1个
|
||||
*
|
||||
* @param origins 待翻译数据集合
|
||||
* @param classes 配置了翻译规则的Entity类型,可传多个
|
||||
* @param <T> 支持Entity或者Map
|
||||
* @return List
|
||||
*/
|
||||
public static <T> List<T> parse(List<T> origins, Class<?>... classes) {
|
||||
if (CollUtil.isEmpty(origins)) {
|
||||
return origins;
|
||||
}
|
||||
|
||||
classes = (classes.length != 0 && classes[0] != void.class) ? classes : new Class[]{origins.get(0).getClass()};
|
||||
|
||||
// 获取bo中需要翻译的属性
|
||||
List<Field> translateFieldList = Arrays.stream(classes)
|
||||
.map(Class::getDeclaredFields)
|
||||
.flatMap(Stream::of)
|
||||
.filter(field -> field.isAnnotationPresent(Translate.class))
|
||||
.collect(Collectors.toList());
|
||||
// 源数据中属性的格式(大写下划线,小写下划线,驼峰)
|
||||
FormatType fieldFormatType = getFieldType(origins);
|
||||
|
||||
for (Field field : translateFieldList) {
|
||||
// 1.获取要翻译的属性名
|
||||
String fieldName = NameUtil.parseCamelTo(field.getName(), fieldFormatType);
|
||||
// 2.获取每个待翻译属性的配置
|
||||
Translate translateConfig = field.getAnnotation(Translate.class);
|
||||
// 配置的字典class
|
||||
Class<?> dictClass = getDictClass(translateConfig);
|
||||
// 获取翻译值写入的字段名
|
||||
List<String> translateFields = NameUtil.parseCamelTo(
|
||||
getTranslateFieldName(translateConfig, field.getName()), fieldFormatType);
|
||||
// 字典组字段值
|
||||
String groupValue = translateConfig.groupValue();
|
||||
// 判断条件字段
|
||||
String conditionField = translateConfig.conditionField();
|
||||
// 获取敏感词模型
|
||||
String desensitizedModel = translateConfig.desensitizedModel();
|
||||
// 字典注解配置
|
||||
com.aizuda.trans.annotation.Dictionary dictionaryConfig = handle(translateConfig);
|
||||
|
||||
for (T origin : origins) {
|
||||
String originValue = Convert.toStr(getProperty(origin, fieldName));
|
||||
if (originValue == null) {
|
||||
continue;
|
||||
}
|
||||
String conditionFieldValue = null;
|
||||
if (StrUtil.isNotBlank(conditionField)) {
|
||||
conditionFieldValue = Convert.toStr(getProperty(origin, conditionField));
|
||||
}
|
||||
|
||||
// 翻译
|
||||
List<String> translateValues = parse(originValue, dictionaryConfig, dictClass, groupValue, conditionFieldValue);
|
||||
|
||||
// 如果敏感词模型不为空,则将结果进行敏感词处理
|
||||
if (StrUtil.isNotBlank(desensitizedModel)) {
|
||||
final DesensitizedUtil.DesensitizedType desensitizedType = EnumUtil.fromString(
|
||||
DesensitizedUtil.DesensitizedType.class, desensitizedModel, null);
|
||||
if (null == desensitizedType && StrUtil.isWrap(desensitizedModel, StrUtil.DELIM_START, StrUtil.DELIM_END)) {
|
||||
// 找不到匹配脱敏模型时,用hide解析
|
||||
final String model = StrUtil.unWrap(desensitizedModel, StrUtil.DELIM_START, StrUtil.DELIM_END);
|
||||
final int[] indexArray = StrUtil.splitToInt(model, StrUtil.COMMA);
|
||||
Assert.isTrue(indexArray.length == 2, "自定义敏感词模型格式:{含开始位置,含结束位置}。举例:{1,2}");
|
||||
translateValues = translateValues.stream()
|
||||
.map(s -> StrUtil.hide(s, indexArray[0], indexArray[1]))
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
translateValues = translateValues.stream()
|
||||
.map(s -> StrUtil.desensitized(s, desensitizedType))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
// 填值
|
||||
setProperty(origin, translateFields, translateValues);
|
||||
}
|
||||
}
|
||||
return origins;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断源数据中的属性格式类型
|
||||
*
|
||||
* @param origins 对象数组
|
||||
* @return {@link FormatType }
|
||||
* @author nn200433
|
||||
*/
|
||||
private static <T> FormatType getFieldType(List<T> origins) {
|
||||
T element = origins.get(0);
|
||||
if (Map.class.isAssignableFrom(element.getClass())) {
|
||||
Set<String> keySet = ((Map) element).keySet();
|
||||
for (String key : keySet) {
|
||||
if (key.toUpperCase().equals(key)) {
|
||||
return FormatType.UPPERCASE_UNDERLINE;
|
||||
} else if (key.contains("_")) {
|
||||
return FormatType.LOWERCASE_UNDERLINE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FormatType.CAMEL;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字典类
|
||||
*
|
||||
* @param translateConfig 转换配置
|
||||
* @return {@link Class }<{@link ? }>
|
||||
* @author nn200433
|
||||
*/
|
||||
private static Class<?> getDictClass(Translate translateConfig) {
|
||||
final Class<?> classType = translateConfig.dictClass();
|
||||
return classType == void.class ? translateConfig.value() : classType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将dictClass类上的Dictionary的配置填充到Translate注解中的dictionary属性中 此步骤将合并两个注解中的配置,且Translate注解中的配置优先级更高
|
||||
*
|
||||
* @param translateConfig 转换配置
|
||||
* @return {@link com.aizuda.trans.annotation.Dictionary }
|
||||
* @author nn200433
|
||||
*/
|
||||
private static com.aizuda.trans.annotation.Dictionary handle(Translate translateConfig) {
|
||||
Class<?> dictClass = getDictClass(translateConfig);
|
||||
com.aizuda.trans.annotation.Dictionary dictionaryConfigOnDictClass = dictClass.getAnnotation(com.aizuda.trans.annotation.Dictionary.class);
|
||||
com.aizuda.trans.annotation.Dictionary dictionaryConfigInTranslateConfig = translateConfig.dictionary();
|
||||
return (com.aizuda.trans.annotation.Dictionary) joinAnnotationValue(dictionaryConfigOnDictClass, dictionaryConfigInTranslateConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将注解属性填充到另一个相同类型的注解中,目标注解中已经存在属性值的不会被覆盖
|
||||
*
|
||||
* @param annotationFrom
|
||||
* @param annotationTo
|
||||
* @return 返回annotationTo,如果annotationTo为空,返回annotationFrom
|
||||
*/
|
||||
private static Annotation joinAnnotationValue(Annotation annotationFrom, Annotation annotationTo) {
|
||||
if (annotationTo == null) {
|
||||
return annotationFrom;
|
||||
}
|
||||
if (annotationFrom == null) {
|
||||
return annotationTo;
|
||||
}
|
||||
Object handlerFrom = Proxy.getInvocationHandler(annotationFrom);
|
||||
Object handlerTo = Proxy.getInvocationHandler(annotationTo);
|
||||
|
||||
Field fieldFrom = LambdaUtil.uncheck(() -> handlerFrom.getClass().getDeclaredField("memberValues"));
|
||||
Field fieldTo = LambdaUtil.uncheck(() -> handlerTo.getClass().getDeclaredField("memberValues"));
|
||||
|
||||
fieldFrom.setAccessible(true);
|
||||
fieldTo.setAccessible(true);
|
||||
|
||||
Map<String, Object> memberValuesFrom = LambdaUtil.uncheck(() -> (Map) fieldFrom.get(handlerFrom));
|
||||
Map<String, Object> memberValuesTo = LambdaUtil.uncheck(() -> (Map) fieldTo.get(handlerTo));
|
||||
|
||||
// 注解默认值,注意不会包含没有默认值的属性
|
||||
Map<String, Object> defaultValueMap = AnnotationType.getInstance(annotationTo.annotationType())
|
||||
.memberDefaults();
|
||||
// 若目标注解中全都是默认值(代表没有设置),则直接返回原注解
|
||||
// 否则属性填充后会直接改变目标注解的默认值,影响其他引用的地方
|
||||
if (defaultValueMap.equals(memberValuesTo)) {
|
||||
return annotationFrom;
|
||||
}
|
||||
// 如果目标注解属性未设置,则往目标里填充值
|
||||
memberValuesTo.forEach((field, value) -> {
|
||||
if (value.equals(defaultValueMap.get(field))) {
|
||||
memberValuesTo.put(field, memberValuesFrom.get(field));
|
||||
}
|
||||
});
|
||||
|
||||
return annotationTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译单值
|
||||
*
|
||||
* @param originValue 原始值
|
||||
* @param dictConfig 字典配置
|
||||
* @param dictClass 字典class(包含组别属性、字典code属性、字典值属性三个信息)
|
||||
* @param groupValue 组别的值,由使用者指定
|
||||
* @param conditionValue 条件字段值
|
||||
* @return 翻译后的值,如果字典中找不到翻译值返回原始值
|
||||
* @author nn200433
|
||||
*/
|
||||
public static List<String> parse(String originValue, Dictionary dictConfig, Class<?> dictClass, String groupValue,
|
||||
String conditionValue) {
|
||||
if (originValue == null) {
|
||||
return null;
|
||||
}
|
||||
Class<? extends Translatable> translatorClass = dictConfig.translator();
|
||||
if (translatorClass == Translatable.class) {
|
||||
if (DictTranslate.class.isAssignableFrom(dictClass)) {
|
||||
// 1.dictClass是字典类,采用字典翻译
|
||||
translatorClass = DictCacheTranslator.class;
|
||||
} else if (IEnum.class.isAssignableFrom(dictClass)) {
|
||||
// 2.dictClass是枚举类,采用枚举翻译
|
||||
translatorClass = EnumTranslator.class;
|
||||
} else if (IDesensitized.class.isAssignableFrom(dictClass)) {
|
||||
// 3.dictClass是脱敏类,采用脱敏翻译
|
||||
translatorClass = DesensitizedTranslator.class;
|
||||
} else {
|
||||
// 4.否则使用数据库翻译
|
||||
translatorClass = DataBaseTranslator.class;
|
||||
}
|
||||
}
|
||||
|
||||
// 调用翻译方法
|
||||
Translatable translator;
|
||||
if (translatorClass.isAnnotationPresent(Component.class)) {
|
||||
// 实现类上配置了@Component则使用Spring容器获取
|
||||
translator = SpringUtil.getBean(translatorClass);
|
||||
} else {
|
||||
translator = LambdaUtil.uncheck(translatorClass::newInstance);
|
||||
}
|
||||
List<String> translateResult = translator.translate(groupValue, conditionValue, originValue, dictConfig,
|
||||
dictClass);
|
||||
return CollUtil.defaultIfEmpty(translateResult, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 若注解中未配置translateField,则默认将原属性名的Id或Code字样替换成Name
|
||||
* <p>
|
||||
* 如: resTypeId -> resTypeCode staff -> staffName
|
||||
*
|
||||
* @param translateConfig 转换配置
|
||||
* @param originFieldName 原始字段名称
|
||||
* @return {@link List }<{@link String }>
|
||||
* @author nn200433
|
||||
*/
|
||||
private static List<String> getTranslateFieldName(Translate translateConfig, String originFieldName) {
|
||||
String[] translateFieldArray = translateConfig.translateField();
|
||||
String newName = originFieldName.replaceFirst("(Id|Code)$|$", "Name");
|
||||
for (int i = 0; i < translateFieldArray.length; i++) {
|
||||
final String translateField = translateFieldArray[i];
|
||||
if (StrUtil.isBlank(translateField)) {
|
||||
translateFieldArray[i] = newName + i;
|
||||
}
|
||||
}
|
||||
return CollUtil.newArrayList(translateFieldArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据对象获取属性
|
||||
*
|
||||
* @param o 对象
|
||||
* @param fieldName 字段名称
|
||||
* @return {@link Object }
|
||||
* @author nn200433
|
||||
*/
|
||||
private static Object getProperty(Object o, String fieldName) {
|
||||
if (o instanceof Map) {
|
||||
return ((Map) o).get(fieldName);
|
||||
} else {
|
||||
Method getMethod = getMethod(o.getClass(), fieldName, "get");
|
||||
// 此处不会抛异常
|
||||
return LambdaUtil.uncheck(() -> getMethod.invoke(o));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性
|
||||
*
|
||||
* @param o 对象
|
||||
* @param fieldNameList 字段名称列表
|
||||
* @param valueList 值列表
|
||||
* @author nn200433
|
||||
*/
|
||||
private static void setProperty(Object o, List<String> fieldNameList, List<String> valueList) {
|
||||
if (fieldNameList.size() > valueList.size()) {
|
||||
log.error(
|
||||
"字典翻译查询结果不够翻译,导致翻译异常。\n ---> 当前翻译数据:{} \n ---> 翻译结果需设置 {} 个字段({})\n ---> 实际翻译出 {} 个字段({})",
|
||||
JSONUtil.toJsonStr(o), fieldNameList.size(), fieldNameList, valueList.size(), valueList);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < fieldNameList.size(); i++) {
|
||||
final String fieldName = fieldNameList.get(i);
|
||||
final String value = valueList.get(i);
|
||||
|
||||
if (o instanceof Map) {
|
||||
((Map) o).put(fieldName, value);
|
||||
} else {
|
||||
Method setMethod = getMethod(o.getClass(), fieldName, "set");
|
||||
// 此处不会抛异常
|
||||
LambdaUtil.uncheck(() -> setMethod.invoke(o, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过方法名找到get或set方法 只是简单根据名称匹配,要求clazz是个常规的bean
|
||||
*
|
||||
* @param clazz 对象类型
|
||||
* @param fieldName 字段名称
|
||||
* @param prefix 前缀
|
||||
* @return {@link Method }
|
||||
* @author nn200433
|
||||
*/
|
||||
private static Method getMethod(Class<?> clazz, String fieldName, String prefix) {
|
||||
String methodName = prefix + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
|
||||
return Stream.of(clazz.getDeclaredMethods())
|
||||
.filter(method -> method.getName().equals(methodName))
|
||||
.findAny()
|
||||
.orElseThrow(() -> new IllegalArgumentException(
|
||||
clazz.getSimpleName() + ".class中未添加翻译属性" + fieldName + "或其对应get/set方法"));
|
||||
}
|
||||
|
||||
}
|
||||
|
190
src/main/java/com/aizuda/trans/util/LambdaUtil.java
Normal file
190
src/main/java/com/aizuda/trans/util/LambdaUtil.java
Normal file
@ -0,0 +1,190 @@
|
||||
package com.aizuda.trans.util;
|
||||
|
||||
import java.util.function.*;
|
||||
|
||||
/**
|
||||
* Lambda异常处理工具类
|
||||
*
|
||||
* @author luozhan
|
||||
* @date 2019-05
|
||||
* @link <a>https://github.com/Robot-L/LambdaExceptionUtil</a>觉得不错的话请给个Star,thx! :D
|
||||
*/
|
||||
public final class LambdaUtil {
|
||||
|
||||
/**
|
||||
* 包装普通函数(Function)
|
||||
* <p>
|
||||
* 注:方法签名中的" throws E "编译器会提示多余,但其实是为了将具体的异常向外传递,如果不抛出的话:
|
||||
* 1.外层代码中编译器将无法提示有异常需要处理
|
||||
* 2.也无法主动在外层捕获具体的异常,如果尝试try一个具体的异常,编译器将提示:在try语句体中永远不会抛出相应异常(Exception 'XXX' is never thrown in the corresponding try block)
|
||||
*/
|
||||
public static <T, R, E extends Exception> Function<T, R> wrapFunction(FunctionWithExceptions<T, R, E> function) throws E {
|
||||
return t -> {
|
||||
try {
|
||||
return function.apply(t);
|
||||
} catch (Exception exception) {
|
||||
throwAsUnchecked(exception);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装双入参普通函数(BiFunction)
|
||||
*/
|
||||
public static <T, U, R, E extends Exception> BiFunction<T, U, R> wrapBiFunction(BiFunctionWithExceptions<T, U, R, E> biFunction) throws E {
|
||||
return (t, u) -> {
|
||||
try {
|
||||
return biFunction.apply(t, u);
|
||||
} catch (Exception exception) {
|
||||
throwAsUnchecked(exception);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装消费函数(Consumer)
|
||||
*/
|
||||
public static <T, E extends Exception> Consumer<T> wrapConsumer(ConsumerWithExceptions<T, E> consumer) throws E {
|
||||
return t -> {
|
||||
try {
|
||||
consumer.accept(t);
|
||||
} catch (Exception exception) {
|
||||
throwAsUnchecked(exception);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装双重消费函数(BiConsumer)
|
||||
*/
|
||||
public static <T, U, E extends Exception> BiConsumer<T, U> wrapBiConsumer(BiConsumerWithExceptions<T, U, E> biConsumer) throws E {
|
||||
return (t, u) -> {
|
||||
try {
|
||||
biConsumer.accept(t, u);
|
||||
} catch (Exception exception) {
|
||||
throwAsUnchecked(exception);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装生产函数(Supplier)
|
||||
*/
|
||||
public static <T, E extends Exception> Supplier<T> wrapSupplier(SupplierWithExceptions<T, E> function) throws E {
|
||||
return () -> {
|
||||
try {
|
||||
return function.get();
|
||||
} catch (Exception exception) {
|
||||
throwAsUnchecked(exception);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装条件函数(Predicate)
|
||||
*/
|
||||
public static <T, E extends Exception> Predicate<T> wrapPredicate(PredicateWithExceptions<T, E> predicate) throws E {
|
||||
return t -> {
|
||||
try {
|
||||
return predicate.test(t);
|
||||
} catch (Exception exception) {
|
||||
throwAsUnchecked(exception);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装双入参条件函数(BiPredicate)
|
||||
*/
|
||||
public static <T, U, E extends Exception> BiPredicate<T, U> wrapBiPredicate(BiPredicateWithExceptions<T, U, E> predicate) throws E {
|
||||
return (t, u) -> {
|
||||
try {
|
||||
return predicate.test(t, u);
|
||||
} catch (Exception exception) {
|
||||
throwAsUnchecked(exception);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装纯执行函数(Runnable)
|
||||
*/
|
||||
public static <E extends Exception> void wrapRunnable(RunnableWithExceptions<E> runnable) throws E {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception exception) {
|
||||
throwAsUnchecked(exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果一个方法绝对不会抛出所申明的异常,可以使用该方法进行包装
|
||||
* 如:new String(byteArr, "UTF-8")申明了UnsupportedEncodingException,
|
||||
* 但编码"UTF-8"是必定不会抛异常的,所以可以使用uncheck()进行包装
|
||||
* String text = uncheck(() -> new String(byteArr, "UTF-8"))
|
||||
*/
|
||||
public static <R, E extends Exception> R uncheck(SupplierWithExceptions<R, E> supplier) {
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (Exception exception) {
|
||||
throwAsUnchecked(exception);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
|
||||
throw (E) exception;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConsumerWithExceptions<T, E extends Exception> {
|
||||
void accept(T t) throws E;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BiConsumerWithExceptions<T, U, E extends Exception> {
|
||||
void accept(T t, U u) throws E;
|
||||
}
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
public interface FunctionWithExceptions<T, R, E extends Exception> {
|
||||
static <T> FunctionWithExceptions<T, T, Exception> identity() {
|
||||
return t -> t;
|
||||
}
|
||||
|
||||
R apply(T t) throws E;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BiFunctionWithExceptions<T, U, R, E extends Exception> {
|
||||
R apply(T t, U u) throws E;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SupplierWithExceptions<T, E extends Exception> {
|
||||
T get() throws E;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface RunnableWithExceptions<E extends Exception> {
|
||||
void run() throws E;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PredicateWithExceptions<T, E extends Exception> {
|
||||
boolean test(T t) throws E;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BiPredicateWithExceptions<T, U, E extends Exception> {
|
||||
boolean test(T t, U u) throws E;
|
||||
}
|
||||
|
||||
}
|
57
src/main/java/com/aizuda/trans/util/NameUtil.java
Normal file
57
src/main/java/com/aizuda/trans/util/NameUtil.java
Normal file
@ -0,0 +1,57 @@
|
||||
package com.aizuda.trans.util;
|
||||
|
||||
import com.aizuda.trans.enums.FormatType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 名字工具类
|
||||
*
|
||||
* @author luozhan
|
||||
* @create 2020-04
|
||||
* @date 2023-04-17 04:30:20
|
||||
*/
|
||||
public class NameUtil {
|
||||
|
||||
/**
|
||||
* 解析驼峰
|
||||
*
|
||||
* @param param 参数
|
||||
* @param type 类型
|
||||
* @return {@link String }
|
||||
* @author nn200433
|
||||
*/
|
||||
public static String parseCamelTo(String param, FormatType type) {
|
||||
if (type == FormatType.CAMEL) {
|
||||
return param;
|
||||
}
|
||||
int len = param.length();
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = param.charAt(i);
|
||||
if (Character.isUpperCase(c)) {
|
||||
sb.append("_");
|
||||
}
|
||||
char result = type == FormatType.UPPERCASE_UNDERLINE ?
|
||||
Character.toUpperCase(c) : Character.toLowerCase(c);
|
||||
sb.append(result);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析驼峰
|
||||
*
|
||||
* @param params 参数个数
|
||||
* @param type 类型
|
||||
* @return {@link List }<{@link String }>
|
||||
* @author nn200433
|
||||
*/
|
||||
public static List<String> parseCamelTo(List<String> params, FormatType type) {
|
||||
for (String param : params) {
|
||||
param = parseCamelTo(param, type);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
}
|
1
src/main/resources/META-INF/spring.factories
Normal file
1
src/main/resources/META-INF/spring.factories
Normal file
@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.aizuda.trans.config.TranslatorConfig
|
47
src/test/java/com/aizuda/trans/TranslatorTest.java
Normal file
47
src/test/java/com/aizuda/trans/TranslatorTest.java
Normal file
@ -0,0 +1,47 @@
|
||||
package com.aizuda.trans;
|
||||
|
||||
import cn.hutool.core.lang.Console;
|
||||
import com.aizuda.trans.demo.DemoService;
|
||||
import com.aizuda.trans.entity.Device;
|
||||
import com.aizuda.trans.entity.People;
|
||||
import com.aizuda.trans.entity.People2;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 转换测试
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2020年05月18日 0018 15:05:05
|
||||
*/
|
||||
@SpringBootTest
|
||||
@RunWith(SpringRunner.class)
|
||||
public class TranslatorTest {
|
||||
|
||||
@Autowired
|
||||
private DemoService demoService;
|
||||
|
||||
@Test
|
||||
public void demo1() {
|
||||
List<People> peopleList = demoService.dictDemo();
|
||||
Console.log("---> 翻译结果:{}", peopleList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demo2() {
|
||||
List<Device> deviceList = demoService.enumDemo();
|
||||
Console.log("---> 翻译结果:{}", deviceList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demo3() {
|
||||
List<People2> peopleList = demoService.dbDemo();
|
||||
Console.log("---> 翻译结果:{}", peopleList);
|
||||
}
|
||||
|
||||
}
|
41
src/test/java/com/aizuda/trans/demo/DemoService.java
Normal file
41
src/test/java/com/aizuda/trans/demo/DemoService.java
Normal file
@ -0,0 +1,41 @@
|
||||
package com.aizuda.trans.demo;
|
||||
|
||||
import com.aizuda.trans.entity.Device;
|
||||
import com.aizuda.trans.entity.People;
|
||||
import com.aizuda.trans.entity.People2;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 演示服务
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 016 11:45:46
|
||||
*/
|
||||
public interface DemoService {
|
||||
|
||||
/**
|
||||
* 字典演示
|
||||
*
|
||||
* @return {@link List }<{@link People }>
|
||||
* @author nn200433
|
||||
*/
|
||||
public List<People> dictDemo();
|
||||
|
||||
/**
|
||||
* 枚举演示
|
||||
*
|
||||
* @return {@link List }<{@link Device }>
|
||||
* @author nn200433
|
||||
*/
|
||||
public List<Device> enumDemo();
|
||||
|
||||
/**
|
||||
* 数据库演示
|
||||
*
|
||||
* @return {@link List }<{@link People2 }>
|
||||
* @author nn200433
|
||||
*/
|
||||
public List<People2> dbDemo();
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.aizuda.trans.demo.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.aizuda.trans.annotation.Dictionary;
|
||||
import com.aizuda.trans.service.Translatable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自定义翻译
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 016 11:46:57
|
||||
*/
|
||||
@Component
|
||||
public class CustomerTranslateServiceImpl implements Translatable {
|
||||
|
||||
@Override
|
||||
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig,
|
||||
Class dictClass) {
|
||||
List<String> rList = new ArrayList<String>(1);
|
||||
if (StrUtil.equals(origin, "1")) {
|
||||
rList.add("结果1");
|
||||
} else {
|
||||
rList.add("结果2");
|
||||
}
|
||||
return rList;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package com.aizuda.trans.demo.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.aizuda.trans.annotation.Translator;
|
||||
import com.aizuda.trans.demo.DemoService;
|
||||
import com.aizuda.trans.entity.Device;
|
||||
import com.aizuda.trans.entity.People;
|
||||
import com.aizuda.trans.entity.People2;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 演示服务实现
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 016 11:46:57
|
||||
*/
|
||||
@Component
|
||||
public class DemoServiceImpl implements DemoService {
|
||||
|
||||
@Translator
|
||||
@Override
|
||||
public List<People> dictDemo() {
|
||||
People man = People.builder().sex("1").id("1").phone("18612345678").build();
|
||||
People woman = People.builder().sex("2").id("2").phone("18612345678").build();
|
||||
return CollUtil.newArrayList(man, woman);
|
||||
}
|
||||
|
||||
@Translator
|
||||
@Override
|
||||
public List<Device> enumDemo() {
|
||||
Device d1 = Device.builder().status("1").build();
|
||||
Device d2 = Device.builder().status("2").build();
|
||||
return CollUtil.newArrayList(d1, d2);
|
||||
}
|
||||
|
||||
@Translator
|
||||
@Override
|
||||
public List<People2> dbDemo() {
|
||||
People2 man = People2.builder().id("1").build();
|
||||
People2 woman = People2.builder().id("2").build();
|
||||
return CollUtil.newArrayList(man, woman);
|
||||
}
|
||||
|
||||
}
|
37
src/test/java/com/aizuda/trans/dict/CustomerDictImpl.java
Normal file
37
src/test/java/com/aizuda/trans/dict/CustomerDictImpl.java
Normal file
@ -0,0 +1,37 @@
|
||||
package com.aizuda.trans.dict;
|
||||
|
||||
import com.aizuda.trans.service.DictTranslateService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 016 11:35:30
|
||||
*/
|
||||
@Component
|
||||
public class CustomerDictImpl implements DictTranslateService {
|
||||
|
||||
private static Map<String, Map<String, String>> DICT_DEMO = new HashMap<String, Map<String, String>>(2);
|
||||
|
||||
static {
|
||||
Map<String, String> sexMap = new HashMap<String, String>(2) {{
|
||||
put("1", "男");
|
||||
put("2", "女");
|
||||
}};
|
||||
Map<String, String> stateMap = new HashMap<String, String>(2) {{
|
||||
put("1", "发布");
|
||||
put("2", "草稿");
|
||||
put("3", "下架");
|
||||
}};
|
||||
DICT_DEMO.put("sex", sexMap);
|
||||
DICT_DEMO.put("state", stateMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String findDictLabel(String dictCode, String dictValue) {
|
||||
return DICT_DEMO.get(dictCode).get(dictValue);
|
||||
}
|
||||
|
||||
}
|
28
src/test/java/com/aizuda/trans/entity/Device.java
Normal file
28
src/test/java/com/aizuda/trans/entity/Device.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.aizuda.trans.entity;
|
||||
|
||||
import com.aizuda.trans.annotation.Translate;
|
||||
import com.aizuda.trans.enums.DeviceStatus;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 设备
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 016 11:40:30
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Device {
|
||||
|
||||
/** 枚举翻译 */
|
||||
@Translate(dictClass = DeviceStatus.class, translateField = "statusName")
|
||||
private String status;
|
||||
|
||||
private String statusName;
|
||||
|
||||
}
|
20
src/test/java/com/aizuda/trans/entity/Dict.java
Normal file
20
src/test/java/com/aizuda/trans/entity/Dict.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.aizuda.trans.entity;
|
||||
|
||||
import com.aizuda.trans.dict.DictTranslate;
|
||||
|
||||
/**
|
||||
* 字典类
|
||||
*
|
||||
* <pre>
|
||||
* 这里直接弄了个空类
|
||||
*
|
||||
* 业务系统的话,字典一般就一个类
|
||||
*
|
||||
* 主要加 implements DictTranslate 这句话,来判断走哪个方法
|
||||
* </pre>
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 016 13:57:20
|
||||
*/
|
||||
public class Dict implements DictTranslate {
|
||||
}
|
40
src/test/java/com/aizuda/trans/entity/People.java
Normal file
40
src/test/java/com/aizuda/trans/entity/People.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.aizuda.trans.entity;
|
||||
|
||||
import com.aizuda.trans.annotation.Dictionary;
|
||||
import com.aizuda.trans.annotation.Translate;
|
||||
import com.aizuda.trans.constants.DesensitizedTypeConstants;
|
||||
import com.aizuda.trans.demo.impl.CustomerTranslateServiceImpl;
|
||||
import com.aizuda.trans.desensitized.Desensitized;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 人
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 016 11:40:30
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class People {
|
||||
|
||||
@Translate(dictClass = Dict.class, groupValue = "sex", translateField = "sexName")
|
||||
private String sex;
|
||||
|
||||
private String sexName;
|
||||
|
||||
/** 手机号脱敏 */
|
||||
@Translate(dictClass = Desensitized.class, translateField = "phone", desensitizedModel = DesensitizedTypeConstants.MOBILE_PHONE)
|
||||
private String phone;
|
||||
|
||||
/** 自定义翻译 */
|
||||
@Translate(dictionary = @Dictionary(translator = CustomerTranslateServiceImpl.class), translateField = "name")
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
}
|
27
src/test/java/com/aizuda/trans/entity/People2.java
Normal file
27
src/test/java/com/aizuda/trans/entity/People2.java
Normal file
@ -0,0 +1,27 @@
|
||||
package com.aizuda.trans.entity;
|
||||
|
||||
import com.aizuda.trans.annotation.Translate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 人
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 016 11:40:30
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class People2 {
|
||||
|
||||
/** 数据库翻译 */
|
||||
@Translate(dictClass = UserDB.class, translateField = "name")
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
}
|
25
src/test/java/com/aizuda/trans/entity/UserDB.java
Normal file
25
src/test/java/com/aizuda/trans/entity/UserDB.java
Normal file
@ -0,0 +1,25 @@
|
||||
package com.aizuda.trans.entity;
|
||||
|
||||
import com.aizuda.trans.annotation.Dictionary;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
/**
|
||||
* 用户(数据库)
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-12-16 016 14:07:27
|
||||
*/
|
||||
@Dictionary(codeColumn = "id", textColumn = {"user_name"})
|
||||
@TableName("sys_user")
|
||||
public class UserDB {
|
||||
|
||||
@TableId(value = "id", type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
|
||||
@TableField(value = "user_name")
|
||||
private String name;
|
||||
|
||||
}
|
28
src/test/java/com/aizuda/trans/enums/DeviceStatus.java
Normal file
28
src/test/java/com/aizuda/trans/enums/DeviceStatus.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.aizuda.trans.enums;
|
||||
|
||||
|
||||
/**
|
||||
* 设备状态
|
||||
*
|
||||
* @author nn200433
|
||||
* @date 2022-08-18 018 20:07:47
|
||||
*/
|
||||
public enum DeviceStatus implements IEnum {
|
||||
|
||||
NORMAL("0", "正常使用"),
|
||||
|
||||
UN_USE("1", "未使用"),
|
||||
|
||||
TRY_USE("2", "试运行"),
|
||||
|
||||
FAULT("3", "故障"),
|
||||
|
||||
REPAIR("4", "维修"),
|
||||
|
||||
REJECT("5", "报废");
|
||||
|
||||
private DeviceStatus(String code, String text) {
|
||||
init(code, text);
|
||||
}
|
||||
|
||||
}
|
6
src/test/resources/application.yml
Normal file
6
src/test/resources/application.yml
Normal file
@ -0,0 +1,6 @@
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
url: jdbc:mysql://192.168.0.44:3306/t_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowMultiQueries=true&useSSL=false&rewriteBatchedStatements=true
|
||||
username: root
|
||||
password: 123456
|
Loading…
x
Reference in New Issue
Block a user