first commit

This commit is contained in:
2023-06-28 22:06:29 +08:00
parent a567befac3
commit a8f323b861
16 changed files with 279 additions and 321 deletions

View File

@ -1,23 +0,0 @@
package com.qiaoba.module.tenant.handler;
import java.sql.Connection;
/**
* 数据处理器
*
* @author ailanyin
* @version 1.0
* @since 2023/6/8 15:27
*/
public interface DataHandler {
/**
* 处理
*
* @param conn conn
* @param needCreateTables 是否需要建表
* @param tenantId tenantId
* @return 结果
*/
boolean handle(Connection conn, String tenantId, Boolean needCreateTables);
}

View File

@ -1,46 +0,0 @@
package com.qiaoba.module.tenant.handler;
import com.qiaoba.common.base.enums.DataBaseEnum;
import com.qiaoba.common.base.exceptions.ServiceException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* 处理器工厂-根据不同类型的数据库-选择不同的处理器
*
* @author ailanyin
* @version 1.0
* @since 2023/6/8 15:34
*/
@Component
@RequiredArgsConstructor
public class DataHandlerFactory {
private static Map<String, DataHandler> handlerMap = new ConcurrentHashMap<>();
private final MysqlDataHandler mysqlDataHandler;
private final OracleDataHandler oracleDataHandler;
private final PostgreSqlHandler postgreSqlHandler;
private final SqlServerHandler sqlServerHandler;
@PostConstruct
public void register() {
handlerMap.put(DataBaseEnum.MY_SQL.getType(), mysqlDataHandler);
handlerMap.put(DataBaseEnum.ORACLE.getType(), oracleDataHandler);
handlerMap.put(DataBaseEnum.POSTGRE_SQL.getType(), postgreSqlHandler);
handlerMap.put(DataBaseEnum.SQL_SERVER.getType(), sqlServerHandler);
}
public static DataHandler getHandler(String name) {
DataHandler dataHandler = handlerMap.get(name);
if (Objects.isNull(dataHandler)) {
throw new ServiceException("数据库处理器工厂异常, 类型:[" + name + "]找不到相对应的解析器");
}
return dataHandler;
}
}

View File

