first commit

This commit is contained in:
2023-04-26 19:48:44 +08:00
commit 6adac3aeb1
62 changed files with 2741 additions and 0 deletions

47
.gitignore vendored Normal file
View File

@ -0,0 +1,47 @@
######################################################################
# Build Tools
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
target/
!.mvn/wrapper/maven-wrapper.jar
######################################################################
# IDE
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### JRebel ###
rebel.xml
### NetBeans ###
nbproject/private/
build/*
nbbuild/
dist/
nbdist/
.nb-gradle/
######################################################################
# Others
*.log
*.xml.versionsBackup
*.swp
!*/build/*.java
!*/build/*.html
!*/build/*.xml

178
pom.xml Normal file
View File

@ -0,0 +1,178 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-boot</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<modules>
<module>qiaoba-modules</module>
<module>qiaoba-apis</module>
<module>qiaoba-commons</module>
<module>qiaoba-application</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.11</version>
<!-- 解决子模块继承不到父模块中dependencyManagement中包的version信息 -->
<relativePath />
</parent>
<properties>
<!-- 项目统一字符集编码:UTF-8 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- maven的一些插件输出文件时的字符编码:UTF-8 -->
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- jdk版本:8 -->
<java.version>8</java.version>
<!-- 不执行且不编译测试用例 -->
<maven.test.skip>true</maven.test.skip>
<!-- 项目模块版本 -->
<qiaoba.version>1.0</qiaoba.version>
<!-- 阿里巴巴数据源 -->
<druid.version>1.2.17</druid.version>
<!-- HuTool工具包 -->
<hutool.version>5.8.18</hutool.version>
<!-- mysql驱动 -->
<mysql-connector.version>8.0.33</mysql-connector.version>
<!-- mybatis -->
<mybatisplus-spring-boot.version>3.5.3.1</mybatisplus-spring-boot.version>
<!-- knife4接口文档 -->
<knife4j.version>4.0.0</knife4j.version>
<!-- spring doc -->
<springdoc.version>1.6.14</springdoc.version>
<!-- 数据校验 -->
<jakarta.version>3.0.2</jakarta.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 阿里巴巴数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- HuTool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- knife4接口文档 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!-- spring doc -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector.version}</version>
</dependency>
<!-- mybatis plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus-spring-boot.version}</version>
</dependency>
<!-- 数据校验 -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>${jakarta.version}</version>
</dependency>
<!-- ======================== system module ======================== -->
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-common-base</artifactId>
<version>${qiaoba.version}</version>
</dependency>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-common-datasource</artifactId>
<version>${qiaoba.version}</version>
</dependency>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-common-web</artifactId>
<version>${qiaoba.version}</version>
</dependency>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-api-system</artifactId>
<version>${qiaoba.version}</version>
</dependency>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-module-system</artifactId>
<version>${qiaoba.version}</version>
</dependency>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-common-doc</artifactId>
<version>${qiaoba.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 所有的子项目默认依赖 -->
<dependencies>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<!-- maven打包工具 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
</build>
<!-- 配置阿里云 Maven仓库 -->
<repositories>
<repository>
<id>public</id>
<name>阿里云</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>阿里云</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

37
qiaoba-apis/pom.xml Normal file
View File

@ -0,0 +1,37 @@
<?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>qiaoba-boot</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-apis</artifactId>
<packaging>pom</packaging>
<modules>
<module>qiaoba-api-system</module>
<module>qiaoba-api-job</module>
</modules>
<dependencies>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-common-base</artifactId>
</dependency>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-common-datasource</artifactId>
</dependency>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-common-doc</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,15 @@
<?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>qiaoba-apis</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-api-job</artifactId>
</project>

View File

@ -0,0 +1 @@
null not found

View File

@ -0,0 +1 @@
null not found

View File

@ -0,0 +1 @@
null not found

View File

@ -0,0 +1,15 @@
<?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>qiaoba-apis</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-api-system</artifactId>
</project>

View File

@ -0,0 +1,45 @@
package com.qiaoba.api.system.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qiaoba.common.base.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 租户表
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 22:02:43
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("sys_tenant")
public class SysTenant extends BaseEntity {
private static final long serialVersionUID = 1L;
private Long id;
private String code;
private String name;
private String domainName;
private Date expiryDate;
private String contact;
private String province;
private String city;
private String district;
private String address;
private String status;
}

View File

@ -0,0 +1,44 @@
package com.qiaoba.api.system.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 租户数据库表
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 22:07:57
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("sys_tenant_datasource")
public class SysTenantDatasource {
@NotBlank(message = "【租户码】不能为空")
private String tenantCode;
@NotBlank(message = "【数据库类型】不能为空")
private String datasourceType;
@NotBlank(message = "【数据库URL】不能为空")
private String datasourceUrl;
@NotBlank(message = "【数据库IP】不能为空")
private String datasourceIp;
@NotBlank(message = "【数据库端口】不能为空")
private String datasourcePort;
@NotBlank(message = "【数据库用户名】不能为空")
private String datasourceUsername;
@NotBlank(message = "【数据库密码】不能为空")
private String datasourcePassword;
@NotBlank(message = "【数据库驱动】不能为空")
private String datasourceDriver;
}

View File

@ -0,0 +1,65 @@
package com.qiaoba.api.system.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qiaoba.common.base.entity.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 用户表
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 15:37:43
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("sys_user")
public class SysUser extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId
@Schema(description = "用户ID")
private Long userId;
@Schema(description = "部门ID")
private Long deptId;
@Schema(description = "登陆账号")
private String username;
@Schema(description = "昵称")
private String nickname;
@Schema(description = "邮箱")
private String email;
@Schema(description = "手机号")
private String phone;
@Schema(description = "性别(0男 1女 2未知)")
private String gender;
@Schema(description = "头像")
private String avatar;
@Schema(description = "密码")
private String password;
@Schema(description = "帐号状态(1正常 0停用)")
private String status;
@Schema(description = "是否是超管")
private Boolean isSuperuser;
@Schema(description = "是否是管理员")
private Boolean isManager;
@Schema(description = "是否已删除")
private Boolean isDelete;
}

View File

@ -0,0 +1,24 @@
package com.qiaoba.api.system.entity.param;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
* 用户查询参数
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 15:37:43
*/
@Getter
@Setter
public class SysUserParam implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "昵称")
private String nickname;
}

