多租户后台系统

master
段云飞 3 months ago
commit 10c1463a9e

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-vue-plus</artifactId>
<version>5.3.0</version>
</parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-modules</artifactId>
<version>5.3.0</version>
<packaging>pom</packaging>
<description>ruoyi-modules 业务模块</description>
<modules>
<module>ruoyi-demo</module>
<module>ruoyi-generator</module>
<module>ruoyi-job</module>
<module>ruoyi-system</module>
<module>ruoyi-workflow</module>
</modules>
</project>

@ -0,0 +1,27 @@
<?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>
<artifactId>ruoyi-vue-plus</artifactId>
<groupId>org.dromara</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<modules>
<module>ruoyi-demo</module>
<module>ruoyi-generator</module>
<module>ruoyi-job</module>
<module>ruoyi-system</module>
<module>ruoyi-workflow</module>
</modules>
<artifactId>ruoyi-modules</artifactId>
<packaging>pom</packaging>
<description>
ruoyi-modules 业务模块
</description>
</project>

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-modules</artifactId>
<version>5.3.0</version>
</parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-demo</artifactId>
<version>5.3.0</version>
<description>demo模块</description>
<dependencies>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-sms</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mail</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-redis</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-idempotent</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-log</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-excel</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-security</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-web</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-ratelimiter</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-translation</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-sensitive</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-encrypt</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-tenant</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-websocket</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,108 @@
<?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.dromara</groupId>
<artifactId>ruoyi-modules</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-demo</artifactId>
<description>
demo模块
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-sms</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mail</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-redis</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-idempotent</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-log</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-excel</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-security</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-web</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-ratelimiter</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-translation</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-sensitive</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-encrypt</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-tenant</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-websocket</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,77 @@
package org.dromara.demo.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.mail.utils.MailUtils;
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.io.File;
import java.util.Arrays;
/**
*
*
* @author Michelle.Chung
*/
@SaIgnore
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/mail")
public class MailController {
@GetMapping("/sendSimpleMessage")
// 定义一个公共方法 sendSimpleMessage返回类型为 R<Void>。R 通常是自定义的统一响应结果类,
// Void 表示该方法不返回具体的数据内容,主要用于返回操作的状态信息
public R<Void> sendSimpleMessage(String to, String subject, String text) {
// 调用 MailUtils 工具类的 sendText 方法,用于发送简单的文本邮件
// to 参数表示收件人的邮箱地址
// subject 参数表示邮件的主题
// text 参数表示邮件的正文内容
MailUtils.sendText(to, subject, text);
// 调用 R 类的 ok 方法,将操作成功的默认信息封装成统一响应结果类 R 的对象并返回
// 以此告知调用者邮件发送操作已成功执行
return R.ok();
}
@GetMapping("/sendMessageWithAttachment")
// 定义一个公共方法 sendMessageWithAttachment返回类型为 R<Void>。
// R 通常是自定义的统一响应结果类,用于封装接口的响应信息,
// Void 表明该方法不返回具体的数据内容,主要用于反馈操作的结果状态
public R<Void> sendMessageWithAttachment(String to, String subject, String text, String filePath) {
// 调用 MailUtils 工具类的 sendText 方法,用于发送带有附件的文本邮件
// to 参数代表收件人的邮箱地址,指定邮件要发送到哪里
// subject 参数是邮件的主题,用于简要概括邮件的主要内容
// text 参数为邮件的正文文本,即邮件要传达的具体信息
// new File(filePath) 创建一个 File 对象filePath 是附件文件的路径,将该文件作为附件添加到邮件中
MailUtils.sendText(to, subject, text, new File(filePath));
// 调用 R 类的 ok 方法,将操作成功的默认信息封装到 R 对象中并返回
// 以此告知调用者带有附件的邮件发送操作已成功完成
return R.ok();
}
@GetMapping("/sendMessageWithAttachments")
// 定义一个公共方法 sendMessageWithAttachments返回类型为 R<Void>。
// R 通常是自定义的统一响应结果类,用于封装接口响应信息,
// Void 表示该方法不返回具体的数据内容,仅用于表示操作结果状态
public R<Void> sendMessageWithAttachments(String to, String subject, String text, String[] paths) {
// 使用 Java 8 的 Stream API 对 paths 数组进行处理
// Arrays.stream(paths) 将 paths 数组转换为流
// .map(File::new) 对流中的每个元素(即文件路径字符串)应用 File 类的构造函数,将其转换为 File 对象
// .toArray(File[]::new) 将流中的元素收集到一个新的 File 类型数组中
File[] array = Arrays.stream(paths).map(File::new).toArray(File[]::new);
// 调用 MailUtils 工具类的 sendText 方法,用于发送带有多个附件的文本邮件
// to 参数为收件人的邮箱地址
// subject 参数是邮件的主题
// text 参数是邮件的正文内容
// array 是包含多个附件文件的 File 数组
MailUtils.sendText(to, subject, text, array);
// 调用 R 类的 ok 方法,将操作成功的默认信息封装到 R 对象中并返回
// 表示带有多个附件的邮件发送操作已成功完成
return R.ok();
}
}

@ -0,0 +1,98 @@
package org.dromara.demo.controller;
import cn.hutool.core.thread.ThreadUtil;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
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
* <p>
* ,
* ,
* ,
* ,,
*
* <p>
* : 使
* : 穿
* <p>
* cacheNames {@link CacheNames}
*/
@Cacheable(cacheNames = "demo:cache#60s#10m#20", key = "#key", condition = "#key != null")
@GetMapping("/test1")
public R<String> test1(String key, String value) {
return R.ok("操作成功", value);
}
/**
* @CachePut
* <p>
* @CachePut,put,使
*
* <p>
* cacheNames {@link CacheNames}
*/
@CachePut(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
@GetMapping("/test2")
public R<String> test2(String key, String value) {
return R.ok("操作成功", value);
}
/**
* @CacheEvict
* <p>
* 使CacheEvict,
*
* <p>
* cacheNames {@link CacheNames}
*/
@CacheEvict(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
@GetMapping("/test3")
public R<String> test3(String key, String value) {
return R.ok("操作成功", value);
}
/**
*
* 10
* 11
*/
@GetMapping("/test6")
// 定义一个公共方法 test6返回类型为 R<Boolean>,通常 R 是自定义的统一响应结果类,这里泛型指定为 Boolean 类型,表示返回的结果是布尔值
public R<Boolean> test6(String key, String value) {
// 调用 RedisUtils 工具类的 setCacheObject 方法,将传入的 key 和 value 存储到 Redis 缓存中
RedisUtils.setCacheObject(key, value);
// 调用 RedisUtils 工具类的 expire 方法,为刚刚存储到 Redis 中的 key 设置过期时间为 10 秒
// 该方法会返回一个布尔值,表示设置过期时间是否成功
boolean flag = RedisUtils.expire(key, Duration.ofSeconds(10));
// 在控制台输出设置过期时间的结果,方便调试查看
System.out.println("***********" + flag);
// 使用 ThreadUtil 工具类让当前线程休眠 11 秒,确保 Redis 中的 key 已经过期
ThreadUtil.sleep(11 * 1000);
// 调用 RedisUtils 工具类的 getCacheObject 方法,尝试从 Redis 中获取指定 key 对应的 value
Object obj = RedisUtils.getCacheObject(key);
// 将比较结果(即传入的 value 是否和从 Redis 中获取的 obj 相等)封装到自定义的统一响应结果类 R 中并返回
return R.ok(value.equals(obj));}
}

@ -0,0 +1,69 @@
package org.dromara.demo.controller;
import cn.hutool.core.thread.ThreadUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.lock.executor.RedissonLockExecutor;
import org.dromara.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
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")
// 定义一个公共方法 testLock4j返回类型为 R<String>。R 通常是自定义的统一响应结果类,泛型指定为 String 类型,表示返回的数据部分为字符串
public R<String> testLock4j(String key, String value) {
// 在控制台输出方法开始执行的信息,包含传入的 key 和当前的本地时间
System.out.println("start:" + key + ",time:" + LocalTime.now());
// 调用 ThreadUtil 工具类的 sleep 方法,让当前线程休眠 10000 毫秒(即 10 秒)
ThreadUtil.sleep(10000);
// 在控制台输出方法执行结束的信息,包含传入的 key 和当前的本地时间
System.out.println("end :" + key + ",time:" + LocalTime.now());
// 将操作成功的消息和传入的 value 封装到自定义的统一响应结果类 R 中并返回
return R.ok("操作成功", value);
}
/**
* lock4j
*/
@GetMapping("/testLock4jLockTemplate")
public R<String> testLock4jLockTemplate(String key, String value) {
final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class);
if (null == lockInfo) {
throw new RuntimeException("业务处理中,请稍后再试");
}
// 获取锁成功,处理业务
try {
ThreadUtil.sleep(8000);
System.out.println("执行简单方法1 , 当前线程:" + Thread.currentThread().getName());
} finally {
//释放锁
lockTemplate.releaseLock(lockInfo);
}
//结束
return R.ok("操作成功", value);
}
}

@ -0,0 +1,62 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
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")
// 定义一个公共方法 pub返回类型为 R<Void>。R 通常是自定义的统一响应结果类,
// Void 表示该方法不返回具体的数据内容,主要用于返回操作结果信息
public R<Void> pub(String key, String value) {
// 调用 RedisUtils 工具类的 publish 方法,用于在 Redis 中进行消息发布操作
// 该方法接收三个参数key 表示消息发布的通道名称value 是要发布的消息内容
// 第三个参数是一个消费者函数,在消息发布时会执行该函数内的逻辑
RedisUtils.publish(key, value, consumer -> {
// 当消息发布时,在控制台输出发布的通道名称和发送的消息值
System.out.println("发布通道 => " + key + ", 发送值 => " + value);
});
// 调用 R 类的 ok 方法,将操作成功的信息封装成统一响应结果类 R 的对象并返回
return R.ok("操作成功");
}
/**
*
*
* @param key Key
*/
@GetMapping("/sub")
// 定义一个公共方法 sub返回类型为 R<Void>。R 通常是自定义的封装响应结果的类,
// Void 表明该方法的响应结果不携带具体的数据,仅传达操作状态
public R<Void> sub(String key) {
// 调用 RedisUtils 工具类的 subscribe 方法,目的是在 Redis 中订阅指定通道的消息
// key 参数代表要订阅的通道名称
// String.class 指定了接收到的消息的数据类型为 String
// 最后一个参数是一个 Lambda 表达式,作为消息处理的回调函数
RedisUtils.subscribe(key, String.class, msg -> {
// 当接收到订阅通道的消息时,在控制台输出订阅的通道名称以及接收到的消息内容
System.out.println("订阅通道 => " + key + ", 接收值 => " + msg);
});
// 调用 R 类的 ok 方法,将 "操作成功" 这个消息封装到 R 对象中并返回,
// 以此告知调用者订阅操作已经成功完成
return R.ok("操作成功");
}
}

@ -0,0 +1,64 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.common.ratelimiter.annotation.RateLimiter;
import org.dromara.common.ratelimiter.enums.LimitType;
import lombok.extern.slf4j.Slf4j;
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<String> test(String value) {
return R.ok("操作成功", value);
}
/**
* IP
* IP
*/
@RateLimiter(count = 2, time = 10, limitType = LimitType.IP)
@GetMapping("/testip")
public R<String> testip(String value) {
return R.ok("操作成功", value);
}
/**
*
*
*/
@RateLimiter(count = 2, time = 10, limitType = LimitType.CLUSTER)
@GetMapping("/testcluster")
public R<String> 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<String> testObj(String value) {
return R.ok("操作成功", value);
}
}

