ソースを参照

多小程序配置增加微信商户号配置

cgp 1 週間 前
コミット
b5497ff7df

+ 18 - 0
fs-admin/src/main/java/com/fs/course/controller/FsCoursePlaySourceConfigController.java

@@ -46,12 +46,14 @@ public class FsCoursePlaySourceConfigController extends BaseController {
     @GetMapping("/list")
     public TableDataInfo list(@RequestParam(required = false) String name,
                               @RequestParam(required = false) String appid,
+                              @RequestParam(required = false) String mchId,
                               @RequestParam(required = false) Integer isMall,
                               @RequestParam(required = false, defaultValue = "1") Integer pageNum,
                               @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
         Map<String, Object> params = new HashMap<>();
         params.put("name", name);
         params.put("appid", appid);
+        params.put("mchId", mchId);
         params.put("isMall", isMall);
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
@@ -105,6 +107,7 @@ public class FsCoursePlaySourceConfigController extends BaseController {
         config.setCreateTime(LocalDateTime.now());
         config.setUpdateTime(LocalDateTime.now());
         fsCoursePlaySourceConfigService.save(config);
+        fsCoursePlaySourceConfigService.cacheMchIdByAppId(config.getAppid(),config.getMchId());//缓存所属微信商户号
         return AjaxResult.success();
     }
 
@@ -126,8 +129,17 @@ public class FsCoursePlaySourceConfigController extends BaseController {
             return AjaxResult.error("appid已存在");
         }
 
+        // 如果 appId 发生变化,先删除旧缓存
+        boolean appIdChanged = !config.getAppid().equals(param.getAppid());
+        if (appIdChanged) {
+            fsCoursePlaySourceConfigService.evictMchIdByAppId(config.getAppid());
+        }
+
         BeanUtils.copyProperties(param, config);
         fsCoursePlaySourceConfigService.updateById(config);
+
+        // 写入新缓存(内部已做空值判断)
+        fsCoursePlaySourceConfigService.cacheMchIdByAppId(config.getAppid(), config.getMchId());
         return AjaxResult.success();
     }
 
@@ -135,6 +147,12 @@ public class FsCoursePlaySourceConfigController extends BaseController {
     @Log(title = "点播播放源配置", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids) {
+        for (Long id : ids) {
+            FsCoursePlaySourceConfig config = fsCoursePlaySourceConfigService.getById(id);
+            if (Objects.nonNull(config)) {
+                fsCoursePlaySourceConfigService.evictMchIdByAppId(config.getAppid());//删除缓存
+            }
+        }
         Wrapper<FsCoursePlaySourceConfig> updateWrapper = Wrappers.<FsCoursePlaySourceConfig>lambdaUpdate()
                 .set(FsCoursePlaySourceConfig::getIsDel, 1)
                 .in(FsCoursePlaySourceConfig::getId, Arrays.asList(ids));

+ 21 - 21
fs-admin/src/main/java/com/fs/course/task/CourseWatchLogScheduler.java

@@ -23,26 +23,26 @@ public class CourseWatchLogScheduler {
     private IFsCourseWatchLogService courseWatchLogService;
 
 
-    /**
-     * 检查飞书看课状态
-     * 每分钟执行一次
-     */
-    @Scheduled(fixedRate = 60000)
-    public void checkFeiShuWatchStatus() {
-        if (!redisCache.setIfAbsent("checkFeiShuWatchStatus", "checkFeiShuWatchStatus", 3, TimeUnit.MINUTES)) {
-            log.warn("检查飞书看课中任务执行 - 上一个任务尚未完成,跳过此次执行");
-            return;
-        }
-        try {
-            log.info("检查飞书看课中任务执行>>>>>>>>>>>>");
-            courseWatchLogService.scheduleFeiShuBatchUpdateToDatabase();
-            courseWatchLogService.checkFeiShuWatchStatus();
-            log.info("检查飞书看课中任务执行完成>>>>>>>>>>>>");
-        }catch (Exception e) {
-            log.error("检查飞书看课中任务执行完成 - 定时任务执行失败", e);
-        } finally {
-            redisCache.deleteObject("checkFeiShuWatchStatus");
-        }
-    }
+//    /**
+//     * 检查飞书看课状态
+//     * 每分钟执行一次
+//     */
+//    @Scheduled(fixedRate = 60000)
+//    public void checkFeiShuWatchStatus() {
+//        if (!redisCache.setIfAbsent("checkFeiShuWatchStatus", "checkFeiShuWatchStatus", 3, TimeUnit.MINUTES)) {
+//            log.warn("检查飞书看课中任务执行 - 上一个任务尚未完成,跳过此次执行");
+//            return;
+//        }
+//        try {
+//            log.info("检查飞书看课中任务执行>>>>>>>>>>>>");
+//            courseWatchLogService.scheduleFeiShuBatchUpdateToDatabase();
+//            courseWatchLogService.checkFeiShuWatchStatus();
+//            log.info("检查飞书看课中任务执行完成>>>>>>>>>>>>");
+//        }catch (Exception e) {
+//            log.error("检查飞书看课中任务执行完成 - 定时任务执行失败", e);
+//        } finally {
+//            redisCache.deleteObject("checkFeiShuWatchStatus");
+//        }
+//    }
 
 }

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

@@ -27,6 +27,11 @@ public class FsCoursePlaySourceConfig {
      */
     private String appid;
 
+    /**
+     * 微信商户号mchId
+     * */
+    private String mchId;
+
     /**
      * 小程序/公众号secret
      */

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

@@ -22,4 +22,10 @@ public interface FsCoursePlaySourceConfigMapper extends BaseMapper<FsCoursePlayS
 
     @Select("SELECT appid dict_value1, `name` dict_label FROM fs_course_play_source_config WHERE is_del = 0")
     List<OptionsVO> allList();
+
+    /**
+     * 根据小程序id查询绑定的微信商户号
+     * */
+    @Select("SELECT mch_id FROM fs_course_play_source_config WHERE appid = #{appId}")
+    String selectMchIdByAppId(@Param("appId") String appId);
 }

+ 3 - 0
fs-service/src/main/java/com/fs/course/param/FsCoursePlaySourceConfigEditParam.java

@@ -19,6 +19,9 @@ public class FsCoursePlaySourceConfigEditParam {
     @ApiModelProperty("小程序/公众号appid")
     private String appid;
 
+    @ApiModelProperty("微信商户号mchId")
+    private String mchId;
+
     @ApiModelProperty("小程序/公众号secret")
     private String secret;
 

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

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.vo.FsCoursePlaySourceConfigVO;
 import com.fs.his.vo.OptionsVO;
+import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
 import java.util.Map;
@@ -18,4 +19,19 @@ public interface IFsCoursePlaySourceConfigService extends IService<FsCoursePlayS
     List<OptionsVO> selectCoursePlaySourceConfigVOList();
 
     List<OptionsVO> allList();
+
+    /**
+     * 将 appId 对应的 mchId 写入缓存
+     */
+    void cacheMchIdByAppId(String appId, String mchId);
+
+    /**
+     * 删除 appId 对应的 mchId 缓存
+     */
+    void evictMchIdByAppId(String appId);
+
+    /**
+     * 根据 appId 获取 mchId
+     */
+    public String getMchIdByAppId(String appId);
 }

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

@@ -1,12 +1,15 @@
 package com.fs.course.service.impl;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.mapper.FsCoursePlaySourceConfigMapper;
 import com.fs.course.service.IFsCoursePlaySourceConfigService;
 import com.fs.course.vo.FsCoursePlaySourceConfigVO;
 import com.fs.his.vo.OptionsVO;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.Collections;
@@ -18,6 +21,12 @@ import java.util.Map;
 public class FsCoursePlaySourceConfigServiceImpl extends ServiceImpl<FsCoursePlaySourceConfigMapper, FsCoursePlaySourceConfig>
         implements IFsCoursePlaySourceConfigService {
 
+    @Autowired
+    private RedisCache redisCache;
+
+    /** 多小程序配置缓存所属微信商户号 key 前缀:appId -> mchId */
+    private static final String APPID_MCHID_CACHE_PREFIX = "sys_config:play_source:mchid:";
+
     /**
      * 查询点播配置列表
      */
@@ -35,4 +44,48 @@ public class FsCoursePlaySourceConfigServiceImpl extends ServiceImpl<FsCoursePla
     public List<OptionsVO> allList() {
         return baseMapper.allList();
     }
+
+    @Override
+    public void cacheMchIdByAppId(String appId, String mchId) {
+        if (StringUtils.isBlank(appId) || StringUtils.isBlank(mchId)) {
+            return;
+        }
+        redisCache.setCacheObject(APPID_MCHID_CACHE_PREFIX + appId, mchId);
+    }
+
+    @Override
+    public void evictMchIdByAppId(String appId) {
+        if (StringUtils.isBlank(appId)) {
+            return;
+        }
+        redisCache.deleteObject(APPID_MCHID_CACHE_PREFIX + appId);
+    }
+
+    /**
+     * 根据 appId 获取商户号 mchId(优先从缓存读取)
+     * @param appId 小程序/公众号 appId
+     * @return mchId,可能为 null
+     */
+    @Override
+    public String getMchIdByAppId(String appId) {
+        if (StringUtils.isBlank(appId)) {
+            return null;
+        }
+        String cacheKey = APPID_MCHID_CACHE_PREFIX + appId;
+        // 1. 查缓存
+        String dbMchId = redisCache.getCacheObject(cacheKey);
+        if (dbMchId != null) {
+            return dbMchId;
+        }
+        // 2. 缓存未命中,查数据库(仅查询有效记录)
+        dbMchId = baseMapper.selectMchIdByAppId(appId);
+        if (StringUtils.isBlank(dbMchId)) {
+            return null;
+        }
+        // 3. 回写缓存
+        if (StringUtils.isNotBlank(dbMchId)) {
+            redisCache.setCacheObject(cacheKey, dbMchId);
+        }
+        return dbMchId;
+    }
 }

+ 3 - 0
fs-service/src/main/java/com/fs/course/vo/FsCoursePlaySourceConfigVO.java

@@ -18,6 +18,9 @@ public class FsCoursePlaySourceConfigVO {
     @ApiModelProperty("小程序/公众号appid")
     private String appid;
 
+    @ApiModelProperty("微信商户号mchId")
+    private String mchId;
+
     @ApiModelProperty("小程序/公众号secret")
     private String secret;
 

+ 1 - 1
fs-service/src/main/java/com/fs/feishu/service/FeishuDocApiService.java

@@ -76,7 +76,7 @@ public class FeishuDocApiService {
                                 .index(0)
                                 .children(new Block[]{
                                         Block.newBuilder()
-                                                .blockType(2)
+                                                .blockType(3)
                                                 .text(Text.newBuilder()
                                                         .elements(new TextElement[]{
                                                                 TextElement.newBuilder()

+ 5 - 0
fs-service/src/main/java/com/fs/his/mapper/SysRedpacketConfigMoreMapper.java

@@ -60,4 +60,9 @@ public interface SysRedpacketConfigMoreMapper extends BaseMapper<SysRedpacketCon
      * @return 结果
      */
     int deleteSysRedpacketConfigMoreByIds(Long[] ids);
+
+    /**
+     * 根据商户号id获取商户号配置
+     * */
+    SysRedpacketConfigMore getSysRedpacketConfigByMchId(String mchId);
 }

+ 56 - 5
fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java

@@ -34,10 +34,8 @@ import com.fs.course.config.RedPacketConfig;
 import com.fs.course.service.IFsCourseRedPacketLogService;
 import com.fs.course.service.IFsUserCourseOrderService;
 import com.fs.course.service.IFsUserVipOrderService;
-import com.fs.his.domain.FsPayConfig;
-import com.fs.his.domain.FsStorePayment;
-import com.fs.his.domain.FsUser;
-import com.fs.his.domain.FsUserWx;
+import com.fs.course.service.impl.FsCoursePlaySourceConfigServiceImpl;
+import com.fs.his.domain.*;
 import com.fs.his.dto.PayConfigDTO;
 import com.fs.his.enums.PaymentMethodEnum;
 import com.fs.his.mapper.*;
@@ -91,6 +89,7 @@ import com.google.gson.Gson;
 import com.hc.openapi.tool.fastjson.JSON;
 import io.netty.util.internal.StringUtil;
 import me.chanjar.weixin.common.error.WxErrorException;
+import org.apache.commons.collections4.CollectionUtils;
 import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -107,6 +106,8 @@ import java.lang.reflect.Field;
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 /**
  * 支付明细Service业务层处理
@@ -196,6 +197,12 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
      */
     private static final String REDPACKET_COMPANY_MONEY = "redpacket_money";
 
+
+    /**
+     * 微信商户号对应的配置key
+     * */
+    private static final String WX_MCH_CONFIG_KEY = "sys_config:redPacket.wx_mchConfig:";
+
     /**
      * 用户领取红包限制
      */
@@ -204,6 +211,12 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
     @Autowired
     private RedisCache redisCache;
 
+    @Autowired
+    private SysRedpacketConfigMoreMapper sysRedpacketConfigMoreMapper;
+
+    @Autowired
+    private FsCoursePlaySourceConfigServiceImpl coursePlaySourceConfigService;
+
 
     /**
      * 查询支付明细
@@ -554,7 +567,16 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
                 if (StringUtil.isNullOrEmpty(json) || json.isEmpty()) {
                     json = configService.selectConfigByKey("redPacket.config");
                 }
-                config = JSONUtil.toBean(json, RedPacketConfig.class);
+                //这里开始优先取多小程序配置的所属微信商户号
+                String mchId = coursePlaySourceConfigService.getMchIdByAppId(param.getAppId());
+                if (mchId != null) {
+                    //读取指定的定商户号配置
+                    config=getMchConfig(mchId);
+                    logger.info("发放红包的小程序appId:{},商户号:{}",param.getAppId(),mchId);
+                }else {
+                    //读取默认商户号配置
+                    config = JSONUtil.toBean(json, RedPacketConfig.class);
+                }
                 break;
             case 2:
                 json = companyConfigService.selectRedPacketConfigByKey(param.getCompanyId());
@@ -591,6 +613,35 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
         return result;
     }
 
+    /**
+     * 根据商户号获取商户号配置(单 Key 缓存版)
+     * 缓存 Key 格式:sys_config:redPacket.wx_mchConfig: + mchId
+     * 缓存 Value:RedPacketConfig 对应的 JSON 字符串
+     */
+    public RedPacketConfig getMchConfig(String mchId) {
+        String cacheKey = WX_MCH_CONFIG_KEY + mchId;
+
+        // 1. 从缓存读取 JSON 字符串
+        String json = redisCache.getCacheObject(cacheKey);
+        if (StringUtils.isNotBlank(json)) {
+            return JSONUtil.toBean(json, RedPacketConfig.class);
+        }
+
+        // 2. 缓存未命中,查数据库
+        SysRedpacketConfigMore configMore = sysRedpacketConfigMoreMapper.getSysRedpacketConfigByMchId(mchId);
+        if (configMore == null) {
+            logger.error("商户号配置不存在, mchId: {}", mchId);
+            throw new CustomException("您刚刚发起提现,请稍后再试!!!");
+        }
+
+        // 3. 序列化为 JSON 字符串并存入缓存
+        String configJson = JSONUtil.toJsonStr(configMore);
+        redisCache.setCacheObject(cacheKey, configJson);
+
+        // 4. 返回 RedPacketConfig 对象(利用字段名一致的特性自动映射)
+        return JSONUtil.toBean(configJson, RedPacketConfig.class);
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
     public R sendRedPacketAppReward(WxSendRedPacketParam param) {

+ 57 - 2
fs-service/src/main/java/com/fs/his/service/impl/SysRedpacketConfigMoreServiceImpl.java

@@ -35,6 +35,10 @@ import java.util.stream.IntStream;
 @Service
 public class SysRedpacketConfigMoreServiceImpl extends ServiceImpl<SysRedpacketConfigMoreMapper, SysRedpacketConfigMore> implements ISysRedpacketConfigMoreService {
 
+    /**
+     * 单个微信商户号对应的配置key
+     * */
+    private static final String WX_MCH_CONFIG_KEY = "sys_config:redPacket.wx_mchConfig:";
 
     @Autowired
     private ISysConfigService configService;
@@ -168,7 +172,11 @@ public class SysRedpacketConfigMoreServiceImpl extends ServiceImpl<SysRedpacketC
     @Override
     public int insertSysRedpacketConfigMore(SysRedpacketConfigMore sysRedpacketConfigMore)
     {
-        return baseMapper.insertSysRedpacketConfigMore(sysRedpacketConfigMore);
+        int rows = baseMapper.insertSysRedpacketConfigMore(sysRedpacketConfigMore);
+        if (rows > 0) {
+            refreshMchConfigCache(sysRedpacketConfigMore);   // 写入缓存
+        }
+        return rows;
     }
 
     /**
@@ -180,7 +188,16 @@ public class SysRedpacketConfigMoreServiceImpl extends ServiceImpl<SysRedpacketC
     @Override
     public int updateSysRedpacketConfigMore(SysRedpacketConfigMore sysRedpacketConfigMore)
     {
-        return baseMapper.updateSysRedpacketConfigMore(sysRedpacketConfigMore);
+        // 若允许修改 mchId,先查旧记录,删除旧缓存
+        SysRedpacketConfigMore old = baseMapper.selectSysRedpacketConfigMoreById(sysRedpacketConfigMore.getId());
+        if (old != null && StringUtils.isNotBlank(old.getMchId())) {
+            deleteMchConfigCache(old.getMchId());
+        }
+        int rows = baseMapper.updateSysRedpacketConfigMore(sysRedpacketConfigMore);
+        if (rows > 0) {
+            refreshMchConfigCache(sysRedpacketConfigMore);   // 写入新缓存
+        }
+        return rows;
     }
 
     /**
@@ -192,6 +209,13 @@ public class SysRedpacketConfigMoreServiceImpl extends ServiceImpl<SysRedpacketC
     @Override
     public int deleteSysRedpacketConfigMoreByIds(Long[] ids)
     {
+        // 先缓存清除
+        for (Long id : ids) {
+            SysRedpacketConfigMore config = baseMapper.selectSysRedpacketConfigMoreById(id);
+            if (config != null) {
+                deleteMchConfigCache(config.getMchId());
+            }
+        }
         return baseMapper.deleteSysRedpacketConfigMoreByIds(ids);
     }
 
@@ -204,6 +228,37 @@ public class SysRedpacketConfigMoreServiceImpl extends ServiceImpl<SysRedpacketC
     @Override
     public int deleteSysRedpacketConfigMoreById(Long id)
     {
+        SysRedpacketConfigMore config = baseMapper.selectSysRedpacketConfigMoreById(id);
+        if (config != null) {
+            deleteMchConfigCache(config.getMchId());  // 先删缓存
+        }
         return baseMapper.deleteSysRedpacketConfigMoreById(id);
     }
+
+
+
+    /**
+     * 刷新单个商户号的微信配置缓存
+     * @param configMore 商户配置对象
+     */
+    private void refreshMchConfigCache(SysRedpacketConfigMore configMore) {
+        if (configMore == null || StringUtils.isBlank(configMore.getMchId())) {
+            return;
+        }
+        String cacheKey = WX_MCH_CONFIG_KEY + configMore.getMchId();
+        String json = JSONUtil.toJsonStr(configMore);
+        redisCache.setCacheObject(cacheKey, json);
+    }
+
+    /**
+     * 删除单个商户号的微信配置缓存
+     * @param mchId 商户号
+     */
+    private void deleteMchConfigCache(String mchId) {
+        if (StringUtils.isBlank(mchId)) {
+            return;
+        }
+        String cacheKey = WX_MCH_CONFIG_KEY + mchId;
+        redisCache.deleteObject(cacheKey);
+    }
 }

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

@@ -15,6 +15,9 @@
         <if test="params.appid != null and params.appid != ''">
             and fcpsc.appid like concat('%', #{params.appid}, '%')
         </if>
+        <if test="params.mchId != null and params.mchId != ''">
+            and fcpsc.mch_id like concat('%', #{params.mchId}, '%')
+        </if>
         <if test="params.companyId != null">
             and fcpsc.company_id = #{params.companyId}
         </if>

+ 4 - 0
fs-service/src/main/resources/mapper/his/SysRedpacketConfigMoreMapper.xml

@@ -49,6 +49,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where id = #{id}
     </select>
 
+    <select id="getSysRedpacketConfigByMchId" resultType="com.fs.his.domain.SysRedpacketConfigMore">
+        select * from sys_redpacket_config_more where mch_id = #{mchId}
+    </select>
+
     <insert id="insertSysRedpacketConfigMore" parameterType="SysRedpacketConfigMore">
         insert into sys_redpacket_config_more
         <trim prefix="(" suffix=")" suffixOverrides=",">