feat(framework): 添加springboot4版本

This commit is contained in:
2026-02-27 16:10:26 +08:00
parent 1b1bb10089
commit f9a8c58b68
304 changed files with 23942 additions and 0 deletions
@@ -0,0 +1,33 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy;
import cn.odboy.framework.context.BootApplication;
import java.net.UnknownHostException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@EnableCaching
@SpringBootApplication
public class AppRun extends BootApplication {
public static void main(String[] args) throws UnknownHostException {
SpringApplication springApplication = new SpringApplication(AppRun.class);
inited(springApplication.run(args));
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy;
import cn.odboy.framework.mybatisplus.core.KitMpCmdGenUtil;
import java.util.List;
/**
* 代码生成入口
*/
public class GenCode {
private static final String ADDR = "127.0.0.1";
private static final Integer PORT = 3306;
private static final String DATABASE_NAME = "cutejava";
private static final String DATABASE_USER = "root";
private static final String DATABASE_PWD = "kd123456";
private static final String URL = String.format("jdbc:mysql://%s:%s/%s", ADDR, PORT, DATABASE_NAME);
public static void main(String[] args) {
KitMpCmdGenUtil generator = new KitMpCmdGenUtil();
generator.setDatabaseUrl(URL);
generator.setDriverClassName("com.mysql.cj.jdbc.Driver");
generator.setDatabaseUsername(DATABASE_USER);
generator.setDatabasePassword(DATABASE_PWD);
genTaskCode(generator);
}
private static void genTaskCode(KitMpCmdGenUtil generator) {
generator.gen("task", "",
List.of("task_instance_info", "task_instance_detail", "task_template_info", "task_instance_step_detail"));
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy;
import cn.odboy.framework.monitor.service.KitHealthCheckPointService;
import cn.odboy.system.dal.mysql.SystemDictMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
@Component
public class HealthCheckPointer implements KitHealthCheckPointService {
@Autowired
private SystemDictMapper systemDictMapper;
/**
* 系统准备就绪
*/
@Override
public ResponseEntity<?> doReadiness() {
return ResponseEntity.ok(null);
}
/**
* 系统是否正常
*/
@Override
public ResponseEntity<?> doLiveness() {
return ResponseEntity.ok(systemDictMapper.selectById(1));
}
}
@@ -0,0 +1,156 @@
server:
port: 8000
http2:
# 启用 HTTP/2 支持,提升传输效率
enabled: true
compression:
# 启用 GZIP 压缩,减少传输数据量
enabled: true
# 需要压缩的 MIME 类型
mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
# 最小压缩响应大小(字节)
min-response-size: 1024
# 启用优雅停机,并遵守spring.lifecycle.timeout-per-shutdown-phase属性中给出的超时
# GRACEFUL (优雅): 当应用程序以"GRACEFUL"模式关闭时,它不会接受新的请求且会尝试完成所有正在进行的请求和处理,然后才会终止。这种方式适用于那些可能需要一些时间来清理资源或完成正在进行的任务的场景。
# IMMEDIATE(立即): 当应用程序以"IMMEDIATE"模式关闭时,它会立即终止,而不管当前是否有任何活动任务或请求。这种方式适用于那些可以立即停止而不会造成严重问题的情况。
shutdown: graceful
spring:
lifecycle:
# 采用java.time.Duration格式的值,如果在这个时间内,优雅停机没有停掉应用,超过这个时间就会强制停止应用
timeout-per-shutdown-phase: 30s
freemarker:
# 是否检查模板位置是否存在
check-template-location: false
# 模板编码
charset: utf-8
cache:
# 解决缓存异常 https://blog.csdn.net/jikeyeka/article/details/125804049
type: redis
data:
redis:
database: ${REDIS_DB:1}
host: ${REDIS_HOST:127.0.0.1}
port: ${REDIS_PORT:6379}
password: ${REDIS_PWD:kd123456}
# 连接超时时间
timeout: 5000
repositories:
# 是否启用Redis存储(关闭防止出现 Multiple Spring Data modules found, entering strict repository configuration mode)
enabled: false
datasource:
driverClassName: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://127.0.0.1:3306/cutejava?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
username: root
password: kd123456
hikari:
connection-timeout: 60000
maximum-pool-size: 10
minimum-idle: 2
idle-timeout: 30000
# 集成mybatis-plus
mybatis-plus:
configuration:
# 开启 Mybatis 二级缓存,默认为 true
cache-enabled: false
# 设置本地缓存作用域, Mybatis 一级缓存, 默认为 SESSION
# 同一个 session 相同查询语句不会再次查询数据库
# 微服务中, 建议设置为STATEMENT, 即关闭一级缓存
local-cache-scope: STATEMENT
# 是否开启自动驼峰命名规则(camel case)映射, 即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
map-underscore-to-camel-case: true
# Sql日志
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 启动时是否检查 MyBatis XML 文件的存在, 默认不检查
check-config-location: true
# MyBatis Mapper 所对应的 XML 文件位置
# Maven 多模块项目的扫描路径需以 classpath*: 开头 (即加载多个 jar 包下的 XML 文件)
mapper-locations: classpath*:mapper/**/*.xml
# MyBatis-Plus 全局策略中的 DB 策略配置
global-config:
db-config:
# 逻辑已删除值(逻辑删除下有效)
logic-delete-value: 0
# 逻辑未删除值(逻辑删除下有效)
logic-not-delete-value: 1
# 全局默认主键类型, 这里为自增主键
id-type: auto
# 表名是否使用驼峰转下划线命名,只对表名生效
table-underline: true
# 是否控制台 print mybatisplus-plus 的 LOGO
banner: true
# 框架配置
app:
# 密码加密传输,前端公钥加密,后端私钥解密
rsa:
private-key: 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
# 鉴权
jwt:
# 必须使用最少88位的Base64对该令牌进行编码 cn.odboy.util.CsBase64SecretUtil
base64-secret: YzE5ZWNjMjlkMGY4NDc4MDlmYzE0Y2I0MTk4YTQ4YjY5NTU0OGQxODNjNjQ0MmU1YjBmZTYwYjljYzI0M2M4MTJkZjk3Y2ZlYjg3ZTQxMjM4MjEwY2ZiYzE4YmFiYmZlZDVhM2M1YmI3M2FmNDFmNWJmM2MyMTM1ZDNhMmRmYzY=
# 令牌过期时间 此处单位/毫秒 ,默认4小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html
token-validity-in-seconds: 14400000
# token 续期检查时间范围(默认30分钟,单位毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期
detect: 1800000
# 续期时间范围,默认1小时,单位毫秒
renew: 3600000
# 登录
login:
# 是否限制单用户登录
single: false
# 验证码
captchaSetting:
# 验证码类型配置 查看 LoginProperties 类
code-type: arithmetic
# 登录图形验证码有效时间/分钟
expiration: 2
# 验证码高度
width: 111
# 验证码宽度
height: 36
# 内容长度
length: 2
# 字体名称,为空则使用默认字体
font-name:
# 字体大小
font-size: 25
# 针对@Async
async-task-pool:
# 核心线程池大小
core-pool-size: 10
# 最大线程数
max-pool-size: 30
# 活跃时间
keep-alive-seconds: 60
# 队列容量
queue-capacity: 50
# oss
oss:
max-size: 100
minio:
endpoint: http://127.0.0.1:9000
bucketName: cutejava
accessKey: 8o5oVzqvKby5TzaGcfmx
secretKey: 8BiK5UbmvXGJNVQ98CGiohHzT1N1FMRZqHRdJllj
captcha:
expireTime: 300
# 序列化白名单
serial-white-list:
- "org.springframework"
- "cn.odboy.system.dal.dataobject"
- "cn.odboy.system.dal.model"
# debug
#debug: true
# 日志配置
logging:
logback:
rollingpolicy:
max-file-size: 100MB
max-history: 7
total-size-cap: 1GB
level:
# root: debug
cn.odboy: info
knife4j:
enable: true
setting:
language: zh_cn
@@ -0,0 +1,167 @@
server:
port: 8000
http2:
# 启用 HTTP/2 支持,提升传输效率
enabled: true
compression:
# 启用 GZIP 压缩,减少传输数据量
enabled: true
# 需要压缩的 MIME 类型
mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
# 最小压缩响应大小(字节)
min-response-size: 1024
# 启用优雅停机,并遵守spring.lifecycle.timeout-per-shutdown-phase属性中给出的超时
# GRACEFUL (优雅): 当应用程序以"GRACEFUL"模式关闭时,它不会接受新的请求且会尝试完成所有正在进行的请求和处理,然后才会终止。这种方式适用于那些可能需要一些时间来清理资源或完成正在进行的任务的场景。
# IMMEDIATE(立即): 当应用程序以"IMMEDIATE"模式关闭时,它会立即终止,而不管当前是否有任何活动任务或请求。这种方式适用于那些可以立即停止而不会造成严重问题的情况。
shutdown: graceful
spring:
lifecycle:
# 采用java.time.Duration格式的值,如果在这个时间内,优雅停机没有停掉应用,超过这个时间就会强制停止应用
timeout-per-shutdown-phase: 30s
freemarker:
# 是否检查模板位置是否存在
check-template-location: false
# 模板编码
charset: utf-8
cache:
# 解决缓存异常 https://blog.csdn.net/jikeyeka/article/details/125804049
type: redis
data:
redis:
database: ${REDIS_DB:1}
host: ${REDIS_HOST:127.0.0.1}
port: ${REDIS_PORT:26379}
password: ${REDIS_PWD:kd123456}
# 连接超时时间
timeout: 5000
# 连接池配置
lettuce:
pool:
# 连接池最大连接数
max-active: 30
# 连接池最大阻塞等待时间(毫秒),负值表示没有限制
max-wait: -1
# 连接池中的最大空闲连接数
max-idle: 20
# 连接池中的最小空闲连接数
min-idle: 1
repositories:
# 是否启用Redis存储(关闭防止出现 Multiple Spring Data modules found, entering strict repository configuration mode)
enabled: false
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:23306/cutejava?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
username: root
password: kd123456
hikari:
connection-timeout: 60000
maximum-pool-size: 10
minimum-idle: 2
idle-timeout: 30000
# 集成mybatis-plus
mybatis-plus:
configuration:
# 开启 Mybatis 二级缓存,默认为 true
cache-enabled: false
# 设置本地缓存作用域, Mybatis 一级缓存, 默认为 SESSION
# 同一个 session 相同查询语句不会再次查询数据库
# 微服务中, 建议设置为STATEMENT, 即关闭一级缓存
local-cache-scope: STATEMENT
# 是否开启自动驼峰命名规则(camel case)映射, 即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
map-underscore-to-camel-case: true
# Sql日志
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 启动时是否检查 MyBatis XML 文件的存在, 默认不检查
check-config-location: true
# MyBatis Mapper 所对应的 XML 文件位置
# Maven 多模块项目的扫描路径需以 classpath*: 开头 (即加载多个 jar 包下的 XML 文件)
mapper-locations: classpath*:mapper/**/*.xml
# MyBatis-Plus 全局策略中的 DB 策略配置
global-config:
db-config:
# 逻辑已删除值(逻辑删除下有效)
logic-delete-value: 0
# 逻辑未删除值(逻辑删除下有效)
logic-not-delete-value: 1
# 全局默认主键类型, 这里为自增主键
id-type: auto
# 表名是否使用驼峰转下划线命名,只对表名生效
table-underline: true
# 是否控制台 print mybatisplus-plus 的 LOGO
banner: true
# 框架配置
app:
# 密码加密传输,前端公钥加密,后端私钥解密
rsa:
private-key: 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
# 鉴权
jwt:
# 必须使用最少88位的Base64对该令牌进行编码 cn.odboy.util.CsBase64SecretUtil
base64-secret: YzE5ZWNjMjlkMGY4NDc4MDlmYzE0Y2I0MTk4YTQ4YjY5NTU0OGQxODNjNjQ0MmU1YjBmZTYwYjljYzI0M2M4MTJkZjk3Y2ZlYjg3ZTQxMjM4MjEwY2ZiYzE4YmFiYmZlZDVhM2M1YmI3M2FmNDFmNWJmM2MyMTM1ZDNhMmRmYzY=
# 令牌过期时间 此处单位/毫秒 ,默认4小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html
token-validity-in-seconds: 14400000
# token 续期检查时间范围(默认30分钟,单位毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期
detect: 1800000
# 续期时间范围,默认1小时,单位毫秒
renew: 3600000
# 登录
login:
# 是否限制单用户登录
single: true
# 验证码
captchaSetting:
# 验证码类型配置 查看 LoginProperties 类
code-type: arithmetic
# 登录图形验证码有效时间/分钟
expiration: 2
# 验证码高度
width: 111
# 验证码宽度
height: 36
# 内容长度
length: 2
# 字体名称,为空则使用默认字体
font-name:
# 字体大小
font-size: 25
# 针对@Async
async-task-pool:
# 核心线程池大小
core-pool-size: 10
# 最大线程数
max-pool-size: 30
# 活跃时间
keep-alive-seconds: 60
# 队列容量
queue-capacity: 50
# oss
oss:
max-size: 100
minio:
endpoint: http://127.0.0.1:9000
bucketName: cutejava
accessKey: 8o5oVzqvKby5TzaGcfmx
secretKey: 8BiK5UbmvXGJNVQ98CGiohHzT1N1FMRZqHRdJllj
captcha:
expireTime: 300
# 序列化白名单
serial-white-list:
- "org.springframework"
- "cn.odboy.system.dal.dataobject"
- "cn.odboy.system.dal.model"
# debug
#debug: true
# 日志配置
logging:
logback:
rollingpolicy:
max-file-size: 100MB
max-history: 7
total-size-cap: 1GB
level:
# root: debug
cn.odboy: info
knife4j:
enable: false
setting:
language: zh_cn
@@ -0,0 +1,50 @@
# 分布式定时任务配置
spring:
# 数据库一定不能区分大小写
quartz:
# 必需,指定使用 JDBC 存储
job-store-type: jdbc
properties:
org:
quartz:
scheduler:
# 必需,指定调度器实例的名称
instanceName: cutejava
# 必需,指定调度器实例的 ID
instanceId: AUTO
threadPool:
# Scheduler使用的线程池名称,实现了ThreadPool接口,参考org.quartz.simpl.SimpleThreadPool
class: org.quartz.simpl.SimpleThreadPool
# 可选,并发个数(1~100)
threadCount: 10
# 可选,线程的优先级(1~10)
threadPriority: 5
# 可选,指定在线程池里面创建的线程是否是守护线程
makeThreadsDaemons: false
# 可选,线程上下文类加载器是否继承自初始线程的加载器
threadsInheritContextClassLoaderOfInitializingThread: true
dataSource:
jobStoreDataSource:
provider: hikaricp
driver: com.mysql.cj.jdbc.Driver
URL: jdbc:mysql://127.0.0.1:3306/cutejava?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
user: root
password: kd123456
maxConnections: 10
validationQuery: select 1
jobStore:
# 可选,如果你不需要集群,可以去掉
isClustered: true
# 可选,集群检查间隔时间,可以根据需要调整
clusterCheckinInterval: 10000
# 可选,分布式事务类
# class: org.quartz.impl.jdbcjobstore.JobStoreTX
# 必需,指定 JDBC 驱动代理类
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 可选,是否使用属性存储,可以根据需要调整
useProperties: false
# 必需,指定表的前缀
tablePrefix: qrtz_
# 可选,指定误触发阈值,可以根据需要调整
misfireThreshold: 60000
dataSource: jobStoreDataSource
@@ -0,0 +1,6 @@
spring:
profiles:
# 激活的环境
active: dev
# active: dev,quartz
@@ -0,0 +1,8 @@
_____ _ _ _____ _____ _ ___ _ _ ___
/ ___| | | | | |_ _| | ____| | | / | | | / / / |
| | | | | | | | | |__ | | / /| | | | / / / /| |
| | | | | | | | | __| _ | | / / | | | | / / / / | |
| |___ | |_| | | | | |___ | |_| | / / | | | |/ / / / | |
\_____| \_____/ |_| |_____| \_____/ /_/ |_| |___/ /_/ |_|
Spring Boot Version: (${spring-boot.version}),CuteJava Version: (1.5.2)
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds" debug="false">
<contextName>CuteJava</contextName>
<property name="log.name" value="cutejava"/>
<property name="log.home" value="/var/logs"/>
<property name="log.charset" value="utf-8"/>
<property name="log.pattern" value="%red(%contextName) - %red(%d{yyyy-MM-dd HH:mm:ss.SSS}) - %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %msg%n"/>
<!-- Spring 日志级别控制 -->
<logger name="org.springframework" level="warn"/>
<!-- DnsServerAddressStreamProviders调整为ERROR -->
<logger name="io.netty.resolver.dns.DnsServerAddressStreamProviders" level="ERROR"/>
<!-- 设置其他类的日志级别为 ERROR -->
<logger name="org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/]" level="ERROR"/>
<logger name="org.springframework.web.servlet.DispatcherServlet" level="ERROR"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${log.pattern}</pattern>
<charset>${log.charset}</charset>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${log.home}/${log.name}.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.home}/${log.name}.log.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>90</maxHistory>
<maxFileSize>100MB</maxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<root level="INFO">
<!--普通日志输出到控制台-->
<appender-ref ref="CONSOLE"/>
<!--普通日志输出到文件-->
<appender-ref ref="FILE"/>
</root>
</configuration>
@@ -0,0 +1,20 @@
# 应用的拦截模块
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=cn.odboy.framework.mybatisplus.config.MpCustomP6SpyLogger
# 日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动 可多个
driverlist=com.mysql.cj.jdbc.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
# 是否过滤 Log
filter=true
# 过滤 Log 时所排除的 sql 关键字,以逗号分隔
exclude=SELECT 1,INSERT INTO system_quartz_log
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
@@ -0,0 +1,53 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.odboy.system.dal.dataobject.SystemUserTb;
import cn.odboy.system.dal.mysql.SystemUserMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import java.util.Date;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyBatisMapperTests {
@Autowired
private SystemUserMapper systemUserMapper;
@Test
public void testMapper1() {
// SELECT * FROM system_user WHERE (create_time > '2025-08-18T14:48:18.400+0800');
systemUserMapper.selectList(new LambdaQueryWrapper<SystemUserTb>().gt(SystemUserTb::getCreateTime, new Date()));
// SELECT * FROM system_user WHERE (create_time > '2025-08-18T14:50:16.786+0800');
systemUserMapper.selectList(
new LambdaQueryWrapper<SystemUserTb>().gt(SystemUserTb::getCreateTime, DateTime.now()));
// SELECT * FROM system_user WHERE (create_time > '2025-08-18 14:53:46');
systemUserMapper.selectList(new LambdaQueryWrapper<SystemUserTb>().gt(SystemUserTb::getCreateTime,
DateUtil.formatDateTime(new Date())));
// SELECT * FROM system_user WHERE (create_time > '2025-08-18');
systemUserMapper.selectList(
new LambdaQueryWrapper<SystemUserTb>().gt(SystemUserTb::getCreateTime, DateUtil.formatDate(new Date())));
}
}
@@ -0,0 +1,65 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy;
import cn.hutool.core.collection.CollUtil;
import cn.odboy.system.dal.dataobject.SystemJobTb;
import cn.odboy.system.dal.dataobject.SystemRoleTb;
import cn.odboy.system.dal.model.response.SystemDeptVo;
import cn.odboy.system.dal.model.response.SystemUserVo;
import cn.odboy.system.service.SystemUserService;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SystemApplicationTests {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private SystemUserService systemUserService;
public static void main(String[] args) {
}
@Test
public void contextLoads() {
SystemDeptVo systemDept = new SystemDeptVo();
systemDept.setId(2L);
SystemJobTb systemJob = new SystemJobTb();
systemJob.setId(11L);
Set<SystemJobTb> systemJobs = CollUtil.newHashSet(systemJob);
SystemRoleTb systemRole = new SystemRoleTb();
systemRole.setId(2L);
Set<SystemRoleTb> systemRoles = CollUtil.newHashSet(systemRole);
String pwd = passwordEncoder.encode("123456");
for (int i = 100; i < 200; i++) {
SystemUserVo systemUser = new SystemUserVo();
systemUser.setDept(systemDept);
systemUser.setJobs(systemJobs);
systemUser.setRoles(systemRoles);
systemUser.setUsername("odboy" + i);
systemUser.setEmail("1943815" + i + "@qq.com");
systemUser.setPhone("18797874" + i);
systemUser.setPassword(pwd);
systemUser.setEnabled(true);
systemUserService.saveUser(systemUser);
}
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.junit.jupiter.api.Test;
public class KitDateUtilTest {
@Test
public void testGetWeekDay() {
SimpleDateFormat simpleDateformat = new SimpleDateFormat("E");
assertEquals(simpleDateformat.format(new Date()), KitDateUtil.getWeekDay());
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy.util;
import static cn.odboy.util.KitDesEncryptUtil.desDecrypt;
import static cn.odboy.util.KitDesEncryptUtil.desEncrypt;
import static org.junit.jupiter.api.Assertions.assertEquals;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
public class KitDesEncryptUtilTest {
@SneakyThrows
public static void main(String[] args) {
System.err.println(desEncrypt("abc12345678"));
}
/**
* 对称加密
*/
@Test
public void testDesEncrypt() {
try {
assertEquals("7772841DC6099402", desEncrypt("abc12345678"));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 对称解密
*/
@Test
public void testDesDecrypt() {
try {
assertEquals("abc12345678", desDecrypt("7772841DC6099402"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,55 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy.util;
import static cn.odboy.util.KitFileUtil.getPrefix;
import static cn.odboy.util.KitFileUtil.getSize;
import static cn.odboy.util.KitFileUtil.getSuffix;
import static cn.odboy.util.KitFileUtil.toFile;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockMultipartFile;
public class KitFileUtilTest {
@Test
public void testToFile() {
long retval = toFile(new MockMultipartFile("foo", (byte[]) null)).getTotalSpace();
assertEquals(500695072768L, retval);
}
@Test
public void testGetExtensionName() {
assertEquals("foo", getSuffix("foo"));
assertEquals("exe", getSuffix("bar.exe"));
}
@Test
public void testGetFileNameNoEx() {
assertEquals("foo", getPrefix("foo"));
assertEquals("bar", getPrefix("bar.txt"));
}
@Test
public void testGetSize() {
assertEquals("1000B ", getSize(1000));
assertEquals("1.00KB ", getSize(1024));
assertEquals("1.00MB ", getSize(1048576));
assertEquals("1.00GB ", getSize(1073741824));
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy.util;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import static cn.odboy.util.KitStringUtil.toCamelCase;
import static cn.odboy.util.KitStringUtil.toCapitalizeCamelCase;
import static cn.odboy.util.KitStringUtil.toUnderScoreCase;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
public class KitStringUtilTest {
@Test
public void testToCamelCase() {
assertNull(toCamelCase(null));
}
@Test
public void testToCapitalizeCamelCase() {
assertNull(KitStringUtil.toCapitalizeCamelCase(null));
assertEquals("HelloWorld", toCapitalizeCamelCase("hello_world"));
}
@Test
public void testToUnderScoreCase() {
assertNull(KitStringUtil.toUnderScoreCase(null));
assertEquals("hello_world", toUnderScoreCase("helloWorld"));
assertEquals("\u0000\u0000", toUnderScoreCase("\u0000\u0000"));
assertEquals("\u0000_a", toUnderScoreCase("\u0000A"));
}
@Test
public void testGetWeekDay() {
SimpleDateFormat simpleDateformat = new SimpleDateFormat("E");
assertEquals(simpleDateformat.format(new Date()), KitDateUtil.getWeekDay());
}
@Test
public void testGetIP() {
Assertions.assertEquals("127.0.0.1", KitBrowserUtil.getIp(new MockHttpServletRequest()));
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy.util;
/**
* 钉钉 Teambition 实际工时数计算
*/
public class WorkTimeTests {
public static void main(String[] args) {
// 总工作时长(时)
double totalHour = 11;
// 实际工作时长(天),一天8小时工作制
double actualHour = totalHour / 8.0;
System.err.println("actualHour=" + actualHour + " 天");
}
}