@ -0,0 +1,101 @@
package org.dromara.demo.controller;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
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.LinkedHashMap;
/**
*
* 使
*
* @author Lion Li
* @version 4.2.0
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/sms")
public class SmsController {
@GetMapping("/sendAliyun")
// 定义一个公共方法 sendAliyun返回类型为 R<Object>。R 通常是自定义的统一响应结果类,
// 泛型为 Object 表示响应中携带的数据可以是任意类型,此方法用于向外返回发送短信操作的结果
public R<Object> sendAliyun(String phones, String templateId) {
// 创建一个初始容量为 1 的 LinkedHashMap 对象 map用于存储短信模板所需的参数
// LinkedHashMap 可以保证元素插入的顺序,便于维护参数顺序
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
// 向 map 中添加一个键值对,键为 "code",值为 "1234"
// 这个参数可能用于填充短信模板中的验证码字段
map.put("code", "1234");
// 调用 SmsFactory 类的 getSmsBlend 方法,根据配置名称 "config1" 获取一个 SmsBlend 对象
// SmsBlend 对象封装了短信发送的相关配置和操作方法
SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
// 调用 smsBlend 对象的 sendMessage 方法,传入手机号码 phones、短信模板 ID templateId 以及模板参数 map
// 该方法会尝试发送短信,并返回一个 SmsResponse 对象,该对象包含了短信发送的结果信息
SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map);
// 调用 R 类的 ok 方法,将短信发送的响应结果 smsResponse 封装到统一响应结果类 R 中并返回
// 表示本次短信发送操作的结果可以正常返回给调用者
return R.ok(smsResponse);
}
@GetMapping("/sendTencent")
// 定义一个公共方法 sendTencent返回类型为 R<Object>。R 通常是自定义的统一响应结果类,
// 泛型使用 Object 意味着响应数据可以是任意类型,此方法用于返回使用腾讯短信服务发送短信的操作结果
public R<Object> sendTencent(String phones, String templateId) {
// 创建一个初始容量为 1 的 LinkedHashMap 对象 map用于存储短信模板所需的参数
// LinkedHashMap 能保证元素插入的顺序,方便维护参数顺序
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
// 这里注释掉的代码,原本是想向 map 中添加键为 "2",值为 "测试测试" 的键值对
// 可能是在开发过程中暂时不需要这个参数,所以注释掉了
// map.put("2", "测试测试");
// 向 map 中添加一个键值对,键为 "1",值为 "1234"
// 这个参数可能用于填充腾讯短信模板中的特定占位符
map.put("1", "1234");
// 调用 SmsFactory 类的 getSmsBlend 方法,根据配置名称 "config2" 获取一个 SmsBlend 对象
// SmsBlend 对象封装了使用腾讯短信服务进行短信发送的相关配置和操作方法
SmsBlend smsBlend = SmsFactory.getSmsBlend("config2");
// 调用 smsBlend 对象的 sendMessage 方法,传入手机号码 phones、短信模板 ID templateId 以及模板参数 map
// 该方法会尝试使用腾讯短信服务发送短信,并返回一个 SmsResponse 对象
// SmsResponse 对象包含了短信发送的结果信息,如发送是否成功、错误码等
SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map);
// 调用 R 类的 ok 方法,将短信发送的响应结果 smsResponse 封装到统一响应结果类 R 中并返回
// 表示本次使用腾讯短信服务发送短信的操作结果可以正常返回给调用者
return R.ok(smsResponse);
}
@GetMapping("/addBlacklist")
// 定义一个公共方法 addBlacklist返回类型为 R<Object>。R 通常是自定义的统一响应结果类,
// 泛型为 Object 表示响应中可携带任意类型的数据,此方法用于返回添加手机号码到黑名单操作的结果
public R<Object> addBlacklist(String phone) {
// 调用 SmsFactory 类的 getSmsBlend 方法,根据配置名称 "config1" 获取一个 SmsBlend 对象
// SmsBlend 对象封装了短信服务相关的配置和操作方法,这里可能是与阿里云短信服务相关的配置
SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
// 调用 smsBlend 对象的 joinInBlacklist 方法,将传入的手机号码 phone 添加到短信服务的黑名单中
smsBlend.joinInBlacklist(phone);
// 调用 R 类的 ok 方法,将操作成功的默认信息封装到统一响应结果类 R 中并返回
// 表示将手机号码添加到黑名单的操作已成功完成
return R.ok();
}
@GetMapping("/removeBlacklist")
// 定义一个公共方法 removeBlacklist返回类型为 R<Object>。
// R 通常是自定义的统一响应结果类,泛型为 Object 表明该方法返回的响应数据可以是任意类型,
// 此方法主要用于返回从短信服务黑名单中移除指定手机号码操作的结果
public R<Object> removeBlacklist(String phone) {
// 调用 SmsFactory 类的 getSmsBlend 方法,依据配置名称 "config1" 获取一个 SmsBlend 对象。
// SmsBlend 对象封装了短信服务相关的配置与操作方法,这里可能关联着特定的短信服务(如阿里云)配置
SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
// 调用 smsBlend 对象的 removeFromBlacklist 方法,将传入的手机号码 phone 从短信服务的黑名单中移除
smsBlend.removeFromBlacklist(phone);
// 调用 R 类的 ok 方法,把操作成功的默认信息封装到统一响应结果类 R 中并返回,
// 以此表示将手机号码从黑名单移除的操作已成功完成
return R.ok();
}
}

@ -0,0 +1,25 @@
package org.dromara.demo.controller;
import org.dromara.common.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;
@RestController
@RequestMapping("/swagger/demo")
// 定义一个名为 Swagger3DemoController 的公共类,通常这是一个控制器类,用于处理 HTTP 请求
public class Swagger3DemoController {
// 使用 @PostMapping 注解,表明该方法处理 HTTP POST 请求
// value = "/upload" 表示该方法映射的 URL 路径为 /upload
// consumes = MediaType.MULTIPART_FORM_DATA_VALUE 表示该方法只处理 multipart/form-data 类型的请求,一般用于文件上传
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
// 定义一个公共方法 upload返回类型为 R<String>。R 通常是自定义的统一响应结果类,泛型为 String 表示响应数据是字符串类型
// @RequestPart("file") 注解用于从请求中获取名为 "file" 的部分,这里期望是一个上传的文件
// MultipartFile 是 Spring 框架提供的用于处理文件上传的接口file 是接收上传文件的参数
public R<String> upload(@RequestPart("file") MultipartFile file) {
// 调用 R 类的 ok 方法,将 "操作成功" 作为消息,文件的原始文件名作为数据封装到统一响应结果类 R 中并返回
return R.ok("操作成功", file.getOriginalFilename());
}
}

@ -0,0 +1,114 @@
package org.dromara.demo.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController;
import org.dromara.demo.domain.TestDemo;
import org.dromara.demo.mapper.TestDemoMapper;
import lombok.RequiredArgsConstructor;
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 {
private final TestDemoMapper testDemoMapper;
@PostMapping("/add")
// @DS("slave")
// 定义一个公共方法 add返回类型为 R<Void>。R 通常是自定义的统一响应结果类,
// Void 表示该方法不返回具体的数据内容,主要用于返回操作结果信息
public R<Void> add() {
// 创建一个用于存储 TestDemo 对象的 ArrayList 列表
// 后续会将创建的 TestDemo 对象添加到这个列表中,以便进行批量操作
List<TestDemo> list = new ArrayList<>();
// 使用 for 循环,循环 1000 次,目的是创建 1000 个 TestDemo 对象
for (int i = 0; i < 1000; i++) {
// 实例化一个 TestDemo 对象,用于表示要添加到数据库中的数据
TestDemo testDemo = new TestDemo();
// 为 TestDemo 对象的 orderNum 属性设置值为 -1
testDemo.setOrderNum(-1);
// 为 TestDemo 对象的 testKey 属性设置值为 "批量新增"
testDemo.setTestKey("批量新增");
// 为 TestDemo 对象的 value 属性设置值为 "测试新增"
testDemo.setValue("测试新增");
// 将创建好并设置好属性的 TestDemo 对象添加到之前创建的列表中
list.add(testDemo);
}
// 调用 toAjax 方法,将 testDemoMapper 的 insertBatch 方法的返回结果作为参数传入
// testDemoMapper 通常是 MyBatis 等持久层框架的 Mapper 接口实例,用于和数据库交互
// insertBatch 方法用于将列表中的 TestDemo 对象批量插入到数据库中
// toAjax 方法的作用可能是将数据库操作的结果封装成统一的响应格式,方便前端处理
return toAjax(testDemoMapper.insertBatch(list));
}
@PostMapping("/addOrUpdate")
// @DS("slave")
// 定义一个公共方法 addOrUpdate返回类型为 R<Void>
public R<Void> addOrUpdate() {
// 创建一个用于存储 TestDemo 对象的列表
List<TestDemo> list = new ArrayList<>();
// 循环 1000 次,用于批量创建 TestDemo 对象
for (int i = 0; i < 1000; i++) {
// 创建一个新的 TestDemo 对象
TestDemo testDemo = new TestDemo();
// 设置 TestDemo 对象的 orderNum 属性为 -1
testDemo.setOrderNum(-1);
// 设置 TestDemo 对象的 testKey 属性为 "批量新增"
testDemo.setTestKey("批量新增");
// 设置 TestDemo 对象的 value 属性为 "测试新增"
testDemo.setValue("测试新增");
// 将创建好的 TestDemo 对象添加到列表中
list.add(testDemo);
}
// 调用 testDemoMapper 的 insertBatch 方法,将列表中的 TestDemo 对象批量插入数据库
testDemoMapper.insertBatch(list);
// 遍历列表中的每个 TestDemo 对象
for (int i = 0; i < list.size(); i++) {
// 从列表中获取当前索引对应的 TestDemo 对象
TestDemo testDemo = list.get(i);
// 更新 TestDemo 对象的 testKey 属性为 "批量新增或修改"
testDemo.setTestKey("批量新增或修改");
// 更新 TestDemo 对象的 value 属性为 "批量新增或修改"
testDemo.setValue("批量新增或修改");
// 如果当前索引是偶数
if (i % 2 == 0) {
// 将 TestDemo 对象的 id 属性设置为 null可能表示这是一个新增操作
testDemo.setId(null);
}
}
// 调用 testDemoMapper 的 insertOrUpdateBatch 方法,批量新增或更新列表中的 TestDemo 对象
// 然后调用 toAjax 方法将操作结果封装成 R 对象并返回
return toAjax(testDemoMapper.insertOrUpdateBatch(list));
}
/**
*
*/
@DeleteMapping()
// @DS("slave")
// 定义一个公共方法 remove返回类型为 R<Void>。R 通常是自定义的统一响应结果类,
// Void 表示该方法不返回具体的数据内容,主要用于返回操作结果信息
public R<Void> remove() {
// 调用 toAjax 方法,将 testDemoMapper 的 delete 方法的返回结果作为参数传入
// testDemoMapper 通常是 MyBatis-Plus 等持久层框架的 Mapper 接口实例,用于和数据库交互
return toAjax(testDemoMapper.delete(
// 创建一个 LambdaQueryWrapper 对象,用于构建查询条件
// LambdaQueryWrapper 是 MyBatis-Plus 提供的用于方便构建查询条件的工具类
new LambdaQueryWrapper<TestDemo>()
// 使用 eq 方法添加一个等值查询条件
// TestDemo::getOrderNum 是 Java 8 的方法引用,用于指定要查询的字段为 orderNum
// -1L 表示要查询的 orderNum 字段的值为 -1这里的 L 表示是长整型)
.eq(TestDemo::getOrderNum, -1L)
));
// 这里多余的 } 应该是代码错误,需要删除
}}

@ -0,0 +1,181 @@
package org.dromara.demo.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.core.validate.QueryGroup;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.demo.domain.TestDemo;
import org.dromara.demo.domain.bo.TestDemoBo;
import org.dromara.demo.domain.bo.TestDemoImportVo;
import org.dromara.demo.domain.vo.TestDemoVo;
import org.dromara.demo.service.ITestDemoService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.*;
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<TestDemoVo> list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
return testDemoService.queryPageList(bo, pageQuery);
}
/**
*
*/
@SaCheckPermission("demo:demo:list")
@GetMapping("/page")
public TableDataInfo<TestDemoVo> page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
return testDemoService.customPageList(bo, pageQuery);
}
// 使用 @Log 注解,用于记录操作日志
// title 属性指定日志的标题为 "测试单表"
// businessType 属性指定业务类型为 IMPORT表示这是一个导入操作
@Log(title = "测试单表", businessType = BusinessType.IMPORT)
// 使用 @SaCheckPermission 注解,用于权限校验
// 表示执行该方法需要具备 "demo:demo:import" 这个权限
@SaCheckPermission("demo:demo:import")
// 使用 @PostMapping 注解,表明该方法处理 HTTP POST 请求
// value = "/importData" 表示该方法映射的 URL 路径为 /importData
// consumes = MediaType.MULTIPART_FORM_DATA_VALUE 表示该方法只处理 multipart/form-data 类型的请求,通常用于文件上传
@PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
// 定义一个公共方法 importData返回类型为 R<Void>。R 通常是自定义的统一响应结果类,
// Void 表示该方法不返回具体的数据内容,主要用于返回操作结果信息
// @RequestPart("file") 注解用于从请求中获取名为 "file" 的部分,这里期望是一个上传的文件
// MultipartFile 是 Spring 框架提供的用于处理文件上传的接口file 是接收上传文件的参数
// throws Exception 表示该方法可能会抛出异常,调用者需要进行异常处理
public R<Void> importData(@RequestPart("file") MultipartFile file) throws Exception {
// 调用 ExcelUtil 工具类的 importExcel 方法,将上传文件的输入流转换为 Excel 解析结果
// file.getInputStream() 获取上传文件的输入流
// TestDemoImportVo.class 指定解析结果的数据类型为 TestDemoImportVo
// true 表示是否严格模式,具体含义取决于 ExcelUtil 类的实现
ExcelResult<TestDemoImportVo> excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true);
// 调用 MapstructUtils 工具类的 convert 方法,将 Excel 解析结果中的列表数据转换为 TestDemo 类型的列表
// excelResult.getList() 获取 Excel 解析结果中的数据列表
// TestDemo.class 指定转换的目标类型为 TestDemo
List<TestDemo> list = MapstructUtils.convert(excelResult.getList(), TestDemo.class);
// 调用 testDemoService 的 saveBatch 方法,将转换后的 TestDemo 列表批量保存到数据库中
// 这里的 testDemoService 是业务逻辑层的服务类,负责与数据库交互
testDemoService.saveBatch(list);
// 调用 R 类的 ok 方法,将 Excel 解析结果中的分析信息封装到统一响应结果类 R 中并返回
// excelResult.getAnalysis() 获取 Excel 解析结果中的分析信息
return R.ok(excelResult.getAnalysis());
}
/**
*
*/
@SaCheckPermission("demo:demo:export")
@Log(title = "测试单表", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Validated TestDemoBo bo, HttpServletResponse response) {
List<TestDemoVo> 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<TestDemoVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable("id") Long id) {
return R.ok(testDemoService.queryById(id));
}
/**
*
*/
@SaCheckPermission("demo:demo:add")
@Log(title = "测试单表", businessType = BusinessType.INSERT)
@RepeatSubmit(interval = 2, timeUnit = TimeUnit.SECONDS, message = "{repeat.submit.message}")
@PostMapping()
public R<Void> add(@RequestBody TestDemoBo bo) {
// 使用校验工具对标 @Validated(AddGroup.class) 注解
// 用于在非 Controller 的地方校验对象
ValidatorUtils.validate(bo, AddGroup.class);
return toAjax(testDemoService.insertByBo(bo));
}
/**
*
*/
@SaCheckPermission("demo:demo:edit")
@Log(title = "测试单表", businessType = BusinessType.UPDATE)
@RepeatSubmit
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody TestDemoBo bo) {
return toAjax(testDemoService.updateByBo(bo));
}
/**
*
*
* @param ids ID
*/
@SaCheckPermission("demo:demo:remove")
@Log(title = "测试单表", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
// 定义一个公共方法 remove返回类型为 R<Void>。R 通常是自定义的统一响应结果类,
// Void 表示该方法不返回具体的数据内容,主要用于返回操作结果信息
public R<Void> remove(
// 使用 @NotEmpty 注解对参数进行校验,若传入的数组为空,会抛出校验异常,
// message 属性指定校验失败时的提示信息为 "主键不能为空"
@NotEmpty(message = "主键不能为空")
// 使用 @PathVariable 注解,表明该参数是从请求的路径变量中获取的,
// 这里期望从路径中获取一组 Long 类型的主键 ID
@PathVariable Long[] ids
) {
// 调用 toAjax 方法,将 testDemoService 的 deleteWithValidByIds 方法的返回结果作为参数传入
// testDemoService 是业务逻辑层的服务类,负责与数据库交互
// Arrays.asList(ids) 将传入的 Long 数组转换为 List 集合
// true 作为第二个参数传入,具体含义取决于 deleteWithValidByIds 方法的实现,
// 可能表示是否进行额外的有效性验证等
return toAjax(testDemoService.deleteWithValidByIds(Arrays.asList(ids), true));
}
}
// 这里多余的 } 应该是代码错误,需要删除

@ -0,0 +1,70 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.demo.domain.TestDemoEncrypt;
import org.dromara.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}")
private Boolean encryptEnable;
/**
*
*
* @param key key
* @param value value
*/
@GetMapping()
// 定义一个公共方法 test返回类型为 R<Map<String, TestDemoEncrypt>>。
// R 通常是自定义的统一响应结果类,这里泛型指定为 Map<String, TestDemoEncrypt>
// 表示响应结果中携带的数据是一个键为 String 类型、值为 TestDemoEncrypt 类型的映射
public R<Map<String, TestDemoEncrypt>> test(String key, String value) {
// 检查加密功能是否开启,如果 encryptEnable 为 false说明加密功能未开启
if (!encryptEnable) {
// 若加密功能未开启,抛出一个运行时异常,提示加密功能未开启
throw new RuntimeException("加密功能未开启!");
}
// 创建一个初始容量为 2 的 HashMap 对象 map用于存储加密和解密后的 TestDemoEncrypt 对象
Map<String, TestDemoEncrypt> map = new HashMap<>(2);
// 创建一个 TestDemoEncrypt 对象 demo用于封装传入的 key 和 value
TestDemoEncrypt demo = new TestDemoEncrypt();
// 为 demo 对象的 testKey 属性设置传入的 key 值
demo.setTestKey(key);
// 为 demo 对象的 value 属性设置传入的 value 值
demo.setValue(value);
// 调用 mapper 的 insert 方法,将 demo 对象插入到数据库中
// mapper 通常是 MyBatis 等持久层框架的映射器接口实例
mapper.insert(demo);
// 将键为 "加密",值为 demo 对象的键值对添加到 map 中
map.put("加密", demo);
// 调用 mapper 的 selectById 方法,根据 demo 对象的 id 从数据库中查询对应的记录
// 并将查询结果赋值给 testDemo 对象,这里可能涉及到数据的解密操作
TestDemoEncrypt testDemo = mapper.selectById(demo.getId());
// 将键为 "解密",值为 testDemo 对象的键值对添加到 map 中
map.put("解密", testDemo);
// 调用 R 类的 ok 方法,将 map 对象封装到统一响应结果类 R 中并返回
// 表示操作成功,并将加密和解密后的对象信息返回给调用者
return R.ok(map);
}
}

