diff --git a/README.md b/README.md
index bb9e126..74f47a1 100644
--- a/README.md
+++ b/README.md
@@ -1,75 +1,14 @@
-## 代码架构
+## 业务模块框架结构
-```mermaid
-flowchart LR
- demo-service-aaa-->demo-common
- demo-service-bbb-->demo-common
-```
+1. DDD 四层架构
-- demo
- - demo-common
- - demo-service
- - demo-service-aaa
- - net.rzdata
- - AaaApplication
- - aaa
- - xxx
- - XxxController
- - XxxService
- - XxxRepository
- - service
- - IXxxService4Yyy
- - IXxxService4Zzz
- - mapper
- - XxxMapper
- - domain
- - Xxx
- - XxxAddReq
- - XxxModifyReq
- - yyy
- - zzz
- - demo-service-bbb
+
-### 工程结构
+2. 服务调用流程
-demo 是一个系统的顶级POM,其 parent 为公司的顶级POM,对系统内部的依赖进行管理。
+
-demo-service 下是该系统的全部后端服务,每一个子模块对应一个后端服务。
-demo-service 的 POM 中添加所有服务的公共依赖,如 spring-boot-starter-web、mybatis-spring-boot-starter 等。
+3. 数据对象传输过程
-demo-common 中为该系统内服务的公共配置、公共接口、异常定义等。
-
-### 包结构
-
-XxxService 是该包的逻辑组织中枢。
-其方法分为 3 部分 —— protected 方法,用于向 XxxController 提供服务;public 方法,是对 service 包中的接口的实现。private 方法,对 protected 方法和 public 方法提供服务。
-如果要依赖其他包的服务,只能注入对应的接口(如 yyy 包需要依赖 xxx 包的服务,则在 YyyService 中注入 IXxxService4Yyy)。IXxxService4Yyy 向其他包提供有限的方法,由 XxxService 实现。
-
-XxxController 是向前端提供服务的门面。通常只依赖于 XxxService。
-
-domain.Xxx 是该包的核心模型,也是唯一的充血模型。Service 是基于核心模型进行逻辑组织。
-
-domain.XxxReq、domain.XxxResp 对应 Controller 中不同请求和响应的返回模型。
-大部分请求是不需要核心模型的全部字段的,比如新增请求不需要id,修改请求有部分字段不可变,不同的请求需要不同的返回字段等,这时可以定义 XxxReq、XxxResp 来实现,并在 Controller 层转换为核心模型。有些请求需要用到完整的核心模型的结构,则可以省略这一步骤。
-
-mapper 包下的类均继承自 mybatis-plus 的 BaseMapper,每一个类对应一张数据库表。
-XxxRepository 是对 XxxMapper 的封装。
-
-## 部署架构
-
-## 如何运行
-
-### 本地运行
-
-复制服务配置文件 application.yml 中的可配置项到 application-local.yml。
-
-在 application-local.yml 中修改配置,其中的配置项会覆盖 application.yml,但不会提交到 git。
-
-### 线上部署
-
-将 opentelemetry-javaagent.jar 映射到容器中的 /opt/agent 目录。
-
-与本地开发不同,部署时通过环境变量进行配置。application.yml 中有如 ${CONTEXT_PATH} 的可配置项,则能在环境变量中设置 CONTEXT_PATH 的值。
-
-## [开发规范](https://github.com/alibaba/p3c)
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index f91a35f..b302449 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,7 +27,7 @@
3.3.3
2.3
1.37.0
- 3.5.3.1
+ 3.5.5
3.9.1
5.8.18
4.10.0
diff --git a/service/system/src/main/java/net/rzdata/demo/controller/RedisCacheController.java b/service/system/src/main/java/net/rzdata/demo/controller/RedisCacheController.java
new file mode 100644
index 0000000..cc9ad22
--- /dev/null
+++ b/service/system/src/main/java/net/rzdata/demo/controller/RedisCacheController.java
@@ -0,0 +1,95 @@
+package net.rzdata.demo.controller;
+
+import lombok.RequiredArgsConstructor;
+import net.rzdata.demo.core.constant.CacheNames;
+import net.rzdata.demo.core.domain.R;
+import net.rzdata.demo.redis.utils.RedisUtils;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.Duration;
+
+/**
+ * spring-cache 演示案例
+ *
+ * @author Lion Li
+ */
+// 类级别 缓存统一配置
+//@CacheConfig(cacheNames = CacheNames.DEMO_CACHE)
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/demo/cache")
+public class RedisCacheController {
+
+ /**
+ * 测试 @Cacheable
+ *
+ * 表示这个方法有了缓存的功能,方法的返回值会被缓存下来
+ * 下一次调用该方法前,会去检查是否缓存中已经有值
+ * 如果有就直接返回,不调用方法
+ * 如果没有,就调用方法,然后把结果缓存起来
+ * 这个注解「一般用在查询方法上」
+ *
+ * 重点说明: 缓存注解严谨与其他筛选数据功能一起使用
+ * 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题
+ *
+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数
+ */
+ @Cacheable(cacheNames = "demo:cache#60s#10m#20", key = "#key", condition = "#key != null")
+ @GetMapping("/test1")
+ public R test1(String key, String value) {
+ return R.ok("操作成功", value);
+ }
+
+ /**
+ * 测试 @CachePut
+ *
+ * 加了@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用
+ * 它「通常用在新增或者实时更新方法上」
+ *
+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数
+ */
+ @CachePut(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
+ @GetMapping("/test2")
+ public R test2(String key, String value) {
+ return R.ok("操作成功", value);
+ }
+
+ /**
+ * 测试 @CacheEvict
+ *
+ * 使用了CacheEvict注解的方法,会清空指定缓存
+ * 「一般用在删除的方法上」
+ *
+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数
+ */
+ @CacheEvict(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
+ @GetMapping("/test3")
+ public R test3(String key, String value) {
+ return R.ok("操作成功", value);
+ }
+
+ /**
+ * 测试设置过期时间
+ * 手动设置过期时间10秒
+ * 11秒后获取 判断是否相等
+ */
+ @GetMapping("/test6")
+ public R test6(String key, String value) {
+ RedisUtils.setCacheObject(key, value);
+ boolean flag = RedisUtils.expire(key, Duration.ofSeconds(10));
+ System.out.println("***********" + flag);
+ try {
+ Thread.sleep(11 * 1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ Object obj = RedisUtils.getCacheObject(key);
+ return R.ok(value.equals(obj));
+ }
+
+}
diff --git a/service/system/src/main/java/net/rzdata/demo/controller/RedisLockController.java b/service/system/src/main/java/net/rzdata/demo/controller/RedisLockController.java
new file mode 100644
index 0000000..05fa443
--- /dev/null
+++ b/service/system/src/main/java/net/rzdata/demo/controller/RedisLockController.java
@@ -0,0 +1,71 @@
+package net.rzdata.demo.controller;
+
+import com.baomidou.lock.LockInfo;
+import com.baomidou.lock.LockTemplate;
+import com.baomidou.lock.annotation.Lock4j;
+import com.baomidou.lock.executor.RedissonLockExecutor;
+import lombok.extern.slf4j.Slf4j;
+import net.rzdata.demo.core.domain.R;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.LocalTime;
+
+
+/**
+ * 测试分布式锁的样例
+ *
+ * @author shenxinquan
+ */
+@Slf4j
+@RestController
+@RequestMapping("/demo/redisLock")
+public class RedisLockController {
+
+ @Autowired
+ private LockTemplate lockTemplate;
+
+ /**
+ * 测试lock4j 注解
+ */
+ @Lock4j(keys = {"#key"})
+ @GetMapping("/testLock4j")
+ public R testLock4j(String key, String value) {
+ System.out.println("start:" + key + ",time:" + LocalTime.now().toString());
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ System.out.println("end :" + key + ",time:" + LocalTime.now().toString());
+ return R.ok("操作成功", value);
+ }
+
+ /**
+ * 测试lock4j 工具
+ */
+ @GetMapping("/testLock4jLockTemplate")
+ public R testLock4jLockTemplate(String key, String value) {
+ final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class);
+ if (null == lockInfo) {
+ throw new RuntimeException("业务处理中,请稍后再试");
+ }
+ // 获取锁成功,处理业务
+ try {
+ try {
+ Thread.sleep(8000);
+ } catch (InterruptedException e) {
+ //
+ }
+ System.out.println("执行简单方法1 , 当前线程:" + Thread.currentThread().getName());
+ } finally {
+ //释放锁
+ lockTemplate.releaseLock(lockInfo);
+ }
+ //结束
+ return R.ok("操作成功", value);
+ }
+
+}
diff --git a/service/system/src/main/java/net/rzdata/demo/controller/RedisPubSubController.java b/service/system/src/main/java/net/rzdata/demo/controller/RedisPubSubController.java
new file mode 100644
index 0000000..6dacff8
--- /dev/null
+++ b/service/system/src/main/java/net/rzdata/demo/controller/RedisPubSubController.java
@@ -0,0 +1,47 @@
+package net.rzdata.demo.controller;
+
+import lombok.RequiredArgsConstructor;
+import net.rzdata.demo.core.domain.R;
+import net.rzdata.demo.redis.utils.RedisUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Redis 发布订阅 演示案例
+ *
+ * @author Lion Li
+ */
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/demo/redis/pubsub")
+public class RedisPubSubController {
+
+ /**
+ * 发布消息
+ *
+ * @param key 通道Key
+ * @param value 发送内容
+ */
+ @GetMapping("/pub")
+ public R pub(String key, String value) {
+ RedisUtils.publish(key, value, consumer -> {
+ System.out.println("发布通道 => " + key + ", 发送值 => " + value);
+ });
+ return R.ok("操作成功");
+ }
+
+ /**
+ * 订阅消息
+ *
+ * @param key 通道Key
+ */
+ @GetMapping("/sub")
+ public R sub(String key) {
+ RedisUtils.subscribe(key, String.class, msg -> {
+ System.out.println("订阅通道 => " + key + ", 接收值 => " + msg);
+ });
+ return R.ok("操作成功");
+ }
+
+}
diff --git a/service/system/src/main/java/net/rzdata/demo/controller/RedisRateLimiterController.java b/service/system/src/main/java/net/rzdata/demo/controller/RedisRateLimiterController.java
new file mode 100644
index 0000000..80d781e
--- /dev/null
+++ b/service/system/src/main/java/net/rzdata/demo/controller/RedisRateLimiterController.java
@@ -0,0 +1,64 @@
+package net.rzdata.demo.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import net.rzdata.demo.core.domain.R;
+import net.rzdata.demo.ratelimiter.annotation.RateLimiter;
+import net.rzdata.demo.ratelimiter.enums.LimitType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+/**
+ * 测试分布式限流样例
+ *
+ * @author Lion Li
+ */
+@Slf4j
+@RestController
+@RequestMapping("/demo/rateLimiter")
+public class RedisRateLimiterController {
+
+ /**
+ * 测试全局限流
+ * 全局影响
+ */
+ @RateLimiter(count = 2, time = 10)
+ @GetMapping("/test")
+ public R test(String value) {
+ return R.ok("操作成功", value);
+ }
+
+ /**
+ * 测试请求IP限流
+ * 同一IP请求受影响
+ */
+ @RateLimiter(count = 2, time = 10, limitType = LimitType.IP)
+ @GetMapping("/testip")
+ public R testip(String value) {
+ return R.ok("操作成功", value);
+ }
+
+ /**
+ * 测试集群实例限流
+ * 启动两个后端服务互不影响
+ */
+ @RateLimiter(count = 2, time = 10, limitType = LimitType.CLUSTER)
+ @GetMapping("/testcluster")
+ public R testcluster(String value) {
+ return R.ok("操作成功", value);
+ }
+
+ /**
+ * 测试请求IP限流(key基于参数获取)
+ * 同一IP请求受影响
+ *
+ * 简单变量获取 #变量 复杂表达式 #{#变量 != 1 ? 1 : 0}
+ */
+ @RateLimiter(count = 2, time = 10, limitType = LimitType.IP, key = "#value")
+ @GetMapping("/testObj")
+ public R testObj(String value) {
+ return R.ok("操作成功", value);
+ }
+
+}
diff --git a/service/system/src/main/java/net/rzdata/demo/controller/Swagger3DemoController.java b/service/system/src/main/java/net/rzdata/demo/controller/Swagger3DemoController.java
new file mode 100644
index 0000000..648f712
--- /dev/null
+++ b/service/system/src/main/java/net/rzdata/demo/controller/Swagger3DemoController.java
@@ -0,0 +1,31 @@
+package net.rzdata.demo.controller;
+
+import net.rzdata.demo.core.domain.R;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * swagger3 用法示例
+ *
+ * @author Lion Li
+ */
+@RestController
+@RequestMapping("/swagger/demo")
+public class Swagger3DemoController {
+
+ /**
+ * 上传请求
+ * 必须使用 @RequestPart 注解标注为文件
+ *
+ * @param file 文件
+ */
+ @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+ public R upload(@RequestPart("file") MultipartFile file) {
+ return R.ok("操作成功", file.getOriginalFilename());
+ }
+
+}
diff --git a/service/system/src/main/java/net/rzdata/demo/controller/TestBatchController.java b/service/system/src/main/java/net/rzdata/demo/controller/TestBatchController.java
new file mode 100644
index 0000000..89ca8e4
--- /dev/null
+++ b/service/system/src/main/java/net/rzdata/demo/controller/TestBatchController.java
@@ -0,0 +1,90 @@
+package net.rzdata.demo.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import net.rzdata.demo.core.domain.R;
+import net.rzdata.demo.web.core.BaseController;
+import net.rzdata.demo.domain.TestDemo;
+import net.rzdata.demo.mapper.TestDemoMapper;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 测试批量方法
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/demo/batch")
+public class TestBatchController extends BaseController {
+
+ /**
+ * 为了便于测试 直接引入mapper
+ */
+ private final TestDemoMapper testDemoMapper;
+
+ /**
+ * 新增批量方法 可完美替代 saveBatch 秒级插入上万数据 (对mysql负荷较大)
+ *
+ * 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度
+ */
+ @PostMapping("/add")
+// @DS("slave")
+ public R add() {
+ List list = new ArrayList<>();
+ for (int i = 0; i < 1000; i++) {
+ TestDemo testDemo = new TestDemo();
+ testDemo.setOrderNum(-1);
+ testDemo.setTestKey("批量新增");
+ testDemo.setValue("测试新增");
+ list.add(testDemo);
+ }
+ return toAjax(testDemoMapper.insertBatch(list));
+ }
+
+ /**
+ * 新增或更新 可完美替代 saveOrUpdateBatch 高性能
+ *
+ * 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度
+ */
+ @PostMapping("/addOrUpdate")
+// @DS("slave")
+ public R addOrUpdate() {
+ List list = new ArrayList<>();
+ for (int i = 0; i < 1000; i++) {
+ TestDemo testDemo = new TestDemo();
+ testDemo.setOrderNum(-1);
+ testDemo.setTestKey("批量新增");
+ testDemo.setValue("测试新增");
+ list.add(testDemo);
+ }
+ testDemoMapper.insertBatch(list);
+ for (int i = 0; i < list.size(); i++) {
+ TestDemo testDemo = list.get(i);
+ testDemo.setTestKey("批量新增或修改");
+ testDemo.setValue("批量新增或修改");
+ if (i % 2 == 0) {
+ testDemo.setId(null);
+ }
+ }
+ return toAjax(testDemoMapper.insertOrUpdateBatch(list));
+ }
+
+ /**
+ * 删除批量方法
+ */
+ @DeleteMapping()
+// @DS("slave")
+ public R remove() {
+ return toAjax(testDemoMapper.delete(new LambdaQueryWrapper()
+ .eq(TestDemo::getOrderNum, -1L)));
+ }
+
+}
diff --git a/service/system/src/main/java/net/rzdata/demo/controller/TestDemoController.java b/service/system/src/main/java/net/rzdata/demo/controller/TestDemoController.java
new file mode 100644
index 0000000..3f4066e
--- /dev/null
+++ b/service/system/src/main/java/net/rzdata/demo/controller/TestDemoController.java
@@ -0,0 +1,140 @@
+package net.rzdata.demo.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import net.rzdata.demo.core.domain.R;
+import net.rzdata.demo.core.utils.MapstructUtils;
+import net.rzdata.demo.core.utils.ValidatorUtils;
+import net.rzdata.demo.core.validate.AddGroup;
+import net.rzdata.demo.core.validate.EditGroup;
+import net.rzdata.demo.core.validate.QueryGroup;
+import net.rzdata.demo.excel.core.ExcelResult;
+import net.rzdata.demo.excel.utils.ExcelUtil;
+import net.rzdata.demo.idempotent.annotation.RepeatSubmit;
+import net.rzdata.demo.mybatis.core.page.PageQuery;
+import net.rzdata.demo.mybatis.core.page.TableDataInfo;
+import net.rzdata.demo.web.core.BaseController;
+import net.rzdata.demo.domain.TestDemo;
+import net.rzdata.demo.domain.bo.TestDemoBo;
+import net.rzdata.demo.domain.bo.TestDemoImportVo;
+import net.rzdata.demo.domain.vo.TestDemoVo;
+import net.rzdata.demo.service.ITestDemoService;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 测试单表Controller
+ *
+ * @author Lion Li
+ * @date 2021-07-26
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/demo/demo")
+public class TestDemoController extends BaseController {
+
+ private final ITestDemoService testDemoService;
+
+ /**
+ * 查询测试单表列表
+ */
+ @SaCheckPermission("demo:demo:list")
+ @GetMapping("/list")
+ public TableDataInfo list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
+ return testDemoService.queryPageList(bo, pageQuery);
+ }
+
+ /**
+ * 自定义分页查询
+ */
+ @SaCheckPermission("demo:demo:list")
+ @GetMapping("/page")
+ public TableDataInfo page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
+ return testDemoService.customPageList(bo, pageQuery);
+ }
+
+ /**
+ * 导入数据
+ *
+ * @param file 导入文件
+ */
+ @SaCheckPermission("demo:demo:import")
+ @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+ public R importData(@RequestPart("file") MultipartFile file) throws Exception {
+ ExcelResult excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true);
+ List list = MapstructUtils.convert(excelResult.getList(), TestDemo.class);
+ testDemoService.saveBatch(list);
+ return R.ok(excelResult.getAnalysis());
+ }
+
+ /**
+ * 导出测试单表列表
+ */
+ @SaCheckPermission("demo:demo:export")
+ @PostMapping("/export")
+ public void export(@Validated TestDemoBo bo, HttpServletResponse response) {
+ List list = testDemoService.queryList(bo);
+ // 测试雪花id导出
+// for (TestDemoVo vo : list) {
+// vo.setId(1234567891234567893L);
+// }
+ ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, response);
+ }
+
+ /**
+ * 获取测试单表详细信息
+ *
+ * @param id 测试ID
+ */
+ @SaCheckPermission("demo:demo:query")
+ @GetMapping("/{id}")
+ public R getInfo(@NotNull(message = "主键不能为空")
+ @PathVariable("id") Long id) {
+ return R.ok(testDemoService.queryById(id));
+ }
+
+ /**
+ * 新增测试单表
+ */
+ @SaCheckPermission("demo:demo:add")
+ @RepeatSubmit(interval = 2, timeUnit = TimeUnit.SECONDS, message = "{repeat.submit.message}")
+ @PostMapping()
+ public R add(@RequestBody TestDemoBo bo) {
+ // 使用校验工具对标 @Validated(AddGroup.class) 注解
+ // 用于在非 Controller 的地方校验对象
+ ValidatorUtils.validate(bo, AddGroup.class);
+ return toAjax(testDemoService.insertByBo(bo));
+ }
+
+ /**
+ * 修改测试单表
+ */
+ @SaCheckPermission("demo:demo:edit")
+ @RepeatSubmit
+ @PutMapping()
+ public R edit(@Validated(EditGroup.class) @RequestBody TestDemoBo bo) {
+ return toAjax(testDemoService.updateByBo(bo));
+ }
+
+ /**
+ * 删除测试单表
+ *
+ * @param ids 测试ID串
+ */
+ @SaCheckPermission("demo:demo:remove")
+ @DeleteMapping("/{ids}")
+ public R remove(@NotEmpty(message = "主键不能为空")
+ @PathVariable Long[] ids) {
+ return toAjax(testDemoService.deleteWithValidByIds(Arrays.asList(ids), true));
+ }
+}
diff --git a/service/system/src/main/java/net/rzdata/demo/controller/TestEncryptController.java b/service/system/src/main/java/net/rzdata/demo/controller/TestEncryptController.java
new file mode 100644
index 0000000..daf2047
--- /dev/null
+++ b/service/system/src/main/java/net/rzdata/demo/controller/TestEncryptController.java
@@ -0,0 +1,55 @@
+package net.rzdata.demo.controller;
+
+import net.rzdata.demo.core.domain.R;
+import net.rzdata.demo.domain.TestDemoEncrypt;
+import net.rzdata.demo.mapper.TestDemoEncryptMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * 测试数据库加解密功能
+ *
+ * @author Lion Li
+ */
+@Validated
+@RestController
+@RequestMapping("/demo/encrypt")
+public class TestEncryptController {
+
+ @Autowired
+ private TestDemoEncryptMapper mapper;
+ @Value("${mybatis-encryptor.enable:false}")
+ private Boolean encryptEnable;
+
+ /**
+ * 测试数据库加解密
+ *
+ * @param key 测试key
+ * @param value 测试value
+ */
+ @GetMapping()
+ public R