ソースを参照

Merge remote-tracking branch 'origin/master'

ct 1 日 前
コミット
34e314e4a4

+ 73 - 35
fs-company/src/main/java/com/fs/company/controller/qw/QwUserVoiceLogController.java

@@ -1,9 +1,12 @@
 package com.fs.company.controller.qw;
 
 import com.fs.common.annotation.Log;
+import com.fs.common.constant.HttpStatus;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.PageDomain;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.page.TableSupport;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
@@ -25,6 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
@@ -73,16 +77,6 @@ public class QwUserVoiceLogController extends BaseController
     @PostMapping("/newList")
     public TableDataInfo newList(@RequestBody QwUserVoiceLogVo qwUserVoiceLog)
     {
-        if(qwUserVoiceLog.getCompanyUserId() == null && qwUserVoiceLog.getDeptId() != null){
-            //获取部门下的所有用户
-            CompanyUser usermap=new CompanyUser();
-            usermap.setDeptId(qwUserVoiceLog.getDeptId());
-            List<CompanyUser> users = userService.getUserListByDeptId(usermap);
-            List<Long> userIds = users.stream().map(element -> element.getUserId()).collect(Collectors.toList());
-            if (!userIds.isEmpty()) {
-                qwUserVoiceLog.setCompanyUserIds(userIds.toArray(new Long[0]));
-            }
-        }
         PageHelper.startPage(qwUserVoiceLog.getPageNum(), qwUserVoiceLog.getPageSize());
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
@@ -154,34 +148,88 @@ public class QwUserVoiceLogController extends BaseController
     @GetMapping("/totalList")
     public TableDataInfo totalList(QwUserVoiceLogTotalVo qwUserVoiceLog)
     {
-        startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
         qwUserVoiceLog.setQwUserId(1L);
         List<QwUserVoiceLogTotalVo> list = qwUserVoiceLogService.selectQwUserVoiceLogTotalList(qwUserVoiceLog);
-        return getDataTable(list);
+        list.forEach(item->{
+            if(item.getQwExternalContact() != null){
+                if (item.getQwExternalContact().getTagIds() != null && !Objects.equals(item.getQwExternalContact().getTagIds(), "[]")) {
+                    QwTagSearchParam param = new QwTagSearchParam();
+                    Gson gson = new Gson();
+                    List<String> tagIds = gson.fromJson(
+                            item.getQwExternalContact().getTagIds(),
+                            new TypeToken<List<String>>() {
+                            }.getType()
+                    );
+                    param.setTagIds(tagIds);
+                    item.setTagIdsName(iQwTagService.selectQwTagListByTagIds(param));
+                }
+            }
+        });
+
+        // 获取分页参数
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        int total = list.size();
+        // 在内存中进行分页处理
+        if (pageNum != null && pageSize != null) {
+            int fromIndex = (pageNum - 1) * pageSize;
+            int toIndex = Math.min(fromIndex + pageSize, total);
+
+            // 确保索引不越界
+            if (fromIndex < total) {
+                list = list.subList(fromIndex, toIndex);
+            } else {
+                list = new ArrayList<>(); // 返回空列表
+            }
+        }
+
+        // 构造返回结果
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(total);
+        return rspData;
     }
 
 
     @GetMapping("/sellTotalList")
     public TableDataInfo sellTotalList(QwUserVoiceLogTotalVo qwUserVoiceLog)
     {
-        if(qwUserVoiceLog.getCompanyUserId() == null && qwUserVoiceLog.getDeptId() != null){
-            //获取部门下的所有用户
-            CompanyUser usermap=new CompanyUser();
-            usermap.setDeptId(qwUserVoiceLog.getDeptId());
-            List<CompanyUser> users = userService.getUserListByDeptId(usermap);
-            List<Long> userIds = users.stream().map(element -> element.getUserId()).collect(Collectors.toList());
-            if (!userIds.isEmpty()) {
-                qwUserVoiceLog.setCompanyUserIds(userIds.toArray(new Long[0]));
-            }
-        }
-
-        startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
         List<QwUserVoiceLogTotalVo> list = qwUserVoiceLogService.selectQwUserVoiceLogTotalList(qwUserVoiceLog);
-        return getDataTable(list);
+
+        // 获取分页参数
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        int total = list.size();
+        // 在内存中进行分页处理
+        if (pageNum != null && pageSize != null) {
+            int fromIndex = (pageNum - 1) * pageSize;
+            int toIndex = Math.min(fromIndex + pageSize, total);
+
+            // 确保索引不越界
+            if (fromIndex < total) {
+                list = list.subList(fromIndex, toIndex);
+            } else {
+                list = new ArrayList<>(); // 返回空列表
+            }
+        }
+
+        // 构造返回结果
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(total);
+        return rspData;
     }
 
     @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:sellTotalExport')")
@@ -189,16 +237,6 @@ public class QwUserVoiceLogController extends BaseController
     @GetMapping("/sellTotalExport")
     public AjaxResult sellTotalExport(QwUserVoiceLogTotalVo qwUserVoiceLog)
     {
-        if(qwUserVoiceLog.getCompanyUserId() == null && qwUserVoiceLog.getDeptId() != null){
-            //获取部门下的所有用户
-            CompanyUser usermap=new CompanyUser();
-            usermap.setDeptId(qwUserVoiceLog.getDeptId());
-            List<CompanyUser> users = userService.getUserListByDeptId(usermap);
-            List<Long> userIds = users.stream().map(element -> element.getUserId()).collect(Collectors.toList());
-            if (!userIds.isEmpty()) {
-                qwUserVoiceLog.setCompanyUserIds(userIds.toArray(new Long[0]));
-            }
-        }
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
         List<QwUserVoiceLogTotalVo> list = qwUserVoiceLogService.selectQwUserVoiceLogTotalList(qwUserVoiceLog);

+ 99 - 134
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -81,6 +81,7 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -247,6 +248,9 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     @Autowired
     private CloudHostProper cloudHostProper;
 
+    @Autowired
+    private RedisTemplate<String,BigDecimal> redisTemplate;
+
 
     /**
      * 查询课堂视频
@@ -1151,7 +1155,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
         BigDecimal companyMoney = null;
         if(StringUtils.equals(ENABLE_RED_PACK_ACCOUNT,"1")) {
-            companyMoney = redisCache.getCacheObject(REDPACKET_COMPANY_MONEY);
+            companyMoney = redisTemplate.opsForValue().get(REDPACKET_COMPANY_MONEY);
+
             if(ObjectUtils.isNull(companyMoney)){
                 SysConfig sysConfig = sysConfigService.selectConfigByConfigKey("company.money");
                 if(ObjectUtils.isNull(sysConfig)){
@@ -1197,19 +1202,17 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
                 redPacketLog.setAccBalanceAfter(companyMoney.subtract(amount));
             }
 
-            CompletableFuture.runAsync(() -> {
-                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
-                // 更新观看记录的奖励类型
-                log.setRewardType(config.getRewardType());
-                courseWatchLogMapper.updateFsCourseWatchLog(log);
-            });
+            redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+            // 更新观看记录的奖励类型
+            log.setRewardType(config.getRewardType());
+            courseWatchLogMapper.updateFsCourseWatchLog(log);
 
             if(StringUtils.equals(ENABLE_RED_PACK_ACCOUNT,"1")) {
                 // 更新账户余额
                 logger.info("[更新账户余额] 当前余额{} 更新后余额{}",companyMoney.toPlainString(),companyMoney.subtract(amount).toPlainString());
 
                 companyMoney = companyMoney.subtract(amount);
-                redisCache.setCacheObject(REDPACKET_COMPANY_MONEY,companyMoney);
+                redisTemplate.opsForValue().set(REDPACKET_COMPANY_MONEY,companyMoney);
             }
             return sendRedPacket;
         } else {
@@ -1311,11 +1314,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         packetParam.setAppId(param.getAppId());
         packetParam.setUser(user);
 
-        System.out.println("红包金额"+amount);
-        System.out.println("红包商户号"+packetParam);
-//        if (ObjectUtils.isNotEmpty(config.getIsNegative())&&config.getIsNegative() == 1) {
-//            return processRedPacket(config, packetParam, param, amount, log);
-//        }
+        logger.info("红包金额 {},红包商户号 {}",amount,packetParam);
+
         //2025.6.19 红包金额为0的时候
         if (amount.compareTo(BigDecimal.ZERO)>0){
 
@@ -1324,42 +1324,93 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             if (money.compareTo(BigDecimal.ZERO)<0) {
                 return R.error("服务商余额不足,请联系群主服务器充值!");
             }
-            // 发送红包
-            R sendRedPacket = paymentService.sendRedPacket(packetParam);
-            if (sendRedPacket.get("code").equals(200)) {
-                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
-                TransferBillsResult transferBillsResult;
-                if (sendRedPacket.get("isNew").equals(1)){
-                    transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
-                    redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
-                    redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
-                }else {
-                    redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
-                    redPacketLog.setBatchId(sendRedPacket.get("batchId").toString());
+
+            //---------------发红包前先判断润天账户余额是否足够---------
+            RLock lock = redissonClient.getLock(REDPACKET_POOL_LOCK);
+            try{
+                boolean locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
+
+                if (!locked) {
+                    logger.error("获取锁失败");
+                    return R.error("[红包领取] 系统繁忙,请重试!");
+                }
+
+                BigDecimal companyMoney = null;
+                if(StringUtils.equals(ENABLE_RED_PACK_ACCOUNT,"1")) {
+                    companyMoney = redisTemplate.opsForValue().get(REDPACKET_COMPANY_MONEY);
+                    if(ObjectUtils.isNull(companyMoney)){
+                        SysConfig sysConfig = sysConfigService.selectConfigByConfigKey("company.money");
+                        if(ObjectUtils.isNull(sysConfig)){
+                            throw new IllegalArgumentException("润天公司账户余额不能为空!请检查配置!");
+                        }
+                        String configValue = sysConfig.getConfigValue();
+                        companyMoney = new BigDecimal(configValue);
+                        logger.info("缓存公司余额为空,从数据库读取 companyMoney: {}",companyMoney);
+                    }
+
+                    if (companyMoney.compareTo(BigDecimal.ZERO) <= 0) {
+                        logger.info("润天账户余额: {} 不足!", companyMoney);
+                        return R.error("[红包领取] 账户余额不足,请联系管理员!");
+                    }
+                }
+                // 发送红包
+                R sendRedPacket = paymentService.sendRedPacket(packetParam);
+                if (sendRedPacket.get("code").equals(200)) {
+                    FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+                    TransferBillsResult transferBillsResult;
+                    if (sendRedPacket.get("isNew").equals(1)){
+                        transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
+                        redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
+                        redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+                    }else {
+                        redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                        redPacketLog.setBatchId(sendRedPacket.get("batchId").toString());
+                    }
+                    // 添加红包记录
+                    redPacketLog.setCourseId(param.getCourseId());
+                    redPacketLog.setCompanyId(param.getCompanyId());
+                    redPacketLog.setUserId(param.getUserId());
+                    redPacketLog.setVideoId(param.getVideoId());
+                    redPacketLog.setStatus(0);
+                    redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+                    redPacketLog.setCompanyUserId(param.getCompanyUserId());
+                    redPacketLog.setCreateTime(new Date());
+                    redPacketLog.setAmount(amount);
+                    redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+                    redPacketLog.setPeriodId(param.getPeriodId());
+                    redPacketLog.setAppId(param.getAppId());
+                    if(StringUtils.equals(ENABLE_RED_PACK_ACCOUNT,"1")) {
+                        redPacketLog.setAccBalanceBefore(companyMoney);
+                        redPacketLog.setAccBalanceAfter(companyMoney.subtract(amount));
+                    }
+                    redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+
+                    // 更新观看记录的奖励类型
+                    log.setRewardType(config.getRewardType());
+                    courseWatchLogMapper.updateFsCourseWatchLog(log);
+
+
+                    if(StringUtils.equals(ENABLE_RED_PACK_ACCOUNT,"1")) {
+                        // 更新账户余额
+                        logger.info("[更新账户余额] 当前余额{} 更新后余额{}",companyMoney.toPlainString(),companyMoney.subtract(amount).toPlainString());
+
+                        companyMoney = companyMoney.subtract(amount);
+                        redisTemplate.opsForValue().set(REDPACKET_COMPANY_MONEY,companyMoney);
+                    }
+
+                    return sendRedPacket;
+                } else {
+                    return R.error("奖励发送失败,请联系客服");
+                }
+            }catch (Exception e){
+                logger.error("领取红包失败原因:{}", ExceptionUtils.getFullStackTrace(e),e);
+                throw new RuntimeException(e);
+            }finally {
+                if (lock.isHeldByCurrentThread()) {
+                    lock.unlock();
                 }
-                // 添加红包记录
-                redPacketLog.setCourseId(param.getCourseId());
-                redPacketLog.setCompanyId(param.getCompanyId());
-                redPacketLog.setUserId(param.getUserId());
-                redPacketLog.setVideoId(param.getVideoId());
-                redPacketLog.setStatus(0);
-                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
-                redPacketLog.setCompanyUserId(param.getCompanyUserId());
-                redPacketLog.setCreateTime(new Date());
-                redPacketLog.setAmount(amount);
-                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
-                redPacketLog.setPeriodId(param.getPeriodId());
-                redPacketLog.setAppId(param.getAppId());
-                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
-
-                // 更新观看记录的奖励类型
-                log.setRewardType(config.getRewardType());
-                courseWatchLogMapper.updateFsCourseWatchLog(log);
-
-                return sendRedPacket;
-            } else {
-                return R.error("奖励发送失败,请联系客服");
             }
+
         } else {
             FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
             // 添加红包记录
@@ -1386,93 +1437,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
     }
 
-    /**
-     * 直接发送奖励
-     *
-     * @param config
-     * @param packetParam
-     * @param param
-     * @param amount
-     * @param log
-     * @return
-     */
-    private R processRedPacket(CourseConfig config, WxSendRedPacketParam packetParam, FsCourseSendRewardUParam param, BigDecimal amount, FsCourseWatchLog log) {
-        R sendRedPacket = paymentService.sendRedPacket(packetParam);
-
-        if (!sendRedPacket.get("code").equals(200)) {
-            return R.error("奖励发送失败,请联系客服");
-        }
-
-        createRedPacketLog(sendRedPacket, param, amount, log);
-        updateWatchLogRewardType(log, config);
-
-        return sendRedPacket;
-    }
-
-    private void createRedPacketLog(R sendRedPacket, FsCourseSendRewardUParam param, BigDecimal amount, FsCourseWatchLog log) {
-        FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
-
-        // Set common fields
-        redPacketLog.setCourseId(param.getCourseId());
-        redPacketLog.setCompanyId(param.getCompanyId());
-        redPacketLog.setUserId(param.getUserId());
-        redPacketLog.setVideoId(param.getVideoId());
-        redPacketLog.setStatus(0);
-        redPacketLog.setQwUserId(param.getQwUserId());
-        redPacketLog.setCompanyUserId(param.getCompanyUserId());
-        redPacketLog.setCreateTime(new Date());
-        redPacketLog.setAmount(amount);
-        redPacketLog.setWatchLogId(log != null ? log.getLogId() : null);
-        redPacketLog.setPeriodId(param.getPeriodId());
-        redPacketLog.setAppId(param.getAppId());
-
-        // Set batch number based on isNew flag
-        if (sendRedPacket.get("isNew").equals(1)) {
-            TransferBillsResult transferBillsResult = (TransferBillsResult) sendRedPacket.get("data");
-            redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
-            redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
-        } else {
-            redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
-        }
-
-        redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
-    }
-
-    private void updateWatchLogRewardType(FsCourseWatchLog log, CourseConfig config) {
-        if (log != null) {
-            log.setRewardType(config.getRewardType());
-            courseWatchLogMapper.updateFsCourseWatchLog(log);
-        }
-    }
-    /**
-     * 获取用户openId
-     *
-     * @param userId    用户ID
-     * @param companyId 公司ID
-     * @param source    来源 1公众号 2小程序
-     * @return openId
-     */
-    private String getOpenId(Long userId, Long companyId, Integer source) {
-        Company company = companyMapper.selectCompanyById(companyId);
-        String appId = source == 1 ? company.getCourseMaAppId() : company.getCourseMiniAppId();
-
-        // 公司配置为空时获取默认配置
-        if (StringUtils.isBlank(appId)) {
-            String json = configService.selectConfigByKey("course.config");
-            CourseConfig config = JSON.parseObject(json, CourseConfig.class);
-            appId = source == 1 ? config.getMpAppId() : config.getMiniprogramAppid();
-        }
-
-        // 查询openId
-        Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery().eq(FsUserWx::getFsUserId, userId).eq(FsUserWx::getAppId, appId);
-        FsUserWx fsUserWx = fsUserWxService.getOne(queryWrapper);
-        if (Objects.isNull(fsUserWx)) {
-            throw new CustomException("获取openId失败");
-        }
-
-        return fsUserWx.getOpenId();
-    }
-
     /**
      * 发放积分奖励
      *
@@ -2225,6 +2189,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         }
 
         link.setCreateTime(sendTime);
+        link.setProjectCode(cloudHostProper.getProjectCode());
 
         FsCourseRealLink courseMap = new FsCourseRealLink();
         BeanUtils.copyProperties(link,courseMap);

+ 1 - 1
fs-service/src/main/java/com/fs/erp/http/JstErpHttpServiceImpl.java

@@ -30,7 +30,7 @@ import java.util.Map;
 public class JstErpHttpServiceImpl implements JstErpHttpService {
 
     //正式
-    private static final String BASE_URL = "https://openapi.jushuitan.com/";
+    private static final String BASE_URL = "https://openapi.jushuitan.com";
     /**
      * 获取access_token、refresh_token url
      */

+ 41 - 14
fs-service/src/main/java/com/fs/qw/service/impl/QwUserVoiceLogServiceImpl.java

@@ -22,10 +22,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -133,23 +130,53 @@ public class QwUserVoiceLogServiceImpl extends ServiceImpl<QwUserVoiceLogMapper,
         if(list != null && !list.isEmpty()){
             List<CompanyUser> companyUserList = companyUserMapper.selectCompanyUserByIds(list);
 
-            qwUserVoiceLogTotalVos.forEach(m-> {
-                companyUserList.forEach(n-> {
-                    if(m.getCompanyUserId() != null && n.getUserId() != null && m.getCompanyUserId().equals(n.getUserId())){
-                        m.setCompanyUserName(n.getNickName());
+            // 优化:使用Map提高查找效率,避免双重循环
+            Map<Long, CompanyUser> companyUserMap = companyUserList.stream()
+                    .collect(Collectors.toMap(CompanyUser::getUserId, user -> user, (existing, replacement) -> existing));
+
+            // 如果有部门过滤条件,则预先过滤用户列表
+            if (qwUserVoiceLog.getDeptId() != null) {
+                companyUserMap.entrySet().removeIf(entry ->
+                        entry.getValue().getDeptId() == null ||
+                                !entry.getValue().getDeptId().equals(qwUserVoiceLog.getDeptId()));
+            }
+
+            // 设置用户姓名和部门ID
+            qwUserVoiceLogTotalVos.forEach(logVo -> {
+                CompanyUser user = companyUserMap.get(logVo.getCompanyUserId());
+                if (user != null) {
+                    logVo.setCompanyUserName(user.getNickName());
+                    if (user.getDeptId() != null) {
+                        logVo.setDeptId(user.getDeptId());
                     }
-                });
+                }
             });
+
+            // 如果有部门过滤条件,过滤掉没有部门ID的记录
+            if (qwUserVoiceLog.getDeptId() != null) {
+                qwUserVoiceLogTotalVos = qwUserVoiceLogTotalVos.stream()
+                        .filter(m -> m.getDeptId() != null)
+                        .collect(Collectors.toList());
+            }
         }
+
         List<QwCompany> companyList = qwCompanyService.selectQwCompanyList(new QwCompany());
-        qwUserVoiceLogTotalVos.forEach(m -> companyList.forEach(n ->{
-            if(m.getCorpId().equals(n.getCorpId())){
-                m.setCorpName(n.getCorpName());
+        // 优化:使用Map提高查找效率
+        Map<String, String> corpIdToNameMap = companyList.stream()
+                .collect(Collectors.toMap(QwCompany::getCorpId, QwCompany::getCorpName, (existing, replacement) -> existing));
+
+        qwUserVoiceLogTotalVos.forEach(m -> {
+            String corpName = corpIdToNameMap.get(m.getCorpId());
+            if (corpName != null) {
+                m.setCorpName(corpName);
             }
-        }));
+        });
+
         String companyUserName = qwUserVoiceLog.getCompanyUserName();
         if(companyUserName != null && !companyUserName.isEmpty()){
-            qwUserVoiceLogTotalVos = qwUserVoiceLogTotalVos.stream().filter(n -> n.getCompanyUserName().contains(companyUserName)).collect(Collectors.toList());
+            qwUserVoiceLogTotalVos = qwUserVoiceLogTotalVos.stream()
+                    .filter(n -> n.getCompanyUserName() != null && n.getCompanyUserName().contains(companyUserName))
+                    .collect(Collectors.toList());
         }
         return qwUserVoiceLogTotalVos;
     }

+ 23 - 0
fs-service/src/main/java/com/fs/qw/vo/QwUserVoiceLogTotalVo.java

@@ -8,6 +8,8 @@ import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwUser;
 import lombok.EqualsAndHashCode;
 
+import java.util.List;
+
 @EqualsAndHashCode(callSuper = true)
 public class QwUserVoiceLogTotalVo extends BaseEntity {
 
@@ -87,6 +89,27 @@ public class QwUserVoiceLogTotalVo extends BaseEntity {
 
     private Long deptId;
 
+
+    private List<String> tagIds;
+
+    private List<String> tagIdsName;
+
+    public List<String> getTagIds() {
+        return tagIds;
+    }
+
+    public void setTagIds(List<String> tagIds) {
+        this.tagIds = tagIds;
+    }
+
+    public List<String> getTagIdsName() {
+        return tagIdsName;
+    }
+
+    public void setTagIdsName(List<String> tagIdsName) {
+        this.tagIdsName = tagIdsName;
+    }
+
     public String getCorpName() {
         return corpName;
     }

+ 2 - 0
fs-service/src/main/resources/application-druid-kyt.yml

@@ -147,3 +147,5 @@ openIM:
   userID: im
 #是否为新商户,新商户不走mpOpenId
 isNewWxMerchant: true
+
+enableRedPackAccount: 1

+ 0 - 2
fs-service/src/main/resources/mapper/course/FsCourseRedPacketLogMapper.xml

@@ -110,8 +110,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="result != null">result,</if>
             <if test="batchId != null">batch_id,</if>
             <if test="appId != null">app_id,</if>
-            <if test="appId != null">app_id,</if>
-            <if test="appId != null">app_id,</if>
             <if test="accBalanceBefore != null">acc_balance_before,</if>
             <if test="accBalanceAfter != null">acc_balance_after,</if>
          </trim>

+ 1 - 0
fs-service/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml

@@ -367,6 +367,7 @@
         where course.is_del = 0 and video.video_id = #{videoId}
          and fcp.period_id = #{periodId}
         and fcpd.del_flag = 0 and video.is_del = 0 and fcp.del_flag = '0' and c.del_flag = '0'
+        limit 1
     </select>
 
     <select id="selectFsUserCourseVideoByVideoIdAndUserId" resultMap="FsUserCourseVideoResult">

+ 10 - 6
fs-service/src/main/resources/mapper/qw/QwUserVoiceLogMapper.xml

@@ -97,19 +97,23 @@
         where id = #{id}
     </select>
     <select id="selectQwUserVoiceLogTotalList" resultMap="QwUserVoiceLogVoTotalResult">
-
-        SELECT
-        SUM(duration) duration,qu.qw_user_name,uvl.corp_id,qu.qw_user_id,uvl.company_user_id,
+        SELECT uvl.id, ext_id, uvl.qw_user_id, duration, title, uvl.status, uvl.corp_id,
+               uvl.company_id, uvl.company_user_id, uvl.create_time,qec.`name`,qec.tag_ids,qu.qw_user_name,
+        SUM(duration) duration,
         COUNT(CASE WHEN uvl.status=1 THEN 1 END) AS connectCount,
         COUNT(CASE WHEN uvl.status=2 THEN 1 END) AS noConnectCount
         FROM qw_user_voice_log uvl
         LEFT JOIN qw_user qu ON uvl.qw_user_id = qu.id
+        left join qw_external_contact qec on uvl.ext_id = qec.id
+
         <where>
             <if test="companyId != null ">and uvl.company_id = #{companyId}</if>
             <if test="companyUserId != null ">and uvl.company_user_id = #{companyUserId}</if>
-            <if test="companyUserId == null and companyUserIds != null and companyUserIds.length > 0"> and uvl.company_user_id in
-                <foreach collection="companyUserIds" item="userId" open="(" separator="," close=")">
-                    #{userId}
+            <if test="tagIds != null">
+                and
+                <foreach collection="tagIds" item="tagId" separator="and">
+                    <!-- 使用JSON_CONTAINS检查JSON数组中是否包含指定元素 -->
+                    JSON_CONTAINS(qec.tag_ids, CONCAT('"', #{tagId}, '"'))
                 </foreach>
             </if>
             <if test="qwUserName != null ">and qu.qw_user_name like concat(#{qwUserName}, '%')</if>

+ 18 - 0
fs-user-app/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -19,6 +19,7 @@ import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.RedisSerializationContext;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
 import java.time.Duration;
 
 /**
@@ -86,7 +87,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {