Browse Source

1.提交sop的token统计

jzp 2 ngày trước cách đây
mục cha
commit
7a4939fe17
25 tập tin đã thay đổi với 1115 bổ sung32 xóa
  1. 1 0
      fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java
  2. 104 4
      fs-admin/src/main/java/com/fs/his/task/Task.java
  3. 1 4
      fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java
  4. 163 0
      fs-admin/src/main/java/com/fs/qw/controller/QwPushCountController.java
  5. 11 0
      fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java
  6. 25 0
      fs-common/src/main/java/com/fs/common/utils/DateUtils.java
  7. 67 18
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java
  8. 22 0
      fs-service/src/main/java/com/fs/fastGpt/domain/FastGptPushTokenTotal.java
  9. 2 2
      fs-service/src/main/java/com/fs/fastgptApi/util/EventLogUtils.java
  10. 47 0
      fs-service/src/main/java/com/fs/qw/domain/QwPushCount.java
  11. 39 0
      fs-service/src/main/java/com/fs/qw/domain/QwRestrictionPushRecord.java
  12. 19 1
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  13. 78 0
      fs-service/src/main/java/com/fs/qw/mapper/QwPushCountMapper.java
  14. 61 0
      fs-service/src/main/java/com/fs/qw/mapper/QwRestrictionPushRecordMapper.java
  15. 9 0
      fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  16. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwExternalContactService.java
  17. 71 0
      fs-service/src/main/java/com/fs/qw/service/IQwPushCountService.java
  18. 8 0
      fs-service/src/main/java/com/fs/qw/service/IQwUserService.java
  19. 7 1
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  20. 117 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwPushCountServiceImpl.java
  21. 16 2
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  22. 127 0
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  23. 71 0
      fs-service/src/main/resources/mapper/qw/QwPushCountMapper.xml
  24. 44 0
      fs-service/src/main/resources/mapper/qw/QwRestrictionPushRecordMapper.xml
  25. 3 0
      fs-service/src/main/resources/mapper/qw/QwUserMapper.xml

+ 1 - 0
fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java

@@ -1,5 +1,6 @@
 package com.fs.api.controller;
 
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysDept;
 import com.fs.common.core.redis.RedisCache;

+ 104 - 4
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -6,6 +6,11 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
+import com.fs.fastGpt.domain.FastgptChatVoiceHomo;
+import com.fs.fastGpt.mapper.FastgptChatVoiceHomoMapper;
+import com.fs.fastgptApi.util.AudioUtils;
+import com.fs.fastgptApi.vo.AudioVO;
+import com.fs.sop.domain.QwSopTempVoice;
 import com.alibaba.fastjson.JSONObject;
 import com.baidu.dev2.api.sdk.payment.api.PaymentService;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
@@ -26,12 +31,15 @@ import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.course.service.ITencentCloudCosService;
 import com.fs.erp.domain.ErpDeliverys;
+import com.fs.erp.domain.ErpOrder;
 import com.fs.erp.domain.ErpOrderQuery;
+import com.fs.erp.domain.FsErpFinishPush;
 import com.fs.erp.dto.ErpOrderQueryRequert;
 import com.fs.erp.dto.ErpOrderQueryResponse;
 import com.fs.erp.dto.OrderQueryResponseDTO;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.fastGpt.domain.FastGptEventTokenLog;
+import com.fs.fastGpt.domain.FastGptPushTokenTotal;
 import com.fs.fastGpt.domain.FastgptEventLogTotal;
 import com.fs.fastGpt.mapper.FastGptChatSessionMapper;
 import com.fs.fastGpt.service.IFastgptEventLogTotalService;
@@ -58,8 +66,10 @@ import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
 import com.fs.im.service.OpenIMService;
 import com.fs.qw.domain.QwCompany;
+import com.fs.qw.mapper.QwRestrictionPushRecordMapper;
 import com.fs.qw.service.*;
 import com.fs.qwApi.service.QwApiService;