@ -0,0 +1,255 @@
package org.dromara.demo.controller;
import cn.hutool.core.collection.CollUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.demo.domain.vo.ExportDemoVo;
import org.dromara.demo.listener.ExportDemoListener;
import org.dromara.demo.service.IExportExcelService;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Excel
*
* @author Lion Li
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/excel")
public class TestExcelController {
private final IExportExcelService exportExcelService;
@GetMapping("/exportTemplateOne")
// 定义一个公共方法 exportTemplateOne该方法用于导出 Excel 模板文件
// 接收一个 HttpServletResponse 对象作为参数,用于向客户端发送响应数据
public void exportTemplateOne(HttpServletResponse response) {
// 创建一个 HashMap 对象 map用于存储一些配置信息或数据
// 这里存储的键值对将用于填充 Excel 模板中的特定位置
Map<String, String> map = new HashMap<>();
// 向 map 中添加键为 "title",值为 "单列表多数据" 的键值对
// 这个值可能用于作为 Excel 表格的标题
map.put("title", "单列表多数据");
// 依次向 map 中添加其他键值对,用于填充 Excel 模板中的不同位置
map.put("test1", "数据测试1");
map.put("test2", "数据测试2");
map.put("test3", "数据测试3");
map.put("test4", "数据测试4");
map.put("testTest", "666");
// 创建一个 ArrayList 对象 list用于存储 TestObj 类型的对象
// 这些对象将作为 Excel 表格中的数据行
List<TestObj> list = new ArrayList<>();
// 创建一个 TestObj 对象,并传入相应的参数,然后将其添加到 list 中
// 这个对象代表 Excel 表格中的一行数据
list.add(new TestObj("单列表测试1", "列表测试1", "列表测试2", "列表测试3", "列表测试4"));
// 继续创建 TestObj 对象并添加到 list 中,增加 Excel 表格的数据行数
list.add(new TestObj("单列表测试2", "列表测试5", "列表测试6", "列表测试7", "列表测试8"));
list.add(new TestObj("单列表测试3", "列表测试9", "列表测试10", "列表测试11", "列表测试12"));
// 调用 ExcelUtil 工具类的 exportTemplate 方法,用于导出 Excel 模板文件
// CollUtil.newArrayList(map, list) 将 map 和 list 合并为一个新的列表,作为导出的数据
// "单列表.xlsx" 是导出文件的文件名
// "excel/单列表.xlsx" 是 Excel 模板文件的路径
// response 是 HttpServletResponse 对象,用于将生成的 Excel 文件发送给客户端
ExcelUtil.exportTemplate(CollUtil.newArrayList(map, list), "单列表.xlsx", "excel/单列表.xlsx", response);
}
@GetMapping("/exportTemplateMuliti")
// 定义一个公共方法 exportTemplateMuliti该方法的作用是导出一个包含多列表数据的 Excel 模板文件
// 接收一个 HttpServletResponse 对象作为参数,用于向客户端发送生成的 Excel 文件
public void exportTemplateMuliti(HttpServletResponse response) {
// 创建一个 HashMap 对象 map用于存储一些配置信息这些信息可能会用于填充 Excel 模板中的特定位置
Map<String, String> map = new HashMap<>();
// 向 map 中添加键值对,键为 "title1",值为 "标题1",可能用于设置 Excel 中某个区域的标题
map.put("title1", "标题1");
// 依次添加其他标题相关的键值对
map.put("title2", "标题2");
map.put("title3", "标题3");
map.put("title4", "标题4");
// 添加作者信息,键为 "author",值为 "Lion Li"
map.put("author", "Lion Li");
// 创建一个存储 TestObj1 对象的列表 list1用于存储第一组数据
List<TestObj1> list1 = new ArrayList<>();
// 创建 TestObj1 对象并添加到 list1 中,每个对象代表 Excel 表格中的一行数据
list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3"));
list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6"));
list1.add(new TestObj1("list1测试7", "list1测试8", "list1测试9"));
// 创建第二个存储 TestObj1 对象的列表 list2用于存储第二组数据
List<TestObj1> list2 = new ArrayList<>();
// 向 list2 中添加 TestObj1 对象
list2.add(new TestObj1("list2测试1", "list2测试2", "list2测试3"));
list2.add(new TestObj1("list2测试4", "list2测试5", "list2测试6"));
// 创建第三个存储 TestObj1 对象的列表 list3用于存储第三组数据
List<TestObj1> list3 = new ArrayList<>();
// 向 list3 中添加 TestObj1 对象
list3.add(new TestObj1("list3测试1", "list3测试2", "list3测试3"));
// 创建第四个存储 TestObj1 对象的列表 list4用于存储第四组数据
List<TestObj1> list4 = new ArrayList<>();
// 向 list4 中添加多个 TestObj1 对象
list4.add(new TestObj1("list4测试1", "list4测试2", "list4测试3"));
list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6"));
list4.add(new TestObj1("list4测试7", "list4测试8", "list4测试9"));
list4.add(new TestObj1("list4测试10", "list4测试11", "list4测试12"));
// 创建一个新的 HashMap 对象 multiListMap用于将前面创建的 map 和各个列表数据整合在一起
Map<String, Object> multiListMap = new HashMap<>();
// 将前面创建的 map 对象添加到 multiListMap 中,键为 "map"
multiListMap.put("map", map);
// 依次将各个列表数据添加到 multiListMap 中,分别用 "data1" 到 "data4" 作为键
multiListMap.put("data1", list1);
multiListMap.put("data2", list2);
multiListMap.put("data3", list3);
multiListMap.put("data4", list4);
// 调用 ExcelUtil 工具类的 exportTemplateMultiList 方法,根据整合后的数据生成 Excel 文件
// multiListMap 是包含所有数据的映射,"多列表.xlsx" 是生成的 Excel 文件的文件名
// "excel/多列表.xlsx" 是 Excel 模板文件的路径response 用于将生成的文件发送给客户端
ExcelUtil.exportTemplateMultiList(multiListMap, "多列表.xlsx", "excel/多列表.xlsx", response);
}
/**
*
*
* @param response /
*/
@GetMapping("/exportWithOptions")
public void exportWithOptions(HttpServletResponse response) {
exportExcelService.exportWithOptions(response);
}
/**
* sheet
*/
@GetMapping("/exportTemplateMultiSheet")
// 定义一个公共方法 exportTemplateMultiSheet用于导出包含多个工作表sheet的 Excel 模板文件
// 接收一个 HttpServletResponse 对象作为参数,用于将生成的 Excel 文件响应给客户端
public void exportTemplateMultiSheet(HttpServletResponse response) {
// 创建一个存储 TestObj1 对象的列表 list1用于存储第一个工作表的数据
List<TestObj1> list1 = new ArrayList<>();
// 向 list1 中添加 TestObj1 对象,每个对象代表 Excel 表格中的一行数据
list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3"));
list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6"));
// 创建第二个存储 TestObj1 对象的列表 list2用于存储第二个工作表的数据
List<TestObj1> list2 = new ArrayList<>();
// 向 list2 中添加 TestObj1 对象
list2.add(new TestObj1("list2测试1", "list2测试2", "list2测试3"));
list2.add(new TestObj1("list2测试4", "list2测试5", "list2测试6"));
// 创建第三个存储 TestObj1 对象的列表 list3用于存储第三个工作表的数据
List<TestObj1> list3 = new ArrayList<>();
// 向 list3 中添加 TestObj1 对象
list3.add(new TestObj1("list3测试1", "list3测试2", "list3测试3"));
list3.add(new TestObj1("list3测试4", "list3测试5", "list3测试6"));
// 创建第四个存储 TestObj1 对象的列表 list4用于存储第四个工作表的数据
List<TestObj1> list4 = new ArrayList<>();
// 向 list4 中添加 TestObj1 对象
list4.add(new TestObj1("list4测试1", "list4测试2", "list4测试3"));
list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6"));
// 创建一个存储 Map<String, Object> 的列表 list用于存储每个工作表的数据映射
List<Map<String, Object>> list = new ArrayList<>();
// 创建一个 Map 对象 sheetMap1用于存储第一个工作表的数据
Map<String, Object> sheetMap1 = new HashMap<>();
// 将 list1 作为值,以 "data1" 为键添加到 sheetMap1 中
sheetMap1.put("data1", list1);
// 创建一个 Map 对象 sheetMap2用于存储第二个工作表的数据
Map<String, Object> sheetMap2 = new HashMap<>();
// 将 list2 作为值,以 "data2" 为键添加到 sheetMap2 中
sheetMap2.put("data2", list2);
// 创建一个 Map 对象 sheetMap3用于存储第三个工作表的数据
Map<String, Object> sheetMap3 = new HashMap<>();
// 将 list3 作为值,以 "data3" 为键添加到 sheetMap3 中
sheetMap3.put("data3", list3);
// 创建一个 Map 对象 sheetMap4用于存储第四个工作表的数据
Map<String, Object> sheetMap4 = new HashMap<>();
// 将 list4 作为值,以 "data4" 为键添加到 sheetMap4 中
sheetMap4.put("data4", list4);
// 将每个工作表的数据映射依次添加到 list 中
list.add(sheetMap1);
list.add(sheetMap2);
list.add(sheetMap3);
list.add(sheetMap4);
// 调用 ExcelUtil 工具类的 exportTemplateMultiSheet 方法,根据整合后的数据生成包含多个工作表的 Excel 文件
// list 是包含所有工作表数据映射的列表,"多sheet列表" 是生成的 Excel 文件的文件名
// "excel/多sheet列表.xlsx" 是 Excel 模板文件的路径response 用于将生成的文件发送给客户端
ExcelUtil.exportTemplateMultiSheet(list, "多sheet列表", "excel/多sheet列表.xlsx", response);
}
/**
*
*/
@PostMapping(value = "/importWithOptions", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
// 定义一个公共方法 importWithOptions该方法用于处理文件导入操作并返回一个 ExportDemoVo 类型的列表
// @RequestPart("file") 注解表明该方法从 HTTP 请求里获取名为 "file" 的部分,此部分应为上传的文件
// MultipartFile 是 Spring 框架提供的用于处理文件上传的接口file 参数用于接收上传的文件
// throws Exception 表示该方法可能会抛出异常,调用者需要对这些异常进行处理
public List<ExportDemoVo> importWithOptions(@RequestPart("file") MultipartFile file) throws Exception {
// 这是一行注释,说明接下来的代码会处理 Excel 文件的解析结果
// 调用 ExcelUtil 工具类的 importExcel 方法,对上传的 Excel 文件进行解析
// file.getInputStream() 用于获取上传文件的输入流,以便后续进行读取操作
// ExportDemoVo.class 指定了解析结果的数据类型为 ExportDemoVo也就是解析后的数据会封装成 ExportDemoVo 对象
// new ExportDemoListener() 是创建了一个 ExportDemoListener 类的实例,该监听器会在 Excel 文件解析过程中发挥作用,
// 例如可以在解析到特定行或单元格时执行特定操作
// 解析结果会被存储在 ExcelResult<ExportDemoVo> 类型的对象 excelResult 中
ExcelResult<ExportDemoVo> excelResult = ExcelUtil.importExcel(file.getInputStream(), ExportDemoVo.class, new ExportDemoListener());
// 从 excelResult 对象里获取解析得到的 ExportDemoVo 对象列表,并将其返回
return excelResult.getList();
}
@Data
// @AllArgsConstructor 是 Lombok 库提供的注解。
// 它会自动为这个类生成一个包含所有已声明字段的构造函数。
// 这样可以减少手动编写构造函数的工作量,提高代码的简洁性。
@AllArgsConstructor
// 定义一个静态内部类 TestObj1。静态内部类意味着它可以在不创建外部类实例的情况下被实例化。
// 该类主要用于封装一组相关的数据,这里包含三个字符串类型的属性。
static class TestObj1 {
// 定义一个私有字符串类型的属性 test1。
// 私有属性可以保证数据的封装性,外部类不能直接访问,需要通过 getter 和 setter 方法来操作。
private String test1;
// 定义一个私有字符串类型的属性 test2。
private String test2;
// 定义一个私有字符串类型的属性 test3。
private String test3;
}
@Data
@AllArgsConstructor
// 定义一个静态内部类 TestObj。静态内部类可以不依赖外部类的实例而被创建
// 通常用于将一组相关的数据封装在一起,方便管理和使用
static class TestObj {
// 定义一个私有字符串类型的属性 name用于存储对象的名称信息
// 私有属性可以保证数据的封装性,外部类不能直接访问该属性,需要通过 getter 和 setter 方法来操作
private String name;
// 定义一个私有字符串类型的属性 list1可用于存储相关列表中的某一项信息
private String list1;
// 定义一个私有字符串类型的属性 list2用途和 list1 类似,可存储列表中的另一项信息
private String list2;
// 定义一个私有字符串类型的属性 list3用于存储相关列表中的又一项信息
private String list3;
// 定义一个私有字符串类型的属性 list4同样用于存储列表中的信息
private String list4;
}
}
// 这里多余的 } 应该是代码错误,需要删除

@ -0,0 +1,86 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.MessageUtils;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
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 jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
*
*
* @author Lion Li
*/
@Validated
@RestController
@RequestMapping("/demo/i18n")
public class TestI18nController {
/**
* code
* code messages.properties key
* <p>
* 使 user.register.success
*
* @param code code
*/
@GetMapping()
public R<Void> get(String code) {
return R.ok(MessageUtils.message(code));
}
/**
* Validator
*
* <p>
* 使 not.null
*/
@GetMapping("/test1")
public R<Void> test1(@NotBlank(message = "{not.null}") String str) {
return R.ok(str);
}
/**
* Bean
*
* <p>
* 使 not.null
*/
@GetMapping("/test2")
// 定义一个公共方法 test2返回类型为 R<TestI18nBo>。
// R 通常是自定义的统一响应结果类,泛型指定为 TestI18nBo 表示响应中携带的数据类型为 TestI18nBo。
// @Validated 注解用于开启对方法参数的校验,确保传入的参数符合指定的校验规则。
// TestI18nBo bo 表示接收一个 TestI18nBo 类型的对象作为参数。
public R<TestI18nBo> test2(@Validated TestI18nBo bo) {
// 调用 R 类的 ok 方法,将传入的 bo 对象封装到统一响应结果类 R 中并返回。
// 表示操作成功,并将传入的 TestI18nBo 对象原样返回给调用者。
return R.ok(bo);
}
// 使用 @Data 注解,这是 Lombok 提供的注解,会自动生成 getter、setter、toString、equals、hashCode 等方法。
// 定义一个公共静态内部类 TestI18nBo用于封装需要进行校验的数据。
@Data
public static class TestI18nBo {
// @NotBlank 注解用于校验字符串类型的属性,确保该属性不为空字符串且不为 null。
// message = "{not.null}" 表示当校验不通过时,会从国际化资源文件中查找键为 "not.null" 的错误信息并返回。
// 这里的 name 属性用于存储名称信息。
@NotBlank(message = "{not.null}")
private String name;
// @NotNull 注解用于校验对象类型的属性,确保该属性不为 null。
// message = "{not.null}" 同样表示校验不通过时从国际化资源文件中查找键为 "not.null" 的错误信息。
// @Range 注解用于对数值类型的属性进行范围校验,这里要求 age 属性的值在 0 到 100 之间。
// message = "{length.not.valid}" 表示当 age 的值不在指定范围内时,从国际化资源文件中查找键为 "length.not.valid" 的错误信息。
// 这里的 age 属性用于存储年龄信息。
@NotNull(message = "{not.null}")
@Range(min = 0, max = 100, message = "{length.not.valid}")
private Integer age;
}}
// 这里多余的 } 应该是代码错误,需要删除

@ -0,0 +1,86 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.sensitive.annotation.Sensitive;
import org.dromara.common.sensitive.core.SensitiveStrategy;
import lombok.Data;
import org.dromara.common.sensitive.core.SensitiveService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* <p>
*
*
*
* @author Lion Li
* @version 3.6.0
* @see SensitiveService
*/
@RestController
@RequestMapping("/demo/sensitive")
// 定义一个公共类 TestSensitiveController它继承自 BaseController 类。
// 通常 BaseController 类会包含一些通用的控制器逻辑,继承它可以复用这些逻辑。
public class TestSensitiveController extends BaseController {
// 使用 @GetMapping 注解,表明该方法处理 HTTP GET 请求。
// "/test" 表示该方法映射的 URL 路径为 /test当客户端发送 GET 请求到这个路径时,会调用此方法。
@GetMapping("/test")
// 定义一个公共方法 test返回类型为 R<TestSensitive>。
// R 通常是自定义的统一响应结果类,泛型指定为 TestSensitive 表示响应中携带的数据类型为 TestSensitive。
public R<TestSensitive> test() {
// 创建一个 TestSensitive 类的实例 testSensitive用于封装敏感信息。
TestSensitive testSensitive = new TestSensitive();
// 调用 testSensitive 对象的 setIdCard 方法,设置身份证号属性的值。
testSensitive.setIdCard("210397198608215431");
// 调用 testSensitive 对象的 setPhone 方法,设置电话号码属性的值。
testSensitive.setPhone("17640125371");
// 调用 testSensitive 对象的 setAddress 方法,设置地址属性的值。
testSensitive.setAddress("北京市朝阳区某某四合院1203室");
// 调用 testSensitive 对象的 setEmail 方法,设置邮箱属性的值。
testSensitive.setEmail("17640125371@163.com");
// 调用 testSensitive 对象的 setBankCard 方法,设置银行卡号属性的值。
testSensitive.setBankCard("6226456952351452853");
// 调用 R 类的 ok 方法,将 testSensitive 对象封装到统一响应结果类 R 中并返回。
// 表示操作成功,并将包含敏感信息的 testSensitive 对象返回给调用者。
return R.ok(testSensitive);
}
@Data
static class TestSensitive {
/**
*
*/
@Sensitive(strategy = SensitiveStrategy.ID_CARD)
private String idCard;
/**
*
*/
@Sensitive(strategy = SensitiveStrategy.PHONE, roleKey = "common")
private String phone;
/**
*
*/
@Sensitive(strategy = SensitiveStrategy.ADDRESS, perms = "system:user:query")
private String address;
/**
*
*/
@Sensitive(strategy = SensitiveStrategy.EMAIL, roleKey = "common", perms = "system:user:query1")
private String email;
/**
*
*/
@Sensitive(strategy = SensitiveStrategy.BANK_CARD, roleKey = "common1", perms = "system:user:query")
private String bankCard;
}
}

@ -0,0 +1,160 @@
package org.dromara.demo.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.core.validate.QueryGroup;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.demo.domain.bo.TestTreeBo;
import org.dromara.demo.domain.vo.TestTreeVo;
import org.dromara.demo.service.ITestTreeService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* Controller
*
* @author Lion Li
* @date 2021-07-26
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/tree")
// 定义一个公共类 TestTreeController它继承自 BaseController 类。
// 通常 BaseController 类会包含一些通用的控制器逻辑,继承它可以复用这些逻辑。
public class TestTreeController extends BaseController {
// 定义一个私有且不可变的 ITestTreeService 类型的成员变量 testTreeService。
// 使用 final 关键字修饰,意味着该变量在初始化后不能再被重新赋值。
// 此变量用于调用 ITestTreeService 接口中定义的方法来处理业务逻辑。
private final ITestTreeService testTreeService;
// 使用 @SaCheckPermission 注解,用于进行权限校验。
// "demo:tree:list" 表示执行该方法需要具备 "demo:tree:list" 这个权限,只有拥有此权限的用户才能访问该接口。
@SaCheckPermission("demo:tree:list")
// 使用 @GetMapping 注解,表明该方法处理 HTTP GET 请求。
// "/list" 表示该方法映射的 URL 路径为 /list当客户端发送 GET 请求到这个路径时,会调用此方法。
@GetMapping("/list")
// 定义一个公共方法 list返回类型为 R<List<TestTreeVo>>。
// R 通常是自定义的统一响应结果类,泛型指定为 List<TestTreeVo> 表示响应中携带的数据是一个 TestTreeVo 类型的列表。
// @Validated(QueryGroup.class) 注解用于开启对方法参数的校验,并且指定使用 QueryGroup 分组的校验规则。
// TestTreeBo bo 表示接收一个 TestTreeBo 类型的对象作为查询参数。
public R<List<TestTreeVo>> list(@Validated(QueryGroup.class) TestTreeBo bo) {
// 调用 testTreeService 的 queryList 方法,传入查询参数 bo获取符合条件的 TestTreeVo 列表。
// queryList 方法是 ITestTreeService 接口中定义的方法,具体实现由其实现类完成。
List<TestTreeVo> list = testTreeService.queryList(bo);
// 调用 R 类的 ok 方法,将查询得到的列表 list 封装到统一响应结果类 R 中并返回。
// 表示操作成功,并将查询结果列表返回给调用者。
return R.ok(list);
}
/**
*
*/
// 使用 @SaCheckPermission 注解,该注解用于进行权限校验。
// "demo:tree:export" 是权限标识,意味着调用此方法的用户必须拥有 "demo:tree:export" 这个权限,
// 若没有相应权限,将无法访问该接口。
@SaCheckPermission("demo:tree:export")
// 使用 @Log 注解,该注解通常用于记录操作日志。
// title 属性指定日志的标题为 "测试树表"businessType 属性指定业务类型为 EXPORT即导出操作。
// 这样在执行该方法时,会记录相关的操作日志信息。
@Log(title = "测试树表", businessType = BusinessType.EXPORT)
// 使用 @GetMapping 注解,表明此方法处理 HTTP GET 请求。
// "/export" 是该方法映射的 URL 路径,当客户端发送 GET 请求到这个路径时,会调用此方法。
@GetMapping("/export")
// 定义一个公共方法 export该方法没有返回值void
// @Validated 注解用于对传入的 TestTreeBo 对象进行数据校验,确保其符合预定义的规则。
// TestTreeBo bo 是一个自定义的业务对象,用于封装查询条件。
// HttpServletResponse response 是 Servlet API 中的响应对象,用于向客户端返回数据,
// 这里主要用于返回导出的 Excel 文件。
public void export(@Validated TestTreeBo bo, HttpServletResponse response) {
// 调用 testTreeService 的 queryList 方法,传入查询条件 bo
// 从业务逻辑层获取符合条件的 TestTreeVo 对象列表。
// testTreeService 是一个业务逻辑服务对象,负责处理与测试树表相关的业务逻辑。
List<TestTreeVo> list = testTreeService.queryList(bo);
// 调用 ExcelUtil 工具类的 exportExcel 方法,将查询得到的列表数据导出为 Excel 文件。
// list 是要导出的数据列表。
// "测试树表" 是导出的 Excel 文件的名称。
// TestTreeVo.class 指定了导出数据的类型,用于确定 Excel 文件的表头和数据格式。
// response 用于将生成的 Excel 文件返回给客户端。
ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response);
}
// 使用 @SaCheckPermission 注解进行权限校验,只有拥有 "demo:tree:query" 权限的用户才能访问此接口
@SaCheckPermission("demo:tree:query")
// 使用 @GetMapping 注解,表明该方法处理 HTTP GET 请求,路径中的 {id} 是一个路径变量
@GetMapping("/{id}")
// 定义一个公共方法 getInfo返回类型为 R<TestTreeVo>R 通常是自定义的统一响应结果类
// @NotNull 注解对参数 id 进行非空校验若为空会抛出校验异常message 属性指定异常提示信息
// @PathVariable("id") 注解表示从路径中获取名为 "id" 的变量并赋值给方法参数 id
public R<TestTreeVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable("id") Long id) {
// 调用 testTreeService 的 queryById 方法,根据 id 查询 TestTreeVo 对象
// 然后将查询结果封装到统一响应结果类 R 中返回
return R.ok(testTreeService.queryById(id));
}
// 使用 @SaCheckPermission 注解进行权限校验,只有拥有 "demo:tree:add" 权限的用户才能访问此接口
@SaCheckPermission("demo:tree:add")
// 使用 @Log 注解记录操作日志title 为 "测试树表"businessType 为 INSERT 表示插入操作
@Log(title = "测试树表", businessType = BusinessType.INSERT)
// @RepeatSubmit 注解用于防止重复提交,避免用户在短时间内多次提交相同请求
@RepeatSubmit
// 使用 @PostMapping 注解,表明该方法处理 HTTP POST 请求
@PostMapping()
// 定义一个公共方法 add返回类型为 R<Void>,表示不返回具体数据,只返回操作结果
// @Validated(AddGroup.class) 注解对传入的 TestTreeBo 对象进行校验,使用 AddGroup 分组的校验规则
// @RequestBody 注解表示从请求体中获取数据并反序列化为 TestTreeBo 对象
public R<Void> add(@Validated(AddGroup.class) @RequestBody TestTreeBo bo) {
// 调用 testTreeService 的 insertByBo 方法,根据传入的 bo 对象插入数据
// 然后调用 toAjax 方法将插入操作的结果封装到统一响应结果类 R 中返回
return toAjax(testTreeService.insertByBo(bo));
}
// 使用 @SaCheckPermission 注解进行权限校验,只有拥有 "demo:tree:edit" 权限的用户才能访问此接口
@SaCheckPermission("demo:tree:edit")
// 使用 @Log 注解记录操作日志title 为 "测试树表"businessType 为 UPDATE 表示更新操作
@Log(title = "测试树表", businessType = BusinessType.UPDATE)
// @RepeatSubmit 注解用于防止重复提交
@RepeatSubmit
// 使用 @PutMapping 注解,表明该方法处理 HTTP PUT 请求
@PutMapping()
// 定义一个公共方法 edit返回类型为 R<Void>
// @Validated(EditGroup.class) 注解对传入的 TestTreeBo 对象进行校验,使用 EditGroup 分组的校验规则
// @RequestBody 注解表示从请求体中获取数据并反序列化为 TestTreeBo 对象
public R<Void> edit(@Validated(EditGroup.class) @RequestBody TestTreeBo bo) {
// 调用 testTreeService 的 updateByBo 方法,根据传入的 bo 对象更新数据
// 然后调用 toAjax 方法将更新操作的结果封装到统一响应结果类 R 中返回
return toAjax(testTreeService.updateByBo(bo));
}
// 使用 @SaCheckPermission 注解进行权限校验,只有拥有 "demo:tree:remove" 权限的用户才能访问此接口
@SaCheckPermission("demo:tree:remove")
// 使用 @Log 注解记录操作日志title 为 "测试树表"businessType 为 DELETE 表示删除操作
@Log(title = "测试树表", businessType = BusinessType.DELETE)
// 使用 @DeleteMapping 注解,表明该方法处理 HTTP DELETE 请求,路径中的 {ids} 是一个路径变量
@DeleteMapping("/{ids}")
// 定义一个公共方法 remove返回类型为 R<Void>
// @NotEmpty 注解对参数 ids 进行非空校验若为空会抛出校验异常message 属性指定异常提示信息
// @PathVariable 注解表示从路径中获取名为 "ids" 的变量并赋值给方法参数 ids
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
// 调用 Arrays.asList 方法将 Long 数组转换为 List 集合
// 然后调用 testTreeService 的 deleteWithValidByIds 方法,根据传入的 id 列表删除数据
// 第二个参数 true 可能表示是否进行额外的有效性验证
// 最后调用 toAjax 方法将删除操作的结果封装到统一响应结果类 R 中返回
return toAjax(testTreeService.deleteWithValidByIds(Arrays.asList(ids), true));
}
}

