Просмотр исходного кода

处理小程序修改配置后不同步问题和配置清空无效问题

yjwang 21 часов назад
Родитель
Сommit
a7f2096012

+ 34 - 4
fs-admin/src/main/java/com/fs/course/controller/FsCoursePlaySourceConfigController.java

@@ -3,8 +3,6 @@ package com.fs.course.controller;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -12,12 +10,13 @@ import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
-import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.bean.BeanUtils;
+import com.fs.common.utils.spring.SpringUtils;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.enums.MiniProgramAgreementEnum;
@@ -35,14 +34,17 @@ import com.fs.his.service.MiniProgramAgreementService;
 import com.fs.system.service.ISysConfigService;
 import com.github.pagehelper.PageHelper;
 import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
+@Slf4j
 @RestController
 @RequestMapping("/course/playSourceConfig")
 @AllArgsConstructor
@@ -136,6 +138,10 @@ public class FsCoursePlaySourceConfigController extends BaseController {
         config.setCreateTime(LocalDateTime.now());
         config.setUpdateTime(LocalDateTime.now());
         fsCoursePlaySourceConfigService.save(config);
+
+        // 新增配置后直接更新 Redis 缓存
+        refreshRedisCache(config);
+
         return AjaxResult.success();
     }
 
@@ -169,7 +175,11 @@ public class FsCoursePlaySourceConfigController extends BaseController {
         BeanUtils.copyProperties(param, config);
         applyCompanyIds(param.getCompanyId(), param.getCompanyIds(), config);
 
-        fsCoursePlaySourceConfigService.updateById(config);
+        fsCoursePlaySourceConfigService.updateCoursePlaySourceConfig(config);
+
+        // 修改配置后直接更新 Redis 缓存
+        refreshRedisCache(config);
+
         return AjaxResult.success();
     }
 
@@ -349,6 +359,26 @@ public class FsCoursePlaySourceConfigController extends BaseController {
         return AjaxResult.success(result);
     }
 