@ -1,175 +0,0 @@
package com.qiaoba.module.tenant.handler;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.DbUtil;
import cn.hutool.db.sql.SqlExecutor;
import com.qiaoba.module.tenant.utils.MenuUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileReader;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* MysqlHandler
*
* @author ailanyin
* @version 1.0
* @since 2023/6/5 16:28
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class MysqlDataHandler implements DataHandler {
/**
* 角色-超管ID
*/
private static final ThreadLocal<Long> ROLE_SUPER_ADMIN_ID = new ThreadLocal<>();
/**
* 用户-超管ID
*/
private static final ThreadLocal<Long> USER_SUPER_ADMIN_ID = new ThreadLocal<>();
@Override
public boolean handle(Connection conn, String tenantId, Boolean needCreateTables) {
try {
// 手动提交
conn.setAutoCommit(false);
// 创建表
if (needCreateTables) {
initTables(conn);
}
// 处理 sys_config
handleSysConfig(conn, tenantId);
// 处理 sys_post
handleSysPost(conn, tenantId);
// 处理 sys_role
handleSysRole(conn, tenantId);
// 处理 sys_user
handleSysUser(conn, tenantId);
// 处理 sys_user_role
bindUserAndRole(conn, tenantId);
// 处理 sys_menu
MenuUtil.handleMenu(conn, tenantId);
// 处理 sys_role_menu
bindRoleAndMenu(conn, tenantId);
conn.commit();
return true;
} catch (Exception e) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
DbUtil.close(conn);
ROLE_SUPER_ADMIN_ID.remove();
USER_SUPER_ADMIN_ID.remove();
}
return false;
}
private void initTables(Connection conn) throws Exception {
ClassPathResource resource = new ClassPathResource("MySQL/table/base_tables");
File file = resource.getFile();
FileReader reader = new FileReader(file);
ScriptRunner scriptRunner = new ScriptRunner(conn);
scriptRunner.setSendFullScript(true);
scriptRunner.runScript(reader);
reader.close();
}
private void handleSysConfig(Connection conn, String tenantId) throws Exception {
handleSql(conn, tenantId, "MySQL/data/sys_config_data");
}
private void handleSql(Connection conn, String tenantId, String fileName) throws Exception {
ClassPathResource resource = new ClassPathResource(fileName);
List<String> lines = FileUtil.readLines(resource.getFile(), Charset.defaultCharset());
StringBuilder sb = new StringBuilder();
for (String line : lines) {
if (StrUtil.isNotBlank(line)) {
Snowflake snowflake = new Snowflake();
line = StrUtil.format(line, snowflake.nextId(), tenantId);
sb.append(line);
}
}
if (StrUtil.isNotBlank(sb.toString())) {
SqlExecutor.execute(conn, sb.toString());
}
}
private void handleSysPost(Connection conn, String tenantId) throws Exception {
handleSql(conn, tenantId, "MySQL/data/sys_post_data");
}
private void handleSysRole(Connection conn, String tenantId) throws Exception {
handleUserOrRole(conn, tenantId, "MySQL/data/sys_role_data", true);
}
private void handleUserOrRole(Connection conn, String tenantId, String fileName, Boolean isRole) throws Exception {
ClassPathResource resource = new ClassPathResource(fileName);
List<String> lines = FileUtil.readLines(resource.getFile(), Charset.defaultCharset());
StringBuilder sb = new StringBuilder();
long superAdminId = new Snowflake().nextId();
if (isRole) {
ROLE_SUPER_ADMIN_ID.set(superAdminId);
} else {
USER_SUPER_ADMIN_ID.set(superAdminId);
}
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (StrUtil.isBlank(line)) {
continue;
}
if (i == 0) {
// 第1行是超管
line = StrUtil.format(line, superAdminId, tenantId);
} else {
Snowflake snowflake = new Snowflake();
line = StrUtil.format(line, snowflake.nextId(), tenantId);
}
sb.append(line);
}
if (StrUtil.isNotBlank(sb.toString())) {
SqlExecutor.execute(conn, sb.toString());
}
}
private void handleSysUser(Connection conn, String tenantId) throws Exception {
handleUserOrRole(conn, tenantId, "MySQL/data/sys_user_data", false);
}
private void bindUserAndRole(Connection conn, String tenantId) throws Exception {
// user_id role_id tenant_id
String sql = StrUtil.format("INSERT INTO `sys_user_role` VALUES ({}, {}, {});",
USER_SUPER_ADMIN_ID.get(),
ROLE_SUPER_ADMIN_ID.get(), tenantId);
SqlExecutor.execute(conn, sql);
}
private void bindRoleAndMenu(Connection conn, String tenantId) throws Exception {
String sql = "INSERT INTO `sys_role_menu` VALUES ({}, {}, {});";
StringBuilder sb = new StringBuilder();
List<String> menuIds = MenuUtil.getAllMenuIdsByTenantId(conn, tenantId);
for (String menuId : menuIds) {
sb.append(StrUtil.format(sql, ROLE_SUPER_ADMIN_ID.get(), menuId, tenantId));
}
if (StrUtil.isNotBlank(sb.toString())) {
SqlExecutor.execute(conn, sb.toString());
}
}
}

View File