@ -0,0 +1,32 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.common.websocket.dto.WebSocketMessageDto;
import org.dromara.common.websocket.utils.WebSocketUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/websocket")
@Slf4j
// 定义一个公共类 WeSocketController它通常作为控制器类负责处理与 WebSocket 相关的 HTTP 请求
public class WeSocketController {
// 使用 @GetMapping 注解,表明该方法处理 HTTP GET 请求
// "/send" 表示该方法映射的 URL 路径为 /send当客户端发送 GET 请求到这个路径时,会调用此方法
@GetMapping("/send")
// 定义一个公共方法 send返回类型为 R<Void>。R 通常是自定义的统一响应结果类,
// Void 表示该方法不返回具体的数据内容,主要用于返回操作结果信息
// WebSocketMessageDto dto 表示接收一个 WebSocketMessageDto 类型的对象作为参数,该对象封装了要发送的 WebSocket 消息相关信息
// throws InterruptedException 表示该方法可能会抛出 InterruptedException 异常,调用者需要进行异常处理
public R<Void> send(WebSocketMessageDto dto) throws InterruptedException {
// 调用 WebSocketUtils 工具类的 publishMessage 方法,将传入的 dto 对象作为参数,
// 该方法的作用是发布 WebSocket 消息,具体实现逻辑由 WebSocketUtils 类决定
WebSocketUtils.publishMessage(dto);
// 调用 R 类的 ok 方法,将 "操作成功" 作为消息封装到统一响应结果类 R 中并返回,
// 表示 WebSocket 消息发布操作成功
return R.ok("操作成功");
}
}

@ -0,0 +1,94 @@
package org.dromara.demo.controller.queue;
import cn.dev33.satoken.annotation.SaIgnore;
import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.QueueUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBoundedBlockingQueue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* <p>
* 使 MQ
* <p>
*
*
*
* @author Lion Li
* @version 3.6.0
*/
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/queue/bounded")
public class BoundedQueueController {
/**
*
*
* @param queueName
* @param capacity
*/
@GetMapping("/add")
public R<Void> add(String queueName, int capacity) {
// 用完了一定要销毁,否则会一直存在。这里先尝试销毁指定名称的有界队列
boolean b = QueueUtils.destroyBoundedQueue(queueName);
log.info("通道: {} , 删除: {}", queueName, b);
// 初始化设置一次即可。尝试为指定队列设置容量
if (QueueUtils.trySetBoundedQueueCapacity(queueName, capacity)) {
log.info("通道: {} , 设置容量: {}", queueName, capacity);
} else {
log.info("通道: {} , 设置容量失败", queueName);
return R.fail("操作失败");
}
// 循环向队列中添加11条数据
for (int i = 0; i < 11; i++) {
String data = "data-" + i;
// 尝试向指定队列添加数据
boolean flag = QueueUtils.addBoundedQueueObject(queueName, data);
if (flag == false) {
log.info("通道: {} , 发送数据: {} 失败, 通道已满", queueName, data);
} else {
log.info("通道: {} , 发送数据: {}", queueName, data);
}
}
return R.ok("操作成功");
}
/**
*
*
* @param queueName
*/
@GetMapping("/remove")
public R<Void> remove(String queueName) {
String data = "data-" + 5;
// 尝试从指定队列中删除指定数据
if (QueueUtils.removeBoundedQueueObject(queueName, data)) {
log.info("通道: {} , 删除数据: {}", queueName, data);
} else {
return R.fail("操作失败");
}
return R.ok("操作成功");
}
/**
*
*
* @param queueName
*/
@GetMapping("/get")
public R<Void> get(String queueName) {
String data;
// 循环从队列中获取数据直到获取到的数据为null表示队列为空
do {
data = QueueUtils.getBoundedQueueObject(queueName);
log.info("通道: {} , 获取数据: {}", queueName, data);
} while (data != null);
return R.ok("操作成功");
}
}

@ -0,0 +1,110 @@
package org.dromara.demo.controller.queue;
import cn.dev33.satoken.annotation.SaIgnore;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.QueueUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
*
* <p>
* 使 MQ
* : 30
* <p>
*
*
*
* @author Lion Li
* @version 3.6.0
*/
@SaIgnore
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/queue/delayed")
public class DelayedQueueController {
/**
*
*
* @param queueName
*/
@GetMapping("/subscribe")
public R<Void> subscribe(String queueName) {
log.info("通道: {} 监听中......", queueName);
// 项目初始化设置一次即可
QueueUtils.subscribeBlockingQueue(queueName, (String orderNum) -> {
// 观察接收时间
log.info("通道: {}, 收到数据: {}", queueName, orderNum);
return CompletableFuture.runAsync(() -> {
// 异步处理数据逻辑 不要在上方处理业务逻辑
log.info("数据处理: {}", orderNum);
});
}, true);
return R.ok("操作成功");
}
/**
*
*
* @param queueName
* @param orderNum
* @param time ()
*/
@GetMapping("/add")
public R<Void> add(String queueName, String orderNum, Long time) {
QueueUtils.addDelayedQueueObject(queueName, orderNum, time, TimeUnit.SECONDS);
// 观察发送时间
log.info("通道: {} , 发送数据: {}", queueName, orderNum);
return R.ok("操作成功");
}
/**
*
*
* @param queueName
* @param orderNum
*/
@GetMapping("/remove")
// 定义一个公共方法 remove返回类型为 R<Void>。R 通常是自定义的统一响应结果类,
// Void 表示该方法不返回具体的数据内容,主要用于传达操作的结果状态
public R<Void> remove(String queueName, String orderNum) {
// 调用 QueueUtils 工具类的 removeDelayedQueueObject 方法,尝试从指定名称的延迟队列中移除指定 orderNum 的数据
// 该方法返回一个布尔值,表示移除操作是否成功
if (QueueUtils.removeDelayedQueueObject(queueName, orderNum)) {
// 如果移除操作成功,使用日志记录工具(如 SLF4J记录移除成功的信息
// 信息中包含队列名称和被移除的数据的 orderNum
log.info("通道: {} , 删除数据: {}", queueName, orderNum);
} else {
// 如果移除操作失败,调用 R 类的 fail 方法,将 "操作失败" 信息封装成统一响应结果类 R 的对象并返回
return R.fail("操作失败");
}
// 如果移除操作成功,调用 R 类的 ok 方法,将 "操作成功" 信息封装成统一响应结果类 R 的对象并返回
return R.ok("操作成功");
}
/**
*
*
* @param queueName
*/
@GetMapping("/destroy")
// 定义一个公共方法 destroy返回类型为 R<Void>。R 通常是自定义的统一响应结果类,
// Void 表明此方法不返回具体的数据内容,主要用于告知调用者操作的结果状态
public R<Void> destroy(String queueName) {
// 这里给出注释提示,强调在使用完队列后必须进行销毁操作,否则队列会一直存在占用资源
// 调用 QueueUtils 工具类的 destroyDelayedQueue 方法,传入队列名称 queueName
// 该方法的作用是销毁指定名称的延迟队列
QueueUtils.destroyDelayedQueue(queueName);
// 调用 R 类的 ok 方法,将 "操作成功" 这个消息封装到 R 对象中并返回,
// 以此告知调用者延迟队列销毁操作已经成功完成
return R.ok("操作成功");
}
}

@ -0,0 +1,30 @@
package org.dromara.demo.controller.queue;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 使
*
* @author Lion Li
* @version 3.6.0
*/
@Data
@NoArgsConstructor
// 定义一个名为 PriorityDemo 的公共类,该类实现了 Comparable 接口,用于对 PriorityDemo 对象进行比较
public class PriorityDemo implements Comparable<PriorityDemo> {
// 声明一个私有字符串类型的成员变量 name用于存储对象的名称
private String name;
// 声明一个私有整数类型的成员变量 orderNum用于存储对象的排序编号
private Integer orderNum;
// 重写 Comparable 接口中的 compareTo 方法,用于定义两个 PriorityDemo 对象的比较规则
@Override
public int compareTo(PriorityDemo other) {
// 调用 Integer 类的 compare 方法,比较当前对象的 orderNum 和传入对象的 orderNum
// 如果当前对象的 orderNum 小于传入对象的 orderNum返回负数
// 如果当前对象的 orderNum 等于传入对象的 orderNum返回 0
// 如果当前对象的 orderNum 大于传入对象的 orderNum返回正数
return Integer.compare(getOrderNum(), other.getOrderNum());
}
}

