yh 1 месяц назад
Родитель
Сommit
b47d3e2c21

+ 1 - 1
fs-common/src/main/java/com/fs/common/utils/CloudHostUtils.java

@@ -2,11 +2,11 @@ package com.fs.common.utils;
 
 
 import com.fs.common.utils.spring.SpringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 
 import java.util.Arrays;
 
 public class CloudHostUtils {
-
     /**
      * 是否指定项目名称配置
      */

+ 1 - 0
fs-company-app/src/main/java/com/fs/app/utils/JwtUtils.java

@@ -115,6 +115,7 @@ public class JwtUtils {
     }
 
     public String getHeader() {
+        getJwtConfig();
         return header;
     }
 

+ 32 - 36
fs-company-app/src/main/java/com/fs/core/filter/CompanySaasContextFilter.java

@@ -3,6 +3,7 @@ package com.fs.core.filter;
 import com.alibaba.fastjson.JSONObject;
 import com.fs.app.utils.JwtUtils;
 import com.fs.common.enums.DataSourceType;
+import com.fs.common.utils.StringUtils;
 import com.fs.config.saas.ProjectConfig;
 import com.fs.core.config.TenantConfigContext;
 import com.fs.core.datasource.DynamicDataSourceContextHolder;
@@ -14,7 +15,6 @@ import com.fs.tenant.domain.TenantInfo;
 import com.fs.tenant.service.TenantInfoService;
 import io.jsonwebtoken.Claims;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.annotation.Order;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -45,17 +45,20 @@ public class CompanySaasContextFilter extends OncePerRequestFilter {
     private TenantDataSourceManager tenantDataSourceManager;
     @Resource
     private SysConfigMapper sysConfigMapper;
+    /**
+     * 租户编码请求头名称。
+     * 前端需在每次请求中携带该头,才能启用多租户能力。
+     */
+    public static final String HEADER_TENANT_CODE = "X-Tenant-Code";
 
     @Override
     protected void doFilterInternal(HttpServletRequest request,
                                     HttpServletResponse response,
                                     FilterChain chain) throws ServletException, IOException {
+        String tenantCode = request.getHeader(HEADER_TENANT_CODE);
+
         try {
-            String loginPath = "/app/user/login";
-            AntPathMatcher pathMatcher = new AntPathMatcher();
-            String requestPath = request.getRequestURI();
-            // 登录接口,直接放行,不执行后续租户逻辑
-            if (pathMatcher.match(loginPath, requestPath)) {
+            if (StringUtils.isBlank(tenantCode)) {
                 SysConfig cfg = sysConfigMapper.selectConfigByConfigKey("projectConfig");
                 if (cfg != null && com.fs.common.utils.StringUtils.isNotBlank(cfg.getConfigValue())) {
                     TenantConfigContext.set(JSONObject.parseObject(cfg.getConfigValue()));
@@ -64,48 +67,40 @@ public class CompanySaasContextFilter extends OncePerRequestFilter {
                 }
                 ProjectConfig.loadTenantConfigsFromContext();
 
+                // 未携带租户编码:保持单库行为,直接放行
                 chain.doFilter(request, response);
                 return;
             }
-            String token = request.getHeader(jwtUtils.getHeader());
+            // Step 1:切到主库,根据 tenantCode 查询租户信息
+            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
 
-            Long tenantId = null;
-            if (StringUtils.isNotBlank(token)) {
-                Claims claims = jwtUtils.getClaimByToken(token);
-                if (claims != null && !jwtUtils.isTokenExpired(claims.getExpiration())) {
-                    tenantId = jwtUtils.getTenantId(claims);
-                }
-            }
-            // 2) 不做租户切换
-            if (tenantId == null) {
+            TenantInfo query = new TenantInfo();
+            query.setTenantCode(tenantCode);
+            List<TenantInfo> tenants = tenantInfoService.selectTenantInfoList(query);
+            if (tenants == null || tenants.isEmpty()) {
+                log.warn("[SaaS user-app] 请求携带的 tenantCode={} 未找到租户记录,按单库模式处理。", tenantCode);
                 chain.doFilter(request, response);
                 return;
             }
-            // 3) 切到主库查 tenant_info(拿到租户库连接信息)
-            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
-            TenantInfo tenant = null;
-            if (tenantId != null) {
-                tenant = tenantInfoService.selectTenantInfoById(String.valueOf(tenantId));
-                if (tenant == null) {
-                    log.warn("[SaaS company-app] tenantId={} 未找到租户", tenantId);
-                    chain.doFilter(request, response);
-                    return;
-                }
-            }
-            // 4) 基础校验
+
+            TenantInfo tenant = tenants.get(0);
+
+            // 基础校验:启用状态 + 未过期
             if (!Integer.valueOf(1).equals(tenant.getStatus())) {
-                log.warn("[SaaS company-app] tenantId={}, tenantCode={} 已禁用", tenant.getId(), tenant.getTenantCode());
+                log.warn("[SaaS user-app] 租户 tenantCode={} 已禁用,按单库模式处理。", tenantCode);
                 chain.doFilter(request, response);
                 return;
             }
             if (tenant.getExpireTime() != null && tenant.getExpireTime().before(new Date())) {
-                log.warn("[SaaS company-app] tenantId={}, tenantCode={} 已过期", tenant.getId(), tenant.getTenantCode());
+                log.warn("[SaaS user-app] 租户 tenantCode={} 已过期,按单库模式处理。", tenantCode);
                 chain.doFilter(request, response);
                 return;
             }
-            // 5) 切租户库
+
+            // Step 2:切换到租户库(如无则动态创建数据源)
             tenantDataSourceManager.switchTenant(tenant);
-            // 6) 加载租户 projectConfig -> TenantConfigContext
+
+            // Step 3:从租户库读取项目配置(projectConfig),填充 TenantConfigContext 和 ProjectConfig
             SysConfig cfg = sysConfigMapper.selectConfigByConfigKey("projectConfig");
             if (cfg != null && StringUtils.isNotBlank(cfg.getConfigValue())) {
                 TenantConfigContext.set(JSONObject.parseObject(cfg.getConfigValue()));
@@ -113,14 +108,15 @@ public class CompanySaasContextFilter extends OncePerRequestFilter {
                 TenantConfigContext.set(null);
             }
             ProjectConfig.loadTenantConfigsFromContext();
-            // 8) 设置 SecurityContext(Redis Key 自动租户前缀依赖这里)
+
+            // 设置租户到 SecurityContext,供 TenantKeyRedisSerializer 自动为 Redis Key 加 tenantid 前缀
             SecurityContextHolder.getContext().setAuthentication(
                     new UsernamePasswordAuthenticationToken(
                             new TenantPrincipal(tenant.getId()),
                             null,
-                            Collections.emptyList()
-                    )
-            );
+                            Collections.emptyList()));
+
+            // Step 4:继续后续过滤器链和业务处理(此时 Mapper 操作的就是当前租户库)
             chain.doFilter(request, response);
         } finally {
             try {

+ 16 - 0
fs-service/src/main/java/com/fs/config/cloud/CloudHostProper.java

@@ -1,11 +1,15 @@
 package com.fs.config.cloud;
 
 import com.alibaba.fastjson.JSONObject;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.spring.SpringUtils;
 import com.fs.config.saas.ProjectConfig;
 import com.fs.core.config.TenantConfigContext;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import java.util.Arrays;
+
 /**
  * 云托管相关配置访问器。
  *
@@ -128,5 +132,17 @@ public class CloudHostProper {
         }
         return null;
     }
+
+    public boolean hasCloudHostName(String... cloudHostName) {
+        String companyName = getCompanyName();
+        if (StringUtils.isBlank(companyName)) {
+            return false;
+        }
+
+        return Arrays.stream(cloudHostName)
+                .filter(StringUtils::isNotBlank)
+                .map(String::trim)
+                .anyMatch(name -> companyName.equalsIgnoreCase(name.trim()));
+    }
 }
 

+ 3 - 1
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -31,6 +31,7 @@ import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.company.service.ICompanyService;
 import com.fs.config.cloud.CloudHostProper;
 import com.fs.config.saas.ProjectConfig;
+import com.fs.core.config.TenantConfigContext;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.config.RandomRedPacketConfig;
 import com.fs.course.config.RandomRedPacketRule;
@@ -2635,8 +2636,9 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
                 return ResponseResult.fail(ExceptionCodeEnum.WATCH_LATEST_COURSE.getCode(), ExceptionCodeEnum.WATCH_LATEST_COURSE.getDescription());
             }
         }
+        String companyName = TenantConfigContext.getString("cloud_host.company_name");
         // 项目看课数限制
-        if (!EXCLUDE_PROJECTS.contains(projectConfig.getCloudHost().getCompanyName()) && !CloudHostUtils.hasCloudHostName("弘德堂")) {
+        if (!EXCLUDE_PROJECTS.contains(companyName) && !cloudHostProper.hasCloudHostName("弘德堂")) {
             log.error("进入了看课限制:传入参数:={},watchCourseVideo={}",param, watchCourseVideo);
             Integer logCount = fsUserCourseMapper.selectTodayCourseWatchLogCountByUserIdAndProjectId(param.getUserId(), courseProject);
             if (CloudHostUtils.hasCloudHostName("四川致医") ) {

+ 1 - 17
fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

@@ -3,20 +3,14 @@ package com.fs.app.controller.course;
 
 
 import cn.hutool.core.util.ObjectUtil;
-import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.app.annotation.UserOperationLog;
 import com.fs.app.controller.AppBaseController;
-import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
-import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.app.annotation.Login;
-import com.fs.common.core.domain.model.LoginUser;
-import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.CloudHostUtils;
-import com.fs.common.utils.SecurityUtils;
-import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.config.cloud.CloudHostProper;
 import com.fs.course.param.*;
 import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
@@ -26,22 +20,15 @@ import com.fs.course.vo.FsUserCourseVideoH5VO;
 import com.fs.course.vo.newfs.FsUserCourseVideoLinkDetailsVO;
 import com.fs.his.domain.FsUser;
 import com.fs.his.enums.FsUserOperationEnum;
-import com.fs.im.dto.OpenImResponseDTO;
-import com.fs.im.service.OpenIMService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
 
 import javax.validation.Valid;
-import java.util.HashMap;
-import java.util.Map;
 
 @Api("会员-看课接口")
 @RestController
@@ -97,9 +84,6 @@ public class CourseFsUserController extends AppBaseController {
     public R getCourseByVideoId(@RequestParam("videoId") Long videoId)
     {
         FsUserCourseVideoH5VO course = courseService.selectFsUserCourseVideoH5VOByVideoId(videoId);
-        if (CloudHostUtils.hasCloudHostName("金牛明医","康年堂")){
-            course.setCourseName("");
-        }
         return R.ok().put("data",course);
     }
 

+ 1 - 0
fs-user-app/src/main/java/com/fs/app/utils/JwtUtils.java

@@ -97,6 +97,7 @@ public class JwtUtils {
     }
 
     public String getHeader() {
+        getJwtConfig();
         return header;
     }