feat():完善多数据源管理

baize
baize 2024-06-04 21:32:59 +08:00
parent a2783ec36e
commit b75b28fce1
19 changed files with 437 additions and 51 deletions

View File

@ -5,7 +5,7 @@ package com.muyu.common.core.exception;
* *
* @author muyu * @author muyu
*/ */
public final class ServiceException extends RuntimeException { public class ServiceException extends RuntimeException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**

View File

@ -0,0 +1,32 @@
<?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>
<parent>
<groupId>com.muyu</groupId>
<artifactId>muyu-common</artifactId>
<version>3.6.3</version>
</parent>
<artifactId>muyu-common-saas</artifactId>
<description>
SaaS公共依赖
</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!---->
<dependency>
<groupId>com.muyu</groupId>
<artifactId>muyu-common-datasource</artifactId>
</dependency>
<dependency>
<groupId>com.muyu</groupId>
<artifactId>muyu-common-security</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,85 @@
package com.muyu.common.many.datasource;
import com.alibaba.druid.pool.DruidDataSource;
import com.muyu.common.core.utils.SpringUtils;
import com.muyu.common.many.datasource.domain.model.DataSourceInfo;
import com.muyu.common.many.datasource.factory.DruidDataSourceFactory;
import com.muyu.common.saas.domain.model.EnterPriseInfo;
import com.muyu.common.many.datasource.role.DynamicDataSource;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* ManyDataSource
*
* @author DeKangLiu
* Date 2024/6/3 20:01
*/
@Component
@Log4j2
@AllArgsConstructor
public class ManyDataSource {
@PostConstruct
public void init(){
new Thread(()->{
try {
Thread.sleep(10000);
} catch (InterruptedException ignored) {}
DruidDataSourceFactory druidDataSourceFactory= SpringUtils.getBean(DruidDataSourceFactory.class);
DynamicDataSource dynamicDataSource= SpringUtils.getBean(DynamicDataSource.class);
EnterPriseInfo enterPriseInfo = EnterPriseInfo.builder()
.entCode("liu_0603")
.ip("192.168.116.129")
.port(3308)
.build();
DataSourceInfo dataSourceInfo = DataSourceInfo.hostAndPortBuild(enterPriseInfo.getEntCode(), enterPriseInfo.getIp(), enterPriseInfo.getPort());
DruidDataSource druidDataSource = druidDataSourceFactory.create(dataSourceInfo);
dynamicDataSource.put(dataSourceInfo.getKey(), druidDataSource);
}).start();
}
private List<EnterPriseInfo> dataSourceInfoList(){
List<EnterPriseInfo> list = new ArrayList<>();
list.add(
EnterPriseInfo.builder()
.entCode("liu_0604")
.ip("192.168.116.129")
.port(3307)
.build()
);
return list;
}
@Bean
@Primary
public DynamicDataSource dynamicDataSource(DruidDataSourceFactory druidDataSourceFactory) {
//企业列表 企业CODE 端口 ip
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceInfoList()
.stream()
.map(enterPriseInfo -> DataSourceInfo.hostAndPortBuild(enterPriseInfo.getEntCode(), enterPriseInfo.getIp(),enterPriseInfo.getPort()))
.forEach(dataSourceInfo -> {
dataSourceMap.put(dataSourceInfo.getKey(), druidDataSourceFactory.create(dataSourceInfo));
});
//设置动态数据源
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
dynamicDataSource.setTargetDataSources(dataSourceMap);
//将数据源信息备份在defineTargetDataSources中
dynamicDataSource.setDefineTargetDataSources(dataSourceMap);
return dynamicDataSource;
}
}

View File

@ -1,4 +1,4 @@
package com.muyu.many.datasource.config; package com.muyu.common.many.datasource.constents;
/** /**
* @author DongZl * @author DongZl
@ -11,5 +11,5 @@ public class DatasourceContent {
public final static String USER_NAME = "root"; public final static String USER_NAME = "root";
public final static String PASSWORD = "root"; public final static String PASSWORD = "L041120D";
} }

View File

@ -1,4 +1,4 @@
package com.muyu.many.datasource.config.domain.model; package com.muyu.common.many.datasource.domain.model;
import com.muyu.common.core.utils.StringUtils; import com.muyu.common.core.utils.StringUtils;
@ -7,7 +7,7 @@ import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import static com.muyu.many.datasource.config.DatasourceContent.*; import static com.muyu.common.many.datasource.constents.DatasourceContent.*;
/** /**
@ -42,7 +42,7 @@ public class DataSourceInfo {
private String password; private String password;
public static DataSourceInfo hostAndPortBuild(String key,String host, String port){ public static DataSourceInfo hostAndPortBuild(String key,String host, Integer port){
return DataSourceInfo.builder() return DataSourceInfo.builder()
.key(key) .key(key)
.url(StringUtils.format(DATASOURCE_URL, host, port,port)) .url(StringUtils.format(DATASOURCE_URL, host, port,port))

View File

@ -1,7 +1,7 @@
package com.muyu.many.datasource.config.factory; package com.muyu.common.many.datasource.factory;
import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSource;
import com.muyu.many.datasource.config.domain.model.DataSourceInfo; import com.muyu.common.many.datasource.domain.model.DataSourceInfo;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;

View File

@ -1,4 +1,4 @@
package com.muyu.many.datasource.config.holder; package com.muyu.common.many.datasource.holder;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert; import org.springframework.util.Assert;

View File

@ -1,6 +1,7 @@
package com.muyu.many.datasource.config.role; package com.muyu.common.many.datasource.role;
import com.muyu.many.datasource.config.holder.DynamicDataSourceHolder; import com.alibaba.druid.pool.DruidDataSource;
import com.muyu.common.many.datasource.holder.DynamicDataSourceHolder;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -21,6 +22,26 @@ public class DynamicDataSource extends AbstractRoutingDataSource {
//备份所有数据源信息,备份的是个 指针!!! //备份所有数据源信息,备份的是个 指针!!!
private Map<Object, Object> defineTargetDataSources; private Map<Object, Object> defineTargetDataSources;
/**
*
* @param key
* @return true false
*/
public boolean hashKye(String key){
return defineTargetDataSources.containsKey(key);
}
/**
*
* @param key
* @param value
*/
public void put(String key, DruidDataSource value){
defineTargetDataSources.put(key,value);
this.afterPropertiesSet();
}
/** /**
* 线使 * 线使
*/ */

View File

@ -0,0 +1,12 @@
package com.muyu.common.saas.constents;
/**
* SaaS SaaSConstant
*
* @author DeKangLiu
* Date 2024/6/4 18:34
*/
public class SaaSConstant {
public final static String SAAS_KEY="enterprise-code";
}

View File

@ -0,0 +1,25 @@
package com.muyu.common.saas.domain.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* EnterPriseInfo
*
* @author DeKangLiu
* Date 2024/6/4 08:53
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EnterPriseInfo {
private String entCode;
private String ip;
private Integer port;
}

View File

@ -0,0 +1,26 @@
package com.muyu.common.saas.exception;
import com.muyu.common.core.exception.ServiceException;
/**
* SaaS SaaSException
*
* @author DeKangLiu
* Date 2024/6/4 18:45
*/
public class SaaSException extends ServiceException {
public SaaSException(String message, Integer code) {
super(message, code);
}
public SaaSException(String message) {
super(message);
}
public SaaSException() {
super();
}
}

View File

@ -0,0 +1,54 @@
package com.muyu.common.saas.interceptor;
import com.muyu.common.core.utils.ServletUtils;
import com.muyu.common.core.utils.SpringUtils;
import com.muyu.common.saas.constents.SaaSConstant;
import com.muyu.common.saas.exception.SaaSException;
import com.muyu.common.many.datasource.holder.DynamicDataSourceHolder;
import com.muyu.common.many.datasource.role.DynamicDataSource;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* SaaS SaaSInterceptor
*
* @author DeKangLiu
* Date 2024/6/4 14:39
*/
public class SaaSInterceptor implements AsyncHandlerInterceptor {
/**
*
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)){
return true;
}
String SaasKey = ServletUtils.getHeader(request, SaaSConstant.SAAS_KEY);
if (SaasKey==null){
throw new SaaSException("SaaS非法访问");
} else {
DynamicDataSource dynamicDataSource = SpringUtils.getBean(DynamicDataSource.class);
if (!dynamicDataSource.hashKye(SaasKey)){
throw new SaaSException("SaaS非法访问");
}
}
DynamicDataSourceHolder.setDynamicDataSourceKey(SaasKey);
return true;
}
/**
*
*/
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
DynamicDataSourceHolder.removeDynamicDataSourceKey();
}
}