@ -0,0 +1,124 @@
package org.dromara.demo.controller.queue;
import cn.hutool.core.util.RandomUtil;
import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.QueueUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* <p>
* 使 MQ
* <p>
*
*
*
* @author Lion Li
* @version 3.6.0
*/
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/queue/priority")
public class PriorityQueueController {
/**
*
*
* @param queueName
*/
@GetMapping("/add")
// 定义一个公共方法 add返回类型为 R<Void>。R 通常是自定义的统一响应结果类,
// Void 表示该方法不返回具体的数据内容,主要用于告知调用者操作的结果状态
public R<Void> add(String queueName) {
// 注释提示,强调使用完队列后需要销毁,否则队列会一直存在
// 调用 QueueUtils 工具类的 destroyPriorityQueue 方法,尝试销毁指定名称的优先队列
// 该方法返回一个布尔值,表示队列是否成功销毁
boolean b = QueueUtils.destroyPriorityQueue(queueName);
// 使用日志记录工具记录队列销毁操作的结果,包含队列名称和销毁结果
log.info("通道: {} , 删除: {}", queueName, b);
// 循环 10 次,向优先队列中添加 10 个元素
for (int i = 0; i < 10; i++) {
// 生成一个 0 到 9 之间的随机整数
int randomNum = RandomUtil.randomInt(10);
// 创建一个 PriorityDemo 对象,用于表示要添加到队列中的数据
PriorityDemo data = new PriorityDemo();
// 设置 PriorityDemo 对象的名称属性
data.setName("data-" + i);
// 设置 PriorityDemo 对象的排序编号属性为随机生成的整数
data.setOrderNum(randomNum);
// 调用 QueueUtils 工具类的 addPriorityQueueObject 方法,尝试将数据添加到指定名称的优先队列中
// 该方法返回一个布尔值,表示数据是否成功添加到队列中
if (QueueUtils.addPriorityQueueObject(queueName, data)) {
// 如果数据添加成功,使用日志记录工具记录添加成功的信息,包含队列名称和添加的数据
log.info("通道: {} , 发送数据: {}", queueName, data);
} else {
// 如果数据添加失败,使用日志记录工具记录添加失败的信息,包含队列名称和添加的数据
log.info("通道: {} , 发送数据: {}, 发送失败", queueName, data);
}
}
// 调用 R 类的 ok 方法,将 "操作成功" 信息封装到统一响应结果类 R 中并返回
return R.ok("操作成功");
}
/**
*
*
* @param queueName
* @param name
* @param orderNum
*/
@GetMapping("/remove")
// 定义一个公共方法 remove其返回类型为 R<Void>。这里的 R 一般是自定义的统一响应结果类,
// Void 表明该方法不返回具体的数据内容,仅用于传达操作的结果状态
public R<Void> remove(String queueName, String name, Integer orderNum) {
// 创建一个 PriorityDemo 对象,用于封装要从队列中移除的数据信息
PriorityDemo data = new PriorityDemo();
// 为 PriorityDemo 对象设置名称属性,该名称由方法参数传入
data.setName(name);
// 为 PriorityDemo 对象设置排序编号属性,该编号由方法参数传入
data.setOrderNum(orderNum);
// 调用 QueueUtils 工具类的 removePriorityQueueObject 方法,尝试从指定名称的优先队列中移除封装好的数据对象
// 该方法会返回一个布尔值,用于表示移除操作是否成功
if (QueueUtils.removePriorityQueueObject(queueName, data)) {
// 若移除操作成功,使用日志记录工具记录移除成功的信息
// 信息中包含队列名称以及被移除的数据对象
log.info("通道: {} , 删除数据: {}", queueName, data);
} else {
// 若移除操作失败,调用 R 类的 fail 方法,将 "操作失败" 信息封装成统一响应结果类 R 的对象并返回
return R.fail("操作失败");
}
// 若移除操作成功,调用 R 类的 ok 方法,将 "操作成功" 信息封装成统一响应结果类 R 的对象并返回
return R.ok("操作成功");
}
/**
*
*
* @param queueName
*/
@GetMapping("/get")
// 定义一个公共方法 get返回类型为 R<Void>。R 通常是自定义的统一响应结果类,
// Void 表示该方法不返回具体的数据内容,主要用于返回操作结果信息
public R<Void> get(String queueName) {
// 声明一个 PriorityDemo 类型的变量 data用于存储从优先队列中获取的数据
PriorityDemo data;
// 使用 do-while 循环,先执行一次循环体,再判断条件是否满足
do {
// 调用 QueueUtils 工具类的 getPriorityQueueObject 方法,从指定名称的优先队列中获取数据
// 并将获取到的数据赋值给 data 变量
data = QueueUtils.getPriorityQueueObject(queueName);
// 使用日志记录工具(如 SLF4J记录从队列中获取数据的信息
// 信息包含队列名称和获取到的数据
log.info("通道: {} , 获取数据: {}", queueName, data);
// 当获取到的数据不为 null 时,继续循环,直到队列为空,获取到的数据为 null 时停止循环
} while (data != null);
// 调用 R 类的 ok 方法,将 "操作成功" 信息封装成统一响应结果类 R 的对象并返回
return R.ok("操作成功");
}
}

@ -0,0 +1,66 @@
package org.dromara.demo.domain;
import com.baomidou.mybatisplus.annotation.*;
import org.dromara.common.tenant.core.TenantEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* test_demo
*
* @author Lion Li
* @date 2021-07-26
*/
// @Data 是 Lombok 提供的注解,它会自动为类生成 getter、setter、toString、equals、hashCode 等方法,
// 减少了样板代码,提高了代码的简洁性。
@Data
// @EqualsAndHashCode(callSuper = true) 同样是 Lombok 注解,用于生成 equals 和 hashCode 方法。
// callSuper = true 表示生成的 equals 和 hashCode 方法会调用父类的对应方法,
// 确保在比较对象时会考虑父类的属性。
@EqualsAndHashCode(callSuper = true)
// @TableName("test_demo") 是 MyBatis-Plus 提供的注解,用于指定该实体类对应的数据库表名,
// 这里表明该类对应数据库中的 test_demo 表。
@TableName("test_demo")
// 定义一个公共类 TestDemo继承自 TenantEntity 类,通常 TenantEntity 类会包含一些与租户相关的公共属性和方法。
public class TestDemo extends TenantEntity {
// @Serial 是 Java 17 引入的注解,用于显式指定序列化版本号。
// serialVersionUID 是一个静态常量,用于在反序列化时验证序列化数据的版本一致性,
// 确保反序列化的对象版本与序列化时的对象版本一致。
@Serial
private static final long serialVersionUID = 1L;
// @TableId(value = "id") 是 MyBatis-Plus 注解,用于指定该属性为数据库表的主键。
// value = "id" 表示主键字段在数据库表中的名称为 id。
@TableId(value = "id")
// 定义一个 Long 类型的私有属性 id作为该实体类的主键。
private Long id;
// 定义一个 Long 类型的私有属性 deptId用于存储部门 ID 信息,
// 未添加特殊注解,默认对应数据库表中同名的字段。
private Long deptId;
// 定义一个 Long 类型的私有属性 userId用于存储用户 ID 信息,
// 未添加特殊注解,默认对应数据库表中同名的字段。
private Long userId;
// @OrderBy(asc = false, sort = 1) 是自定义注解(假设是自定义的),
// asc = false 表示降序排序sort = 1 可能表示排序的优先级为 1
// 用于在查询时对该字段进行排序。
@OrderBy(asc = false, sort = 1)
// 定义一个 Integer 类型的私有属性 orderNum用于存储排序号信息。
private Integer orderNum;
// 定义一个 String 类型的私有属性 testKey用于存储测试的 key 信息,
// 未添加特殊注解,默认对应数据库表中同名的字段。
private String testKey;
// 定义一个 String 类型的私有属性 value用于存储值信息
// 未添加特殊注解,默认对应数据库表中同名的字段。
private String value;
// @Version 是 MyBatis-Plus 注解,用于实现乐观锁机制。
// 当对数据库记录进行更新操作时,会比较版本号,若版本号不一致则更新失败。
@Version
// 定义一个 Long 类型的私有属性 version作为乐观锁的版本号。
private Long version;
// @TableLogic 是 MyBatis-Plus 注解,用于实现逻辑删除功能。
// 逻辑删除不会真正从数据库中删除记录,而是通过修改该字段的值来标记记录已删除。
@TableLogic
// 定义一个 Long 类型的私有属性 delFlag作为逻辑删除的标记字段。
private Long delFlag;
}

@ -0,0 +1,20 @@
package org.dromara.demo.domain;
import com.baomidou.mybatisplus.annotation.TableName;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("test_demo")
public class TestDemoEncrypt extends TestDemo {
// @EncryptField(algorithm=AlgorithmType.SM2, privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgZSlOvw8FBiH+aFJWLYZP/VRjg9wjfRarTkGBZd/T3N+gCgYIKoEcz1UBgi2hRANCAAR5DGuQwJqkxnbCsP+iPSDoHWIF4RwcR5EsSvT8QPxO1wRkR2IhCkzvRb32x2CUgJFdvoqVqfApFDPZzShqzBwX", publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEeQxrkMCapMZ2wrD/oj0g6B1iBeEcHEeRLEr0/ED8TtcEZEdiIQpM70W99sdglICRXb6KlanwKRQz2c0oaswcFw==")
@EncryptField(algorithm = AlgorithmType.RSA, privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANBBEeueWlXlkkj2+WY5l+IWe42d8b5K28g+G/CFKC/yYAEHtqGlCsBOrb+YBkG9mPzmuYA/n9k0NFIc8E8yY5vZQaroyFBrTTWEzG9RY2f7Y3svVyybs6jpXSUs4xff8abo7wL1Y/wUaeatTViamxYnyTvdTmLm3d+JjRij68rxAgMBAAECgYAB0TnhXraSopwIVRfmboea1b0upl+BUdTJcmci412UjrKr5aE695ZLPkXbFXijVu7HJlyyv94NVUdaMACV7Ku/S2RuNB70M7YJm8rAjHFC3/i2ZeIM60h1Ziy4QKv0XM3pRATlDCDNhC1WUrtQCQSgU8kcp6eUUppruOqDzcY04QJBAPm9+sBP9CwDRgy3e5+V8aZtJkwDstb0lVVV/KY890cydVxiCwvX3fqVnxKMlb+x0YtH0sb9v+71xvK2lGobaRECQQDVePU6r/cCEfpc+nkWF6osAH1f8Mux3rYv2DoBGvaPzV2BGfsLed4neRfCwWNCKvGPCdW+L0xMJg8+RwaoBUPhAkAT5kViqXxFPYWJYd1h2+rDXhMdH3ZSlm6HvDBDdrwlWinr0Iwcx3iSjPV93uHXwm118aUj4fg3LDJMCKxOwBxhAkByrQXfvwOMYygBprRBf/j0plazoWFrbd6lGR0f1uI5IfNnFRPdeFw1DEINZ2Hw+6zEUF44SqRMC+4IYJNc02dBAkBCgy7RvfyV/A7N6kKXxTHauY0v6XwSSvpeKtRJkbIcRWOdIYvaHO9L7cklj3vIEdwjSUp9K4VTBYYlmAz1xh03", publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQQRHrnlpV5ZJI9vlmOZfiFnuNnfG+StvIPhvwhSgv8mABB7ahpQrATq2/mAZBvZj85rmAP5/ZNDRSHPBPMmOb2UGq6MhQa001hMxvUWNn+2N7L1csm7Oo6V0lLOMX3/Gm6O8C9WP8FGnmrU1YmpsWJ8k73U5i5t3fiY0Yo+vK8QIDAQAB")
private String testKey;
// @EncryptField // 什么也不写走默认yml配置
// @EncryptField(algorithm = AlgorithmType.SM4, password = "10rfylhtccpuyke5")
@EncryptField(algorithm = AlgorithmType.AES, password = "10rfylhtccpuyke5")
private String value;
}

@ -0,0 +1,65 @@
package org.dromara.demo.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import org.dromara.common.tenant.core.TenantEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* test_tree
*
* @author Lion Li
* @date 2021-07-26
*/
// @Data 是 Lombok 提供的注解,它会自动为类生成 getter、setter、toString、equals、hashCode 等方法,
// 减少了手动编写这些方法的工作量,提高了代码的简洁性。
@Data
// @EqualsAndHashCode(callSuper = true) 同样是 Lombok 注解,用于生成 equals 和 hashCode 方法。
// callSuper = true 表示生成的 equals 和 hashCode 方法会调用父类的对应方法,
// 确保在比较对象时会考虑父类的属性。
@EqualsAndHashCode(callSuper = true)
// @TableName("test_tree") 是 MyBatis-Plus 框架提供的注解,
// 用于指定当前实体类对应的数据库表名,这里表明该类对应数据库中的 test_tree 表。
@TableName("test_tree")
// 定义一个公共类 TestTree继承自 TenantEntity 类。
// 通常 TenantEntity 类会包含一些与租户相关的通用属性和方法,继承它可以复用这些内容。
public class TestTree extends TenantEntity {
// @Serial 是 Java 17 引入的注解,用于显式指定序列化版本号。
// serialVersionUID 是一个静态常量,用于在反序列化时验证序列化数据的版本一致性,
// 确保反序列化的对象版本与序列化时的对象版本一致。
@Serial
private static final long serialVersionUID = 1L;
// @TableId(value = "id") 是 MyBatis-Plus 注解,用于指定该属性为数据库表的主键。
// value = "id" 表示主键字段在数据库表中的名称是 id。
@TableId(value = "id")
// 定义一个 Long 类型的私有属性 id作为该实体类的主键用于唯一标识一条记录。
private Long id;
// 定义一个 Long 类型的私有属性 parentId用于存储该树节点的父节点 ID。
// 没有添加特殊注解,默认该属性对应数据库表中同名的字段。
private Long parentId;
// 定义一个 Long 类型的私有属性 deptId用于存储部门 ID 信息。
// 未添加特殊注解,默认对应数据库表中同名的字段。
private Long deptId;
// 定义一个 Long 类型的私有属性 userId用于存储用户 ID 信息。
// 未添加特殊注解,默认对应数据库表中同名的字段。
private Long userId;
// 定义一个 String 类型的私有属性 treeName用于存储树节点的名称。
// 未添加特殊注解,默认对应数据库表中同名的字段。
private String treeName;
// @Version 是 MyBatis-Plus 注解,用于实现乐观锁机制。
// 当对数据库记录进行更新操作时,会比较版本号,若版本号不一致则更新失败,
// 以此保证数据的一致性。
@Version
// 定义一个 Long 类型的私有属性 version作为乐观锁的版本号。
private Long version;
// @TableLogic 是 MyBatis-Plus 注解,用于实现逻辑删除功能。
// 逻辑删除不会真正从数据库中删除记录,而是通过修改该字段的值来标记记录已删除。
@TableLogic
// 定义一个 Long 类型的私有属性 delFlag作为逻辑删除的标记字段。
private Long delFlag;
}

@ -0,0 +1,73 @@
package org.dromara.demo.domain.bo;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.demo.domain.TestDemo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
* test_demo
*
* @author Lion Li
* @date 2021-07-26
*/
// @Data 是 Lombok 提供的注解,它会自动为类生成 getter、setter、toString、equals、hashCode 等方法,
// 减少了手动编写这些方法的工作量,提高了代码的简洁性。
@Data
// @EqualsAndHashCode(callSuper = true) 也是 Lombok 注解,用于生成 equals 和 hashCode 方法。
// callSuper = true 表示在生成的 equals 和 hashCode 方法中会调用父类的相应方法,
// 确保子类对象比较时会考虑父类的属性。
@EqualsAndHashCode(callSuper = true)
// @AutoMapper 是自定义的注解,用于自动生成映射代码。
// target = TestDemo.class 表示该类可以自动映射到 TestDemo 类,
// reverseConvertGenerate = false 表示不生成反向转换的代码,即从 TestDemo 到当前类的转换代码不会自动生成。
@AutoMapper(target = TestDemo.class, reverseConvertGenerate = false)
// 定义一个公共类 TestDemoBo它继承自 BaseEntity 类。
// 通常 BaseEntity 类会包含一些通用的实体属性和方法,继承它可以复用这些内容。
// Bo 一般代表 Business Object即业务对象用于封装业务逻辑中的数据。
public class TestDemoBo extends BaseEntity {
// @NotNull 是校验注解,用于确保属性值不为 null。
// message = "主键不能为空" 是校验失败时的提示信息,
// groups = {EditGroup.class} 表示该校验规则只在 EditGroup 分组下生效,
// 通常用于在不同的业务场景下应用不同的校验规则。
@NotNull(message = "主键不能为空", groups = {EditGroup.class})
// 定义一个 Long 类型的私有属性 id用于表示主键。
private Long id;
// @NotNull 注解,确保 deptId 属性值不为 null。
// message = "部门id不能为空" 是校验失败时的提示信息,
// groups = {AddGroup.class, EditGroup.class} 表示该校验规则在 AddGroup 和 EditGroup 分组下都生效。
@NotNull(message = "部门id不能为空", groups = {AddGroup.class, EditGroup.class})
// 定义一个 Long 类型的私有属性 deptId用于表示部门 ID。
private Long deptId;
// @NotNull 注解,确保 userId 属性值不为 null。
// message = "用户id不能为空" 是校验失败时的提示信息,
// groups = {AddGroup.class, EditGroup.class} 表示该校验规则在 AddGroup 和 EditGroup 分组下都生效。
@NotNull(message = "用户id不能为空", groups = {AddGroup.class, EditGroup.class})
// 定义一个 Long 类型的私有属性 userId用于表示用户 ID。
private Long userId;
// @NotNull 注解,确保 orderNum 属性值不为 null。
// message = "排序号不能为空" 是校验失败时的提示信息,
// groups = {AddGroup.class, EditGroup.class} 表示该校验规则在 AddGroup 和 EditGroup 分组下都生效。
@NotNull(message = "排序号不能为空", groups = {AddGroup.class, EditGroup.class})
// 定义一个 Integer 类型的私有属性 orderNum用于表示排序号。
private Integer orderNum;
// @NotBlank 注解,用于校验字符串类型的属性,确保该属性不为空字符串且不为 null。
// message = "key键不能为空" 是校验失败时的提示信息,
// groups = {AddGroup.class, EditGroup.class} 表示该校验规则在 AddGroup 和 EditGroup 分组下都生效。
@NotBlank(message = "key键不能为空", groups = {AddGroup.class, EditGroup.class})
// 定义一个 String 类型的私有属性 testKey用于表示 key 键。
private String testKey;
// @NotBlank 注解,确保 value 属性不为空字符串且不为 null。
// message = "值不能为空" 是校验失败时的提示信息,
// groups = {AddGroup.class, EditGroup.class} 表示该校验规则在 AddGroup 和 EditGroup 分组下都生效。
@NotBlank(message = "值不能为空", groups = {AddGroup.class, EditGroup.class})
// 定义一个 String 类型的私有属性 value用于表示值。
private String value;
}