+import com.fs.sop.service.IQwSopTempVoiceService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.wx.domain.FsWxExpressTask;
@@ -202,6 +212,16 @@ public class Task {
     private IFsStorePaymentService paymentScrmService;
     @Autowired
     ShippingService shippingService;
+
+    @Autowired
+    private QwRestrictionPushRecordMapper qwRestrictionPushRecordMapper;
+
+    @Autowired
+    private FastgptChatVoiceHomoMapper fastgptChatVoiceHomoMapper;
+    @Autowired
+    private IQwSopTempVoiceService qwSopTempVoiceService;
+
+    public static final String SOP_TEMP_VOICE_KEY = "sop:tempVoice";
     public void syncExpressToWx() {
         List<FsWxExpressTask> fsWxExpressTasks = fsWxExpressTaskMapper.selectPendingData();
         if (CollectionUtils.isEmpty(fsWxExpressTasks)) {
@@ -286,6 +306,87 @@ public class Task {
 //        fsWxExpressTaskMapper.batchUpdate(fsWxExpressTasks);
 
     }
+    /**
+     * sop任务token消耗统计
+     */
+    public void sopPushTokenTotal() {
+        // 判断是否是凌晨 00:00 - 00:59
+        boolean isEarlyMorning = isEarlyMorning();
+
+        // 获取日期字符串(今天或昨天)
+        String dateTime;
+        if (isEarlyMorning) {
+            dateTime = DateUtils.addDateDays(-1); // 昨天
+        } else {
+            dateTime = DateUtils.getDate(); // 今天
+        }
+        log.info("开始执行sop任务token消耗统计");
+        try {
+            List<FastGptPushTokenTotal> fastGptPushTotalList = qwRestrictionPushRecordMapper.selectFastgptPushTokenTotal(dateTime);
+            if (fastGptPushTotalList != null && !fastGptPushTotalList.isEmpty()) {
+                for (FastGptPushTokenTotal fastGptPushTotal : fastGptPushTotalList) {
+                    // 获取统计数据
+                    Integer type = fastGptPushTotal.getType();
+                    Long count = 0L;
+                    if(type == 7){
+                        count = fastGptPushTotal.getCount() * 450;
+                    }else{
+                        count = fastGptPushTotal.getCount() * 150;
+                    }
+                    fastGptPushTotal.setCount(count);
+                    FastGptPushTokenTotal pushTotal = qwRestrictionPushRecordMapper.selectFastGptPushTokenTotalByInfo(fastGptPushTotal);
+                    if(pushTotal == null){
+                        qwRestrictionPushRecordMapper.insertPushTokenTotal(fastGptPushTotal);
+                    }else{
+                        fastGptPushTotal.setId(pushTotal.getId());
+                        qwRestrictionPushRecordMapper.updatePushTokenTotal(fastGptPushTotal);
+                    }
+                }
+            }
+            log.info("结束执行sop任务token消耗统计");
+        } catch (Exception e) {
+            log.error("执行sop任务token消耗统计异常", e);
+        }
+    }
+
+    /**
+     * 一键生成语音定时任务
+     */
+    public void ConsumerSopTempVoice() {
+        try {
+            Long newCompanyUserId = redisCache.popVoiceKey(SOP_TEMP_VOICE_KEY);
+            if (newCompanyUserId != null) {
+                List<QwSopTempVoice> sopTempVoices = redisCache.getVoiceAllList(SOP_TEMP_VOICE_KEY + ":" + newCompanyUserId);
+                if (sopTempVoices != null && !sopTempVoices.isEmpty()) {
+                    try {
+                        for (QwSopTempVoice qwSopTempVoice : sopTempVoices) {
+                            try {
+                                AudioVO audioVO = new AudioVO();
+                                List<FastgptChatVoiceHomo> homos = fastgptChatVoiceHomoMapper.selectFastgptChatVoiceHomoList(new FastgptChatVoiceHomo());
+                                audioVO = AudioUtils.createUserUrlAndUrl(homos, qwSopTempVoice.getCompanyUserId(), qwSopTempVoice.getVoiceTxt());
+                                if (audioVO != null && audioVO.getWavUrl() != null && audioVO.getUrl() != null) {
+                                    qwSopTempVoice.setVoiceUrl(audioVO.getUrl());
+                                    qwSopTempVoice.setUserVoiceUrl(audioVO.getWavUrl());
+                                    qwSopTempVoice.setDuration(audioVO.getDuration());
+                                    qwSopTempVoice.setRecordType(1);
+                                    qwSopTempVoiceService.updateQwSopTempVoice(qwSopTempVoice);
+                                }
+                            } catch (Exception e) {
+
+                            }
+                        }
+                    } finally {
+                        redisCache.deleteObject(SOP_TEMP_VOICE_KEY + ":" + newCompanyUserId);
+                    }
+                }
+            } else {
+                log.info("没有需要生成的语音");
+            }
+        } catch (Exception e) {
+            log.error("生成语音定时任务执行异常", e);
+        }
+    }
+
 
     /**
      * 获取订单商品信息
@@ -651,7 +752,6 @@ public class Task {
             fsPrescribeService.auditPrescribe();
         }
     }
-
     public void queryOrder(){
         IErpOrderService erpOrderService = getErpService();
 
@@ -759,8 +859,7 @@ public class Task {
 
     }
 
-    public void deliveryOp()
-    {
+    public void deliveryOp() {
         IErpOrderService erpOrderService = getErpService();
         List<FsStoreOrder> orders = null;
         if (erpOrderService == gyOrderService){
@@ -770,7 +869,8 @@ public class Task {
         }
         for(FsStoreOrder order:orders){
 
-            ErpOrderQueryRequert request=new ErpOrderQueryRequert();
+            ErpOrderQueryRequert request = new ErpOrderQueryRequert();
+
             request.setCode(order.getExtendOrderId());
             if (erpOrderService != null){
                 ErpOrderQueryResponse response= null;

+ 1 - 4
fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java

@@ -57,11 +57,8 @@ public class QwExternalContactController extends BaseController
     @GetMapping("/list")
     public TableDataInfo list(QwExternalContactParam qwExternalContact)
     {
-        if(ObjectUtil.isEmpty(qwExternalContact.getCompanyId())){
-            throw new ServiceException("操作失败,请选择企业!");
-        }
         startPage();
-        List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVO(qwExternalContact);
+        List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVONewSys(qwExternalContact);
         list.forEach(item->{
 
             if (!Objects.equals(item.getTagIds(), "[]") && item.getTagIds()!=null) {

+ 163 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwPushCountController.java

@@ -0,0 +1,163 @@
+package com.fs.qw.controller;
+
+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.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.fastGpt.domain.FastGptPushTokenTotal;
+import com.fs.qw.domain.QwPushCount;
+import com.fs.qw.service.IQwPushCountService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+
+/**
+ * 定义销售推送不同类型的企业消息的次数Controller
+ *
+ * @author fs
+ * @date 2025-08-22
+ */
+@RestController
+@RequestMapping("/qw/qwPushCount")
+public class QwPushCountController extends BaseController {
+    @Autowired
+    private IQwPushCountService qwPushCountService;
+    @Autowired
+    private ResourceLoader resourceLoader;
+
+    /**
+     * 查询定义销售推送不同类型的企业消息的次数列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwPushCount:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwPushCount qwPushCount) {
+        startPage();
+        List<QwPushCount> list = qwPushCountService.selectQwPushCountList(qwPushCount);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出定义销售推送不同类型的企业消息的次数列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwPushCount:export')")
+    @Log(title = "定义销售推送不同类型的企业消息的次数", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwPushCount qwPushCount) {
+        List<QwPushCount> list = qwPushCountService.selectQwPushCountList(qwPushCount);
+        ExcelUtil<QwPushCount> util = new ExcelUtil<QwPushCount>(QwPushCount.class);
+        return util.exportExcel(list, "定义销售推送不同类型的企业消息的次数数据");
+    }
+
+    /**
+     * 获取定义销售推送不同类型的企业消息的次数详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwPushCount:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        List<Long> companyIdList=new ArrayList<>();
+        QwPushCount qwPushCount = qwPushCountService.selectQwPushCountById(id);
+        companyIdList.add(qwPushCount.getCompanyId());
+        qwPushCount.setCompanyIdList(companyIdList);
+        return AjaxResult.success(qwPushCount);
+    }
+
+
+    /**
+     * 新增定义销售推送不同类型的企业消息的次数
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwPushCount:add')")
+    @Log(title = "定义销售推送不同类型的企业消息的次数", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwPushCount qwPushCount) {
+        List<Long> existingIds = new ArrayList<>();
+        Map<String, Long> existingDataMap = new HashMap<>();
+        List<QwPushCount> existingPushCounts = qwPushCountService.selectQwPushCountLists();
+        if (existingPushCounts != null && !existingPushCounts.isEmpty()) {
+            existingPushCounts.forEach(item -> {
+                String key = buildDataKey(item.getType(), item.getCompanyId());
+                existingDataMap.put(key, item.getId());
+            });
+        }
+        // 处理公司ID列表(可能为null或空)
+        List<Long> companyIdList = qwPushCount.getCompanyIdList();
+        boolean isEmptyList = companyIdList == null || companyIdList.isEmpty();
+
+        if (isEmptyList) {
+            // 处理无公司ID列表的情况
+            String key = buildDataKey(qwPushCount.getType(), null);
+            if (existingDataMap.containsKey(key)) {
+                existingIds.add(qwPushCount.getId());
+            } else {
+                qwPushCountService.insertQwPushCount(qwPushCount);
+            }
+        } else {
+            // 处理有公司ID列表的情况
+            companyIdList.forEach(companyId -> {
+                String key = buildDataKey(qwPushCount.getType(), companyId);
+                if (existingDataMap.containsKey(key)) {
+                    existingIds.add(companyId);
+                } else {
+                    qwPushCount.setCompanyId(companyId);
+                    qwPushCountService.insertQwPushCount(qwPushCount);
+                }
+            });
+        }
+
+        // 统一处理返回结果
+        if (!existingIds.isEmpty()) {
+            return error("新增限定类型已存在:失败条数" + existingIds.size());
+        } else {
+            return toAjax(1);
+        }
+    }
+    private String buildDataKey(Integer type, Long companyId) {
+        return type + "_" + (companyId == null ? "null" : companyId);
+    }
+
+    /**
+     * 修改定义销售推送不同类型的企业消息的次数
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwPushCount:edit')")
+    @Log(title = "定义销售推送不同类型的企业消息的次数", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwPushCount qwPushCount) {
+        QwPushCount pushCount;
+        if (qwPushCount.getCompanyId() != null) {
+            pushCount = qwPushCountService.SelectQwPushCountByCompanyId(qwPushCount.getType(), qwPushCount.getCompanyId());
+        } else {
+            pushCount = qwPushCountService.SelectQwPushCountByType(qwPushCount.getType());
+        }
+        if (pushCount != null) {
+            if (!Objects.equals(pushCount.getId(), qwPushCount.getId()) && Objects.equals(pushCount.getCompanyId(), qwPushCount.getCompanyId()) && Objects.equals(pushCount.getType(), qwPushCount.getType())) {
+                return toAjax(0);
+            }
+            if (Objects.equals(pushCount.getPushCount(), qwPushCount.getPushCount())) {
+                return toAjax(0);
+            }
+        }
+        return toAjax(qwPushCountService.updateQwPushCount(qwPushCount));
+    }
+
+    /**
+     * 删除定义销售推送不同类型的企业消息的次数
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwPushCount:remove')")
+    @Log(title = "定义销售推送不同类型的企业消息的次数", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        return toAjax(qwPushCountService.deleteQwPushCountByIds(ids));
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:qwPushCount:tokenList')")
+    @GetMapping("/tokenList")
+    public TableDataInfo tokenList(FastGptPushTokenTotal pushTokenInfo) {
+        startPage();
+        List<FastGptPushTokenTotal> list = qwPushCountService.selectFastGptPushTokenTotalList(pushTokenInfo);
+        return getDataTable(list);
+    }
+}

+ 11 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java

@@ -5,11 +5,14 @@ import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.service.IQwUserService;
+import com.fs.qw.vo.QwOptionsVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.List;
+
 /**
  * 企微用户Controller
  *
@@ -34,4 +37,12 @@ public class QwUserController extends BaseController {
     public R getQwUserInfo(QwFsUserParam param){
         return R.ok().put("data",qwUserService.getQwUserInfo(param));
     }
+
+
+   @GetMapping("/getMyQwCompanyList")
+    public R getMyQwCompanyList()
+    {
+        List<QwOptionsVO> list = qwUserService.selectQwCompanyListOptionsVOBySys();
+        return  R.ok().put("data",list);
+    }
 }

+ 25 - 0
fs-common/src/main/java/com/fs/common/utils/DateUtils.java

@@ -270,5 +270,30 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
         cal.add(Calendar.DATE, days);
         return new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime());
     }
+    /**
+     * 获取到当天时间的开始:当天0时0分0秒0毫秒
+     * @return
+     */
+    public static Long toStartTime( ) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        return calendar.getTimeInMillis();
+    }
+
+    /**
+     * 获取到当天时间的结束:当天23时59分59秒999毫秒
+     * @return
+     */
+    public static Long toEndTime() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 23);
+        calendar.set(Calendar.MINUTE, 59);
+        calendar.set(Calendar.SECOND, 59);
+        calendar.set(Calendar.MILLISECOND, 999);
+        return calendar.getTimeInMillis();
+    }
 
 }

