feat(tenant): 初始化新租户的默认MinIO上传配置

- 在TenantServiceImpl中新增initDefaultUploadSetting方法
- 判断租户是否已有upload配置,避免重复写入
- 创建默认MinIO配置内容并插入数据库
- 预热redis上传配置缓存,提升首次读取性能
- 自动建桶并应用匿名只读策略,失败不阻断流程
- 在租户菜单授权成功后调用初始化方法
- SettingMapper新增countByKeyAndTenant和insertSetting方法,绕过多租户插件执行数据库操作
This commit is contained in:
2026-08-08 14:21:01 +08:00
parent 70bd81c45d
commit 0fda607612
2 changed files with 76 additions and 0 deletions
@@ -5,7 +5,9 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gxwebsoft.common.system.entity.Setting; import com.gxwebsoft.common.system.entity.Setting;
import com.gxwebsoft.common.system.param.SettingParam; import com.gxwebsoft.common.system.param.SettingParam;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List; import java.util.List;
@@ -37,4 +39,20 @@ public interface SettingMapper extends BaseMapper<Setting> {
@InterceptorIgnore(tenantLine = "true") @InterceptorIgnore(tenantLine = "true")
Setting getBySettingKeyIgnore(@Param("param") SettingParam param); Setting getBySettingKeyIgnore(@Param("param") SettingParam param);
/**
* 统计指定租户下某 setting_key 的记录数(绕过多租户插件,自行按 tenant_id 过滤)。
* 用于初始化时幂等判断,避免被上下文租户覆盖。
*/
@InterceptorIgnore(tenantLine = "true")
@Select("SELECT COUNT(1) FROM sys_setting WHERE setting_key = #{settingKey} AND tenant_id = #{tenantId} AND deleted = 0")
int countByKeyAndTenant(@Param("settingKey") String settingKey, @Param("tenantId") Integer tenantId);
/**
* 显式插入系统设置(绕过多租户插件,tenant_id 由实体携带,确保写入目标租户)。
*/
@InterceptorIgnore(tenantLine = "true")
@Insert("INSERT INTO sys_setting (setting_key, content, sort_number, comments, tenant_id, create_time, update_time, deleted) "
+ "VALUES (#{settingKey}, #{content}, #{sortNumber}, #{comments}, #{tenantId}, #{createTime}, #{updateTime}, 0)")
int insertSetting(Setting setting);
} }
@@ -7,11 +7,13 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.Constants; import com.gxwebsoft.common.core.Constants;
import com.gxwebsoft.common.core.utils.CommonUtil; import com.gxwebsoft.common.core.utils.CommonUtil;
import com.gxwebsoft.common.core.utils.DomainUtil; import com.gxwebsoft.common.core.utils.DomainUtil;
import com.gxwebsoft.common.core.utils.MinioUtil;
import com.gxwebsoft.common.core.utils.RedisUtil; import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.system.entity.*; import com.gxwebsoft.common.system.entity.*;
import com.gxwebsoft.common.system.mapper.MenuMapper; import com.gxwebsoft.common.system.mapper.MenuMapper;
import com.gxwebsoft.common.system.mapper.RoleMapper; import com.gxwebsoft.common.system.mapper.RoleMapper;
import com.gxwebsoft.common.system.mapper.RoleMenuMapper; import com.gxwebsoft.common.system.mapper.RoleMenuMapper;
import com.gxwebsoft.common.system.mapper.SettingMapper;
import com.gxwebsoft.common.system.mapper.UserRoleMapper; import com.gxwebsoft.common.system.mapper.UserRoleMapper;
import com.gxwebsoft.common.system.mapper.TenantMapper; import com.gxwebsoft.common.system.mapper.TenantMapper;
import com.gxwebsoft.common.system.param.MenuParam; import com.gxwebsoft.common.system.param.MenuParam;
@@ -21,6 +23,7 @@ import com.gxwebsoft.common.system.param.TenantParam;
import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.core.web.PageResult;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@@ -28,6 +31,7 @@ import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -73,6 +77,8 @@ import java.util.stream.Collectors;
private UserService userService; private UserService userService;
@Resource @Resource
private RedisUtil redisUtil; private RedisUtil redisUtil;
@Resource
private SettingMapper settingMapper;
@Override @Override
public PageResult<Tenant> pageRel(TenantParam param) { public PageResult<Tenant> pageRel(TenantParam param) {
@@ -616,6 +622,12 @@ import java.util.stream.Collectors;
// 添加菜单ID到超级管理员所属角色ID // 添加菜单ID到超级管理员所属角色ID
if (resultMenu) { if (resultMenu) {
saveRedis(company); saveRedis(company);
// 初始化默认 MinIO 云存储配置(oss-{tenantId} 桶 + 自动建桶/策略),失败不阻断开通
try {
initDefaultUploadSetting(company.getTid());
} catch (Exception e) {
System.out.println("[Tenant] 初始化默认上传配置失败(不影响开通): " + e.getMessage());
}
} }
} }
@@ -667,6 +679,52 @@ import java.util.stream.Collectors;
redisUtil.set(key, tenant); redisUtil.set(key, tenant);
} }
/**
* 为新开通的租户写入默认 MinIO 上传配置,并自动建桶 + 匿名只读策略。
* 幂等:若已存在 upload 配置则跳过。
*/
private void initDefaultUploadSetting(Integer tenantId) {
// 绕过多租户插件,按目标租户精确计数,避免被上下文租户覆盖
int exists = settingMapper.countByKeyAndTenant("upload", tenantId);
if (exists > 0) {
return;
}
final String bucketName = "oss-" + tenantId;
final String endpoint = "https://minio.sitelnk.cn";
final String accessKeyId = "ZB1RK0ARI4XPF0CWA2FF";
final String accessKeySecret = "QPnFBGLmXxto1lBEiaTyXgf4E4H46e8TOR4sYyiY";
JSONObject content = new JSONObject();
content.put("settingKey", "upload");
content.put("uploadMethod", "minio");
content.put("bucketName", bucketName);
content.put("bucketEndpoint", endpoint);
content.put("accessKeyId", accessKeyId);
content.put("accessKeySecret", accessKeySecret);
content.put("bucketDomain", endpoint + "/" + bucketName);
Setting setting = new Setting();
setting.setSettingKey("upload");
setting.setContent(content.toJSONString());
setting.setSortNumber(0);
setting.setComments("默认MinIO存储");
setting.setTenantId(tenantId);
setting.setDeleted(0);
setting.setCreateTime(new Date());
setting.setUpdateTime(new Date());
settingMapper.insertSetting(setting);
// 预热上传配置缓存,避免新租户首次读取命中空缓存
redisUtil.set("Upload:" + tenantId, content);
// 自动建桶 + 匿名只读策略(失败不阻断开通)
try {
MinioUtil.ensureBucketReady(MinioUtil.buildClient(endpoint, accessKeyId, accessKeySecret), endpoint, bucketName);
} catch (Exception e) {
System.out.println("[Tenant] 初始化时自动建桶失败(忽略): " + e.getMessage());
}
}
@Override @Override
public boolean destructionAll(Integer tenantId){ public boolean destructionAll(Integer tenantId){
if (baseMapper.destructionAll(tenantId)) { if (baseMapper.destructionAll(tenantId)) {