@ -0,0 +1,56 @@
package org.dromara.demo.domain.bo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
* test_demo
*
* @author Lion Li
* @date 2021-07-26
*/
@Data
// 定义一个公共类 TestDemoImportVoVo 通常代表 View Object即视图对象
// 这里用于封装从 Excel 导入的数据,以便在程序中进行处理。
public class TestDemoImportVo {
// @NotNull 是一个校验注解,用于确保该属性的值不为 null。
// message = "部门id不能为空" 是校验失败时给出的提示信息。
@NotNull(message = "部门id不能为空")
// @ExcelProperty 是 EasyExcel 框架提供的注解,用于指定该属性与 Excel 表格中列的对应关系。
// value = "部门id" 表示该属性对应 Excel 表格中列名为 "部门id" 的列。
@ExcelProperty(value = "部门id")
// 定义一个 Long 类型的私有属性 deptId用于存储从 Excel 中导入的部门 ID 信息。
private Long deptId;
// @NotNull 注解,确保该属性的值不为 null校验失败时提示 "用户id不能为空"。
@NotNull(message = "用户id不能为空")
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "用户id" 的列。
@ExcelProperty(value = "用户id")
// 定义一个 Long 类型的私有属性 userId用于存储从 Excel 中导入的用户 ID 信息。
private Long userId;
// @NotNull 注解,确保该属性的值不为 null校验失败时提示 "排序号不能为空"。
@NotNull(message = "排序号不能为空")
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "排序号" 的列。
@ExcelProperty(value = "排序号")
// 定义一个 Long 类型的私有属性 orderNum用于存储从 Excel 中导入的排序号信息。
private Long orderNum;
// @NotBlank 是用于校验字符串类型属性的注解,确保该属性不为空字符串且不为 null。
// 校验失败时提示 "key键不能为空"。
@NotBlank(message = "key键不能为空")
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "key键" 的列。
@ExcelProperty(value = "key键")
// 定义一个 String 类型的私有属性 testKey用于存储从 Excel 中导入的 key 键信息。
private String testKey;
// @NotBlank 注解,确保该属性不为空字符串且不为 null校验失败时提示 "值不能为空"。
@NotBlank(message = "值不能为空")
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "值" 的列。
@ExcelProperty(value = "值")
// 定义一个 String 类型的私有属性 value用于存储从 Excel 中导入的值信息。
private String value;
}

@ -0,0 +1,61 @@
package org.dromara.demo.domain.bo;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.demo.domain.TestTree;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* test_tree
*
* @author Lion Li
* @date 2021-07-26
*/
// @Data 是 Lombok 库提供的注解,它会自动为类生成 getter、setter、toString、equals、hashCode 等方法,
// 可以减少样板代码,提高代码的简洁性。
@Data
// @EqualsAndHashCode(callSuper = true) 同样是 Lombok 注解,用于生成 equals 和 hashCode 方法。
// callSuper = true 表示生成的 equals 和 hashCode 方法会调用父类的对应方法,
// 确保在比较对象时会考虑父类的属性。
@EqualsAndHashCode(callSuper = true)
// @AutoMapper 是自定义的注解,用于自动生成对象之间的映射代码。
// target = TestTree.class 表示该类可以自动映射到 TestTree 类,
// reverseConvertGenerate = false 表示不生成反向转换(从 TestTree 到 TestTreeBo的代码。
@AutoMapper(target = TestTree.class, reverseConvertGenerate = false)
// 定义一个公共类 TestTreeBo继承自 BaseEntity 类。
// Bo 通常代表 Business Object即业务对象用于封装业务逻辑中的数据。
// 继承 BaseEntity 可以复用一些公共的实体属性和方法。
public class TestTreeBo extends BaseEntity {
// @NotNull 是 Bean Validation 规范中的注解,用于校验属性值不能为 null。
// message = "主键不能为空" 是校验失败时显示的错误信息。
// groups = {EditGroup.class} 表示该校验规则只在 EditGroup 分组下生效,
// 通常用于在不同业务场景下应用不同的校验规则。
@NotNull(message = "主键不能为空", groups = {EditGroup.class})
// 定义一个 Long 类型的私有属性 id用于表示该业务对象的主键。
private Long id;
// 定义一个 Long 类型的私有属性 parentId用于表示该树节点的父节点 ID。
// 没有添加校验注解,意味着该属性可以为 null。
private Long parentId;
// @NotNull 注解,校验属性值不能为 null校验失败时显示 "部门id不能为空"。
// groups = {AddGroup.class, EditGroup.class} 表示该校验规则在 AddGroup 和 EditGroup 分组下都生效。
@NotNull(message = "部门id不能为空", groups = {AddGroup.class, EditGroup.class})
// 定义一个 Long 类型的私有属性 deptId用于表示所属部门的 ID。
private Long deptId;
// @NotNull 注解,校验属性值不能为 null校验失败时显示 "用户id不能为空"。
// 该校验规则在 AddGroup 和 EditGroup 分组下都生效。
@NotNull(message = "用户id不能为空", groups = {AddGroup.class, EditGroup.class})
// 定义一个 Long 类型的私有属性 userId用于表示关联的用户 ID。
private Long userId;
// @NotBlank 是 Bean Validation 规范中的注解,用于校验字符串类型的属性,
// 确保属性值不为 null 且不为空字符串(去除首尾空格后长度大于 0
// 校验失败时显示 "树节点名不能为空",该校验规则在 AddGroup 和 EditGroup 分组下都生效。
@NotBlank(message = "树节点名不能为空", groups = {AddGroup.class, EditGroup.class})
// 定义一个 String 类型的私有属性 treeName用于表示树节点的名称。
private String treeName;
}

@ -0,0 +1,142 @@
package org.dromara.demo.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.common.core.enums.UserStatus;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.annotation.ExcelEnumFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.common.excel.convert.ExcelEnumConvert;
import java.io.Serial;
import java.io.Serializable;
/**
* Excel
*
* @author Emil.Zhang
*/
// @Data 是 Lombok 提供的注解,会自动为类生成 getter、setter、toString、equals、hashCode 等方法,
// 减少了手动编写这些方法的工作量,提高代码的简洁性。
@Data
// @ExcelIgnoreUnannotated 是 EasyExcel 框架的注解,
// 表示忽略类中未被 @ExcelProperty 注解标注的属性,即这些属性不会参与 Excel 的读写操作。
@ExcelIgnoreUnannotated
// @AllArgsConstructor 是 Lombok 注解,会自动生成一个包含所有已声明字段的构造函数。
@AllArgsConstructor
// @NoArgsConstructor 是 Lombok 注解,会自动生成一个无参构造函数。
@NoArgsConstructor
// 定义一个公共类 ExportDemoVo实现了 Serializable 接口,
// 表明该类的对象可以被序列化,即可以在网络传输或保存到文件中。
public class ExportDemoVo implements Serializable {
// @Serial 是 Java 17 引入的注解,用于显式指定序列化版本号。
// serialVersionUID 是一个静态常量,用于在反序列化时验证序列化数据的版本一致性,
// 确保反序列化的对象版本与序列化时的对象版本一致。
@Serial
private static final long serialVersionUID = 1L;
// @ExcelProperty 是 EasyExcel 框架的注解,用于指定该属性与 Excel 表格中列的对应关系。
// value = "用户名" 表示该属性对应 Excel 表格中列名为 "用户名" 的列,
// index = 0 表示该列在 Excel 中的索引位置为 0从 0 开始计数)。
@ExcelProperty(value = "用户名", index = 0)
// @NotEmpty 是 Bean Validation 规范中的注解,用于校验字符串类型的属性,
// 确保该属性不为 null 且不为空字符串(去除首尾空格后长度大于 0
// message = "用户名不能为空" 是校验失败时的提示信息,
// groups = AddGroup.class 表示该校验规则只在 AddGroup 分组下生效。
@NotEmpty(message = "用户名不能为空", groups = AddGroup.class)
// 定义一个 String 类型的私有属性 nickName用于存储用户名信息。
private String nickName;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "用户类型" 的列,索引位置为 1。
// converter = ExcelEnumConvert.class 表示使用 ExcelEnumConvert 类进行数据转换,
// 通常用于将枚举类型的数据转换为 Excel 中合适的显示格式。
@ExcelProperty(value = "用户类型", index = 1, converter = ExcelEnumConvert.class)
// @ExcelEnumFormat 是自定义注解,用于指定枚举类型的格式化规则。
// enumClass = UserStatus.class 表示该属性对应的枚举类为 UserStatus
// textField = "info" 表示使用枚举类中的 info 字段作为显示文本。
@ExcelEnumFormat(enumClass = UserStatus.class, textField = "info")
// @NotEmpty 注解,校验该属性不为空,校验失败时提示 "用户类型不能为空"
// 该校验规则在 AddGroup 分组下生效。
@NotEmpty(message = "用户类型不能为空", groups = AddGroup.class)
// 定义一个 String 类型的私有属性 userStatus用于存储用户类型信息。
private String userStatus;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "性别" 的列,索引位置为 2。
// converter = ExcelDictConvert.class 表示使用 ExcelDictConvert 类进行数据转换,
// 通常用于将字典类型的数据转换为 Excel 中合适的显示格式。
@ExcelProperty(value = "性别", index = 2, converter = ExcelDictConvert.class)
// @ExcelDictFormat 是自定义注解,用于指定字典类型的格式化规则。
// dictType = "sys_user_sex" 表示该属性对应的字典类型为 "sys_user_sex"。
@ExcelDictFormat(dictType = "sys_user_sex")
// @NotEmpty 注解,校验该属性不为空,校验失败时提示 "性别不能为空"
// 该校验规则在 AddGroup 分组下生效。
@NotEmpty(message = "性别不能为空", groups = AddGroup.class)
// 定义一个 String 类型的私有属性 gender用于存储性别信息。
private String gender;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "手机号" 的列,索引位置为 3。
@ExcelProperty(value = "手机号", index = 3)
// @NotEmpty 注解,校验该属性不为空,校验失败时提示 "手机号不能为空"
// 该校验规则在 AddGroup 分组下生效。
@NotEmpty(message = "手机号不能为空", groups = AddGroup.class)
// 定义一个 String 类型的私有属性 phoneNumber用于存储手机号信息。
private String phoneNumber;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "Email" 的列,索引位置为 4。
@ExcelProperty(value = "Email", index = 4)
// @NotEmpty 注解,校验该属性不为空,校验失败时提示 "Email不能为空"
// 该校验规则在 AddGroup 分组下生效。
@NotEmpty(message = "Email不能为空", groups = AddGroup.class)
// 定义一个 String 类型的私有属性 email用于存储 Email 信息。
private String email;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "省" 的列,索引位置为 5。
@ExcelProperty(value = "省", index = 5)
// @NotNull 是 Bean Validation 规范中的注解,用于校验属性值不能为 null。
// message = "省不能为空" 是校验失败时的提示信息,
// groups = AddGroup.class 表示该校验规则在 AddGroup 分组下生效。
@NotNull(message = "省不能为空", groups = AddGroup.class)
// 定义一个 String 类型的私有属性 province用于存储省份信息。
private String province;
// @NotNull 注解,校验该属性值不能为 null校验失败时提示 "请勿手动输入"
// 该校验规则在 EditGroup 分组下生效。
@NotNull(message = "请勿手动输入", groups = EditGroup.class)
// 定义一个 Integer 类型的私有属性 provinceId用于存储省份的 ID 信息。
private Integer provinceId;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "市" 的列,索引位置为 6。
@ExcelProperty(value = "市", index = 6)
// @NotNull 注解,校验该属性值不能为 null校验失败时提示 "市不能为空"
// 该校验规则在 AddGroup 分组下生效。
@NotNull(message = "市不能为空", groups = AddGroup.class)
// 定义一个 String 类型的私有属性 city用于存储城市信息。
private String city;
// @NotNull 注解,校验该属性值不能为 null校验失败时提示 "请勿手动输入"
// 该校验规则在 EditGroup 分组下生效。
@NotNull(message = "请勿手动输入", groups = EditGroup.class)
// 定义一个 Integer 类型的私有属性 cityId用于存储城市的 ID 信息。
private Integer cityId;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "县" 的列,索引位置为 7。
@ExcelProperty(value = "县", index = 7)
// @NotNull 注解,校验该属性值不能为 null校验失败时提示 "县不能为空"
// 该校验规则在 AddGroup 分组下生效。
@NotNull(message = "县不能为空", groups = AddGroup.class)
// 定义一个 String 类型的私有属性 area用于存储县的信息。
private String area;
// @NotNull 注解,校验该属性值不能为 null校验失败时提示 "请勿手动输入"
// 该校验规则在 EditGroup 分组下生效。
@NotNull(message = "请勿手动输入", groups = EditGroup.class)
// 定义一个 Integer 类型的私有属性 areaId用于存储县的 ID 信息。
private Integer areaId;
}

@ -0,0 +1,120 @@
package org.dromara.demo.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelNotation;
import org.dromara.common.excel.annotation.ExcelRequired;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.demo.domain.TestDemo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* test_demo
*
* @author Lion Li
* @date 2021-07-26
*/
// @Data 是 Lombok 提供的注解,它会自动为类生成 getter、setter、toString、equals、hashCode 等方法,
// 能减少样板代码,提高代码的简洁性。
@Data
// @ExcelIgnoreUnannotated 是 EasyExcel 框架的注解,
// 其作用是忽略类中未被 @ExcelProperty 注解标注的属性,这些属性不会参与 Excel 的读写操作。
@ExcelIgnoreUnannotated
// @AutoMapper 是自定义的注解,用于自动生成对象之间的映射代码。
// target = TestDemo.class 表示该类可以自动映射到 TestDemo 类。
@AutoMapper(target = TestDemo.class)
// 定义一个公共类 TestDemoVo实现了 Serializable 接口,
// 这意味着该类的对象可以被序列化,可用于网络传输或保存到文件中。
public class TestDemoVo implements Serializable {
// @Serial 是 Java 17 引入的注解,用于显式指定序列化版本号。
// serialVersionUID 是一个静态常量,用于在反序列化时验证序列化数据的版本一致性,
// 确保反序列化的对象版本与序列化时的对象版本一致。
@Serial
private static final long serialVersionUID = 1L;
// @ExcelProperty 是 EasyExcel 框架的注解,用于指定该属性与 Excel 表格中列的对应关系。
// value = "主键" 表示该属性对应 Excel 表格中列名为 "主键" 的列。
@ExcelProperty(value = "主键")
// 定义一个 Long 类型的私有属性 id用于存储主键信息。
private Long id;
// @ExcelRequired 是自定义注解,用于标记该属性在 Excel 导入时是必需的。
@ExcelRequired
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "部门id" 的列。
@ExcelProperty(value = "部门id")
// 定义一个 Long 类型的私有属性 deptId用于存储部门 ID 信息。
private Long deptId;
// @ExcelRequired 注解,表明该属性在 Excel 导入时是必需的。
@ExcelRequired
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "用户id" 的列。
@ExcelProperty(value = "用户id")
// 定义一个 Long 类型的私有属性 userId用于存储用户 ID 信息。
private Long userId;
// @ExcelRequired 注解,标记该属性在 Excel 导入时是必需的。
@ExcelRequired
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "排序号" 的列。
@ExcelProperty(value = "排序号")
// 定义一个 Integer 类型的私有属性 orderNum用于存储排序号信息。
private Integer orderNum;
// @ExcelNotation 是自定义注解value = "测试key" 可能用于添加额外的注释或说明。
@ExcelNotation(value = "测试key")
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "key键" 的列。
@ExcelProperty(value = "key键")
// 定义一个 String 类型的私有属性 testKey用于存储 key 键信息。
private String testKey;
// @ExcelNotation 注解value = "测试value" 可能用于添加额外的注释或说明。
@ExcelNotation(value = "测试value")
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "值" 的列。
@ExcelProperty(value = "值")
// 定义一个 String 类型的私有属性 value用于存储值信息。
private String value;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "创建时间" 的列。
@ExcelProperty(value = "创建时间")
// 定义一个 Date 类型的私有属性 createTime用于存储创建时间信息。
private Date createTime;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "创建人" 的列。
@ExcelProperty(value = "创建人")
// 定义一个 Long 类型的私有属性 createBy用于存储创建人的 ID 信息。
private Long createBy;
// @Translation 是自定义注解,用于数据翻译。
// type = TransConstant.USER_ID_TO_NAME 表示翻译类型是将用户 ID 转换为用户名,
// mapper = "createBy" 表示根据 createBy 属性的值进行翻译。
@Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy")
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "创建人账号" 的列。
@ExcelProperty(value = "创建人账号")
// 定义一个 String 类型的私有属性 createByName用于存储创建人的账号信息。
private String createByName;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "更新时间" 的列。
@ExcelProperty(value = "更新时间")
// 定义一个 Date 类型的私有属性 updateTime用于存储更新时间信息。
private Date updateTime;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "更新人" 的列。
@ExcelProperty(value = "更新人")
// 定义一个 Long 类型的私有属性 updateBy用于存储更新人的 ID 信息。
private Long updateBy;
// @Translation 注解,用于数据翻译。
// type = TransConstant.USER_ID_TO_NAME 表示翻译类型是将用户 ID 转换为用户名,
// mapper = "updateBy" 表示根据 updateBy 属性的值进行翻译。
@Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "updateBy")
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "更新人账号" 的列。
@ExcelProperty(value = "更新人账号")
// 定义一个 String 类型的私有属性 updateByName用于存储更新人的账号信息。
private String updateByName;
}