View File

@ -0,0 +1,29 @@
package com.qiaoba.api.system.service;
import com.qiaoba.api.system.entity.SysTenant;
/**
* 租户对外暴露接口
*
* @author ailanyin
* @version 1.0
* @since 2023-04-24 19:20:40
*/
public interface SysTenantApiService {
/**
* 新增租户
*
* @param sysTenant sysTenant
* @return > 0 = success
*/
int insert(SysTenant sysTenant);
/**
* 更新租户
*
* @param sysTenant sysTenant
* @return > 0 = success
*/
int update(SysTenant sysTenant);
}

View File

@ -0,0 +1,29 @@
package com.qiaoba.api.system.service;
import com.qiaoba.api.system.entity.SysUser;
/**
* 用户对外暴露接口
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 20:33:43
*/
public interface SysUserApiService {
/**
* 新增用户
*
* @param sysUser sysUser
* @return > 0 = success
*/
int insert(SysUser sysUser);
/**
* 更新租户
*
* @param sysUser sysUser
* @return > 0 = success
*/
int update(SysUser sysUser);
}

View File

@ -0,0 +1 @@
null not found

View File

@ -0,0 +1 @@
null not found

View File

@ -0,0 +1,31 @@
<?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>qiaoba-boot</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-application</artifactId>
<dependencies>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-module-system</artifactId>
</dependency>
</dependencies>
<build>
<!-- 控制打包成的jar的名称 -->
<finalName>qiaoba-boot</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,26 @@
package com.qiaoba.application;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 启动类
*
* @author ailanyin
* @version 1.0
* @date 1/29/23 10:44 AM
*/
@EnableScheduling
@ComponentScan({"com.qiaoba.module.**", "com.qiaoba.application"})
@MapperScan("com.qiaoba.module.**.mapper")
@SpringBootApplication
public class QiaoBaApplication {
public static void main(String[] args) {
SpringApplication.run(QiaoBaApplication.class, args);
}
}

View File

@ -0,0 +1,37 @@
package com.qiaoba.application.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* 全局跨域配置
*
* @author ailanyin
* @version 1.0
* @since 2021/10/15 0015 下午 16:43
*/
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置访问源地址
config.addAllowedOriginPattern("*");
// 设置访问源请求头
config.addAllowedHeader("*");
// 设置访问源请求方法
config.addAllowedMethod("*");
// 有效期 半小时
config.setMaxAge(1800L);
// 添加映射路径,拦截一切请求
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**" , config);
// 返回新的CorsFilter
return new CorsFilter(source);
}
}

View File

@ -0,0 +1,11 @@
spring:
datasource:
url: jdbc:mysql://121.5.136.69:3306/qiaoba-boot?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true
username: root
password: LpYN7LUoL?l0OSpR2
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5 #连接池初始化大小
min-idle: 10 #最小空闲连接数
max-active: 20 #最大连接数

View File

@ -0,0 +1,40 @@
server:
port: 80
spring:
application:
name: qiaoba-boot
profiles:
active: dev
servlet:
multipart:
enabled: true #开启文件上传
max-file-size: 200MB #限制文件上传大小为10M
max-request-size: 200MB #限制文件上传大小为10M
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
main:
allow-bean-definition-overriding: true
# springdoc-openapi项目配置
springdoc:
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
api-docs:
path: /v3/api-docs
group-configs:
- group: '系统管理'
paths-to-match: '/**'
packages-to-scan: com.qiaoba.module.system.controller
# knife4j的增强配置不需要增强可以不配
knife4j:
enable: true
setting:
language: zh_cn
enable-swagger-models: false
enable-footer-custom: true
footer-custom-content: create by ailanyin

View File

@ -0,0 +1 @@
null not found

23
qiaoba-commons/pom.xml Normal file
View File

@ -0,0 +1,23 @@
<?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>qiaoba-boot</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-commons</artifactId>
<packaging>pom</packaging>
<modules>
<module>qiaoba-common-base</module>
<module>qiaoba-common-datasource</module>
<module>qiaoba-common-web</module>
<module>qiaoba-common-doc</module>
<module>qiaoba-common-redis</module>
</modules>
</project>

View File

@ -0,0 +1,15 @@
<?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>qiaoba-commons</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-common-base</artifactId>
</project>

View File

@ -0,0 +1,21 @@
package com.qiaoba.common.base.constants;
/**
* BaseConstant
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 15:37:43
*/
public class BaseConstant {
/**
* UTF-8 字符集
*/
public static final String UTF8 = "UTF-8";
/**
* 默认的字符拼接/切割符号: ','(英文逗号)
*/
public static final String DEFAULT_SPLIT_STR = ",";
}

View File

@ -0,0 +1,53 @@
package com.qiaoba.common.base.entity;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.Date;
/**
* BaseEntity
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 15:37:43
*/
@Getter
@Setter
public class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 创建者
*/
private String createUser;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新者
*/
private String updateUser;
/**
* 更新时间
*/
private Date updateTime;
/**
* 备注
*/
private String remark;
/**
* 租户Id
*/
private Long tenantId;
}

View File

@ -0,0 +1,31 @@
package com.qiaoba.common.base.enums;
import lombok.Getter;
/**
* BaseEnum
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 15:34:59
*/
@Getter
public enum BaseEnum {
// 是
YES("1" , ""),
// 否
NO("0" , ""),
// 正常
NORMAL("1" , "正常"),
// 不正常
ABNORMAL("0" , "不正常");
private final String code;
private final String info;
BaseEnum(String code, String info) {
this.code = code;
this.info = info;
}
}

View File

@ -0,0 +1,47 @@
package com.qiaoba.common.base.exceptions;
import lombok.Getter;
import lombok.Setter;
/**
* 自定义 业务异常
*
* @author ailanyin
* @version 1.0
* @since 2021-08-31
*/
@Getter
@Setter
public class ServiceException extends RuntimeException {
/**
* 错误码
*/
private Integer errorCode;
/**
* 错误信息
*/
private String message;
public ServiceException(String message) {
this.message = message;
}
public ServiceException(Integer code, String message) {
this.message = message;
this.errorCode = code;
}
/**
* 控制台不打印自定义错误的堆栈信息, 提高性能
*
* @return this
*/
@Override
public Throwable fillInStackTrace() {
return this;
}
}

View File

@ -0,0 +1 @@
null not found

View File

@ -0,0 +1,36 @@
<?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>qiaoba-commons</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-common-datasource</artifactId>
<dependencies>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!-- Mysql Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-common-web</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,83 @@
package com.qiaoba.common.database.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.qiaoba.common.database.entity.DynamicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
* 全局跨域配置
*
* @author ailanyin
* @version 1.0
* @since 2021/10/15 0015 下午 16:43
*/
@Configuration
public class DynamicDataSourceConfig {
/**
* 把DynamicDataSourceContext 纳入容器管理其他地方使用DynamicDataSourceConfig 类可以直接从容器取对象并调用freshDataSource方法
*/
@Bean
@Primary
public static DynamicDataSourceContext dataSource() {
Map<Object, Object> targetDataSource = getDataSource();
//把DynamicDataSourceContext纳入容器管理
DynamicDataSourceContext dynamicDataSourceContext = new DynamicDataSourceContext();
dynamicDataSourceContext.freshDataSource(targetDataSource);
return dynamicDataSourceContext;
}
/**
* 构建初始化数据源 TODO 生成中去其他地方获取初始化数据源(例如:表里面获取)
*
* @return
*/
public static Map<Object, Object> getDataSource() {
DynamicDataSource ds2 = new DynamicDataSource();
ds2.setTenantCode("1");
ds2.setDriver("com.mysql.cj.jdbc.Driver");
ds2.setUrl("jdbc:mysql://121.5.136.69:3306/qiaoba-boot?useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai");
ds2.setUsername("root");
ds2.setPassword("LpYN7LUoL?l0OSpR2");
DynamicDataSource ds1 = new DynamicDataSource();
ds1.setTenantCode("2");
ds1.setDriver("com.mysql.cj.jdbc.Driver");
ds1.setUrl("jdbc:mysql://120.79.217.22:3306/qiaoba-boot?useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai");
ds1.setUsername("root");
ds1.setPassword("FeyZ7xZr6JtuKibm");
Map<Object, Object> map = new HashMap<>();
map.put(ds1.getTenantCode(), buildDataSource(ds1));
map.put(ds2.getTenantCode(), buildDataSource(ds2));
return map;
}
/**
* 把数据源对象组装成HikariDataSource
*
* @param DynamicDataSource
* @return
*/
private static Object buildDataSource(DynamicDataSource DynamicDataSource) {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(DynamicDataSource.getUrl());
dataSource.setUsername(DynamicDataSource.getUsername());
dataSource.setPassword(DynamicDataSource.getPassword());
dataSource.setDriverClassName(DynamicDataSource.getDriver());
dataSource.setInitialSize(5);
dataSource.setMinIdle(10);
dataSource.setMaxActive(20);
try {
dataSource.init();
} catch (SQLException e) {
e.printStackTrace();
}
return dataSource;
}
}

View File

@ -0,0 +1,62 @@
package com.qiaoba.common.database.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.util.Map;
/**
* 动态数据源上下文对象
*
* @author ailanyin
* @version 1.0
* @since 2021/10/15 0015 下午 16:43
*/
public class DynamicDataSourceContext extends AbstractRoutingDataSource {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置默认数据源、全部数据源,及刷新
*/
public void freshDataSource(Map<Object, Object> targetDataSources) {
//默认数据源
super.setDefaultTargetDataSource(targetDataSources.get("master"));
//设置全部数据源
super.setTargetDataSources(targetDataSources);
//刷新(即把targetDataSources刷到resolvedDataSources中去resolvedDataSources才是我们真正存放数据源的map)
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
//获取当前指定的数据源
return getDataSource();
}
@Override
public void afterPropertiesSet() {
}
/**
* 获取上下文中的数据源
*/
public String getDataSource() {
return CONTEXT_HOLDER.get();
}
/**
* 设置上下文中的数据源
*/
public void setDataSource(String dataSource) {
CONTEXT_HOLDER.set(dataSource);
}
/**
* 清除上下文中的数据源
*/
public void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}

View File

@ -0,0 +1,46 @@
package com.qiaoba.common.database.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 动态数据源实体
*
* @author ailanyin
* @version 1.0
* @date 2023-04-24 21:47:48
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class DynamicDataSource {
/**
* 租户Code
*/
private String tenantCode;
/**
* 数据库-url
*/
private String url;
/**
* 数据库-username
*/
private String username;
/**
* 数据库-password
*/
private String password;
/**
* 数据库-驱动
*/
private String driver;
}

View File

@ -0,0 +1,33 @@
package com.qiaoba.common.database.factories;
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
import com.qiaoba.common.database.config.DynamicDataSourceContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.util.Map;
/**
* 动态数据源的工厂,主要解决缓存依赖
*
* @version 1.0
* @author ailanyin
* @since 2023-04-25 21:47:34
*/
@Configuration
public class DynamicDataSourceFactory {
/**
* 把DynamicDataSourceContext 纳入容器管理其他地方使用DynamicDataSourceConfig 类可以直接从容器取对象并调用freshDataSource方法
*/
@Bean
@Primary
public static DynamicDataSourceContext dataSource() {
Map<Object, Object> targetDataSource = DynamicDataSourceConfig.getDataSource();
//把DynamicDataSourceContext纳入容器管理
DynamicDataSourceContext dynamicDataSourceContext = new DynamicDataSourceContext();
dynamicDataSourceContext.freshDataSource(targetDataSource);
return dynamicDataSourceContext;
}
}

View File

@ -0,0 +1,36 @@
package com.qiaoba.common.database.filters;
import com.qiaoba.common.database.config.DynamicDataSourceContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 动态切换不同租户的数据源
*
* @author ailanyin
* @version 1.0
* @since 2023-04-25 22:48:43
*/
@Component
@Order(-10000)
public class HandleTenantFilter implements Filter {
@Resource
private DynamicDataSourceContext dynamicDataSourceContext;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String instanceId = request.getParameter("tenant");
//设置当前租户对应的数据库
dynamicDataSourceContext.setDataSource(instanceId);
System.out.println("当前数据源是:" + dynamicDataSourceContext.getDataSource());
filterChain.doFilter(servletRequest, servletResponse);
dynamicDataSourceContext.clearDataSource();
}
}

View File

@ -0,0 +1,306 @@
package com.qiaoba.common.database.mapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.qiaoba.common.web.utils.BeanCopyUtil;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 自定义 Mapper 接口, 实现 自定义扩展
*
* @param <M> mapper 泛型
* @param <T> table 泛型
* @param <V> vo 泛型
* @author ailanyin
* @since 2023-04-23 17:20:32
*/
@SuppressWarnings("unchecked")
public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {
/**
* currentVoClass
*
* @return Vo
*/
default Class<V> currentVoClass() {
return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2);
}
/**
* currentModelClass
*
* @return model
*/
default Class<T> currentModelClass() {
return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1);
}
/**
* currentMapperClass
*
* @return mapper
*/
default Class<M> currentMapperClass() {
return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 0);
}
/**
* 查询所有
*
* @return all lst
*/
default List<T> selectList() {
return this.selectList(new QueryWrapper<>());
}
/**
* 批量新增
*
* @param entityList list
* @return 结果
*/
default boolean insertBatch(Collection<T> entityList) {
return Db.saveBatch(entityList);
}
/**
* 批量更新
*
* @param entityList list
* @return 结果
*/
default boolean updateBatchById(Collection<T> entityList) {
return Db.updateBatchById(entityList);
}
/**
* 批量插入或更新
*
* @param entityList list
* @return 结果
*/
default boolean insertOrUpdateBatch(Collection<T> entityList) {
return Db.saveOrUpdateBatch(entityList);
}
/**
* 批量插入(包含限制条数)
*
* @param entityList list
* @param batchSize 最大调数
* @return 结果
*/
default boolean insertBatch(Collection<T> entityList, int batchSize) {
return Db.saveBatch(entityList, batchSize);
}
/**
* 批量更新(包含限制条数)
*
* @param entityList list
* @param batchSize 最大调数
* @return 结果
*/
default boolean updateBatchById(Collection<T> entityList, int batchSize) {
return Db.updateBatchById(entityList, batchSize);
}
/**
* 批量插入或更新(包含限制条数)
*
* @param entityList list
* @param batchSize 最大调数
* @return 结果
*/
default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
return Db.saveOrUpdateBatch(entityList, batchSize);
}
/**
* 插入或更新
* @param entity entity
* @return 结果
*/
default boolean insertOrUpdate(T entity) {
return Db.saveOrUpdate(entity);
}
/**
* 查询Vo by id
* @param id 主键
* @return Vo
*/
default V selectVoById(Serializable id) {
return selectVoById(id, this.currentVoClass());
}
/**
* 根据 ID 查询
*
* @param id 主键
* @param voClass Vo
* @param <C> 泛型
* @return Vo
*/
default <C> C selectVoById(Serializable id, Class<C> voClass) {
T obj = this.selectById(id);
if (ObjectUtil.isNull(obj)) {
return null;
}
return BeanCopyUtil.copy(obj, voClass);
}
/**
* 通过Ids 查询 list
*
* @param idList ids
* @return list
*/
default List<V> selectVoBatchIds(Collection<? extends Serializable> idList) {
return selectVoBatchIds(idList, this.currentVoClass());
}
/**
* 通过Ids 查询 list
*
* @param idList ids
* @param voClass Vo
* @param <C> 泛型
* @return list
*/
default <C> List<C> selectVoBatchIds(Collection<? extends Serializable> idList, Class<C> voClass) {
List<T> list = this.selectBatchIds(idList);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtil.copyList(list, voClass);
}
/**
* selectVoByMap
*
* @param map map
* @return Vo
*/
default List<V> selectVoByMap(Map<String, Object> map) {
return selectVoByMap(map, this.currentVoClass());
}
/**
* selectVoByMap
*
* @param map map
* @param voClass Vo
* @param <C> 泛型
* @return Vo
*/
default <C> List<C> selectVoByMap(Map<String, Object> map, Class<C> voClass) {
List<T> list = this.selectByMap(map);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtil.copyList(list, voClass);
}
/**
* 根据条件查询一条记录
*
* @param wrapper 条件
* @return Vo
*/
default V selectVoOne(Wrapper<T> wrapper) {
return selectVoOne(wrapper, this.currentVoClass());
}
/**
* 根据条件查询一条记录
*
* @param wrapper 条件
* @param voClass Vo
* @param <C> 泛型
* @return Vo
*/
default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass) {
T obj = this.selectOne(wrapper);
if (ObjectUtil.isNull(obj)) {
return null;
}
return BeanCopyUtil.copy(obj, voClass);
}
/**
* 根据条件查询所有
*
* @param wrapper 条件
* @return list
*/
default List<V> selectVoList(Wrapper<T> wrapper) {
return selectVoList(wrapper, this.currentVoClass());
}
/**
* 根据条件查询所有
*
* @param wrapper 条件
* @param voClass Vo
* @param <C> 泛型
* @return list vo
*/
default <C> List<C> selectVoList(Wrapper<T> wrapper, Class<C> voClass) {
List<T> list = this.selectList(wrapper);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtil.copyList(list, voClass);
}
/**
* 分页查询Vo
*
* @param page 分页对象
* @param wrapper 条件
* @param <P> 泛型
* @return IPage
*/
default <P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper) {
return selectVoPage(page, wrapper, this.currentVoClass());
}
/**
* 分页查询Vo
*
* @param page 分页对象
* @param wrapper 条件
* @param voClass Vo
* @param <C> 泛型
* @param <P> 泛型
* @return IPage
*/
default <C, P extends IPage<C>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<C> voClass) {
IPage<T> pageData = this.selectPage(page, wrapper);
IPage<C> voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal());
if (CollUtil.isEmpty(pageData.getRecords())) {
return (P) voPage;
}
voPage.setRecords(BeanCopyUtil.copyList(pageData.getRecords(), voClass));
return (P) voPage;
}
}