+ 67 - 18
fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fs.app.service.IpadSendServer;
 import com.fs.common.core.redis.RedisCacheT;
+import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.PubFun;
 import com.fs.company.service.ICompanyMiniappService;
 import com.fs.course.config.CourseConfig;
@@ -12,8 +13,12 @@ import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.service.IFsCoursePlaySourceConfigService;
 import com.fs.ipad.vo.BaseVo;
 import com.fs.qw.domain.QwIpadServer;
+import com.fs.qw.domain.QwPushCount;
+import com.fs.qw.domain.QwRestrictionPushRecord;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwIpadServerMapper;
+import com.fs.qw.mapper.QwPushCountMapper;
+import com.fs.qw.mapper.QwRestrictionPushRecordMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.service.impl.AsyncSopTestService;
 import com.fs.qw.vo.QwSopCourseFinishTempSetting;
@@ -55,6 +60,8 @@ public class SendMsg {
     private final AsyncSopTestService asyncSopTestService;
     private final ICompanyMiniappService companyMiniappService;
     private final IFsCoursePlaySourceConfigService fsCoursePlaySourceConfigService;
+    private final QwPushCountMapper qwPushCountMapper;
+    private final QwRestrictionPushRecordMapper qwRestrictionPushRecordMapper;
 
     @Value("${group-no}")
     private String groupNo;
@@ -65,7 +72,7 @@ public class SendMsg {
     @Qualifier("customThreadPool")
     private ThreadPoolTaskExecutor customThreadPool;
 
-    public SendMsg(QwUserMapper qwUserMapper, QwSopLogsMapper qwSopLogsMapper, IpadSendServer sendServer, SysConfigMapper sysConfigMapper, IQwSopLogsService qwSopLogsService, QwIpadServerMapper qwIpadServerMapper, RedisCacheT<Long> redisCache, AsyncSopTestService asyncSopTestService, ICompanyMiniappService companyMiniappService, IFsCoursePlaySourceConfigService fsCoursePlaySourceConfigService) {
+    public SendMsg(QwUserMapper qwUserMapper, QwSopLogsMapper qwSopLogsMapper, IpadSendServer sendServer, SysConfigMapper sysConfigMapper, IQwSopLogsService qwSopLogsService, QwIpadServerMapper qwIpadServerMapper, RedisCacheT<Long> redisCache, AsyncSopTestService asyncSopTestService, ICompanyMiniappService companyMiniappService, IFsCoursePlaySourceConfigService fsCoursePlaySourceConfigService, QwPushCountMapper qwPushCountMapper, QwRestrictionPushRecordMapper qwRestrictionPushRecordMapper) {
         this.qwUserMapper = qwUserMapper;
         this.qwSopLogsMapper = qwSopLogsMapper;
         this.sendServer = sendServer;
@@ -76,6 +83,8 @@ public class SendMsg {
         this.asyncSopTestService = asyncSopTestService;
         this.companyMiniappService = companyMiniappService;
         this.fsCoursePlaySourceConfigService = fsCoursePlaySourceConfigService;
+        this.qwPushCountMapper = qwPushCountMapper;
+        this.qwRestrictionPushRecordMapper = qwRestrictionPushRecordMapper;
     }
     private List<QwUser> getQwUserList() {
         if (qwUserList.isEmpty()) {
@@ -191,27 +200,67 @@ public class SendMsg {
                 continue;
             }
             redisCache.setCacheObject(key, System.currentTimeMillis(), 24, TimeUnit.HOURS);
+            List<QwPushCount> pushCountList = qwPushCountMapper.selectQwPushCountLists();
+            Map<Integer, List<QwPushCount>> pushMap = pushCountList.stream().collect(Collectors.groupingBy(QwPushCount::getType));
             // 循环发送消息里面的每一条消息
             for (QwSopCourseFinishTempSetting.Setting content : setting.getSetting()) {
                 long start4 = System.currentTimeMillis();
-                // 发送
-                sendServer.send(content, user, qwSopLogs, miniMap, parentVo);
-                long end4 = System.currentTimeMillis();
-                log.info("请求pad发送完成:{}, {}, 时长4:{}", user.getQwUserName(), qwSopLogs.getId(), end4 - start4);
-                if(content.getSendStatus() == 2 && ("请求失败:消息发送过于频繁,请稍后再试".equals(content.getSendRemarks()) || "请求失败:请求频率异常".equals(content.getSendRemarks()))){
-                    QwUser update = new QwUser();
-                    update.setRemark("请求频率异常,暂停发送,三小时后恢复继续发送");
-                    update.setUpdateTime(new Date());
-                    qwUserMapper.update(update, new QueryWrapper<QwUser>().eq("id", user.getId()));
-                    redisCache.setCacheObject("qw:user:id:" + user.getId(), user.getId(), 3, TimeUnit.HOURS);
-                    return;
+                //判断当前销售推送客户消息限制
+                Long qwUserId = qwUser.getId();//销售的Id
+                Integer type = Integer.valueOf(content.getContentType());//发送消息的类型
+                Long customerId = qwSopLogs.getExternalId();//客户ID
+                Long companyId = qwSopLogs.getCompanyId();//公司ID
+                Integer pushCount = -99;
+                if(pushMap.containsKey(type)){
+                    List<QwPushCount> qwPushCounts = pushMap.get(type);
+                    Optional<QwPushCount> optional = qwPushCounts.stream().filter(e -> Objects.equals(e.getCompanyId(), companyId)).findFirst();
+                    if(optional.isPresent()){
+                        pushCount = optional.get().getPushCount();
+                    }else{
+                        Optional<QwPushCount> nullCount = qwPushCounts.stream().filter(e -> e.getCompanyId() == null).findFirst();
+                        if(nullCount.isPresent()){
+                            pushCount = nullCount.get().getPushCount();
+                        }
+                    }
                 }
-                try {
-                    int delay = ThreadLocalRandom.current().nextInt(300, 1000);
-                    log.debug("pad发送消息等待:{}ms", delay);
-                    Thread.sleep(delay);
-                } catch (InterruptedException e) {
-                    log.error("线程等待错误!");
+                //查询是否有设置限制客服推送消息次数
+//                    Integer pushCount=pushCountMap.containsKey(String.valueOf(companyId)) ? pushCountMap.get(String.valueOf(companyId)): pushCountMap.getOrDefault(String.valueOf(type), -99);
+                int salesPushCustomerMessageCount = qwRestrictionPushRecordMapper.selectQwRestrictionPushRecord(qwUserId, customerId, type, DateUtils.toStartTime(), DateUtils.toEndTime());
+                if (pushCount != -99 && salesPushCustomerMessageCount >= pushCount) {
+                    content.setSendStatus(2);//设置发送失败状态
+                    content.setSendRemarks("发送次数达到上限");
+                } else {
+                    // 发送
+                    sendServer.send(content, user, qwSopLogs, miniMap, parentVo);
+                    //判断销售推送成功:保存记录
+                    if (content.getSendStatus() != 2) {
+                        QwRestrictionPushRecord qrpr = new QwRestrictionPushRecord();
+                        qrpr.setType(type);
+                        qrpr.setQwUserId(qwUserId);
+                        qrpr.setQwExternalId(customerId);
+                        qrpr.setCompanyId(companyId);
+                        qrpr.setStatus(1);
+                        qrpr.setCreateTime(DateUtils.getTime());
+                        qrpr.setTime(System.currentTimeMillis());
+                        qwRestrictionPushRecordMapper.insert(qrpr);
+                    }
+                    long end4 = System.currentTimeMillis();
+                    log.info("请求pad发送完成:{}, {}, 时长4:{}", user.getQwUserName(), qwSopLogs.getId(), end4 - start4);
+                    if (content.getSendStatus() == 2 && ("请求失败:消息发送过于频繁,请稍后再试".equals(content.getSendRemarks()) || "请求失败:请求频率异常".equals(content.getSendRemarks()))) {
+                        QwUser update = new QwUser();
+                        update.setRemark("请求频率异常,暂停发送,三小时后恢复继续发送");
+                        update.setUpdateTime(new Date());
+                        qwUserMapper.update(update, new QueryWrapper<QwUser>().eq("id", user.getId()));
+                        redisCache.setCacheObject("qw:user:id:" + user.getId(), user.getId(), 3, TimeUnit.HOURS);
+                        return;
+                    }
+                    try {
+                        int delay = ThreadLocalRandom.current().nextInt(300, 1000);
+                        log.debug("pad发送消息等待:{}ms", delay);
+                        Thread.sleep(delay);
+                    } catch (InterruptedException e) {
+                        log.error("线程等待错误!");
+                    }
                 }
             }
             // 推送 APP

+ 22 - 0
fs-service/src/main/java/com/fs/fastGpt/domain/FastGptPushTokenTotal.java

@@ -0,0 +1,22 @@
+package com.fs.fastGpt.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+
+@Data
+public class FastGptPushTokenTotal extends BaseEntity {
+
+    private Long id;
+    private Integer type;
+    private Long qwUserId;
+    private Long companyId;
+    @Excel(name = "公司名称")
+    private String companyName;
+    @Excel(name = "token消耗数")
+    private Long count;
+    private Integer status;
+    @Excel(name = "生成时间")
+    private String statTime;
+
+}

+ 2 - 2
fs-service/src/main/java/com/fs/fastgptApi/util/EventLogUtils.java

@@ -80,7 +80,7 @@ public class EventLogUtils {
 
 
         EventLogQueue.addEventLog(fastGptEventLog); // 入队
-        fastGptChatMsgService.insertFastGptEventLog(fastGptEventLog);
+        //fastGptChatMsgService.insertFastGptEventLog(fastGptEventLog);
     }
 
     /**
@@ -104,7 +104,7 @@ public class EventLogUtils {
         fastGptEventTokenLog.setQwUserId(user.getId());
         fastGptEventTokenLog.setCreateTime(new Date());
 
-        //EventLogQueue.addEventTokenLog(fastGptEventTokenLog); // 入队
+        EventLogQueue.addEventTokenLog(fastGptEventTokenLog); // 入队
         //fastGptChatMsgService.insertFastGptEventTokenLog(fastGptEventTokenLog);
     }
 

+ 47 - 0
fs-service/src/main/java/com/fs/qw/domain/QwPushCount.java

@@ -0,0 +1,47 @@
+package com.fs.qw.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 定义销售推送不同类型的企业消息的次数对象 qw_push_count
+ *
+ * @author fs
+ * @date 2025-08-22
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class QwPushCount extends BaseEntity{
+
+    /** $column.columnComment */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 推送类型 */
+    @Excel(name = "推送类型")
+    private Integer type;
+
+    /** 销售推送企业微信消息的限定次数 */
+    @Excel(name = "销售推送企业微信消息的限定次数")
+    private Integer pushCount;
+
+    /** 推送公司id */
+    @Excel(name = "推送公司id")
+    private Long companyId;
+
+    /** 状态 */
+    @Excel(name = "状态")
+    private Integer status;
+
+    // 标记该字段不与数据库字段对应
+    @TableField(exist = false)
+    private List<Long> companyIdList;
+
+}

+ 39 - 0
fs-service/src/main/java/com/fs/qw/domain/QwRestrictionPushRecord.java

@@ -0,0 +1,39 @@
+package com.fs.qw.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 记录销售推送成功信息
+ *
+ * @author liupeng
+ * @date 2025-08-20
+ */
+@Data
+public class QwRestrictionPushRecord implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    private Integer type;
+
+    private Long qwUserId;
+
+    private Long qwExternalId;
+
+    private Long companyId;
+
+    private Integer status;
+
+    private String createTime;
+
+    private Long time;
+
+    private String remarks;
+
+}

+ 19 - 1
fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

@@ -227,14 +227,17 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
     public int deleteQwExternalContactByIds(Long[] ids);
 
     @Select({"<script> " +
-            "select ec.*,qu.qw_user_name,qd.dept_name as departmentName from qw_external_contact ec " +
+            "select ec.*,qu.qw_user_name,qd.dept_name as departmentName,cw.name way_name,wg.group_name way_group_name from qw_external_contact ec " +
             "left join qw_user qu on ec.user_id=qu.qw_user_id and qu.corp_id=ec.corp_id " +
             "left join qw_dept qd on qd.dept_id=qu.department and qd.corp_id=qu.corp_id " +
             "left join company_user cu on ec.company_user_id=cu.user_id " +
+            "left join qw_contact_way cw on cw.id = ec.way_id " +
+            "left join qw_contact_way_group wg on wg.id=cw.group_id" +
             "<where>  \n" +
             "            <if test=\"userId != null  and userId != ''\"> and ec.user_id   like concat( #{userId}, '%') </if>\n" +
             "            <if test=\"qwUserName != null  and qwUserName != ''\"> and qu.qw_user_name   like concat( #{qwUserName}, '%') </if>\n" +
             "            <if test=\"externalUserId != null  and externalUserId != ''\"> and ec.external_user_id = #{externalUserId}</if>\n" +
+            "            <if test=\"wayId != null  and wayId != ''\"> and  SUBSTRING_INDEX(ec.state, ':', -1) = #{wayId} </if>\n" +
             "            <if test=\"name != null  and name != ''\"> and ec.name like concat( #{name}, '%')</if>\n" +
             "            <if test=\"type != null \"> and ec.type = #{type}</if>\n" +
             "            <if test=\"gender != null \"> and ec.gender = #{gender}</if>\n" +
@@ -259,6 +262,7 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
             "            <if test=\"customerId != null \"> and ec.customer_id = #{customerId}</if>\n" +
             "            <if test=\"status != null \"> and ec.status = #{status}</if>\n" +
             "            <if test=\"stageStatus != null \"> and ec.stage_status = #{stageStatus}</if>\n" +
+            "            <if test=\"userRepeat != null \"> and ec.user_repeat = #{userRepeat}</if>\n" +
             "            <if test=\"transferStatus != null \"> and ec.transfer_status = #{transferStatus}</if>\n" +
             "            <if test=\"qwUserId != null \"> and ec.qw_user_id = #{qwUserId}</if>\n" +
             "            <if test=\"level != null \"> and ec.level = #{level}</if>\n" +
@@ -320,6 +324,7 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
             "            <if test=\"customerId != null \"> and ec.customer_id = #{customerId}</if>\n" +
             "            <if test=\"status != null \"> and ec.status = #{status}</if>\n" +
             "            <if test=\"stageStatus != null \"> and ec.stage_status = #{stageStatus}</if>\n" +
+            "            <if test=\"userRepeat != null \"> and ec.user_repeat = #{userRepeat}</if>\n" +
             "            <if test=\"transferStatus != null \"> and ec.transfer_status = #{transferStatus}</if>\n" +
             "            <if test=\"qwUserId != null \"> and ec.qw_user_id = #{qwUserId}</if>\n" +
             "            <if test=\"level != null \"> and ec.level = #{level}</if>\n" +
@@ -507,4 +512,17 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
     void updateQwExternalContactIsRePlyById(@Param("id")Long id);
     @Select("select * from qw_external_contact where unionid = #{unionID} ")
     QwExternalContact selectQwExternalByUnionID(@Param("unionID")String unionID);
+
+    @Select("SELECT max(id) FROM qw_external_contact where status <>'4' and unionid is NULL")
+    Long selectSyncMaxId();
+
+    @Select("SELECT * FROM qw_external_contact where id >#{recordId} and id <=#{maxId} and status <>'4' and unionid is NULL order by id asc limit 500")
+    List<QwExternalContact> selectSyncData(@Param("recordId")Long recordId,@Param("maxId")Long maxId);
+
+    @Update("update qw_external_contact set unionid = #{item.unionid} where id = #{item.id}")
+    int batchUpdateUnionId(@Param("item") QwExternalContact item);
+
+    void updateQwExternalContactStatusById(QwExternalContact qwExternalContact);
+
+    List<QwExternalContactVO> selectQwExternalContactListVONewSys(QwExternalContactParam qwExternalContact);
 }

+ 78 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwPushCountMapper.java

@@ -0,0 +1,78 @@
+package com.fs.qw.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.qw.domain.QwPushCount;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * 销售推送不同类型消息次数Mapper接口
+ *
+ * @author liupeng
+ * @date 2025-08-20
+ */
+@Repository
+public interface QwPushCountMapper extends BaseMapper<QwPushCount> {
+
+    @Select("select id,type,push_count,company_id,status from qw_push_count WHERE status=0")
+    public List<QwPushCount> selectQwPushCountLists();
+
+    @Select("select id,type,push_count,company_id,status from qw_push_count WHERE type = #{type} and company_id=#{companyId} and status=0")
+    public QwPushCount SelectQwPushCountByCompanyId(@Param("type") Integer type, @Param("companyId") Long companyId);  // 注解和SQL中的参数名都使用companyId
+
+    @Select("select id,type,push_count,company_id,status from qw_push_count WHERE type = #{type} and company_id is null and status=0")
+    public QwPushCount SelectQwPushCountByType(@Param("type") Integer type);
+
+
+    /**
+     * 查询定义销售推送不同类型的企业消息的次数
+     *
+     * @param id 定义销售推送不同类型的企业消息的次数主键
+     * @return 定义销售推送不同类型的企业消息的次数
+     */
+    QwPushCount selectQwPushCountById(Long id);
+
+    /**
+     * 查询定义销售推送不同类型的企业消息的次数列表
+     *
+     * @param qwPushCount 定义销售推送不同类型的企业消息的次数
+     * @return 定义销售推送不同类型的企业消息的次数集合
+     */
+    List<QwPushCount> selectQwPushCountList(QwPushCount qwPushCount);
+
+    /**
+     * 新增定义销售推送不同类型的企业消息的次数
+     *
+     * @param qwPushCount 定义销售推送不同类型的企业消息的次数
+     * @return 结果
+     */
+    int insertQwPushCount(QwPushCount qwPushCount);
+
+    /**
+     * 修改定义销售推送不同类型的企业消息的次数
+     *
+     * @param qwPushCount 定义销售推送不同类型的企业消息的次数
+     * @return 结果
+     */
+    int updateQwPushCount(QwPushCount qwPushCount);
+
+    /**
+     * 删除定义销售推送不同类型的企业消息的次数
+     *
+     * @param id 定义销售推送不同类型的企业消息的次数主键
+     * @return 结果
+     */
+    int deleteQwPushCountById(Long id);
+
+    /**
+     * 批量删除定义销售推送不同类型的企业消息的次数
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteQwPushCountByIds(Long[] ids);
+
+}

+ 61 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwRestrictionPushRecordMapper.java

@@ -0,0 +1,61 @@
+package com.fs.qw.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.fastGpt.domain.FastGptPushTokenTotal;
+import com.fs.qw.domain.QwRestrictionPushRecord;
+import org.apache.ibatis.annotations.Delete;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * 记录销售推送成功信息Mapper接口
+ *
+ * @author liupeng
+ * @date 2025-08-20
+ */
+@Repository
+public interface QwRestrictionPushRecordMapper extends BaseMapper<QwRestrictionPushRecord> {
+
+    int insert(QwRestrictionPushRecord qwRestrictionPushRecord);
+    /**
+     * 查询当前销售ID对应类型的QW推送成功的记录
+     *
+     * @param type
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    @Select("select count(id) from qw_restriction_push_record where qw_user_id=#{qwUserId} and qw_external_id=#{qwExternalId} and type=#{type} and time>#{startTime} and time<=#{endTime}")
+    int selectQwRestrictionPushRecord(@Param("qwUserId") Long qwUserId,
+                                      @Param("qwExternalId") Long qwExternalId,
+                                      @Param("type") Integer type,
+                                      @Param("startTime") Long startTime,
+                                      @Param("endTime") Long endTime);
+
+    @Select("select id from qw_restriction_push_record where time<#{time}")
+    List<Long> selectExpirePushRecord(@Param("time") Long time);
+
+    @Delete("<script>" +
+            "delete from qw_restriction_push_record " +
+            "where id in " +
+            "<foreach collection='ids' item='id' open='(' separator=',' close=')'>" +
+            "#{id}" +
+            "</foreach>" +
+            "</script>")
+    public int deleteQwRestrictionPushRecordIds(@Param("ids") Long[] ids);
+
+    List<FastGptPushTokenTotal> selectFastgptPushTokenTotal(String dateTime);
+
+    FastGptPushTokenTotal selectFastGptPushTokenTotalByInfo(FastGptPushTokenTotal fastGptPushTotal);
+
+    void insertPushTokenTotal(FastGptPushTokenTotal fastGptPushTotal);
+
+    void updatePushTokenTotal(FastGptPushTokenTotal fastGptPushTotal);
+
+    List<FastGptPushTokenTotal> selectFastGptPushTokenTotalList(FastGptPushTokenTotal pushTokenInfo);
+
+}
+

+ 9 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java

@@ -207,6 +207,7 @@ public interface QwUserMapper extends BaseMapper<QwUser>
             "            <if test=\"qwUserName != null  and qwUserName != ''\"> and qu.qw_user_uame like concat( #{qwUserName}, '%') </if>\n" +
             "            <if test=\"companyUserId != null \"> and qu.company_user_id = #{companyUserId}</if>\n" +
             "            <if test=\"corpId != null \"> and qu.corp_id = #{corpId}</if>\n" +
+            "            <if test=\"deptId != null \"> and qd.dept_id = #{deptId}</if>\n" +
             "            <if test=\"status != null \"> and qu.status = #{status}</if>\n" +
             "            <if test=\"type != null and sendType !=null and type==2 and (sendType==2 or sendType==4 or sendType==11) \"> and qu.app_key IS NOT NULL  </if>\n" +
             "</script>"})
@@ -433,4 +434,12 @@ public interface QwUserMapper extends BaseMapper<QwUser>
     void updateSendType(UpdateSendTypeVo vo);
 
     QwUser selectOfflineUser();
+
+    /**
+     * 根据销售公司和企微ID查询企微用户
+     */
+    List<QwUserVO> selectQwUserListVOByCompanyIdAndCorpIdAndNickName(@Param("companyId") Long companyId, @Param("corpId") String corpId, @Param("nickName") String nickName);
+
+    List<QwOptionsVO> selectQwCompanyListOptionsVOBySys();
+
 }

+ 2 - 0
fs-service/src/main/java/com/fs/qw/service/IQwExternalContactService.java

@@ -247,4 +247,6 @@ public interface IQwExternalContactService extends IService<QwExternalContact> {
      */
     List<QwUserDelLossLogVO> selectQwUserDelLossLogList(QwUserDelLossLogParam param);
 
+
+    List<QwExternalContactVO> selectQwExternalContactListVONewSys(QwExternalContactParam qwExternalContact);
 }

+ 71 - 0
fs-service/src/main/java/com/fs/qw/service/IQwPushCountService.java

@@ -0,0 +1,71 @@
+package com.fs.qw.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.fastGpt.domain.FastGptPushTokenTotal;
+import com.fs.qw.domain.QwPushCount;
+
+import java.util.List;
+
+/**
+ * 定义销售推送不同类型的企业消息的次数Service接口
+ * 
+ * @author fs
+ * @date 2025-08-22
+ */
+public interface IQwPushCountService extends IService<QwPushCount>{
+
+    public List<QwPushCount> selectQwPushCountLists();
+
+    public QwPushCount SelectQwPushCountByCompanyId( Integer type,Long companyId);  // 注解和SQL中的参数名都使用companyId
+
+    public QwPushCount SelectQwPushCountByType(Integer type);
+    /**
+     * 查询定义销售推送不同类型的企业消息的次数
+     * 
+     * @param id 定义销售推送不同类型的企业消息的次数主键
+     * @return 定义销售推送不同类型的企业消息的次数
+     */
+    QwPushCount selectQwPushCountById(Long id);
+
+    /**
+     * 查询定义销售推送不同类型的企业消息的次数列表
+     * 
+     * @param qwPushCount 定义销售推送不同类型的企业消息的次数
+     * @return 定义销售推送不同类型的企业消息的次数集合
+     */
+    List<QwPushCount> selectQwPushCountList(QwPushCount qwPushCount);
+
+    /**
+     * 新增定义销售推送不同类型的企业消息的次数
+     * 
+     * @param qwPushCount 定义销售推送不同类型的企业消息的次数
+     * @return 结果
+     */
+    int insertQwPushCount(QwPushCount qwPushCount);
+
+    /**
+     * 修改定义销售推送不同类型的企业消息的次数
+     * 
+     * @param qwPushCount 定义销售推送不同类型的企业消息的次数
+     * @return 结果
+     */
+    int updateQwPushCount(QwPushCount qwPushCount);
+
+    /**
+     * 批量删除定义销售推送不同类型的企业消息的次数
+     * 
+     * @param ids 需要删除的定义销售推送不同类型的企业消息的次数主键集合
+     * @return 结果
+     */
+    int deleteQwPushCountByIds(Long[] ids);
+
+    /**
+     * 删除定义销售推送不同类型的企业消息的次数信息
+     * 
+     * @param id 定义销售推送不同类型的企业消息的次数主键
+     * @return 结果
+     */
+    int deleteQwPushCountById(Long id);
+
+    List<FastGptPushTokenTotal> selectFastGptPushTokenTotalList(FastGptPushTokenTotal pushTokenInfo);
+}

+ 8 - 0
fs-service/src/main/java/com/fs/qw/service/IQwUserService.java

@@ -193,4 +193,12 @@ public interface IQwUserService
     void atMsg(QwUser qwUser, String s);
 
     void changeVideoStatus(Long id);
+
+    /**
+     * 根据销售公司和企微ID查询企微用户
+     */
+    List<QwUserVO> selectQwUserListByCompanyIdAndCorpIdAndNickName(Long companyId, String corpId, String nickName);
+
+    List<QwOptionsVO> selectQwCompanyListOptionsVOBySys();
+
 }

+ 7 - 1
fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java

@@ -66,6 +66,7 @@ import com.fs.system.service.ISysDictTypeService;
 import com.fs.voice.utils.StringUtil;
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -94,7 +95,7 @@ import java.util.stream.Stream;
  * @date 2024-06-20
  */
 @Service
-
+@Slf4j
 public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactMapper, QwExternalContact> implements IQwExternalContactService {
 
     private static final String miniappRealLink = "/pages_course/video.html?course=";
@@ -5700,6 +5701,11 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         return qwExternalContactMapper.selectQwUserDelLossList(param);
     }
 
+    @Override
+    public List<QwExternalContactVO> selectQwExternalContactListVONewSys(QwExternalContactParam qwExternalContact) {
+        return qwExternalContactMapper.selectQwExternalContactListVONewSys(qwExternalContact);
+    }
+
 
     public Boolean getSopAiChatByRedis(String qwUserId,String corpId,String externalUserId){
 

+ 117 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwPushCountServiceImpl.java

@@ -0,0 +1,117 @@
+package com.fs.qw.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.fastGpt.domain.FastGptPushTokenTotal;
+import com.fs.qw.domain.QwPushCount;
+import com.fs.qw.mapper.QwPushCountMapper;
+import com.fs.qw.mapper.QwRestrictionPushRecordMapper;
+import com.fs.qw.service.IQwPushCountService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 定义销售推送不同类型的企业消息的次数Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-08-22
+ */
+@Service
+public class QwPushCountServiceImpl extends ServiceImpl<QwPushCountMapper, QwPushCount> implements IQwPushCountService {
+
+    @Autowired
+    private QwRestrictionPushRecordMapper pushRecordMapper;
+
+    @Override
+    public List<QwPushCount> selectQwPushCountLists() {
+        return baseMapper.selectQwPushCountLists();
+    }
+
+    @Override
+    public QwPushCount SelectQwPushCountByCompanyId(Integer type, Long companyId) {
+        return baseMapper.SelectQwPushCountByCompanyId(type, companyId);
+    }
+
+    @Override
+    public QwPushCount SelectQwPushCountByType(Integer type) {
+        return baseMapper.SelectQwPushCountByType(type);
+    }
+
+    /**
+     * 查询定义销售推送不同类型的企业消息的次数
+     * 
+     * @param id 定义销售推送不同类型的企业消息的次数主键
+     * @return 定义销售推送不同类型的企业消息的次数
+     */
+    @Override
+    public QwPushCount selectQwPushCountById(Long id)
+    {
+        return baseMapper.selectQwPushCountById(id);
+    }
+
+    /**
+     * 查询定义销售推送不同类型的企业消息的次数列表
+     * 
+     * @param qwPushCount 定义销售推送不同类型的企业消息的次数
+     * @return 定义销售推送不同类型的企业消息的次数
+     */
+    @Override
+    public List<QwPushCount> selectQwPushCountList(QwPushCount qwPushCount)
+    {
+        return baseMapper.selectQwPushCountList(qwPushCount);
+    }
+
+    /**
+     * 新增定义销售推送不同类型的企业消息的次数
+     * 
+     * @param qwPushCount 定义销售推送不同类型的企业消息的次数
+     * @return 结果
+     */
+    @Override
+    public int insertQwPushCount(QwPushCount qwPushCount)
+    {
+        return baseMapper.insertQwPushCount(qwPushCount);
+    }
+
+    /**
+     * 修改定义销售推送不同类型的企业消息的次数
+     * 
+     * @param qwPushCount 定义销售推送不同类型的企业消息的次数
+     * @return 结果
+     */
+    @Override
+    public int updateQwPushCount(QwPushCount qwPushCount)
+    {
+        return baseMapper.updateQwPushCount(qwPushCount);
+    }
+
+    /**
+     * 批量删除定义销售推送不同类型的企业消息的次数
+     * 
+     * @param ids 需要删除的定义销售推送不同类型的企业消息的次数主键
+     * @return 结果
+     */
+    @Override
+    public int deleteQwPushCountByIds(Long[] ids)
+    {
+        return baseMapper.deleteQwPushCountByIds(ids);
+    }
+
+    /**
+     * 删除定义销售推送不同类型的企业消息的次数信息
+     * 
+     * @param id 定义销售推送不同类型的企业消息的次数主键
+     * @return 结果
+     */
+    @Override
+    public int deleteQwPushCountById(Long id)
+    {
+        return baseMapper.deleteQwPushCountById(id);
+    }
+
+    @Override
+    public List<FastGptPushTokenTotal> selectFastGptPushTokenTotalList(FastGptPushTokenTotal pushTokenInfo) {
+        return pushRecordMapper.selectFastGptPushTokenTotalList(pushTokenInfo);
+    }
+}

+ 16 - 2
fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java

@@ -862,8 +862,9 @@ public class QwUserServiceImpl implements IQwUserService
     {
         int i = qwUserMapper.updateQwUser(qwUser);
         if (i>0){
-            redisCache.setCacheObject("qwUserRdById:"+qwUser.getId() ,JSON.toJSONString(qwUser),1, TimeUnit.HOURS);
-            redisCache.setCacheObject("qwUserRd:"+qwUser.getCorpId()+":"+qwUser.getQwUserId() ,JSON.toJSONString(qwUser),1, TimeUnit.HOURS);
+            QwUser qwUserById = qwUserMapper.selectQwUserById(qwUser.getId());
+            redisCache.setCacheObject("qwUserRdById:"+qwUser.getId() ,JSON.toJSONString(qwUserById),1, TimeUnit.HOURS);
+            redisCache.setCacheObject("qwUserRd:"+qwUser.getCorpId()+":"+qwUser.getQwUserId() ,JSON.toJSONString(qwUserById),1, TimeUnit.HOURS);
         }
         return i;
     }
@@ -1538,6 +1539,19 @@ public class QwUserServiceImpl implements IQwUserService
         qwUserMapper.updateById(qwUser);
     }
 
+    /**
+     * 根据销售公司和企微ID查询企微用户
+     */
+    @Override
+    public List<QwUserVO> selectQwUserListByCompanyIdAndCorpIdAndNickName(Long companyId, String corpId, String nickName) {
+        return qwUserMapper.selectQwUserListVOByCompanyIdAndCorpIdAndNickName(companyId, corpId, nickName);
+    }
+
+    @Override
+    public List<QwOptionsVO> selectQwCompanyListOptionsVOBySys() {
+        return qwUserMapper.selectQwCompanyListOptionsVOBySys();
+    }
+
 
     /**
      * 构建查询条件

+ 127 - 0
fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml

@@ -652,6 +652,133 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectQwExternalContactVo"/>
         where fs_user_id = #{userId} and company_user_id = #{companyUserId}
     </select>
+    <select id="selectQwExternalContactListVONewSys" resultType="com.fs.qw.vo.QwExternalContactVO">
+            select ec.*, qu.qw_user_name, qd.dept_name as departmentName
+            from qw_external_contact ec
+            left join qw_user qu on ec.user_id = qu.qw_user_id and qu.corp_id = ec.corp_id
+            left join qw_dept qd on qd.dept_id = qu.department and qd.corp_id = qu.corp_id
+            left join company_user cu on ec.company_user_id = cu.user_id
+            <where>
+                <if test="userId != null and userId != ''">
+                    and ec.user_id like concat(#{userId}, '%')
+                </if>
+                <if test="qwUserName != null and qwUserName != ''">
+                    and qu.qw_user_name like concat(#{qwUserName}, '%')
+                </if>
+                <if test="externalUserId != null and externalUserId != ''">
+                    and ec.external_user_id = #{externalUserId}
+                </if>
+                <if test="wayId != null and wayId != ''">
+                    and SUBSTRING_INDEX(ec.state, ':', -1) = #{wayId}
+                </if>
+                <if test="name != null and name != ''">
+                    and ec.name like concat(#{name}, '%')
+                </if>
+                <if test="type != null">
+                    and ec.type = #{type}
+                </if>
+                <if test="gender != null">
+                    and ec.gender = #{gender}
+                </if>
+                <if test="description != null and description != ''">
+                    and ec.description = #{description}
+                </if>
+
+                <if test="tagIds != null and tagIds.size() != 0">
+                    and (
+                    <foreach collection="tagIds" item="item" index="index" separator=" AND ">
+                        find_in_set(#{item}, REGEXP_REPLACE(ec.tag_ids, '[\"\\[\\]]', ''))
+                    </foreach>
+                    )
+                </if>
+
+                <if test="remarkMobiles != null and remarkMobiles != ''">
+                    and ec.remark_mobiles like concat(#{remarkMobiles}, '%')
+                </if>
+                <if test="remark != null and remark != ''">
+                    and ec.remark like concat('%', #{remark}, '%')
+                </if>
+                <if test="remarkCorpName != null and remarkCorpName != ''">
+                    and ec.remark_corp_name like concat('%', #{remarkCorpName}, '%')
+                </if>
+                <if test="addWay != null">
+                    and ec.add_way = #{addWay}
+                </if>
+                <if test="operUserid != null and operUserid != ''">
+                    and ec.oper_userid = #{operUserid}
+                </if>
+                <if test="corpId != null and corpId != ''">
+                    and ec.corp_id = #{corpId}
+                </if>
+                <!--<if test="companyId != null">
+                    and qu.company_id = #{companyId}
+                </if>
+                <if test="companyUserId != null">
+                    and ec.company_user_id = #{companyUserId}
+                </if>-->
+                <if test="customerId != null">
+                    and ec.customer_id = #{customerId}
+                </if>
+                <if test="status != null">
+                    and ec.status = #{status}
+                </if>
+                <if test="stageStatus != null">
+                    and ec.stage_status = #{stageStatus}
+                </if>
+                <if test="transferStatus != null">
+                    and ec.transfer_status = #{transferStatus}
+                </if>
+                <if test="qwUserId != null">
+                    and ec.qw_user_id = #{qwUserId}
+                </if>
+                <if test="level != null">
+                    and ec.level = #{level}
+                </if>
+                <if test="levelType != null">
+                    and ec.level_type = #{levelType}
+                </if>
+                <if test="isBind == 'isBind'">
+                    and ec.customer_id is not null
+                </if>
+                <if test="isBind == 'noBind'">
+                    and ec.customer_id is null
+                </if>
+                <if test="isBindMini == 'isBindMini'">
+                    and ec.fs_user_id is not null
+                </if>
+                <if test="isBindMini == 'noBindMini'">
+                    and ec.fs_user_id is null
+                </if>
+                <if test="lossTime != null">
+                    and DATE(ec.loss_time) = DATE(#{lossTime})
+                </if>
+                <if test="createTime != null">
+                    and DATE(ec.create_time) = DATE(#{createTime})
+                </if>
+                <if test="delTime != null">
+                    and DATE(ec.del_time) = DATE(#{delTime})
+                </if>
+                <if test="sTime != null">
+                    and DATE(ec.create_time) &gt;= DATE(#{sTime})
+                </if>
+                <if test="eTime != null">
+                    and DATE(ec.create_time) &lt;= DATE(#{eTime})
+                </if>
+                <if test="companyUserName != null and companyUserName != ''">
+                    and cu.user_name = #{companyUserName}
+                </if>
+                <if test="cuDeptIdList != null and !cuDeptIdList.isEmpty() and userType != '00'">
+                    AND cu.dept_id IN
+                    <foreach collection="cuDeptIdList" item="item" open="(" separator="," close=")">
+                        #{item}
+                    </foreach>
+                </if>
+                <if test="companyUser != null">
+                    and (cu.nick_name like concat('%', #{companyUser}, '%') or cu.phonenumber = #{companyUser})
+                </if>
+            </where>
+            order by ec.create_time desc, ec.id desc
+    </select>
 
 
 </mapper>

+ 71 - 0
fs-service/src/main/resources/mapper/qw/QwPushCountMapper.xml

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.qw.mapper.QwPushCountMapper">
+    
+    <resultMap type="QwPushCount" id="QwPushCountResult">
+        <result property="id"    column="id"    />
+        <result property="type"    column="type"    />
+        <result property="pushCount"    column="push_count"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="status"    column="status"    />
+    </resultMap>
+
+    <sql id="selectQwPushCountVo">
+        select id, type, push_count, company_id, status from qw_push_count
+    </sql>
+
+    <select id="selectQwPushCountList" parameterType="QwPushCount" resultMap="QwPushCountResult">
+        <include refid="selectQwPushCountVo"/>
+        <where>  
+            <if test="type != null "> and type = #{type}</if>
+            <if test="pushCount != null "> and push_count = #{pushCount}</if>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="status != null "> and status = #{status}</if>
+        </where>
+    </select>
+    
+    <select id="selectQwPushCountById" parameterType="Long" resultMap="QwPushCountResult">
+        <include refid="selectQwPushCountVo"/>
+        where id = #{id}
+    </select>
+        
+    <insert id="insertQwPushCount" parameterType="QwPushCount" useGeneratedKeys="true" keyProperty="id">
+        insert into qw_push_count
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="type != null">type,</if>
+            <if test="pushCount != null">push_count,</if>
+            <if test="companyId != null">company_id,</if>
+            <if test="status != null">status,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="type != null">#{type},</if>
+            <if test="pushCount != null">#{pushCount},</if>
+            <if test="companyId != null">#{companyId},</if>
+            <if test="status != null">#{status},</if>
+         </trim>
+    </insert>
+
+    <update id="updateQwPushCount" parameterType="QwPushCount">
+        update qw_push_count
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="type != null">type = #{type},</if>
+            <if test="pushCount != null">push_count = #{pushCount},</if>
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="status != null">status = #{status},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteQwPushCountById" parameterType="Long">
+        delete from qw_push_count where id = #{id}
+    </delete>
+
+    <delete id="deleteQwPushCountByIds" parameterType="String">
+        delete from qw_push_count where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 44 - 0
fs-service/src/main/resources/mapper/qw/QwRestrictionPushRecordMapper.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.qw.mapper.QwRestrictionPushRecordMapper">
+    <insert id="insertPushTokenTotal">
+        insert into fastgpt_push_token_total(type,qw_user_id,company_id,status,stat_time,count)
+        values(#{type},#{qwUserId},#{companyId},#{status},#{statTime},#{count})
+    </insert>
+    <update id="updatePushTokenTotal">
+        update fastgpt_push_token_total
+        set count=#{count}
+        where id=#{id}
+    </update>
+
+    <select id="selectFastgptPushTokenTotal" resultType="com.fs.fastGpt.domain.FastGptPushTokenTotal">
+        select id,`type`,qw_user_id as qwUserId,company_id as companyId,status,date_format(create_time,'%Y-%m-%d') as statTime,count(id) count
+        from qw_restriction_push_record
+        where  create_time like concat(#{createTime}, '%') and qw_user_id is not null and status = 1
+        GROUP BY `type`,company_id,qw_user_id
+        ORDER BY `type`,company_id,qw_user_id
+    </select>
+
+    <select id="selectFastGptPushTokenTotalByInfo" resultType="com.fs.fastGpt.domain.FastGptPushTokenTotal">
+        select id,type,qw_user_id as qwUserId,company_id as companyId,status,stat_time statTime,count
+        from fastgpt_push_token_total
+        where  type=#{type} and qw_user_id=#{qwUserId} and company_id=#{companyId} and status=#{status} and stat_time=#{statTime}
+    </select>
+    <select id="selectFastGptPushTokenTotalList" resultType="com.fs.fastGpt.domain.FastGptPushTokenTotal">
+        select ft.id,ft.type,ft.qw_user_id as qwUserId,ft.company_id as companyId,com.company_name as companyName,ft.status,ft.stat_time statTime,sum(ft.count) count
+        from fastgpt_push_token_total ft left join company com on ft.company_id= com.company_id
+        <where>
+            <if test="type != null" >and `ft.type` = #{type} </if>
+            <if test="qwUserId != null" >and ft.qw_user_id= #{qwUserId} </if>
+            <if test="companyId != null" >and ft.company_id= #{companyId} </if>
+            <if test="status != null" >and ft.status= #{status} </if>
+            <if test="beginTime != null and endTime != null" >
+                    AND date_format(ft.stat_time,'%Y-%m-%d') &gt;= #{beginTime}
+                    AND date_format(ft.stat_time,'%Y-%m-%d') &lt;= #{endTime}
+            </if>
+        </where>
+        group by ft.company_id,ft.stat_time
+    </select>
+</mapper>

+ 3 - 0
fs-service/src/main/resources/mapper/qw/QwUserMapper.xml

@@ -268,5 +268,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             (qw_user_id = #{query.qwUserId} AND corp_id = #{query.corpId})
         </foreach>
     </select>
+    <select id="selectQwCompanyListOptionsVOBySys" resultType="com.fs.qw.vo.QwOptionsVO">
+        select corp_id as dictValue,corp_name as dictLabel from qw_company
+    </select>
 
 </mapper>