@ -0,0 +1,61 @@
package org.dromara.demo.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import org.dromara.demo.domain.TestTree;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* test_tree
*
* @author Lion Li
* @date 2021-07-26
*/
// @Data 是 Lombok 库提供的注解,它会自动为类生成 getter、setter、toString、equals、hashCode 等方法,
// 减少了样板代码,提高了代码的简洁性。
@Data
// @ExcelIgnoreUnannotated 是 EasyExcel 框架中的注解,
// 它的作用是在进行 Excel 读写操作时,忽略类中没有被 @ExcelProperty 注解标注的属性。
@ExcelIgnoreUnannotated
// @AutoMapper 是自定义的注解,用于自动生成对象映射代码。
// target = TestTree.class 表明该类可以自动映射到 TestTree 类,方便对象之间的数据转换。
@AutoMapper(target = TestTree.class)
// 定义一个公共类 TestTreeVo实现了 Serializable 接口,
// 这意味着该类的对象可以被序列化,能在网络传输或者文件存储中使用。
public class TestTreeVo implements Serializable {
// @Serial 是 Java 17 引入的注解,用于显式指定序列化版本号。
// serialVersionUID 是一个静态常量,在反序列化时用于验证序列化数据的版本一致性,
// 保证反序列化的对象版本和序列化时的对象版本一致。
@Serial
private static final long serialVersionUID = 1L;
// 定义一个 Long 类型的私有属性 id通常用于作为对象的唯一标识这里未添加 @ExcelProperty 注解,
// 根据 @ExcelIgnoreUnannotated 注解,该属性不会参与 Excel 的读写操作。
private Long id;
// @ExcelProperty 是 EasyExcel 框架的注解,用于指定属性和 Excel 表格列的对应关系。
// value = "父id" 表示该属性对应 Excel 表格中列名为 "父id" 的列。
@ExcelProperty(value = "父id")
// 定义一个 Long 类型的私有属性 parentId用于存储树节点的父节点 ID。
private Long parentId;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "部门id" 的列。
@ExcelProperty(value = "部门id")
// 定义一个 Long 类型的私有属性 deptId用于存储部门的 ID。
private Long deptId;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "用户id" 的列。
@ExcelProperty(value = "用户id")
// 定义一个 Long 类型的私有属性 userId用于存储用户的 ID。
private Long userId;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "树节点名" 的列。
@ExcelProperty(value = "树节点名")
// 定义一个 String 类型的私有属性 treeName用于存储树节点的名称。
private String treeName;
// @ExcelProperty 注解,指定该属性对应 Excel 表格中列名为 "创建时间" 的列。
@ExcelProperty(value = "创建时间")
// 定义一个 Date 类型的私有属性 createTime用于存储树节点的创建时间。
private Date createTime;
}

@ -0,0 +1,99 @@
package org.dromara.demo.listener;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.excel.context.AnalysisContext;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.core.DefaultExcelListener;
import org.dromara.common.excel.core.DropDownOptions;
import org.dromara.demo.domain.vo.ExportDemoVo;
import java.util.List;
/**
* Excel
*
* @author Emil.Zhang
*/
// 定义一个公共类 ExportDemoListener它继承自 DefaultExcelListener 类,并且指定泛型为 ExportDemoVo。
// DefaultExcelListener 通常是 EasyExcel 框架提供的用于处理 Excel 数据读取的监听器基类,
// 这里表示该监听器用于处理 ExportDemoVo 类型的数据。
public class ExportDemoListener extends DefaultExcelListener<ExportDemoVo> {
// 定义类的构造函数,在创建 ExportDemoListener 对象时会调用此构造函数。
public ExportDemoListener() {
// 调用父类 DefaultExcelListener 的构造函数,并传入 true 作为参数。
// 这里的 true 可能表示某种初始化配置,比如是否开启某些特定的处理逻辑等。
super(true);
}
// 重写父类的 invoke 方法,该方法会在 EasyExcel 读取到每一行数据时被调用。
// data 参数表示当前读取到的一行数据,其类型为 ExportDemoVo。
// context 参数是 AnalysisContext 类型,它包含了 Excel 解析过程中的上下文信息。
@Override
public void invoke(ExportDemoVo data, AnalysisContext context) {
// 调用 ValidatorUtils 工具类的 validate 方法,对当前读取到的数据 data 进行校验。
// AddGroup.class 表示使用 AddGroup 分组的校验规则,确保数据符合添加操作的要求。
ValidatorUtils.validate(data, AddGroup.class);
// 从当前读取到的数据 data 中获取省份信息,并将其赋值给 province 变量。
String province = data.getProvince();
// 从当前读取到的数据 data 中获取城市信息,并将其赋值给 city 变量。
String city = data.getCity();
// 从当前读取到的数据 data 中获取县区信息,并将其赋值给 area 变量。
String area = data.getArea();
// 调用 DropDownOptions 工具类的 analyzeOptionValue 方法,对省份信息进行解析,
// 得到一个包含解析结果的列表,并将其赋值给 thisRowSelectedProvinceOption 变量。
List<String> thisRowSelectedProvinceOption = DropDownOptions.analyzeOptionValue(province);
// 判断解析省份信息得到的列表的长度是否为 2。
if (thisRowSelectedProvinceOption.size() == 2) {
// 如果列表长度为 2从列表中获取第二个元素索引为 1即省份 ID 的字符串表示,
// 并将其赋值给 provinceIdStr 变量。
String provinceIdStr = thisRowSelectedProvinceOption.get(1);
// 调用 NumberUtil 工具类的 isNumber 方法,判断省份 ID 的字符串表示是否为有效的数字。
if (NumberUtil.isNumber(provinceIdStr)) {
// 如果是有效的数字,将其转换为 Integer 类型,并设置到当前读取到的数据 data 的 provinceId 属性中。
data.setProvinceId(Integer.parseInt(provinceIdStr));
}
}
// 调用 DropDownOptions 工具类的 analyzeOptionValue 方法,对城市信息进行解析,
// 得到一个包含解析结果的列表,并将其赋值给 thisRowSelectedCityOption 变量。
List<String> thisRowSelectedCityOption = DropDownOptions.analyzeOptionValue(city);
// 判断解析城市信息得到的列表的长度是否为 2。
if (thisRowSelectedCityOption.size() == 2) {
// 如果列表长度为 2从列表中获取第二个元素索引为 1即城市 ID 的字符串表示,
// 并将其赋值给 cityIdStr 变量。
String cityIdStr = thisRowSelectedCityOption.get(1);
// 调用 NumberUtil 工具类的 isNumber 方法,判断城市 ID 的字符串表示是否为有效的数字。
if (NumberUtil.isNumber(cityIdStr)) {
// 如果是有效的数字,将其转换为 Integer 类型,并设置到当前读取到的数据 data 的 cityId 属性中。
data.setCityId(Integer.parseInt(cityIdStr));
}
}
// 调用 DropDownOptions 工具类的 analyzeOptionValue 方法,对县区信息进行解析,
// 得到一个包含解析结果的列表,并将其赋值给 thisRowSelectedAreaOption 变量。
List<String> thisRowSelectedAreaOption = DropDownOptions.analyzeOptionValue(area);
// 判断解析县区信息得到的列表的长度是否为 2。
if (thisRowSelectedAreaOption.size() == 2) {
// 如果列表长度为 2从列表中获取第二个元素索引为 1即县区 ID 的字符串表示,
// 并将其赋值给 areaIdStr 变量。
String areaIdStr = thisRowSelectedAreaOption.get(1);
// 调用 NumberUtil 工具类的 isNumber 方法,判断县区 ID 的字符串表示是否为有效的数字。
if (NumberUtil.isNumber(areaIdStr)) {
// 如果是有效的数字,将其转换为 Integer 类型,并设置到当前读取到的数据 data 的 areaId 属性中。
data.setAreaId(Integer.parseInt(areaIdStr));
}
}
// 再次调用 ValidatorUtils 工具类的 validate 方法,对当前读取到的数据 data 进行校验。
// EditGroup.class 表示使用 EditGroup 分组的校验规则,确保数据符合编辑操作的要求。
ValidatorUtils.validate(data, EditGroup.class);
// 调用 getExcelResult 方法获取 Excel 解析结果对象,再调用该对象的 getList 方法获取结果列表,
// 最后将当前读取并处理好的数据 data 添加到结果列表中。
getExcelResult().getList().add(data);
}
}

@ -0,0 +1,13 @@
package org.dromara.demo.mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.demo.domain.TestDemoEncrypt;
/**
*
*
* @author Lion Li
*/
public interface TestDemoEncryptMapper extends BaseMapperPlus<TestDemoEncrypt, TestDemoEncrypt> {
}

@ -0,0 +1,64 @@
package org.dromara.demo.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.annotation.DataColumn;
import org.dromara.common.mybatis.annotation.DataPermission;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.demo.domain.TestDemo;
import org.dromara.demo.domain.vo.TestDemoVo;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
/**
* Mapper
*
* @author Lion Li
* @date 2021-07-26
*/
public interface TestDemoMapper extends BaseMapperPlus<TestDemo, TestDemoVo> {
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
})
Page<TestDemoVo> customPageList(@Param("page") Page<TestDemo> page, @Param("ew") Wrapper<TestDemo> wrapper);
@Override
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
})
default <P extends IPage<TestDemoVo>> P selectVoPage(IPage<TestDemo> page, Wrapper<TestDemo> wrapper) {
return selectVoPage(page, wrapper, this.currentVoClass());
}
@Override
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
})
default List<TestDemoVo> selectVoList(Wrapper<TestDemo> wrapper) {
return selectVoList(wrapper, this.currentVoClass());
}
@Override
@DataPermission(value = {
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
}, joinStr = "AND")
List<TestDemo> selectByIds(@Param(Constants.COLL) Collection<? extends Serializable> idList);
@Override
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
})
int updateById(@Param(Constants.ENTITY) TestDemo entity);
}

@ -0,0 +1,21 @@
package org.dromara.demo.mapper;
import org.dromara.common.mybatis.annotation.DataColumn;
import org.dromara.common.mybatis.annotation.DataPermission;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.demo.domain.TestTree;
import org.dromara.demo.domain.vo.TestTreeVo;
/**
* Mapper
*
* @author Lion Li
* @date 2021-07-26
*/
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
})
public interface TestTreeMapper extends BaseMapperPlus<TestTree, TestTreeVo> {
}

@ -0,0 +1,18 @@
package org.dromara.demo.service;
import jakarta.servlet.http.HttpServletResponse;
/**
* Excel
*
* @author Emil.Zhang
*/
public interface IExportExcelService {
/**
*
*
* @param response /
*/
void exportWithOptions(HttpServletResponse response);
}

@ -0,0 +1,71 @@
package org.dromara.demo.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.demo.domain.TestDemo;
import org.dromara.demo.domain.bo.TestDemoBo;
import org.dromara.demo.domain.vo.TestDemoVo;
import java.util.Collection;
import java.util.List;
/**
* Service
*
* @author Lion Li
* @date 2021-07-26
*/
public interface ITestDemoService {
/**
*
*
* @return
*/
TestDemoVo queryById(Long id);
/**
*
*/
TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo, PageQuery pageQuery);
/**
*
*/
TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo, PageQuery pageQuery);
/**
*
*/
List<TestDemoVo> queryList(TestDemoBo bo);
/**
*
*
* @param bo
* @return
*/
Boolean insertByBo(TestDemoBo bo);
/**
*
*
* @param bo
* @return
*/
Boolean updateByBo(TestDemoBo bo);
/**
*
*
* @param ids
* @param isValid ,true-,false-
* @return
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
*
*/
Boolean saveBatch(List<TestDemo> list);
}

@ -0,0 +1,52 @@
package org.dromara.demo.service;
import org.dromara.demo.domain.bo.TestTreeBo;
import org.dromara.demo.domain.vo.TestTreeVo;
import java.util.Collection;
import java.util.List;
/**
* Service
*
* @author Lion Li
* @date 2021-07-26
*/
public interface ITestTreeService {
/**
*
*
* @return
*/
TestTreeVo queryById(Long id);
/**
*
*/
List<TestTreeVo> queryList(TestTreeBo bo);
/**
*
*
* @param bo
* @return
*/
Boolean insertByBo(TestTreeBo bo);
/**
*
*
* @param bo
* @return
*/
Boolean updateByBo(TestTreeBo bo);
/**
*
*
* @param ids
* @param isValid ,true-,false-
* @return
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

@ -0,0 +1,236 @@
package org.dromara.demo.service.impl;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.excel.core.DropDownOptions;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.demo.domain.vo.ExportDemoVo;
import org.dromara.demo.service.IExportExcelService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Excel
*
* @author Emil.Zhang
*/
@Service
@RequiredArgsConstructor
public class ExportExcelServiceImpl implements IExportExcelService {
@Override
public void exportWithOptions(HttpServletResponse response) {
// 创建表格数据,业务中一般通过数据库查询
List<ExportDemoVo> excelDataList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
// 模拟数据库中的一条数据
ExportDemoVo everyRowData = new ExportDemoVo();
everyRowData.setNickName("用户-" + i);
everyRowData.setUserStatus(SystemConstants.NORMAL);
everyRowData.setGender("1");
everyRowData.setPhoneNumber(String.format("175%08d", i));
everyRowData.setEmail(String.format("175%08d", i) + "@163.com");
everyRowData.setProvinceId(i);
everyRowData.setCityId(i);
everyRowData.setAreaId(i);
excelDataList.add(everyRowData);
}
// 通过@ExcelIgnoreUnannotated配合@ExcelProperty合理显示需要的列
// 并通过@DropDown注解指定下拉值或者通过创建ExcelOptions来指定下拉框
// 使用ExcelOptions时建议指定列index防止出现下拉列解析不对齐
// 首先从数据库中查询下拉框内的可选项
// 这里模拟查询结果
List<DemoCityData> provinceList = getProvinceList(),
cityList = getCityList(provinceList),
areaList = getAreaList(cityList);
int provinceIndex = 5, cityIndex = 6, areaIndex = 7;
DropDownOptions provinceToCity = DropDownOptions.buildLinkedOptions(
provinceList,
provinceIndex,
cityList,
cityIndex,
DemoCityData::getId,
DemoCityData::getPid,
everyOptions -> DropDownOptions.createOptionValue(
everyOptions.getName(),
everyOptions.getId()
)
);
DropDownOptions cityToArea = DropDownOptions.buildLinkedOptions(
cityList,
cityIndex,
areaList,
areaIndex,
DemoCityData::getId,
DemoCityData::getPid,
everyOptions -> DropDownOptions.createOptionValue(
everyOptions.getName(),
everyOptions.getId()
)
);
// 把所有的下拉框存储
List<DropDownOptions> options = new ArrayList<>();
options.add(provinceToCity);
options.add(cityToArea);
// 到此为止所有的下拉框可选项已全部配置完毕
// 接下来需要将Excel中的展示数据转换为对应的下拉选
List<ExportDemoVo> outList = StreamUtils.toList(excelDataList, everyRowData -> {
// 只需要处理没有使用@ExcelDictFormat注解的下拉框
// 一般来说,可以直接在数据库查询即查询出省市县信息,这里通过模拟操作赋值
everyRowData.setProvince(buildOptions(provinceList, everyRowData.getProvinceId()));
everyRowData.setCity(buildOptions(cityList, everyRowData.getCityId()));
everyRowData.setArea(buildOptions(areaList, everyRowData.getAreaId()));
return everyRowData;
});
ExcelUtil.exportExcel(outList, "下拉框示例", ExportDemoVo.class, response, options);
}
private String buildOptions(List<DemoCityData> cityDataList, Integer id) {
Map<Integer, List<DemoCityData>> groupByIdMap =
cityDataList.stream().collect(Collectors.groupingBy(DemoCityData::getId));
if (groupByIdMap.containsKey(id)) {
DemoCityData demoCityData = groupByIdMap.get(id).get(0);
return DropDownOptions.createOptionValue(demoCityData.getName(), demoCityData.getId());
} else {
return StrUtil.EMPTY;
}
}
/**
*
*
* @return /
*/
private List<DemoCityData> getProvinceList() {
List<DemoCityData> provinceList = new ArrayList<>();
// 实际业务中一般采用数据库读取的形式,这里直接拼接创建
provinceList.add(new DemoCityData(0, null, "P100000"));
provinceList.add(new DemoCityData(1, null, "P200000"));
provinceList.add(new DemoCityData(2, null, "P300000"));
return provinceList;
}
/**
*
*
* @param provinceList
* @return /
*/
private List<DemoCityData> getCityList(List<DemoCityData> provinceList) {
List<DemoCityData> cityList = new ArrayList<>();
// 实际业务中一般采用数据库读取的形式,这里直接拼接创建
cityList.add(new DemoCityData(0, 0, "C110000"));
cityList.add(new DemoCityData(1, 0, "C120000"));
cityList.add(new DemoCityData(2, 1, "C210000"));
cityList.add(new DemoCityData(3, 1, "C220000"));
cityList.add(new DemoCityData(4, 1, "C230000"));
selectParentData(provinceList, cityList);
return cityList;
}
/**
*
*
* @param cityList
* @return /
*/
private List<DemoCityData> getAreaList(List<DemoCityData> cityList) {
List<DemoCityData> areaList = new ArrayList<>();
int minCount = 500;
int maxCount = 10000;
// 实际业务中一般采用数据库读取的形式,这里直接拼接创建
for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) {
areaList.add(new DemoCityData(areaList.size(), 0, String.format("A11%04d", i)));
}
for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) {
areaList.add(new DemoCityData(areaList.size(), 1, String.format("A12%04d", i)));
}
for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) {
areaList.add(new DemoCityData(areaList.size(), 2, String.format("A21%04d", i)));
}
for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) {
areaList.add(new DemoCityData(areaList.size(), 3, String.format("A22%04d", i)));
}
for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) {
areaList.add(new DemoCityData(areaList.size(), 4, String.format("A23%04d", i)));
}
selectParentData(cityList, areaList);
return areaList;
}
/**
*
*
* @param parentList /
* @param sonList /
*/
private void selectParentData(List<DemoCityData> parentList, List<DemoCityData> sonList) {
Map<Integer, List<DemoCityData>> parentGroupByIdMap =
parentList.stream().collect(Collectors.groupingBy(DemoCityData::getId));
sonList.forEach(everySon -> {
if (parentGroupByIdMap.containsKey(everySon.getPid())) {
everySon.setPData(parentGroupByIdMap.get(everySon.getPid()).get(0));
}
});
}
/**
*
*/
@Data
private static class DemoCityData {
/**
* id
*/
private Integer id;
/**
* pid
*/
private Integer pid;
/**
* name
*/
private String name;
/**
* MyBatisPlus
*/
private DemoCityData pData;
public DemoCityData(Integer id, Integer pid, String name) {
this.id = id;
this.pid = pid;
this.name = name;
}
}
}