View File

@ -0,0 +1,5 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.qiaoba.common.database.config.DynamicDataSourceConfig,\
com.qiaoba.common.database.filters.HandleTenantFilter

View File

@ -0,0 +1,24 @@
<?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>qiaoba-commons</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-common-doc</artifactId>
<dependencies>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1 @@
null not found

View File

@ -0,0 +1 @@
null not found

View File

@ -0,0 +1,26 @@
<?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>qiaoba-commons</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-common-redis</artifactId>
<dependencies>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,71 @@
package com.qiaoba.common.redis.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* Redis 基础配置
*
* @author ailanyin
* @version 1.0
* @since 2021/10/2 0003 下午 17:44
*/
@Configuration
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisSerializer<Object> serializer = redisSerializer();
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(serializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public RedisSerializer<Object> redisSerializer() {
//创建JSON序列化器
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//实体类存在子类继承父类,必须设置
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//必须设置,否则无法将JSON转化为对象会转化成Map类型
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(objectMapper);
return serializer;
}
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
//设置Redis缓存有效期为1天
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1));
return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
}
}

View File

@ -0,0 +1,51 @@
package com.qiaoba.common.redis.constants;
/**
* 缓存的key 常量
*
* @author ailanyin
* @version 1.0
* @since 2021/10/2 0002 下午 17:41
*/
public class CacheConstant {
/**
* 登录用户 redis key
*/
public static final String LOGIN_USER_KEY = "login_users:";
/**
* 验证码 redis key
*/
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
/**
* 参数管理 cache key
*/
public static final String SYS_CONFIG_KEY = "sys_config:";
/**
* 字典管理 cache key
*/
public static final String SYS_DICT_KEY = "sys_dict:";
/**
* 防重提交 redis key
*/
public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
/**
* 限流 redis key
*/
public static final String RATE_LIMIT_KEY = "rate_limit:";
/**
* 登录账户密码错误次数 redis key
*/
public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
/**
* 角色拥有的权限Set
*/
public static final String ROLE_PERMS = "role_perms:role_";
}

View File