View File

@ -0,0 +1,31 @@
package com.muyu.common.saas.interceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
*
*
* @author muyu
*/
public class WebMvcSaaSConfig implements WebMvcConfigurer {
/**
*
*/
public static final String[] excludeUrls = {"/login", "/logout", "/refresh"};
@Override
public void addInterceptors (InterceptorRegistry registry) {
registry.addInterceptor(getHeaderInterceptor())
.addPathPatterns("/**")
.excludePathPatterns(excludeUrls)
.order(-10);
}
/**
*
*/
public SaaSInterceptor getHeaderInterceptor () {
return new SaaSInterceptor();
}
}

View File

@ -18,6 +18,7 @@
<module>muyu-common-datascope</module> <module>muyu-common-datascope</module>
<module>muyu-common-datasource</module> <module>muyu-common-datasource</module>
<module>muyu-common-system</module> <module>muyu-common-system</module>
<module>muyu-common-saas</module>
</modules> </modules>
<artifactId>muyu-common</artifactId> <artifactId>muyu-common</artifactId>

View File

@ -1,40 +0,0 @@
package com.muyu.many.datasource.config;
import com.muyu.many.datasource.config.factory.DruidDataSourceFactory;
import com.muyu.many.datasource.config.role.DynamicDataSource;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* ManyDataSource
*
* @author DeKangLiu
* Date 2024/6/3 20:01
*/
@Configuration
@Log4j2
public class ManyDataSource {
@Bean
@Primary
public DynamicDataSource dynamicDataSource(DruidDataSourceFactory druidDataSourceFactory) {
Map<Object, Object> dataSourceMap = new HashMap<>();
getDataSourceInfoList().forEach(dataSourceInfo -> {
dataSourceMap.put(dataSourceInfo.getKey(), createDataSourceConnection(dataSourceInfo));
});
//设置动态数据源
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
dynamicDataSource.setTargetDataSources(dataSourceMap);
//将数据源信息备份在defineTargetDataSources中
dynamicDataSource.setDefineTargetDataSources(dataSourceMap);
return dynamicDataSource;
}
}