@ -0,0 +1,116 @@
package org.dromara.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.demo.domain.TestDemo;
import org.dromara.demo.domain.bo.TestDemoBo;
import org.dromara.demo.domain.vo.TestDemoVo;
import org.dromara.demo.mapper.TestDemoMapper;
import org.dromara.demo.service.ITestDemoService;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Service
*
* @author Lion Li
* @date 2021-07-26
*/
@RequiredArgsConstructor
@Service
public class TestDemoServiceImpl implements ITestDemoService {
private final TestDemoMapper baseMapper;
@Override
public TestDemoVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
@Override
public TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
Page<TestDemoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*/
@Override
public TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
Page<TestDemoVo> result = baseMapper.customPageList(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
@Override
public List<TestDemoVo> queryList(TestDemoBo bo) {
return baseMapper.selectVoList(buildQueryWrapper(bo));
}
private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
lqw.orderByAsc(TestDemo::getId);
return lqw;
}
@Override
public Boolean insertByBo(TestDemoBo bo) {
TestDemo add = MapstructUtils.convert(bo, TestDemo.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
@Override
public Boolean updateByBo(TestDemoBo bo) {
TestDemo update = MapstructUtils.convert(bo, TestDemo.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
*
*
* @param entity
*/
private void validEntityBeforeSave(TestDemo entity) {
//TODO 做一些数据校验,如唯一约束
}
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 做一些业务上的校验,判断是否需要校验
List<TestDemo> list = baseMapper.selectByIds(ids);
if (list.size() != ids.size()) {
throw new ServiceException("您没有删除权限!");
}
}
return baseMapper.deleteByIds(ids) > 0;
}
@Override
public Boolean saveBatch(List<TestDemo> list) {
return baseMapper.insertBatch(list);
}
}

@ -0,0 +1,88 @@
package org.dromara.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.demo.domain.TestTree;
import org.dromara.demo.domain.bo.TestTreeBo;
import org.dromara.demo.domain.vo.TestTreeVo;
import org.dromara.demo.mapper.TestTreeMapper;
import org.dromara.demo.service.ITestTreeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Service
*
* @author Lion Li
* @date 2021-07-26
*/
// @DS("slave") // 切换从库查询
@RequiredArgsConstructor
@Service
public class TestTreeServiceImpl implements ITestTreeService {
private final TestTreeMapper baseMapper;
@Override
public TestTreeVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
// @DS("slave") // 切换从库查询
@Override
public List<TestTreeVo> queryList(TestTreeBo bo) {
LambdaQueryWrapper<TestTree> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<TestTree> buildQueryWrapper(TestTreeBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
lqw.orderByAsc(TestTree::getId);
return lqw;
}
@Override
public Boolean insertByBo(TestTreeBo bo) {
TestTree add = MapstructUtils.convert(bo, TestTree.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
@Override
public Boolean updateByBo(TestTreeBo bo) {
TestTree update = MapstructUtils.convert(bo, TestTree.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
*
*
* @param entity
*/
private void validEntityBeforeSave(TestTree entity) {
//TODO 做一些数据校验,如唯一约束
}
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.demo.mapper.TestDemoMapper">
<select id="customPageList" resultType="org.dromara.demo.domain.vo.TestDemoVo">
SELECT * FROM test_demo ${ew.customSqlSegment}
</select>
</mapper>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.demo.mapper.TestTreeMapper">
</mapper>

@ -0,0 +1,3 @@
java包使用 `.` 分割 resource 目录使用 `/` 分割
<br>
此文件目的 防止文件夹粘连找不到 `xml` 文件

@ -0,0 +1,4 @@
org.dromara.demo.domain.vo.TestTreeVo
org.dromara.demo.domain.bo.TestTreeBo
org.dromara.demo.domain.vo.TestDemoVo
org.dromara.demo.domain.bo.TestDemoBo

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.demo.mapper.TestDemoMapper">
<select id="customPageList" resultType="org.dromara.demo.domain.vo.TestDemoVo">
SELECT * FROM test_demo ${ew.customSqlSegment}
</select>
</mapper>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.demo.mapper.TestTreeMapper">
</mapper>

@ -0,0 +1,3 @@
java包使用 `.` 分割 resource 目录使用 `/` 分割
<br>
此文件目的 防止文件夹粘连找不到 `xml` 文件

@ -0,0 +1 @@
{"doc":" 邮件发送案例\n\n @author Michelle.Chung\n","fields":[],"enumConstants":[],"methods":[{"name":"sendSimpleMessage","paramTypes":["java.lang.String","java.lang.String","java.lang.String"],"doc":" 发送邮件\n\n @param to 接收人\n @param subject 标题\n @param text 内容\n"},{"name":"sendMessageWithAttachment","paramTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String"],"doc":" 发送邮件(带附件)\n\n @param to 接收人\n @param subject 标题\n @param text 内容\n @param filePath 附件路径\n"},{"name":"sendMessageWithAttachments","paramTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String[]"],"doc":" 发送邮件(多附件)\n\n @param to 接收人\n @param subject 标题\n @param text 内容\n @param paths 附件路径\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" spring-cache 演示案例\n\n @author Lion Li\n","fields":[],"enumConstants":[],"methods":[{"name":"test1","paramTypes":["java.lang.String","java.lang.String"],"doc":" 测试 @Cacheable\n <p>\n 表示这个方法有了缓存的功能,方法的返回值会被缓存下来\n 下一次调用该方法前,会去检查是否缓存中已经有值\n 如果有就直接返回,不调用方法\n 如果没有,就调用方法,然后把结果缓存起来\n 这个注解「一般用在查询方法上」\n <p>\n 重点说明: 缓存注解严谨与其他筛选数据功能一起使用\n 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题\n <p>\n cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数\n"},{"name":"test2","paramTypes":["java.lang.String","java.lang.String"],"doc":" 测试 @CachePut\n <p>\n 加了@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用\n 它「通常用在新增或者实时更新方法上」\n <p>\n cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数\n"},{"name":"test3","paramTypes":["java.lang.String","java.lang.String"],"doc":" 测试 @CacheEvict\n <p>\n 使用了CacheEvict注解的方法,会清空指定缓存\n 「一般用在删除的方法上」\n <p>\n cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数\n"},{"name":"test6","paramTypes":["java.lang.String","java.lang.String"],"doc":" 测试设置过期时间\n 手动设置过期时间10秒\n 11秒后获取 判断是否相等\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" 测试分布式锁的样例\n\n @author shenxinquan\n","fields":[],"enumConstants":[],"methods":[{"name":"testLock4j","paramTypes":["java.lang.String","java.lang.String"],"doc":" 测试lock4j 注解\n"},{"name":"testLock4jLockTemplate","paramTypes":["java.lang.String","java.lang.String"],"doc":" 测试lock4j 工具\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" Redis 发布订阅 演示案例\n\n @author Lion Li\n","fields":[],"enumConstants":[],"methods":[{"name":"pub","paramTypes":["java.lang.String","java.lang.String"],"doc":" 发布消息\n\n @param key 通道Key\n @param value 发送内容\n"},{"name":"sub","paramTypes":["java.lang.String"],"doc":" 订阅消息\n\n @param key 通道Key\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" 测试分布式限流样例\n\n @author Lion Li\n","fields":[],"enumConstants":[],"methods":[{"name":"test","paramTypes":["java.lang.String"],"doc":" 测试全局限流\n 全局影响\n"},{"name":"testip","paramTypes":["java.lang.String"],"doc":" 测试请求IP限流\n 同一IP请求受影响\n"},{"name":"testcluster","paramTypes":["java.lang.String"],"doc":" 测试集群实例限流\n 启动两个后端服务互不影响\n"},{"name":"testObj","paramTypes":["java.lang.String"],"doc":" 测试请求IP限流(key基于参数获取)\n 同一IP请求受影响\n\n 简单变量获取 #变量 复杂表达式 #{#变量 != 1 ? 1 : 0}\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" 短信演示案例\n 请先阅读文档 否则无法使用\n\n @author Lion Li\n @version 4.2.0\n","fields":[],"enumConstants":[],"methods":[{"name":"sendAliyun","paramTypes":["java.lang.String","java.lang.String"],"doc":" 发送短信Aliyun\n\n @param phones 电话号\n @param templateId 模板ID\n"},{"name":"sendTencent","paramTypes":["java.lang.String","java.lang.String"],"doc":" 发送短信Tencent\n\n @param phones 电话号\n @param templateId 模板ID\n"},{"name":"addBlacklist","paramTypes":["java.lang.String"],"doc":" 添加黑名单\n\n @param phone 手机号\n"},{"name":"removeBlacklist","paramTypes":["java.lang.String"],"doc":" 移除黑名单\n\n @param phone 手机号\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" swagger3 用法示例\n\n @author Lion Li\n","fields":[],"enumConstants":[],"methods":[{"name":"upload","paramTypes":["org.springframework.web.multipart.MultipartFile"],"doc":" 上传请求\n 必须使用 @RequestPart 注解标注为文件\n\n @param file 文件\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" 测试批量方法\n\n @author Lion Li\n @date 2021-05-30\n","fields":[{"name":"testDemoMapper","doc":" 为了便于测试 直接引入mapper\n"}],"enumConstants":[],"methods":[{"name":"add","paramTypes":[],"doc":" 新增批量方法 可完美替代 saveBatch 秒级插入上万数据 (对mysql负荷较大)\n <p>\n 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度\n"},{"name":"addOrUpdate","paramTypes":[],"doc":" 新增或更新 可完美替代 saveOrUpdateBatch 高性能\n <p>\n 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度\n"},{"name":"remove","paramTypes":[],"doc":" 删除批量方法\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" 测试单表Controller\n\n @author Lion Li\n @date 2021-07-26\n","fields":[],"enumConstants":[],"methods":[{"name":"list","paramTypes":["org.dromara.demo.domain.bo.TestDemoBo","org.dromara.common.mybatis.core.page.PageQuery"],"doc":" 查询测试单表列表\n"},{"name":"page","paramTypes":["org.dromara.demo.domain.bo.TestDemoBo","org.dromara.common.mybatis.core.page.PageQuery"],"doc":" 自定义分页查询\n"},{"name":"importData","paramTypes":["org.springframework.web.multipart.MultipartFile"],"doc":" 导入数据\n\n @param file 导入文件\n"},{"name":"export","paramTypes":["org.dromara.demo.domain.bo.TestDemoBo","jakarta.servlet.http.HttpServletResponse"],"doc":" 导出测试单表列表\n"},{"name":"getInfo","paramTypes":["java.lang.Long"],"doc":" 获取测试单表详细信息\n\n @param id 测试ID\n"},{"name":"add","paramTypes":["org.dromara.demo.domain.bo.TestDemoBo"],"doc":" 新增测试单表\n"},{"name":"edit","paramTypes":["org.dromara.demo.domain.bo.TestDemoBo"],"doc":" 修改测试单表\n"},{"name":"remove","paramTypes":["java.lang.Long[]"],"doc":" 删除测试单表\n\n @param ids 测试ID串\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" 测试数据库加解密功能\n\n @author Lion Li\n","fields":[],"enumConstants":[],"methods":[{"name":"test","paramTypes":["java.lang.String","java.lang.String"],"doc":" 测试数据库加解密\n\n @param key 测试key\n @param value 测试value\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" 测试Excel功能\n\n @author Lion Li\n","fields":[],"enumConstants":[],"methods":[{"name":"exportTemplateOne","paramTypes":["jakarta.servlet.http.HttpServletResponse"],"doc":" 单列表多数据\n"},{"name":"exportTemplateMuliti","paramTypes":["jakarta.servlet.http.HttpServletResponse"],"doc":" 多列表多数据\n"},{"name":"exportWithOptions","paramTypes":["jakarta.servlet.http.HttpServletResponse"],"doc":" 导出下拉框\n\n @param response /\n"},{"name":"exportTemplateMultiSheet","paramTypes":["jakarta.servlet.http.HttpServletResponse"],"doc":" 多个sheet导出\n"},{"name":"importWithOptions","paramTypes":["org.springframework.web.multipart.MultipartFile"],"doc":" 导入表格\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" 测试国际化\n\n @author Lion Li\n","fields":[],"enumConstants":[],"methods":[{"name":"get","paramTypes":["java.lang.String"],"doc":" 通过code获取国际化内容\n code为 messages.properties 中的 key\n <p>\n 测试使用 user.register.success\n\n @param code 国际化code\n"},{"name":"test1","paramTypes":["java.lang.String"],"doc":" Validator 校验国际化\n 不传值 分别查看异常返回\n <p>\n 测试使用 not.null\n"},{"name":"test2","paramTypes":["org.dromara.demo.controller.TestI18nController.TestI18nBo"],"doc":" Bean 校验国际化\n 不传值 分别查看异常返回\n <p>\n 测试使用 not.null\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":"","fields":[{"name":"idCard","doc":" 身份证\n"},{"name":"phone","doc":" 电话\n"},{"name":"address","doc":" 地址\n"},{"name":"email","doc":" 邮箱\n"},{"name":"bankCard","doc":" 银行卡\n"}],"enumConstants":[],"methods":[],"constructors":[]}

@ -0,0 +1 @@
{"doc":" 测试数据脱敏控制器\n <p>\n 默认管理员不过滤\n 需自行根据业务重写实现\n\n @author Lion Li\n @version 3.6.0\n @see SensitiveService\n","fields":[],"enumConstants":[],"methods":[{"name":"test","paramTypes":[],"doc":" 测试数据脱敏\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" 测试树表Controller\n\n @author Lion Li\n @date 2021-07-26\n","fields":[],"enumConstants":[],"methods":[{"name":"list","paramTypes":["org.dromara.demo.domain.bo.TestTreeBo"],"doc":" 查询测试树表列表\n"},{"name":"export","paramTypes":["org.dromara.demo.domain.bo.TestTreeBo","jakarta.servlet.http.HttpServletResponse"],"doc":" 导出测试树表列表\n"},{"name":"getInfo","paramTypes":["java.lang.Long"],"doc":" 获取测试树表详细信息\n\n @param id 测试树ID\n"},{"name":"add","paramTypes":["org.dromara.demo.domain.bo.TestTreeBo"],"doc":" 新增测试树表\n"},{"name":"edit","paramTypes":["org.dromara.demo.domain.bo.TestTreeBo"],"doc":" 修改测试树表\n"},{"name":"remove","paramTypes":["java.lang.Long[]"],"doc":" 删除测试树表\n\n @param ids 测试树ID串\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" WebSocket 演示案例\n\n @author zendwang\n","fields":[],"enumConstants":[],"methods":[{"name":"send","paramTypes":["org.dromara.common.websocket.dto.WebSocketMessageDto"],"doc":" 发布消息\n\n @param dto 发送内容\n"}],"constructors":[]}

@ -0,0 +1 @@
{"doc":" 有界队列 演示案例\n <p>\n 轻量级队列 重量级数据量 请使用 MQ\n <p>\n 集群测试通过 同一个数据只会被消费一次 做好事务补偿\n 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条\n\n @author Lion Li\n @version 3.6.0\n","fields":[],"enumConstants":[],"methods":[{"name":"add","paramTypes":["java.lang.String","int"],"doc":" 添加队列数据\n\n @param queueName 队列名\n @param capacity 容量\n"},{"name":"remove","paramTypes":["java.lang.String"],"doc":" 删除队列数据\n\n @param queueName 队列名\n"},{"name":"get","paramTypes":["java.lang.String"],"doc":" 获取队列数据\n\n @param queueName 队列名\n"}],"constructors":[]}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save