@ -0,0 +1,357 @@
package com.qiaoba.common.redis.service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 自定义Redis接口
*
* @author ailanyin
* @version 1.0
* @since 2021/10/2 0002 下午 17:41
*/
public interface RedisService {
/**
* 保存为 Set
*
* @param key key
* @param value value
* @param time 存活时间 单位秒
*/
void set(String key, Object value, long time);
/**
* 保存为 Set, 永久
*
* @param key key
* @param value value
*/
void set(String key, Object value);
/**
* 通过 key 获取值
*
* @param key key
* @return Object
*/
Object get(String key);
/**
* 删除 key
*
* @param key key
* @return Boolean
*/
Boolean del(String key);
/**
* 批量删除
*
* @param collection keys
*/
void del(Collection<String> collection);
/**
* 设置过期时间
*
* @param key key
* @param time 存活时间 单位秒
* @return Boolean
*/
Boolean expire(String key, long time);
/**
* 获取过期时间
*
* @param key key
* @return long 单位秒
*/
Long getExpire(String key);
/**
* 判断是否有该属性
*
* @param key key
* @return Boolean
*/
Boolean hasKey(String key);
/**
* 按 delta 递增
* <p>
* value = value + delta
*
* @param key key
* @param delta 值
* @return Long 操作后的值
*/
Long incr(String key, long delta);
/**
* 按 delta 递减
* <p>
* value = value - delta
*
* @param key key
* @param delta 值
* @return Long 操作后的值
*/
Long decr(String key, long delta);
/**
* 获取Hash结构中的属性
*
* @param key key
* @param hashKey hashKey
* @return Object
*/
Object hGet(String key, String hashKey);
/**
* 向Hash结构中放入一个属性
*
* @param key key
* @param hashKey hashKey
* @param value value
* @param time 存活时间 单位秒
* @return Boolean
*/
Boolean hSet(String key, String hashKey, Object value, long time);
/**
* 向Hash结构中放入一个属性 永久
*
* @param key key
* @param hashKey hashKey
* @param value value
*/
void hSet(String key, String hashKey, Object value);
/**
* 直接获取整个Hash结构
*
* @param key key
* @return Map<Object, Object>
*/
Map<Object, Object> hGetAll(String key);
/**
* 直接设置整个Hash结构
*
* @param key key
* @param map map
* @param time 存活时间 单位秒
* @return Boolean
*/
Boolean hSetAll(String key, Map<String, Object> map, long time);
/**
* 直接设置整个Hash结构
*
* @param key key
* @param map map
*/
void hSetAll(String key, Map<String, ?> map);
/**
* 删除Hash结构中的属性
*
* @param key key
* @param hashKey hashKey
*/
void hDel(String key, Object... hashKey);
/**
* 判断Hash结构中是否有该属性
*
* @param key key
* @param hashKey hashKey
* @return Boolean
*/
Boolean hHasKey(String key, String hashKey);
/**
* Hash结构中属性递增
*
* @param key key
* @param hashKey hashKey
* @param delta 数值
* @return 操作后最终的值
*/
Long hIncr(String key, String hashKey, Long delta);
/**
* Hash结构中属性递减
*
* @param key key
* @param hashKey hashKey
* @param delta 数值
* @return 操作后最终的值
*/
Long hDecr(String key, String hashKey, Long delta);
/**
* 获取Set结构
*
* @param key key
* @return Set<Object>
*/
Set<Object> sMembers(String key);
/**
* 向Set结构中添加属性
*
* @param key key
* @param values values
* @return 被添加到集合中的新元素的数量,不包括被忽略的元素
*/
Long sAdd(String key, Object... values);
/**
* 向Set结构中添加属性
*
* @param key key
* @param time 存活时间 单位秒
* @param values values
* @return 被添加到集合中的新元素的数量,不包括被忽略的元素
*/
Long sAdd(String key, long time, Object... values);
/**
* 是否为Set中的属性
*
* @param key key
* @param value value
* @return Boolean
*/
Boolean sIsMember(String key, Object value);
/**
* 获取Set结构的长度
*
* @param key key
* @return Set结构的长度
*/
Long sSize(String key);
/**
* 删除Set结构中的属性
*
* @param key key
* @param values values
* @return 被成功移除的元素的数量,不包括被忽略的元素
*/
Long sRemove(String key, Object... values);
/**
* 获取List结构中的属性
*
* @param key key
* @param start 开始索引
* @param end 结束索引
* @return List<Object>
*/
List<Object> lRange(String key, long start, long end);
/**
* 获取List结构的长度
*
* @param key key
* @return list.size
*/
Long lSize(String key);
/**
* 根据索引获取List中的属性
*
* @param key key
* @param index 索引
* @return Object
*/
Object lIndex(String key, long index);
/**
* 向List结构中添加属性
*
* @param key key
* @param value value
* @return 执行命令后,列表的长度
*/
Long lPush(String key, Object value);
/**
* 向List结构中添加属性
*
* @param key key
* @param value value
* @param time 存活时间 单位秒
* @return 执行命令后,列表的长度
*/
Long lPush(String key, Object value, long time);
/**
* 向List结构中批量添加属性
*
* @param key key
* @param values values
* @return 执行命令后,列表的长度
*/
Long lPushAll(String key, Object... values);
/**
* 向List结构中批量添加属性
*
* @param key key
* @param time 存活时间 单位秒
* @param values values
* @return 执行命令后,列表的长度
*/
Long lPushAll(String key, Long time, Object... values);
/**
* 从List结构中移除属性
*
* @param key key
* @param count 数量
* count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。
* count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。
* count = 0 : 移除表中所有与 VALUE 相等的值。
* @param value value
* @return 被移除元素的数量
*/
Long lRemove(String key, long count, Object value);
/**
* 模糊查询所有key
*
* @param key key
* @return keys
*/
Collection<String> getKeys(String key);
/**
* get object
*
* @param key key
* @param clazz clazz
* @param <T> T
* @return clazz
*/
<T> T getObject(String key, Class<T> clazz);
/**
* get list
*
* @param key key
* @param clazz clazz
* @param <T> T
* @return // List<clazz>
*/
<T> List<T> getObjectList(String key, Class<T> clazz);
}

View File

@ -0,0 +1,224 @@
package com.qiaoba.common.redis.service.impl;
import com.qiaoba.common.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* 自定义Redis接口实现类
*
* @author ailanyin
* @version 1.0
* @since 2021/10/2 0003 下午 17:44
*/
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public void set(String key, Object value, long time) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}
@Override
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
@Override
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
@Override
public Boolean del(String key) {
return redisTemplate.delete(key);
}
@Override
public void del(Collection<String> collection) {
redisTemplate.delete(collection);
}
@Override
public Boolean expire(String key, long time) {
return redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
@Override
public Long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
@Override
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
@Override
public Long incr(String key, long delta) {
return redisTemplate.opsForValue().increment(key, delta);
}
@Override
public Long decr(String key, long delta) {
return redisTemplate.opsForValue().increment(key, -delta);
}
@Override
public Object hGet(String key, String hashKey) {
return redisTemplate.opsForHash().get(key, hashKey);
}
@Override
public Boolean hSet(String key, String hashKey, Object value, long time) {
redisTemplate.opsForHash().put(key, hashKey, value);
return expire(key, time);
}
@Override
public void hSet(String key, String hashKey, Object value) {
redisTemplate.opsForHash().put(key, hashKey, value);
}
@Override
public Map<Object, Object> hGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
@Override
public Boolean hSetAll(String key, Map<String, Object> map, long time) {
redisTemplate.opsForHash().putAll(key, map);
return expire(key, time);
}
@Override
public void hSetAll(String key, Map<String, ?> map) {
redisTemplate.opsForHash().putAll(key, map);
}
@Override
public void hDel(String key, Object... hashKey) {
redisTemplate.opsForHash().delete(key, hashKey);
}
@Override
public Boolean hHasKey(String key, String hashKey) {
return redisTemplate.opsForHash().hasKey(key, hashKey);
}
@Override
public Long hIncr(String key, String hashKey, Long delta) {
return redisTemplate.opsForHash().increment(key, hashKey, delta);
}
@Override
public Long hDecr(String key, String hashKey, Long delta) {
return redisTemplate.opsForHash().increment(key, hashKey, -delta);
}
@Override
public Set<Object> sMembers(String key) {
return redisTemplate.opsForSet().members(key);
}
@Override
public Long sAdd(String key, Object... values) {
return redisTemplate.opsForSet().add(key, values);
}
@Override
public Long sAdd(String key, long time, Object... values) {
Long count = redisTemplate.opsForSet().add(key, values);
expire(key, time);
return count;
}
@Override
public Boolean sIsMember(String key, Object value) {
return redisTemplate.opsForSet().isMember(key, value);
}
@Override
public Long sSize(String key) {
return redisTemplate.opsForSet().size(key);
}
@Override
public Long sRemove(String key, Object... values) {
return redisTemplate.opsForSet().remove(key, values);
}
@Override
public List<Object> lRange(String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}
@Override
public Long lSize(String key) {
return redisTemplate.opsForList().size(key);
}
@Override
public Object lIndex(String key, long index) {
return redisTemplate.opsForList().index(key, index);
}
@Override
public Long lPush(String key, Object value) {
return redisTemplate.opsForList().rightPush(key, value);
}
@Override
public Long lPush(String key, Object value, long time) {
Long index = redisTemplate.opsForList().rightPush(key, value);
expire(key, time);
return index;
}
@Override
public Long lPushAll(String key, Object... values) {
return redisTemplate.opsForList().rightPushAll(key, values);
}
@Override
public Long lPushAll(String key, Long time, Object... values) {
Long count = redisTemplate.opsForList().rightPushAll(key, values);
expire(key, time);
return count;
}
@Override
public Long lRemove(String key, long count, Object value) {
return redisTemplate.opsForList().remove(key, count, value);
}
@Override
public Collection<String> getKeys(String key) {
return redisTemplate.keys(key);
}
@Override
@SuppressWarnings("unchecked")
public <T> T getObject(String key, Class<T> clazz) {
return (T) get(key);
}
@Override
@SuppressWarnings("unchecked")
public <T> List<T> getObjectList(String key, Class<T> clazz) {
return (List<T>) get(key);
}
}