@ -1,25 +0,0 @@
package com.qiaoba.module.tenant.handler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.sql.Connection;
/**
* OracleDataHandler
*
* @author ailanyin
* @version 1.0
* @since 2023/6/5 16:28
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class OracleDataHandler implements DataHandler {
@Override
public boolean handle(Connection conn, String tenantId, Boolean needCreateTables) {
return false;
}
}

View File

@ -1,25 +0,0 @@
package com.qiaoba.module.tenant.handler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.sql.Connection;
/**
* PostgreSqlHandler
*
* @author ailanyin
* @version 1.0
* @since 2023/6/5 16:28
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class PostgreSqlHandler implements DataHandler {
@Override
public boolean handle(Connection conn, String tenantId, Boolean needCreateTables) {
return false;
}
}

View File

@ -1,25 +0,0 @@
package com.qiaoba.module.tenant.handler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.sql.Connection;
/**
* SqlServerHandler
*
* @author ailanyin
* @version 1.0
* @since 2023/6/5 16:28
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class SqlServerHandler implements DataHandler {
@Override
public boolean handle(Connection conn, String tenantId, Boolean needCreateTables) {
return false;
}
}

View File

@ -1,5 +1,9 @@
package com.qiaoba.module.tenant.init;
import com.qiaoba.module.tenant.entity.vo.TenantInitVo;
import java.sql.Connection;
/**
* 初始化数据策略
*
@ -8,4 +12,13 @@ package com.qiaoba.module.tenant.init;
* @since 2023/6/27 13:05
*/
public interface InitDataStrategy {
/**
* 初始化数据
*
* @param conn 连接对象
* @param tenantId 租户ID
* @return 结果
*/
TenantInitVo init(Connection conn, String tenantId);
}

View File

@ -0,0 +1,42 @@
package com.qiaoba.module.tenant.init;
import com.qiaoba.common.base.enums.DataBaseEnum;
import com.qiaoba.common.base.exceptions.ServiceException;
import com.qiaoba.module.tenant.init.impl.MysqlInitDataStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* 策略工厂-租户初始化-导入数据
*
* @author ailanyin
* @version 1.0
* @since 2023/6/27 16:41
*/
@Component
@RequiredArgsConstructor
public class InitDataStrategyFactory {
private static Map<String, InitDataStrategy> FACTORY = new ConcurrentHashMap<>();
private final MysqlInitDataStrategy mysqlInitDataStrategy;
@PostConstruct
public void register() {
FACTORY.put(DataBaseEnum.MY_SQL.getType(), mysqlInitDataStrategy);
}
public static InitDataStrategy getStrategy(String name) {
InitDataStrategy strategy = FACTORY.get(name);
if (Objects.isNull(strategy)) {
throw new ServiceException("[ InitDataStrategy ]策略工厂异常, 类型:[" + name + "]找不到相对应的策略");
}
return strategy;
}
}

View File

@ -21,4 +21,12 @@ public interface InitTablesStrategy {
* @return 结果
*/
TenantInitVo create(Connection conn, String schema);
/**
* 创建表
*
* @param conn 连接对象
* @return 结果
*/
TenantInitVo create(Connection conn);
}

View File

@ -0,0 +1,56 @@
package com.qiaoba.module.tenant.init.impl;
import cn.hutool.db.DbUtil;
import cn.hutool.http.HttpStatus;
import com.qiaoba.common.base.code.DatasourceErrorCode;
import com.qiaoba.module.tenant.entity.vo.TenantInitVo;
import com.qiaoba.module.tenant.init.InitDataStrategy;
import com.qiaoba.module.tenant.utils.InitDataUtil;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 初始化数据策略
*
* @author ailanyin
* @version 1.0
* @since 2023/6/27 13:05
*/
@Component
public class MysqlInitDataStrategy implements InitDataStrategy {
@Override
public TenantInitVo init(Connection conn, String tenantId) {
try {
// 手动提交
conn.setAutoCommit(false);
// 处理 sys_config
InitDataUtil.handleSysConfig(conn, tenantId, "MySQL/data/sys_config_data");
// 处理 sys_post
InitDataUtil.handleSysPost(conn, tenantId, "MySQL/data/sys_post_data");
// 处理 sys_role
InitDataUtil.handleSysRole(conn, tenantId, "MySQL/data/sys_role_data");
// 处理 sys_user
InitDataUtil.handleSysUser(conn, tenantId, "MySQL/data/sys_user_data");
// 处理 sys_user_role
InitDataUtil.bindUserAndRole(conn, tenantId);
// 处理 sys_menu todo
//MenuUtil.handleMenu(conn, tenantId);
// 处理 sys_role_menu
InitDataUtil.bindRoleAndMenu(conn, tenantId);
conn.commit();
return new TenantInitVo(HttpStatus.HTTP_OK, null);
} catch (Exception e) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
return new TenantInitVo(DatasourceErrorCode.INIT_DATA_ERROR.getCode(), e.getMessage());
} finally {
DbUtil.close(conn);
}
}
}

