From 0fda607612e84917f951f8d6671a4613ebf40b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Sat, 8 Aug 2026 14:21:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(tenant):=20=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E6=96=B0=E7=A7=9F=E6=88=B7=E7=9A=84=E9=BB=98=E8=AE=A4MinIO?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在TenantServiceImpl中新增initDefaultUploadSetting方法 - 判断租户是否已有upload配置,避免重复写入 - 创建默认MinIO配置内容并插入数据库 - 预热redis上传配置缓存,提升首次读取性能 - 自动建桶并应用匿名只读策略,失败不阻断流程 - 在租户菜单授权成功后调用初始化方法 - SettingMapper新增countByKeyAndTenant和insertSetting方法,绕过多租户插件执行数据库操作 --- .../common/system/mapper/SettingMapper.java | 18 ++++++ .../service/impl/TenantServiceImpl.java | 58 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/main/java/com/gxwebsoft/common/system/mapper/SettingMapper.java b/src/main/java/com/gxwebsoft/common/system/mapper/SettingMapper.java index ffc7f55..1045814 100644 --- a/src/main/java/com/gxwebsoft/common/system/mapper/SettingMapper.java +++ b/src/main/java/com/gxwebsoft/common/system/mapper/SettingMapper.java @@ -5,7 +5,9 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.gxwebsoft.common.system.entity.Setting; import com.gxwebsoft.common.system.param.SettingParam; +import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import java.util.List; @@ -37,4 +39,20 @@ public interface SettingMapper extends BaseMapper { @InterceptorIgnore(tenantLine = "true") 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); } diff --git a/src/main/java/com/gxwebsoft/common/system/service/impl/TenantServiceImpl.java b/src/main/java/com/gxwebsoft/common/system/service/impl/TenantServiceImpl.java index efe09ae..8eefa20 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/impl/TenantServiceImpl.java +++ b/src/main/java/com/gxwebsoft/common/system/service/impl/TenantServiceImpl.java @@ -7,11 +7,13 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gxwebsoft.common.core.Constants; import com.gxwebsoft.common.core.utils.CommonUtil; 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.system.entity.*; import com.gxwebsoft.common.system.mapper.MenuMapper; import com.gxwebsoft.common.system.mapper.RoleMapper; 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.TenantMapper; 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.PageParam; import com.gxwebsoft.common.core.web.PageResult; +import com.alibaba.fastjson.JSONObject; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; @@ -28,6 +31,7 @@ import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.util.ArrayList; import java.util.Comparator; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -73,6 +77,8 @@ import java.util.stream.Collectors; private UserService userService; @Resource private RedisUtil redisUtil; + @Resource + private SettingMapper settingMapper; @Override public PageResult pageRel(TenantParam param) { @@ -616,6 +622,12 @@ import java.util.stream.Collectors; // 添加菜单ID到超级管理员所属角色ID if (resultMenu) { 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); } + /** + * 为新开通的租户写入默认 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 public boolean destructionAll(Integer tenantId){ if (baseMapper.destructionAll(tenantId)) {