View File

@ -0,0 +1,5 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.qiaoba.common.redis.config.RedisConfig,\
com.qiaoba.common.redis.service.impl.RedisServiceImpl

View File

@ -0,0 +1,24 @@
<?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>qiaoba-commons</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-common-web</artifactId>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,227 @@
package com.qiaoba.common.web.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.cglib.core.Converter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* bean深拷贝工具(基于 cglib 性能优异)
* <p>
* 重点 cglib 不支持 拷贝到链式对象
* 例如: 源对象 拷贝到 目标(链式对象)
* 请区分好`浅拷贝`和`深拷贝`再做使用
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 15:34:59
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanCopyUtil {
/**
* 单对象基于class创建拷贝
*
* @param source 数据来源实体
* @param desc 描述对象 转换后的对象
* @return desc
*/
public static <T, V> V copy(T source, Class<V> desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
if (ObjectUtil.isNull(desc)) {
return null;
}
final V target = ReflectUtil.newInstanceIfPossible(desc);
return copy(source, target);
}
/**
* 单对象基于对象创建拷贝
*
* @param source 数据来源实体
* @param desc 转换后的对象
* @return desc
*/
public static <T, V> V copy(T source, V desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
if (ObjectUtil.isNull(desc)) {
return null;
}
BeanCopier beanCopier = BeanCopierCache.INSTANCE.get(source.getClass(), desc.getClass(), null);
beanCopier.copy(source, desc, null);
return desc;
}
/**
* 列表对象基于class创建拷贝
*
* @param sourceList 数据来源实体列表
* @param desc 描述对象 转换后的对象
* @return desc
*/
public static <T, V> List<V> copyList(List<T> sourceList, Class<V> desc) {
if (ObjectUtil.isNull(sourceList)) {
return null;
}
if (CollUtil.isEmpty(sourceList)) {
return CollUtil.newArrayList();
}
return toList(sourceList, source -> {
V target = ReflectUtil.newInstanceIfPossible(desc);
copy(source, target);
return target;
});
}
/**
* bean拷贝到map
*
* @param bean 数据来源实体
* @return map对象
*/
@SuppressWarnings("unchecked")
public static <T> Map<String, Object> copyToMap(T bean) {
if (ObjectUtil.isNull(bean)) {
return null;
}
return BeanMap.create(bean);
}
/**
* map拷贝到bean
*
* @param map 数据来源
* @param beanClass bean类
* @return bean对象
*/
public static <T> T mapToBean(Map<String, Object> map, Class<T> beanClass) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(beanClass)) {
return null;
}
T bean = ReflectUtil.newInstanceIfPossible(beanClass);
return mapToBean(map, bean);
}
/**
* map拷贝到bean
*
* @param map 数据来源
* @param bean bean对象
* @return bean对象
*/
public static <T> T mapToBean(Map<String, Object> map, T bean) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(bean)) {
return null;
}
BeanMap.create(bean).putAll(map);
return bean;
}
/**
* map拷贝到map
*
* @param map 数据来源
* @param clazz 返回的对象类型
* @return map对象
*/
public static <T, V> Map<String, V> mapToMap(Map<String, T> map, Class<V> clazz) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(clazz)) {
return null;
}
Map<String, V> copyMap = new LinkedHashMap<>(map.size());
map.forEach((k, v) -> copyMap.put(k, copy(v, clazz)));
return copyMap;
}
/**
* BeanCopier属性缓存<br>
* 缓存用于防止多次反射造成的性能问题
*
* @author Looly
* @since 5.4.1
*/
public enum BeanCopierCache {
/**
* BeanCopier属性缓存单例
*/
INSTANCE;
private final SimpleCache<String, BeanCopier> cache = new SimpleCache<>();
/**
* 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素
*
* @param srcClass 源Bean的类
* @param targetClass 目标Bean的类
* @param converter 转换器
* @return Map中对应的BeanCopier
*/
public BeanCopier get(Class<?> srcClass, Class<?> targetClass, Converter converter) {
final String key = genKey(srcClass, targetClass, converter);
return cache.get(key, () -> BeanCopier.create(srcClass, targetClass, converter != null));
}
/**
* 获得类与转换器生成的key
*
* @param srcClass 源Bean的类
* @param targetClass 目标Bean的类
* @param converter 转换器
* @return 属性名和Map映射的key
*/
private String genKey(Class<?> srcClass, Class<?> targetClass, Converter converter) {
final StringBuilder key = StrUtil.builder()
.append(srcClass.getName()).append('#').append(targetClass.getName());
if (null != converter) {
key.append('#').append(converter.getClass().getName());
}
return key.toString();
}
}
/**
* 将collection转化为List集合但是两者的泛型不同<br>
* <B>{@code Collection<E> ------> List<T> } </B>
*
* @param collection 需要转化的集合
* @param function collection中的泛型转化为list泛型的lambda表达式
* @param <E> collection中的泛型
* @param <T> List中的泛型
* @return 转化后的list
*/
private static <E, T> List<T> toList(Collection<E> collection, Function<E, T> function) {
if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList();
}
return collection
.stream()
.map(function)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1 @@
null not found