View File

@ -52,4 +52,16 @@ public class MysqlInitTablesStrategy implements InitTablesStrategy {
}
}
@Override
public TenantInitVo create(Connection conn) {
try {
DbUtil.runScript(conn, "MySQL/table/base_tables");
return new TenantInitVo(HttpStatus.HTTP_OK, null);
} catch (IOException e) {
return new TenantInitVo(DatasourceErrorCode.CREATE_TABLE_ERROR.getCode(), e.getMessage());
} finally {
IoUtil.close(conn);
}
}
}

View File

@ -22,6 +22,15 @@ public interface SysTenantDatasourceService {
*/
SysTenantDatasource selectPrimary(String tenantId);
/**
* 查询租户的主要数据源
*
* @param tenantId 租户ID
* @param allowNull 允许空
* @return 数据源
*/
SysTenantDatasource selectPrimary(String tenantId, boolean allowNull);
/**
* 通过IP查询
*

View File

@ -2,8 +2,10 @@ package com.qiaoba.module.tenant.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qiaoba.common.base.code.DatasourceErrorCode;
import com.qiaoba.common.base.constants.BaseConstant;
import com.qiaoba.common.base.enums.BaseEnum;
import com.qiaoba.common.base.exceptions.ServiceException;
import com.qiaoba.module.tenant.entity.SysTenantDatasource;
import com.qiaoba.module.tenant.entity.param.SysTenantDatasourceParam;
import com.qiaoba.module.tenant.mapper.SysTenantDatasourceMapper;
@ -13,6 +15,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
/**
* 租户数据源 服务层实现
@ -32,6 +35,15 @@ public class SysTenantDatasourceServiceImpl implements SysTenantDatasourceServic
return sysTenantDatasourceMapper.selectOne(paramToWrapper(new SysTenantDatasourceParam(tenantId, BaseEnum.YES.getCode())));
}
@Override
public SysTenantDatasource selectPrimary(String tenantId, boolean allowNull) {
SysTenantDatasource primary = selectPrimary(tenantId);
if (!allowNull && Objects.isNull(primary)) {
throw new ServiceException(DatasourceErrorCode.NOT_FIND.getCode(), DatasourceErrorCode.NOT_FIND.getMsg());
}
return primary;
}
@Override
public SysTenantDatasource selectByIp(String tenantId, String ip) {
SysTenantDatasourceParam param = new SysTenantDatasourceParam(tenantId);

View File

@ -94,7 +94,11 @@ public class SysTenantInitServiceImpl implements SysTenantInitService {
}
// 数据源模式
if (SysTenant.DATASOURCE_MODE.equals(sysTenant.getMode())) {
SysTenantDatasource primary = sysTenantDatasourceService.selectPrimary(tenantId, false);
Connection connection = JdbcUtil.getConnection(DataBaseEnum.getDriver(primary.getType()),
DataBaseEnum.getUrl(primary.getType(), primary.getIp(), primary.getPort(), primary.getDbName(), primary.getSchemaName()),
primary.getUsername(), primary.getPassword());
return InitTablesStrategyFactory.getStrategy(primary.getType()).create(connection);
}
return new TenantInitVo(500, "未知异常");
}

View File

@ -0,0 +1,116 @@
package com.qiaoba.module.tenant.utils;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.sql.SqlExecutor;
import org.springframework.core.io.ClassPathResource;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.util.List;
/**
* 数据初始化工具类
*
* @author ailanyin
* @version 1.0
* @since 2023/6/5 16:28
*/
public class InitDataUtil {
private InitDataUtil() {
}
/**
* 角色-超管ID
*/
private static final ThreadLocal<Long> ROLE_SUPER_ADMIN_ID = new ThreadLocal<>();
/**
* 用户-超管ID
*/
private static final ThreadLocal<Long> USER_SUPER_ADMIN_ID = new ThreadLocal<>();
public static void handleSysConfig(Connection conn, String tenantId, String filePath) throws Exception {
handleSql(conn, tenantId, filePath);
}
public static void handleSysPost(Connection conn, String tenantId, String filePath) throws Exception {
handleSql(conn, tenantId, filePath);
}
public static void handleSysRole(Connection conn, String tenantId, String filePath) throws Exception {
handleUserOrRole(conn, tenantId, filePath, true);
}
private static void handleUserOrRole(Connection conn, String tenantId, String filePath, Boolean isRole) throws Exception {
ClassPathResource resource = new ClassPathResource(filePath);
List<String> lines = FileUtil.readLines(resource.getFile(), Charset.defaultCharset());
StringBuilder sb = new StringBuilder();
long superAdminId = new Snowflake().nextId();
if (isRole) {
ROLE_SUPER_ADMIN_ID.set(superAdminId);
} else {
USER_SUPER_ADMIN_ID.set(superAdminId);
}
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (StrUtil.isBlank(line)) {
continue;
}
if (i == 0) {
// 第1行是超管
line = StrUtil.format(line, superAdminId, tenantId);
} else {
Snowflake snowflake = new Snowflake();
line = StrUtil.format(line, snowflake.nextId(), tenantId);
}
sb.append(line);
}
if (StrUtil.isNotBlank(sb.toString())) {
SqlExecutor.execute(conn, sb.toString());
}
}
public static void handleSysUser(Connection conn, String tenantId, String filePath) throws Exception {
handleUserOrRole(conn, tenantId, filePath, false);
}
public static void bindUserAndRole(Connection conn, String tenantId) throws Exception {
// user_id role_id tenant_id
String sql = StrUtil.format("INSERT INTO sys_user_role VALUES ('{}', '{}', '{}');",
USER_SUPER_ADMIN_ID.get(),
ROLE_SUPER_ADMIN_ID.get(), tenantId);
SqlExecutor.execute(conn, sql);
}
public static void bindRoleAndMenu(Connection conn, String tenantId) throws Exception {
String sql = "INSERT INTO sys_role_menu VALUES ('{}', '{}', '{}');";
StringBuilder sb = new StringBuilder();
List<String> menuIds = MenuUtil.getAllMenuIdsByTenantId(conn, tenantId);
for (String menuId : menuIds) {
sb.append(StrUtil.format(sql, ROLE_SUPER_ADMIN_ID.get(), menuId, tenantId));
}
if (StrUtil.isNotBlank(sb.toString())) {
SqlExecutor.execute(conn, sb.toString());
}
}
private static void handleSql(Connection conn, String tenantId, String filePath) throws Exception {
ClassPathResource resource = new ClassPathResource(filePath);
List<String> lines = FileUtil.readLines(resource.getFile(), Charset.defaultCharset());
StringBuilder sb = new StringBuilder();
for (String line : lines) {
if (StrUtil.isNotBlank(line)) {
Snowflake snowflake = new Snowflake();
line = StrUtil.format(line, snowflake.nextId(), tenantId);
sb.append(line);
}
}
if (StrUtil.isNotBlank(sb.toString())) {
SqlExecutor.execute(conn, sb.toString());
}
}
}