feat: 项目初始化

This commit is contained in:
2025-08-28 15:28:10 +08:00
commit 6f8726000d
222 changed files with 12454 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
<?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.byhah</groupId>
<artifactId>cloud-starters</artifactId>
<version>1.0.2-SNAPSHOT</version>
</parent>
<artifactId>cloud-starter-orm</artifactId>
<dependencies>
<dependency>
<groupId>com.byhah</groupId>
<artifactId>cloud-core</artifactId>
</dependency>
<dependency>
<groupId>com.byhah</groupId>
<artifactId>cloud-constant</artifactId>
</dependency>
<dependency>
<groupId>com.byhah.deploy</groupId>
<artifactId>byhah-deploy-basic-core</artifactId>
</dependency>
<dependency>
<groupId>com.byhah.deploy</groupId>
<artifactId>byhah-deploy-basic-file</artifactId>
</dependency>
<dependency>
<groupId>com.byhah.deploy</groupId>
<artifactId>byhah-deploy-starter-redisson</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
<version>${sa.token.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>io.github.zlika</groupId>
<artifactId>reproducible-build-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,27 @@
package com.byhah.cloud.autoconfigure;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
public interface IBaseMapper<T> extends BaseMapper<T> {
default QueryChainWrapper<T> queryChain() {
return ChainWrappers.queryChain(this);
}
default LambdaQueryChainWrapper<T> lambdaQueryChain() {
return ChainWrappers.lambdaQueryChain(this);
}
default UpdateChainWrapper<T> updateChain() {
return ChainWrappers.updateChain(this);
}
default LambdaUpdateChainWrapper<T> lambdaUpdateChain() {
return ChainWrappers.lambdaUpdateChain(this);
}
}
@@ -0,0 +1,97 @@
package com.byhah.cloud.autoconfigure;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusPropertiesCustomizer;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.ReplacePlaceholderInnerInterceptor;
import com.byhah.deploy.basic.core.model.GeoPoint;
import com.byhah.deploy.basic.core.model.LongList;
import com.byhah.deploy.basic.core.model.StringList;
import com.byhah.cloud.autoconfigure.auto.TableInfoInitHandler;
import com.byhah.cloud.autoconfigure.properties.CloudProperties;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.util.ArrayList;
@Configuration
@MapperScan("com.byhah.cloud.orm.mapper")
@AutoConfiguration(before = MybatisPlusAutoConfiguration.class, after = DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(CloudProperties.class)
public class OrmAutoConfiguration {
private static final String CREATED_AT = "createdAt";
private static final String CREATED_BY = "createdBy";
private static final String UPDATED_AT = "updatedAt";
private static final String UPDATED_BY = "updatedBy";
@Bean
public MybatisPlusPropertiesCustomizer mybatisPlusProperties() {
return i -> i.getGlobalConfig().setEnableSqlRunner(true).getDbConfig()
.setLogicDeleteValue("true")
.setLogicNotDeleteValue("false")
.setReplacePlaceholder(false).setEscapeSymbol("\"");
}
@Bean
ConfigurationCustomizer configurationCustomizer() {
return i -> {
TypeHandlerRegistry registry = i.getTypeHandlerRegistry();
registry.register(GeoPoint.class, PgJsonTypeHandler.class);
registry.register(StringList.class, PgJsonTypeHandler.class);
registry.register(LongList.class, PgJsonTypeHandler.class);
registry.register(ArrayList.class, PgJsonbTypeHandler.class);
};
}
@Bean
TableInfoInitHandler tableInfoInitHandler() {
return new TableInfoInitHandler();
}
@Bean
public MetaObjectHandler metaObjectHandler() {
return new MetaObjectHandler() {
@Override
public void insertFill(MetaObject metaObject) {
fillStrategy(metaObject, CREATED_AT, LocalDateTime.now());
fillStrategy(metaObject, CREATED_BY, getUserId());
}
@Override
public void updateFill(MetaObject metaObject) {
setFieldValByName(UPDATED_AT, LocalDateTime.now(), metaObject);
setFieldValByName(UPDATED_BY, getUserId(), metaObject);
}
private Long getUserId() {
try {
return StpUtil.getLoginIdAsLong();
} catch (Exception e) {
return -1L;
}
}
};
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new ReplacePlaceholderInnerInterceptor("\""));
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
return interceptor;
}
}
@@ -0,0 +1,12 @@
package com.byhah.cloud.autoconfigure;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.byhah.cloud.orm.support.service")
@AutoConfigureAfter({OrmAutoConfiguration.class, MybatisPlusAutoConfiguration.class})
public class OrmSupportAutoConfiguration {
}
@@ -0,0 +1,71 @@
package com.byhah.cloud.autoconfigure;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.postgresql.util.PGobject;
import org.springframework.util.StringUtils;
import com.byhah.deploy.basic.core.utils.Asserts;
import com.byhah.deploy.basic.jackson.util.JacksonUtil;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@Slf4j
public class PgJsonTypeHandler extends BaseTypeHandler<Object> {
private final Class<?> type;
public PgJsonTypeHandler(Class<?> type) {
if (log.isDebugEnabled()) {
log.debug("PgJsonTypeHandler(" + type + ")");
}
Asserts.notNull(type, "Type argument cannot be null");
this.type = type;
}
@Override
public void setNonNullParameter(PreparedStatement statement, int i, Object t, JdbcType jdbcType) throws SQLException {
PGobject object = new PGobject();
object.setType("json");
object.setValue(toJson(t));
statement.setObject(i, object);
}
@Override
public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
return parse(rs.getString(columnName));
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return parse(rs.getString(columnIndex));
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return parse(cs.getString(columnIndex));
}
protected String toJson(Object t) {
try {
return JacksonUtil.toJson(t);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
protected Object parse(String json) {
if (StringUtils.hasText(json)) {
try {
return JacksonUtil.fromJson(json, type);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
return null;
}
}
@@ -0,0 +1,71 @@
package com.byhah.cloud.autoconfigure;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.postgresql.util.PGobject;
import org.springframework.util.StringUtils;
import com.byhah.deploy.basic.core.utils.Asserts;
import com.byhah.deploy.basic.jackson.util.JacksonUtil;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@Slf4j
public class PgJsonbTypeHandler extends BaseTypeHandler<Object> {
private final Class<?> type;
public PgJsonbTypeHandler(Class<?> type) {
if (log.isDebugEnabled()) {
log.debug("PgJsonbTypeHandler(" + type + ")");
}
Asserts.notNull(type, "Type argument cannot be null");
this.type = type;
}
@Override
public void setNonNullParameter(PreparedStatement statement, int i, Object t, JdbcType jdbcType) throws SQLException {
PGobject object = new PGobject();
object.setType("jsonb");
object.setValue(toJson(t));
statement.setObject(i, object);
}
@Override
public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
return parse(rs.getString(columnName));
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return parse(rs.getString(columnIndex));
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return parse(cs.getString(columnIndex));
}
protected String toJson(Object t) {
try {
return JacksonUtil.toJson(t);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
protected Object parse(String json) {
if (StringUtils.hasText(json)) {
try {
return JacksonUtil.fromJson(json, type);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
return null;
}
}
@@ -0,0 +1,24 @@
package com.byhah.cloud.autoconfigure.auto;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.ResolvableType;
public class EndAutoTableApplicationListener implements GenericApplicationListener {
@Override
public boolean supportsEventType(ResolvableType eventType) {
return eventType.getRawClass().isAssignableFrom(ApplicationStartedEvent.class);
}
/**
* 自动建表
*
* @param event
*/
@Override
public void onApplicationEvent(ApplicationEvent event) {
TableInfoInitHandler.initClear();
}
}
@@ -0,0 +1,172 @@
package com.byhah.cloud.autoconfigure.auto;
import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.MetaDataAccessException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.CollectionUtils;
import com.byhah.cloud.autoconfigure.PgJsonTypeHandler;
import com.byhah.cloud.autoconfigure.PgJsonbTypeHandler;
import com.byhah.deploy.basic.core.annotation.PgJson;
import com.byhah.deploy.basic.core.annotation.PgJsonb;
import com.byhah.deploy.basic.core.model.GeoPoint;
import com.byhah.deploy.basic.core.model.LongList;
import com.byhah.deploy.basic.core.model.StringList;
import com.byhah.deploy.basic.core.utils.AutoTableUtils;
import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 自动建表 - 自动新增字段 - 自动创建索引
* 其他的没有比如: 更新字段类型 - 删除字段
*/
@Slf4j
public class TableInfoInitHandler implements PostInitTableInfoHandler {
private static final List<Class<?>> cache = new ArrayList<>();
private static final List<String> tableNames = new ArrayList<>();
private static final List<String> tableIndex = new ArrayList<>();
static DataSourceTransactionManager transactionManager;
static TransactionStatus transactionStatus;
private static boolean init = false;
static {
cache.add(GeoPoint.class);
cache.add(StringList.class);
cache.add(LongList.class);
}
private static void checkColumn(DataSource dataSource, String tableName, Map<String, String> columns, List<String> sqlList) {
Map<String, String> copy = new HashMap<>(columns);
List<String> tableColumns = tableColumns(dataSource, tableName);
tableColumns.forEach(copy::remove);
if (!CollectionUtils.isEmpty(copy)) {
copy.forEach((k, v) -> sqlList.add(String.format(AutoTableUtils.addColumn, tableName, v)));
}
}
private static void inits(DataSource dataSource) {
if (!init) {
init(dataSource);
try {
JdbcUtils.extractDatabaseMetaData(dataSource, i -> {
ResultSet rs = i.getTables(null, "public", null, new String[]{"TABLE", "INDEX"});
while (rs.next()) {
String name = rs.getString(3);
String type = rs.getString(4);
if (type.equals("TABLE")) {
tableNames.add(name);
} else {
if (!name.endsWith("_pk")) {
tableIndex.add(name);
}
}
}
return rs;
});
} catch (MetaDataAccessException e) {
throw new RuntimeException(e);
}
init = true;
}
}
private static List<String> tableColumns(DataSource dataSource, String tableName) {
List<String> list = new ArrayList<>();
try {
JdbcUtils.extractDatabaseMetaData(dataSource, i -> {
ResultSet rs = i.getColumns(null, "public", tableName, null);
while (rs.next()) {
String name = rs.getString(4);
list.add(name);
}
return rs;
});
} catch (MetaDataAccessException e) {
throw new RuntimeException(e);
}
return list;
}
public static void init(DataSource dataSource) {
if (transactionStatus == null) {
log.info("初始化建表事务");
transactionManager = new DataSourceTransactionManager(dataSource);
transactionStatus = transactionManager.getTransaction(null);
}
}
public static void initClear() {
log.info("提交建表事务");
if (transactionStatus != null) {
transactionManager.commit(transactionStatus);
transactionStatus = null;
transactionManager = null;
}
}
@Override
public void postFieldInfo(TableFieldInfo fieldInfo, org.apache.ibatis.session.Configuration configuration) {
Field field = fieldInfo.getField();
if (field.getAnnotation(PgJson.class) != null) {
Class<?> type = field.getType();
if (!cache.contains(type)) {
configuration.getTypeHandlerRegistry().register(type, PgJsonTypeHandler.class);
cache.add(type);
}
} else if (field.getAnnotation(PgJsonb.class) != null) {
Class<?> type = field.getType();
if (!cache.contains(type)) {
configuration.getTypeHandlerRegistry().register(type, PgJsonbTypeHandler.class);
cache.add(type);
}
}
}
@Override
public void postTableInfo(TableInfo tableInfo, org.apache.ibatis.session.Configuration configuration) {
DataSource dataSource = configuration.getEnvironment().getDataSource();
inits(dataSource);
// final Class<?> entity = tableInfo.getEntityType();
// AutoTable autoTable = entity.getAnnotation(AutoTable.class);
// if (Objects.isNull(autoTable)) return;
// if (autoTable.notProd() && SbProfileUtil.isProd()) return;
String tableName = tableInfo.getTableName();
AutoTableUtils.Result result = AutoTableUtils.autoTable(tableInfo, false);
List<String> sqlList = new ArrayList<>();
if (!tableNames.contains(tableName)) {
sqlList.add(result.getCreateTable());
} else {
checkColumn(dataSource, tableName, result.getColumns(), sqlList);
}
result.getCreateIndexMap().forEach((k, v) -> {
if (!tableIndex.contains(k)) {
sqlList.add(v);
}
});
if (!CollectionUtils.isEmpty(sqlList)) {
Connection connection = DataSourceUtils.getConnection(dataSource);
try (Statement stmt = connection.createStatement()) {
for (String sql : sqlList) {
log.info("执行sql: {}", sql);
stmt.execute(sql);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
@@ -0,0 +1,24 @@
package com.byhah.cloud.autoconfigure.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 通用基础配置
*/
@Data
@ConfigurationProperties(prefix = CloudProperties.PREFIX)
public class CloudProperties {
public static final String PREFIX = "byhah.config";
/**
* 服务域名
*/
private String url;
/**
* 后端接口地址
*/
private String serverUrl;
}
@@ -0,0 +1,8 @@
package com.byhah.cloud.orm.mapper.dev;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.dev.DevConfig;
public interface IDevConfigMapper extends IBaseMapper<DevConfig> {
}
@@ -0,0 +1,12 @@
package com.byhah.cloud.orm.mapper.dev;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.dev.DevDict;
/**
* 字典Mapper接口
*
**/
public interface IDevDictMapper extends IBaseMapper<DevDict> {
}
@@ -0,0 +1,14 @@
package com.byhah.cloud.orm.mapper.dev;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.dev.DevEmail;
/**
* 邮件Mapper接口
*
*
*
**/
public interface IDevEmailMapper extends IBaseMapper<DevEmail> {
}
@@ -0,0 +1,12 @@
package com.byhah.cloud.orm.mapper.dev;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.dev.DevFile;
/**
* 文件Mapper接口
*
**/
public interface IDevFileMapper extends IBaseMapper<DevFile> {
}
@@ -0,0 +1,12 @@
package com.byhah.cloud.orm.mapper.dev;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.dev.DevLog;
/**
* 日志Mapper接口
*
**/
public interface IDevLogMapper extends IBaseMapper<DevLog> {
}
@@ -0,0 +1,11 @@
package com.byhah.cloud.orm.mapper.dev;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.dev.DevMessage;
/**
* 站内信Mapper接口
**/
public interface IDevMessageMapper extends IBaseMapper<DevMessage> {
}
@@ -0,0 +1,7 @@
package com.byhah.cloud.orm.mapper.dev;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.dev.DevMessageUser;
public interface IDevMessageUserMapper extends IBaseMapper<DevMessageUser> {
}
@@ -0,0 +1,12 @@
package com.byhah.cloud.orm.mapper.dev;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.dev.DevSms;
/**
* 短信Mapper接口
*
**/
public interface IDevSmsMapper extends IBaseMapper<DevSms> {
}
@@ -0,0 +1,12 @@
package com.byhah.cloud.orm.mapper.gen;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.gen.GenBasic;
/**
* 代码生成基础Mapper接口
*
**/
public interface GenBasicMapper extends IBaseMapper<GenBasic> {
}
@@ -0,0 +1,12 @@
package com.byhah.cloud.orm.mapper.gen;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.gen.GenConfig;
/**
* 代码生成详细配置Mapper接口
*
**/
public interface GenConfigMapper extends IBaseMapper<GenConfig> {
}
@@ -0,0 +1,12 @@
package com.byhah.cloud.orm.mapper.sys;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.system.SysOrg;
/**
* 组织Mapper接口
*
**/
public interface ISysOrgMapper extends IBaseMapper<SysOrg> {
}
@@ -0,0 +1,14 @@
package com.byhah.cloud.orm.mapper.sys;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.system.SysRelation;
/**
* 关系Mapper接口
*
*
*
**/
public interface ISysRelationMapper extends IBaseMapper<SysRelation> {
}
@@ -0,0 +1,11 @@
package com.byhah.cloud.orm.mapper.sys;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.system.SysResource;
/**
* 按钮Mapper接口
**/
public interface ISysResourceMapper extends IBaseMapper<SysResource> {
}
@@ -0,0 +1,11 @@
package com.byhah.cloud.orm.mapper.sys;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.system.SysRole;
/**
* 角色Mapper接口
**/
public interface ISysRoleMapper extends IBaseMapper<SysRole> {
}
@@ -0,0 +1,7 @@
package com.byhah.cloud.orm.mapper.sys;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.system.SysTenant;
public interface ISysTenantMapper extends IBaseMapper<SysTenant> {
}
@@ -0,0 +1,13 @@
package com.byhah.cloud.orm.mapper.sys;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.system.SysUser;
/**
* 用户Mapper接口
*
**/
public interface ISysUserMapper extends IBaseMapper<SysUser> {
}
@@ -0,0 +1,7 @@
package com.byhah.cloud.orm.mapper.sys;
import com.byhah.cloud.autoconfigure.IBaseMapper;
import com.byhah.cloud.basic.core.entity.system.ThirdPlatform;
public interface IThirdPlatformMapper extends IBaseMapper<ThirdPlatform> {
}
@@ -0,0 +1,227 @@
package com.byhah.cloud.orm.support.service;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.byhah.deploy.basic.core.exception.CommonException;
import com.byhah.deploy.basic.file.client.*;
import com.byhah.cloud.orm.mapper.dev.IDevConfigMapper;
import com.byhah.cloud.orm.mapper.dev.IDevFileMapper;
import com.byhah.cloud.autoconfigure.properties.CloudProperties;
import com.byhah.cloud.basic.constant.CacheKey;
import com.byhah.cloud.basic.core.entity.dev.DevConfig;
import com.byhah.cloud.basic.core.entity.dev.DevFile;
import com.byhah.cloud.basic.core.enums.dev.DevFileEngine;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.awt.*;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import static com.byhah.cloud.basic.constant.ConfigKey.File.*;
import static com.byhah.cloud.basic.constant.ConfigKey.Sys.SYS_DEFAULT_FILE_ENGINE_KEY;
import static com.byhah.cloud.basic.core.enums.dev.DevFileEngine.LOCAL;
@Slf4j
@Service
@RequiredArgsConstructor
public class DevSupportService {
private final IDevFileMapper fileMapper;
private final IDevConfigMapper devConfigMapper;
private final CloudProperties cloudProperties;
private final RedissonClient redissonClient;
private final ConcurrentHashMap<DevFileEngine, FileBaseClient> fileClientMap = new ConcurrentHashMap<>();
public String upload(Integer engine, Boolean isReturnId, Long maxSize, MultipartFile file) throws IOException {
if (Objects.isNull(engine)) {
engine = Integer.valueOf(getConfig(SYS_DEFAULT_FILE_ENGINE_KEY));
}
return storageFile(DevFileEngine.forValue(engine), file.getBytes(), isReturnId, file.getOriginalFilename(), maxSize);
}
/**
* 上传后返回url
*/
public String upload(byte[] bytes, String fileName, Long maxSize) {
return storageFile(DevFileEngine.forValue(Integer.valueOf(getConfig(SYS_DEFAULT_FILE_ENGINE_KEY))), bytes, false, fileName, maxSize);
}
/**
* 存储文件
**/
private String storageFile(DevFileEngine engine, byte[] bytes, boolean returnFileId, String fileName, Long maxSize) {
// 如果引擎为空,默认使用本地
if (ObjectUtil.isEmpty(engine)) {
engine = LOCAL;
}
// 获取bucketName
FileBaseClient client = getFileClientByEngine(engine);
String bucketName = client.getDefaultBucketName();
if (StrUtil.isBlank(bucketName)) {
bucketName = "defaultBucketName";
}
// 验证文件md5
String md5 = SecureUtil.md5().digestHex(bytes);
if (StrUtil.isNotBlank(md5)) {
DevFile existFile = fileMapper.lambdaQueryChain()
.eq(DevFile::getMd5, md5)
.eq(DevFile::getBucket, bucketName)
.eq(DevFile::getEngine, engine)
.list().stream().findFirst().orElse(null);
if (Objects.nonNull(existFile)) {
return returnFileId ? existFile.getId().toString() : existFile.getDownloadPath();
}
}
Long fileId = IdWorker.getId();
DevFile devFile = new DevFile();
devFile.setId(fileId);
String suffix = FileUtil.getSuffix(fileName);
try {
uploadFile(devFile, engine, bytes, suffix, maxSize, client, bucketName);
} catch (Exception e) {
log.error("上传文件错误", e);
throw new CommonException("上传异常");
}
// 保存文件信息
devFile.setMd5(md5);
devFile.setName(fileName);
devFile.setSizeKb(NumberUtil.div(new BigDecimal(bytes.length), BigDecimal.valueOf(1024)).setScale(0, RoundingMode.HALF_UP).longValue());
devFile.setSizeInfo(FileUtil.readableFileSize(bytes.length));
devFile.setObjName(ObjectUtil.isNotEmpty(devFile.getSuffix()) ? fileId + StrUtil.DOT + devFile.getSuffix() : null);
fileMapper.insert(devFile);
return returnFileId ? fileId.toString() : devFile.getDownloadPath();
}
/**
* 上传文件
*/
private DevFile uploadFile(DevFile file, DevFileEngine engine, byte[] bytes,
String suffix, Long maxSize, FileBaseClient client, String bucketName) {
if (Objects.isNull(file.getId())) {
file.setId(IdWorker.getId());
}
// 如果是图片,则压缩
if (ObjectUtil.isNotEmpty(suffix) && com.byhah.cloud.basic.core.util.FileUtil.isPic(suffix)) {
if (Objects.nonNull(maxSize)) {
try {
while (bytes.length > maxSize) {
Image image = ImgUtil.scale(ImgUtil.toImage(bytes), 0.9f);
bytes = ImgUtil.toBytes(image, suffix);
}
} catch (Exception ignored) {
log.error("e", ignored);
}
}
}
String filePath = genFileKey(file.getId(), suffix);
String storageUrl = client.storageFileWithReturnUrl(bucketName, filePath, bytes);
file.setBucket(bucketName);
file.setStoragePath(storageUrl);
file.setEngine(engine);
file.setSuffix(suffix);
if (engine == LOCAL) {
String apiUrl = cloudProperties.getServerUrl();
if (ObjectUtil.isEmpty(apiUrl)) {
throw new CommonException("后端域名地址未正确配置:quick.config.common.server-url为空");
}
file.setDownloadPath(apiUrl + "/dev/file/download?id=" + file.getId());
} else {
// 阿里云、腾讯云、MINIO可以直接使用存储地址(公网)作为下载地址
file.setDownloadPath(storageUrl);
}
return file;
}
public String genFileKey(Long fileId, String fileSuffix) {
LocalDate now = LocalDate.now();
// 生成文件的对象名称,格式如:1377109572375810050.docx
String fileObjectName = fileId + StrUtil.DOT + fileSuffix;
// 获取日期文件夹,格式如,2021/10/11/
String dateFolderPath = now.getYear() + StrUtil.SLASH + now.getMonthValue() + StrUtil.SLASH + now.getDayOfMonth() + StrUtil.SLASH;
// 返回
return dateFolderPath + fileObjectName;
}
public FileBaseClient getFileClientByEngine() {
DevFileEngine engine = DevFileEngine.forValue(Integer.valueOf(getConfig(SYS_DEFAULT_FILE_ENGINE_KEY)));
if (Objects.isNull(engine)) {
engine = LOCAL;
}
return getFileClientByEngine(engine);
}
private FileBaseClient getFileClientByEngine(DevFileEngine engine) {
FileBaseClient client = fileClientMap.get(engine);
if (client == null) {
if (engine == LOCAL) {
String folder = getConfig(FILE_LOCAL_FOLDER_KEY);
String bucketName = getConfig(FILE_LOCAL_DEFAULT_BUCKET_NAME);
client = new FileLocalClient(folder, bucketName);
} else if (engine == DevFileEngine.ALIYUN) {
String accessKey = getConfig(FILE_ALIYUN_ACCESS_KEY_ID_KEY);
String accessSecret = getConfig(FILE_ALIYUN_ACCESS_KEY_SECRET_KEY);
String endpoint = getConfig(FILE_ALIYUN_END_POINT_KEY);
String bucketName = getConfig(FILE_ALIYUN_DEFAULT_BUCKET_NAME);
client = new FileAliyunClient(accessKey, accessSecret, endpoint, bucketName);
} else if (engine == DevFileEngine.TENCENT) {
String secretId = getConfig(FILE_TENCENT_SECRET_ID_KEY);
String secretKey = getConfig(FILE_TENCENT_SECRET_KEY_KEY);
String regionId = getConfig(FILE_TENCENT_REGION_ID_KEY);
String bucketName = getConfig(FILE_TENCENT_DEFAULT_BUCKET_NAME);
client = new FileTencentClient(secretId, secretKey, regionId, bucketName);
} else if (engine == DevFileEngine.MINIO) {
String accessKey = getConfig(FILE_MINIO_ACCESS_KEY_KEY);
String accessSecret = getConfig(FILE_MINIO_SECRET_KEY_KEY);
String endpoint = getConfig(FILE_MINIO_END_POINT_KEY);
String bucketName = getConfig(FILE_MINIO_DEFAULT_BUCKET_NAME);
client = new FileMinIoClient(accessKey, accessSecret, endpoint, bucketName);
}
fileClientMap.put(engine, client);
}
return client;
}
/**
* 获取配置
*/
public String getConfig(String key) {
// 从缓存中取
RMapCache<String, String> mapCache = redissonClient.getMapCache(CacheKey.CONFIG_CACHE_KEY);
String cacheValue = mapCache.get(key);
if (CharSequenceUtil.isNotBlank(cacheValue)) {
return cacheValue;
}
DevConfig config = devConfigMapper.lambdaQueryChain().eq(DevConfig::getConfigKey, key).one();
if (ObjectUtil.isNotEmpty(config)) {
// 更新到缓存
mapCache.put(key, config.getConfigValue());
return config.getConfigValue();
}
return null;
}
/**
* 移除配置缓存
*/
public void removeConfigCache(String key) {
redissonClient.getMapCache(CacheKey.CONFIG_CACHE_KEY).remove(key);
}
}
@@ -0,0 +1,8 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.byhah.cloud.autoconfigure.OrmAutoConfiguration,\
com.byhah.cloud.autoconfigure.OrmSupportAutoConfiguration
# Application Listeners
org.springframework.context.ApplicationListener=\
com.byhah.cloud.autoconfigure.auto.EndAutoTableApplicationListener
@@ -0,0 +1,2 @@
com.byhah.cloud.autoconfigure.OrmAutoConfiguration
com.byhah.cloud.autoconfigure.OrmSupportAutoConfiguration