20
qiaoba-modules/pom.xml Normal file
View File

@ -0,0 +1,20 @@
<?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>qiaoba-boot</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-modules</artifactId>
<packaging>pom</packaging>
<modules>
<module>qiaoba-module-system</module>
<module>qiaoba-module-job</module>
</modules>
</project>

View File

@ -0,0 +1,15 @@
<?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>qiaoba-modules</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-module-job</artifactId>
</project>

View File

@ -0,0 +1 @@
null not found

View File

@ -0,0 +1 @@
null not found

View File

@ -0,0 +1 @@
null not found

View File

@ -0,0 +1,20 @@
<?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>qiaoba-modules</artifactId>
<groupId>com.qiaoba</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qiaoba-module-system</artifactId>
<dependencies>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-api-system</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,49 @@
package com.qiaoba.module.system.controller;
import cn.hutool.core.util.RandomUtil;
import com.qiaoba.api.system.entity.SysUser;
import com.qiaoba.api.system.entity.param.SysUserParam;
import com.qiaoba.module.system.service.SysUserService;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* 用户管理 web层
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 20:51:26
*/
@RestController
@RequestMapping("/system/user")
@Tag(name = "用户管理")
public class SysUserController {
@Resource
private SysUserService baseService;
@GetMapping("/add")
public String add() {
SysUser sysUser = new SysUser();
sysUser.setUsername(RandomUtil.randomString(8));
sysUser.setNickname(RandomUtil.randomString(8));
sysUser.setIsSuperuser(false);
sysUser.setIsDelete(true);
sysUser.setIsManager(true);
baseService.insert(sysUser);
return "OK";
}
@GetMapping("/list")
public List<SysUser> list(@RequestParam String name) {
SysUserParam dto = new SysUserParam();
dto.setNickname(name);
return baseService.selectList(dto);
}
}

View File

@ -0,0 +1,14 @@
package com.qiaoba.module.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qiaoba.api.system.entity.SysTenant;
/**
* 租户管理 数据层
*
* @author ailanyin
* @version 1.0
* @since 2023-04-24 19:44:48
*/
public interface SysTenantMapper extends BaseMapper<SysTenant> {
}

View File

@ -0,0 +1,14 @@
package com.qiaoba.module.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qiaoba.api.system.entity.SysUser;
/**
* 用户管理 数据层
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 20:38:55
*/
public interface SysUserMapper extends BaseMapper<SysUser> {
}

View File

@ -0,0 +1,13 @@
package com.qiaoba.module.system.service;
import com.qiaoba.api.system.service.SysTenantApiService;
/**
* 租户管理 服务层
*
* @author ailanyin
* @version 1.0
* @since 2023-04-24 19:23:08
*/
public interface SysTenantService extends SysTenantApiService {
}

View File

@ -0,0 +1,25 @@
package com.qiaoba.module.system.service;
import com.qiaoba.api.system.entity.SysUser;
import com.qiaoba.api.system.entity.param.SysUserParam;
import com.qiaoba.api.system.service.SysUserApiService;
import java.util.List;
/**
* 用户管理 服务层
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 20:33:37
*/
public interface SysUserService extends SysUserApiService {
/**
* 条件查询列表
*
* @param sysUserParam 查询参数
* @return list
*/
List<SysUser> selectList(SysUserParam sysUserParam);
}

View File

@ -0,0 +1,32 @@
package com.qiaoba.module.system.service.impl;
import com.qiaoba.api.system.entity.SysTenant;
import com.qiaoba.module.system.mapper.SysTenantMapper;
import com.qiaoba.module.system.service.SysTenantService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 租户管理 业务层实现
*
* @author ailanyin
* @version 1.0
* @since 2023-04-24 19:46:00
*/
@Service
public class SysTenantServiceImpl implements SysTenantService {
@Resource
private SysTenantMapper baseMapper;
@Override
public int insert(SysTenant sysTenant) {
return baseMapper.insert(sysTenant);
}
@Override
public int update(SysTenant sysTenant) {
return baseMapper.updateById(sysTenant);
}
}

View File

@ -0,0 +1,47 @@
package com.qiaoba.module.system.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qiaoba.api.system.entity.SysUser;
import com.qiaoba.api.system.entity.param.SysUserParam;
import com.qiaoba.module.system.mapper.SysUserMapper;
import com.qiaoba.module.system.service.SysUserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* 用户管理 服务层实现
*
* @author ailanyin
* @version 1.0
* @since 2023-04-23 20:38:09
*/
@Service
public class SysUserServiceImpl implements SysUserService {
@Resource
private SysUserMapper baseMapper;
@Override
public int insert(SysUser sysUser) {
return baseMapper.insert(sysUser);
}
@Override
public int update(SysUser sysUser) {
return baseMapper.updateById(sysUser);
}
@Override
public List<SysUser> selectList(SysUserParam sysUserParam) {
return baseMapper.selectList(param2Wrapper(sysUserParam));
}
private QueryWrapper<SysUser> param2Wrapper(SysUserParam sysUserParam) {
QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
wrapper.lambda().like(StrUtil.isNotBlank(sysUserParam.getNickname()), SysUser::getNickname, sysUserParam.getNickname());
return wrapper;
}
}