+    /**
+     * 小程序配置 Redis 缓存 key 前缀(与 WxMaConfiguration 保持一致)
+     */
+    private static final String MINIPROGRAM_CONFIG_CACHE_KEY = "miniprogram:config:";
+
+    /**
+     * 更新 Redis 中的小程序配置缓存,随机 6-8 小时过期
+     */
+    private static void refreshRedisCache(FsCoursePlaySourceConfig config) {
+        try {
+            RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
+            String redisKey = MINIPROGRAM_CONFIG_CACHE_KEY + config.getAppid();
+            int hours = new Random().nextInt(3) + 6;
+            redisCache.setCacheObject(redisKey, config, hours, TimeUnit.HOURS);
+        } catch (Exception e) {
+            // Redis 异常不影响主流程,缓存会在下次读取时自动回源 DB
+            log.warn("更新小程序配置Redis缓存失败,appId: {}", config.getAppid(), e);
+        }
+    }
+
     private static void applyCompanyIds(Long companyId, List<Long> companyIds, FsCoursePlaySourceConfig config) {
         List<Long> ids = new ArrayList<>();
         if (companyIds != null) {

+ 33 - 13
fs-service/src/main/java/com/fs/core/config/WxMaConfiguration.java

@@ -11,8 +11,8 @@ import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.spring.SpringUtils;
-import com.fs.course.config.CourseMaConfig;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.mapper.FsCoursePlaySourceConfigMapper;
 import com.fs.course.service.IFsCoursePlaySourceConfigService;
@@ -25,20 +25,15 @@ import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.error.WxRuntimeException;
-import org.jetbrains.annotations.NotNull;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
-import org.yaml.snakeyaml.events.Event;
-
 import javax.annotation.PostConstruct;
 import java.io.File;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -106,22 +101,47 @@ public class WxMaConfiguration {
         log.info("配置加载完毕! 配置文件: {}",JSON.toJSONString(this.properties));
     }
 
+    /**
+     * 小程序配置 Redis 缓存 key 前缀
+     */
+    private static final String MINIPROGRAM_CONFIG_CACHE_KEY = "miniprogram:config:";
+
     public static WxMaService getMaService(String appid) {
-        return maServices.computeIfAbsent(appid,e->{
-            // 查询数据库
+        RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
+        String redisKey = MINIPROGRAM_CONFIG_CACHE_KEY + appid;
+
+        FsCoursePlaySourceConfig config = redisCache.getCacheObject(redisKey);
+
+        if (config == null) {
             FsCoursePlaySourceConfigMapper configMapper = SpringUtils.getBean(FsCoursePlaySourceConfigMapper.class);
             Wrapper<FsCoursePlaySourceConfig> queryWrapper = Wrappers.<FsCoursePlaySourceConfig>lambdaQuery()
                     .eq(FsCoursePlaySourceConfig::getAppid, appid)
                     .eq(FsCoursePlaySourceConfig::getIsDel, 0);
-            FsCoursePlaySourceConfig config = configMapper.selectOne(queryWrapper);
+            config = configMapper.selectOne(queryWrapper);
             if (config == null) {
                 throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
             }
 
-            return getWxMaService(config.getAppid(), config.getSecret(), config.getToken(), config.getAesKey(), config.getMsgDataFormat());
-        });
+            int hours = new Random().nextInt(3) + 6;
+            redisCache.setCacheObject(redisKey, config, hours, TimeUnit.HOURS);
+            log.info("小程序配置缓存未命中,从DB加载并缓存至Redis,appId: {}, 过期时间: {}小时", appid, hours);
+        }
+
+        return getWxMaService(config.getAppid(), config.getSecret(), config.getToken(), config.getAesKey(), config.getMsgDataFormat());
+    }
+
+    /**
+     * 刷新指定 appId 的小程序配置缓存
+     * 场景:线上修改了 fs_course_play_source_config 表的 secret 后,调用此方法清除缓存,
+     * 下次 getMaService 会重新从 DB 加载最新配置,无需重启应用
+     */
+    public static void refreshMaService(String appid) {
+        maServices.remove(appid);
+        routers.remove(appid);
+        log.info("已清除小程序配置缓存,appId: {}", appid);
     }
 
+
     /**
      * 初始化WxMaService
      */

+ 5 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCoursePlaySourceConfigMapper.java

@@ -19,4 +19,9 @@ public interface FsCoursePlaySourceConfigMapper extends BaseMapper<FsCoursePlayS
 
     @DataSource(DataSourceType.SLAVE)
     FsCoursePlaySourceConfig selectCoursePlaySourceConfigByAppId(String appId);
+
+    /**
+     * 更新点播播放源配置,company_ids 不做非空判断,允许置为 NULL
+     */
+    int updateCoursePlaySourceConfig(FsCoursePlaySourceConfig config);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/course/service/IFsCoursePlaySourceConfigService.java

@@ -22,4 +22,9 @@ public interface IFsCoursePlaySourceConfigService extends IService<FsCoursePlayS
      * @return
      */
     FsCoursePlaySourceConfig selectCoursePlaySourceConfigByAppId(String appId);
+
+    /**
+     * 更新点播播放源配置,company_ids 不做非空判断,允许置为 NULL
+     */
+    int updateCoursePlaySourceConfig(FsCoursePlaySourceConfig config);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCoursePlaySourceConfigServiceImpl.java

@@ -40,4 +40,9 @@ public class FsCoursePlaySourceConfigServiceImpl extends ServiceImpl<FsCoursePla
     public FsCoursePlaySourceConfig selectCoursePlaySourceConfigByAppId(String appId) {
         return baseMapper.selectCoursePlaySourceConfigByAppId(appId);
     }
+
+    @Override
+    public int updateCoursePlaySourceConfig(FsCoursePlaySourceConfig config) {
+        return baseMapper.updateCoursePlaySourceConfig(config);
+    }
 }

+ 23 - 0
fs-service/src/main/resources/mapper/course/FsCoursePlaySourceConfigMapper.xml

@@ -62,4 +62,27 @@
         where fcpsc.is_del = 0 and fcpsc.appid= #{appid}
        limit 1
     </select>
+
+    <update id="updateCoursePlaySourceConfig" parameterType="com.fs.course.domain.FsCoursePlaySourceConfig">
+        update fs_course_play_source_config
+        set name = #{name},
+            appid = #{appid},
+            secret = #{secret},
+            img = #{img},
+            original_id = #{originalId},
+            token = #{token},
+            aes_key = #{aesKey},
+            msg_data_format = #{msgDataFormat},
+            type = #{type},
+            company_id = #{companyId},
+            company_ids = #{companyIds},
+            set_company_ids = #{setCompanyIds},
+            is_mall = #{isMall},
+            status = #{status},
+            merchant_config_id = #{merchantConfigId},
+            custom_auth_enabled = #{customAuthEnabled},
+            mini_app_auth_type = #{miniAppAuthType},
+            user_course_auth_domain = #{userCourseAuthDomain}
+        where id = #{id}
+    </update>
 </mapper>