View File

@ -0,0 +1,31 @@
package com.muyu.many.datasource.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.muyu.common.core.domain.Result;
import com.muyu.many.datasource.config.holder.DynamicDataSourceHolder;
import com.muyu.many.datasource.domain.Vehicle;
import com.muyu.many.datasource.mapper.VehicleMapper;
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.util.List;
/**
* VehicleController
*
* @author DeKangLiu
* Date 2024/6/4 14:08
*/
@RestController
@RequestMapping("/vehicle")
public class VehicleController {
@Autowired
private VehicleMapper vehicleMapper;
@GetMapping("/list/all")
public Result<List<Vehicle>> findAll () {
return Result.success(vehicleMapper.selectList(new QueryWrapper<>()));
}
}

View File

@ -0,0 +1,93 @@
package com.muyu.many.datasource.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.muyu.common.core.annotation.Excel;
import com.muyu.common.core.web.domain.BaseEntity;
import com.muyu.common.security.utils.SecurityUtils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
* vehicle
*
* @author muyu
* @date 2024-05-27
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "vehicle")
public class Vehicle extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 车辆id */
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/** 车辆vin */
private String vin;
/** 品牌 */
private String brand;
/** 型号 */
private String model;
/** 生产日期 */
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date productionDate;
/** 车身类型 */
private String bodyType;
/** 车身颜色 */
private String color;
/** 发动机排量 */
private BigDecimal engineCapacity;
/** 燃油类型 */
private String fuelType;
/** 变速器类型 */
private String transmission;
/** 驱动方式 */
private String driveType;
/** 行驶里程 */
@Excel(name = "行驶里程")
@ApiModelProperty(name = "行驶里程", value = "行驶里程")
private BigDecimal mileage;
/** 注册日期 */
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date registrationDate;
/** 车牌号码 */
private String licenseNumber;
/** 持有者 */
private String holder;
/** 车辆类型 */
private String vehicleType;
}

View File

@ -0,0 +1,14 @@
package com.muyu.many.datasource.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.muyu.many.datasource.domain.Vehicle;
/**
* mapper VehicleMapper
*
* @author DeKangLiu
* Date 2024/6/4 14:07
*/
public interface VehicleMapper extends BaseMapper<Vehicle> {
}