浏览代码

济南发送手机短信,商城展示直播中奖订单

yuhongqi 3 周之前
父节点
当前提交
74913ed2f0
共有 24 个文件被更改,包括 549 次插入80 次删除
  1. 2 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java
  2. 40 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java
  3. 129 11
      fs-ipad-task/src/main/java/com/fs/app/task/SendSmsMsg.java
  4. 78 0
      fs-live-ws/src/main/resources/logback-spring.xml
  5. 2 0
      fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java
  6. 9 1
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  7. 22 2
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java
  8. 22 2
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java
  9. 3 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderParam.java
  10. 5 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java
  11. 27 23
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java
  12. 22 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  13. 22 22
      fs-service/src/main/java/com/fs/hisStore/service/impl/MergedOrderServiceImpl.java
  14. 1 1
      fs-service/src/main/java/com/fs/live/mapper/LiveLotteryConfMapper.java
  15. 4 0
      fs-service/src/main/java/com/fs/qw/domain/QwExternalContact.java
  16. 3 0
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  17. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwExternalContactService.java
  18. 5 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  19. 13 2
      fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java
  20. 8 0
      fs-service/src/main/resources/db/changelog/changes/20260613-live-user-add-is-del.sql
  21. 33 0
      fs-service/src/main/resources/db/changelog/dictData/dict_data_update.sql
  22. 88 15
      fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml
  23. 3 1
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  24. 6 0
      fs-service/src/main/resources/mapper/qw/QwSopSmsLogsMapper.xml

+ 2 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java

@@ -103,6 +103,7 @@ public class FsStoreHealthOrderScrmController extends BaseController {
         if ("北京卓美".equals(signProjectName)) {
           param.setIsHealth("1");
         }
+        fsStoreOrderService.applyHealthStoreQueryFlags(param);
         List<FsStoreOrderVO> list = fsStoreOrderService.selectFsStoreOrderListVO(param);
         //金牛需求 区别其他项目 status = 6 (金牛代服管家) ,其他项目请避免使用订单状态status = 6
         TableDataInfo dataTable = getDataTable(list);
@@ -519,6 +520,7 @@ public class FsStoreHealthOrderScrmController extends BaseController {
             String[] arr = param.getOrderCodeList().split("[,,\\s]+");
             param.setOrderCodes(new ArrayList<>(Arrays.asList(arr)));
         }
+        fsStoreOrderService.applyHealthStoreQueryFlags(param);
     }
 
     // 检查文件是否为有效的Excel文件

+ 40 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java

@@ -602,6 +602,46 @@ public class QwExternalContactController extends BaseController
         return toAjax(qwExternalContactService.updateQwExternalContact(qwExternalContact));
     }
 
+    /**
+     * 修改外部联系人联系手机号码(仅更新数据库 contact_mobiles 字段)
+     */
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:edit') or @ss.hasPermi('qw:externalContact:deptEdit') or @ss.hasPermi('qw:externalContact:myEdit')")
+    @Log(title = "企业微信客户", businessType = BusinessType.UPDATE)
+    @PutMapping("/editRemarkMobiles")
+    public R editRemarkMobiles(@RequestBody QwExternalContact qwExternalContact)
+    {
+        if (qwExternalContact.getId() == null) {
+            return R.error("客户ID不能为空");
+        }
+        String contactMobiles = qwExternalContact.getContactMobiles();
+        if (contactMobiles == null) {
+            contactMobiles = "";
+        }
+        contactMobiles = contactMobiles.trim();
+        if (StringUtils.isNotEmpty(contactMobiles)) {
+            if (contactMobiles.contains(",")) {
+                return R.error("请使用英文逗号分隔手机号码");
+            }
+            if (contactMobiles.contains(",,") || contactMobiles.startsWith(",") || contactMobiles.endsWith(",")) {
+                return R.error("手机号码格式不正确,请使用英文逗号分隔");
+            }
+            String[] parts = contactMobiles.split(",");
+            LinkedHashSet<String> uniqueMobiles = new LinkedHashSet<>();
+            for (String part : parts) {
+                String mobile = part.trim();
+                if (StringUtils.isEmpty(mobile)) {
+                    return R.error("手机号码格式不正确,请使用英文逗号分隔");
+                }
+                if (!uniqueMobiles.add(mobile)) {
+                    return R.error("手机号码重复:" + mobile);
+                }
+            }
+            contactMobiles = String.join(",", uniqueMobiles);
+        }
+        int rows = qwExternalContactService.updateContactMobilesById(qwExternalContact.getId(), contactMobiles);
+        return rows > 0 ? R.ok() : R.error("修改失败");
+    }
+
     @PreAuthorize("@ss.hasPermi('qw:externalContact:edit')")
     @Log(title = "企业微信客户", businessType = BusinessType.UPDATE)
     @PutMapping("/editStatus")

+ 129 - 11
fs-ipad-task/src/main/java/com/fs/app/task/SendSmsMsg.java

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.service.ISmsService;
 import com.fs.company.domain.CompanySmsLogs;
@@ -13,10 +14,13 @@ import com.fs.his.domain.FsUser;
 import com.fs.his.dto.SendResultDetailDTO;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.utils.PhoneUtil;
+import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwIpadServer;
 import com.fs.qw.domain.QwSopSmsLogs;
+import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwIpadServerMapper;
 import com.fs.qw.service.IQwSopSmsLogsService;
+import com.fs.system.service.ISysDictTypeService;
 import com.fs.sop.domain.QwSopLogs;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.service.IQwSopLogsService;
@@ -47,6 +51,10 @@ public class SendSmsMsg {
     @Value("${group-no}")
     private String groupNo;
 
+    private static final String DICT_TYPE_QW_EXTERNAL_CONTACT_FUNC = "sys_qw_external_contact_func";
+    /** 字典 dict_sort=2:是否开启根据外部联系人手机号码发送短信 */
+    private static final long DICT_SORT_SEND_SMS_BY_CONTACT_MOBILE = 2L;
+
     private final RedisCache redisCache;
 
     private final ISmsService smsService;
@@ -65,6 +73,10 @@ public class SendSmsMsg {
 
     private final IFsCourseWatchLogService watchLogService;
 
+    private final ISysDictTypeService sysDictTypeService;
+
+    private final QwExternalContactMapper qwExternalContactMapper;
+
     // 线程池配置
     private static final int CORE_POOL_SIZE = 50;
     private static final int MAX_POOL_SIZE = 150;
@@ -74,12 +86,18 @@ public class SendSmsMsg {
     // 分页大小
     private static final int PAGE_SIZE = 3000;
 
-    // 手机号缓存
+    // fs_user 手机号缓存
     private final Cache<Long, String> phoneCache = CacheBuilder.newBuilder()
             .expireAfterWrite(1, TimeUnit.HOURS)
             .maximumSize(100000)
             .build();
 
+    // 外部联系人 contact_mobiles 缓存(取逗号分隔的第一个号码)
+    private final Cache<Long, String> contactMobileCache = CacheBuilder.newBuilder()
+            .expireAfterWrite(1, TimeUnit.HOURS)
+            .maximumSize(100000)
+            .build();
+
     // 限流器:控制全局发送速率
     private final RateLimiter rateLimiter = RateLimiter.create(500); // 每秒500条
 
@@ -105,7 +123,9 @@ public class SendSmsMsg {
                       QwSopLogsMapper qwSopLogsMapper,
                       IQwSopLogsService qwSopLogsService,
                       IFsCourseWatchLogService watchLogService,
-                      CompanySmsLogsMapper companySmsLogsMapper) {
+                      CompanySmsLogsMapper companySmsLogsMapper,
+                      ISysDictTypeService sysDictTypeService,
+                      QwExternalContactMapper qwExternalContactMapper) {
         this.qwIpadServerMapper = qwIpadServerMapper;
         this.qwSopSmsLogsService = qwSopSmsLogsService;
         this.smsService = smsService;
@@ -115,6 +135,8 @@ public class SendSmsMsg {
         this.qwSopLogsService = qwSopLogsService;
         this.watchLogService = watchLogService;
         this.companySmsLogsMapper = companySmsLogsMapper;
+        this.sysDictTypeService = sysDictTypeService;
+        this.qwExternalContactMapper = qwExternalContactMapper;
     }
 
     @Scheduled(cron = "0 0 * * * ?") // 每小时执行一次
@@ -171,6 +193,8 @@ public class SendSmsMsg {
 
         // 计算时间范围
         TimeRange timeRange = calculateTimeRange();
+        boolean sendByContactMobile = isSendSmsByContactMobileEnabled();
+        log.info("sendSms: 外部联系人手机号发送开关={}", sendByContactMobile);
 
         long lastId = 0L;
         AtomicLong totalProcessed = new AtomicLong(0);
@@ -204,7 +228,7 @@ public class SendSmsMsg {
                 List<QwSopSmsLogs> serverBatch = entry.getValue();
                 CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                     try {
-                        SendResult result = processServerBatch(serverBatch,sopLogsMap);
+                        SendResult result = processServerBatch(serverBatch, sopLogsMap, sendByContactMobile);
                         totalProcessed.addAndGet(result.success);
                         totalFailed.addAndGet(result.failed);
                     } catch (Exception e) {
@@ -261,15 +285,29 @@ public class SendSmsMsg {
         return new TimeRange(startTime, endTime);
     }
 
+    /**
+     * 是否开启根据外部联系人手机号码发送短信(字典 sys_qw_external_contact_func,dict_sort=2,dict_value=1)
+     */
+    private boolean isSendSmsByContactMobileEnabled() {
+        List<SysDictData> dictList = sysDictTypeService.selectDictDataByType(DICT_TYPE_QW_EXTERNAL_CONTACT_FUNC);
+        if (dictList == null || dictList.isEmpty()) {
+            return false;
+        }
+        return dictList.stream()
+                .filter(item -> "0".equals(item.getStatus()))
+                .filter(item -> item.getDictSort() != null && DICT_SORT_SEND_SMS_BY_CONTACT_MOBILE == item.getDictSort())
+                .anyMatch(item -> "1".equals(item.getDictValue() != null ? item.getDictValue().trim() : ""));
+    }
+
     /**
      * 处理单个server的一批短信(同步发送,避免线程爆炸)
      */
-    private SendResult processServerBatch(List<QwSopSmsLogs> batch,Map<Long, QwSopLogs> sopLogsMap) {
+    private SendResult processServerBatch(List<QwSopSmsLogs> batch, Map<Long, QwSopLogs> sopLogsMap, boolean sendByContactMobile) {
         int success = 0;
         List<SendResultDetailDTO> failReasonsList = Collections.synchronizedList(new ArrayList<>());
         List<Long> failedIds = Collections.synchronizedList(new ArrayList<>());
         // 批量获取手机号
-        Map<Long, String> userPhoneMap = getPhoneNumbers(batch);
+        Map<Long, String> phoneMap = resolvePhones(batch, sendByContactMobile);
 
         for (QwSopSmsLogs logRecord : batch) {
             rateLimiter.acquire();
@@ -301,7 +339,7 @@ public class SendSmsMsg {
                     continue;
                 }
 
-                SendResultDetailDTO detail = sendSingleSms(logRecord, userPhoneMap, redisKey);
+                SendResultDetailDTO detail = sendSingleSms(logRecord, phoneMap, redisKey);
                 if (detail.isSuccess()) {
                     success++;
                 } else {
@@ -333,9 +371,40 @@ public class SendSmsMsg {
     }
 
     /**
-     * 获取手机号映射(缓存+批量查询)
+     * 解析待发送记录的手机号(key 为 qw_sop_sms_logs.id)
+     */
+    private Map<Long, String> resolvePhones(List<QwSopSmsLogs> batch, boolean sendByContactMobile) {
+        Map<Long, String> result = new HashMap<>();
+        if (sendByContactMobile) {
+            Map<Long, String> contactPhoneMap = getContactMobiles(batch);
+            for (QwSopSmsLogs logRecord : batch) {
+                if (logRecord.getContactId() == null) {
+                    continue;
+                }
+                String phone = contactPhoneMap.get(logRecord.getContactId());
+                if (phone != null) {
+                    result.put(logRecord.getId(), phone);
+                }
+            }
+            return result;
+        }
+        Map<Long, String> userPhoneMap = getFsUserPhones(batch);
+        for (QwSopSmsLogs logRecord : batch) {
+            if (logRecord.getFsUserId() == null) {
+                continue;
+            }
+            String phone = userPhoneMap.get(logRecord.getFsUserId());
+            if (phone != null) {
+                result.put(logRecord.getId(), phone);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 获取 fs_user 手机号映射(缓存+批量查询)
      */
-    private Map<Long, String> getPhoneNumbers(List<QwSopSmsLogs> batch) {
+    private Map<Long, String> getFsUserPhones(List<QwSopSmsLogs> batch) {
         // 收集所有用户ID
         Set<Long> userIds = batch.stream()
                 .map(QwSopSmsLogs::getFsUserId)
@@ -369,11 +438,57 @@ public class SendSmsMsg {
         return result;
     }
 
+    /**
+     * 获取外部联系人 contact_mobiles 映射(取逗号分隔的第一个号码)
+     */
+    private Map<Long, String> getContactMobiles(List<QwSopSmsLogs> batch) {
+        Set<Long> contactIds = batch.stream()
+                .map(QwSopSmsLogs::getContactId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        Map<Long, String> result = new HashMap<>();
+        Set<Long> missingContactIds = new HashSet<>();
+        for (Long contactId : contactIds) {
+            String phone = contactMobileCache.getIfPresent(contactId);
+            if (phone != null) {
+                result.put(contactId, phone);
+            } else {
+                missingContactIds.add(contactId);
+            }
+        }
+
+        if (!missingContactIds.isEmpty()) {
+            List<QwExternalContact> contacts = qwExternalContactMapper.selectQwExternalContactByIds(new ArrayList<>(missingContactIds));
+            for (QwExternalContact contact : contacts) {
+                String phone = pickFirstContactMobile(contact.getContactMobiles());
+                if (phone != null) {
+                    result.put(contact.getId(), phone);
+                    contactMobileCache.put(contact.getId(), phone);
+                }
+            }
+        }
+        return result;
+    }
+
+    private String pickFirstContactMobile(String contactMobiles) {
+        if (StringUtils.isEmpty(contactMobiles)) {
+            return null;
+        }
+        for (String part : contactMobiles.split(",")) {
+            String mobile = part.trim();
+            if (!StringUtils.isEmpty(mobile)) {
+                return mobile;
+            }
+        }
+        return null;
+    }
+
     /**
      * 发送单条短信,返回是否成功
      */
-    private SendResultDetailDTO sendSingleSms(QwSopSmsLogs logRecord, Map<Long, String> userPhoneMap, String redisKey) {
-        String phone = userPhoneMap.get(logRecord.getFsUserId());
+    private SendResultDetailDTO sendSingleSms(QwSopSmsLogs logRecord, Map<Long, String> phoneMap, String redisKey) {
+        String phone = phoneMap.get(logRecord.getId());
         String content = logRecord.getContent();
 
         if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(content)) {
@@ -576,6 +691,9 @@ public class SendSmsMsg {
             return;
         }
 
+        boolean sendByContactMobile = isSendSmsByContactMobileEnabled();
+        log.info("processSendLogs: 外部联系人手机号发送开关={}", sendByContactMobile);
+
         List<Long> sopLogIds = needCheckList.stream()
                 .map(QwSopSmsLogs::getSopLogId)
                 .collect(Collectors.toList());
@@ -595,7 +713,7 @@ public class SendSmsMsg {
             List<QwSopSmsLogs> serverBatch = entry.getValue();
             CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                 try {
-                    SendResult result = processServerBatch(serverBatch, sopLogsMap);
+                    SendResult result = processServerBatch(serverBatch, sopLogsMap, sendByContactMobile);
                     totalProcessed.addAndGet(result.success);
                     totalFailed.addAndGet(result.failed);
                 } catch (Exception e) {

+ 78 - 0
fs-live-ws/src/main/resources/logback-spring.xml

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="60 seconds">
+
+    <!-- 日志目录,可通过 logging.file.path 或环境变量 LOG_PATH 覆盖 -->
+    <springProperty scope="context" name="LOG_PATH" source="logging.file.path" defaultValue="/home/fs-live-ws/logs"/>
+    <!-- 单文件大小上限:超过后自动切割 -->
+    <springProperty scope="context" name="MAX_FILE_SIZE" source="logging.logback.rollingpolicy.max-file-size" defaultValue="200MB"/>
+    <!-- 归档日志保留天数,超过自动删除 -->
+    <springProperty scope="context" name="MAX_HISTORY" source="logging.logback.rollingpolicy.max-history" defaultValue="15"/>
+    <!-- 所有归档日志总大小上限(兜底) -->
+    <springProperty scope="context" name="TOTAL_SIZE_CAP" source="logging.logback.rollingpolicy.total-size-cap" defaultValue="10GB"/>
+
+    <property name="APP_NAME" value="fs-live-ws"/>
+    <property name="LOG_CHARSET" value="UTF-8"/>
+    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
+
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>${LOG_CHARSET}</charset>
+            <immediateFlush>true</immediateFlush>
+        </encoder>
+    </appender>
+
+    <!--
+        按「日期 + 大小」滚动,归档 gzip 压缩:
+        - 每天跨日自动切割
+        - 单文件超过 200MB 时在同一天内继续切割(.%i)
+        - 超过 15 天的归档自动删除
+    -->
+    <appender name="FILE_ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_PATH}/${APP_NAME}.log</file>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>${LOG_CHARSET}</charset>
+            <immediateFlush>true</immediateFlush>
+        </encoder>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/archive/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
+            <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
+            <maxHistory>${MAX_HISTORY}</maxHistory>
+            <totalSizeCap>${TOTAL_SIZE_CAP}</totalSizeCap>
+            <cleanHistoryOnStart>false</cleanHistoryOnStart>
+        </rollingPolicy>
+    </appender>
+
+    <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_PATH}/${APP_NAME}-error.log</file>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>${LOG_CHARSET}</charset>
+            <immediateFlush>true</immediateFlush>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/archive/${APP_NAME}-error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
+            <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
+            <maxHistory>${MAX_HISTORY}</maxHistory>
+            <totalSizeCap>${TOTAL_SIZE_CAP}</totalSizeCap>
+            <cleanHistoryOnStart>false</cleanHistoryOnStart>
+        </rollingPolicy>
+    </appender>
+
+    <root level="INFO">
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="FILE_ALL"/>
+        <appender-ref ref="FILE_ERROR"/>
+    </root>
+
+    <logger name="com.fs" level="INFO"/>
+    <logger name="org.springframework" level="WARN"/>
+    <logger name="io.netty" level="WARN"/>
+
+</configuration>

+ 2 - 0
fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java

@@ -211,6 +211,8 @@ public class CommonController {
 
     @Autowired
     private ILiveRedPacketLogService redPacketLogService;
+    @Autowired
+    private com.fs.app.task.qwTask qwTask;
 
     @GetMapping("/test")
     public R test(){

+ 9 - 1
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java

@@ -58,6 +58,7 @@ import com.fs.voice.utils.StringUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.retry.annotation.Backoff;
 import org.springframework.retry.annotation.Retryable;
 import org.springframework.scheduling.annotation.Async;
@@ -223,6 +224,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     @Autowired
     private ConfigUtil configUtil;
 
+    @Value("${cloud_host.company_name}")
+    private String signProjectName;
+
     @PostConstruct
     public void init() {
         loadCourseConfig();
@@ -1987,7 +1991,11 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     createLiveWatchLogAndEnQueue(companyId,companyUserId,externalId, setting.getLiveId(),sysConfig.getAppId(),1,qwUserId,logVo.getCorpId());
                     break;
                 case "21"://短信看课
-                    if (sopLogs.getFsUserId() != null && !Long.valueOf(0L).equals(sopLogs.getFsUserId())) {
+                    boolean check = !Long.valueOf(0L).equals(sopLogs.getFsUserId());
+                    if ("济南联志健康".equals(signProjectName)) {
+                        check = true;
+                    }
+                    if (sopLogs.getFsUserId() != null && check) {
                         addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId, logVo,2);
                         sortLink = generateSmsShortLink(setting, logVo, sendTime, courseId, videoId,
                                 qwUserId, companyUserId, companyId, externalId, isOfficial, sopLogs.getFsUserId());

+ 22 - 2
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java

@@ -158,7 +158,10 @@ public interface FsStoreOrderItemScrmMapper
             "and o.company_id =#{maps.companyId} " +
             "</if>" +
             "<if test = 'maps.isHealth != null and maps.isHealth !=  \"\"  '> " +
-            " and (o.company_id is null or o.order_type = 2 or o.order_type = 3)" +
+            " and (o.company_id is null or o.order_type = 2 or o.order_type = 3<if test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1\"> or o.order_type = 5</if>)" +
+            "</if>" +
+            "<if test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 0\">" +
+            " and IFNULL(o.order_type, 0) != 5" +
             "</if>" +
             "<if test = 'maps.notHealth != null  '> " +
             "and o.company_id is not null and o.order_type = 0 " +
@@ -179,7 +182,14 @@ public interface FsStoreOrderItemScrmMapper
             "and o.order_type =#{maps.orderType} " +
             "</if>" +
             "<if test = 'maps.orderType != null and maps.orderType == -1    '> " +
+            "<choose>" +
+            "<when test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1\">" +
+            "and o.order_type in (2, 3, 5) " +
+            "</when>" +
+            "<otherwise>" +
             "and o.order_type in (2, 3) " +
+            "</otherwise>" +
+            "</choose>" +
             "</if>" +
             "<if test = 'maps.payType != null    '> " +
             "and o.pay_type =#{maps.payType} " +
@@ -313,7 +323,10 @@ public interface FsStoreOrderItemScrmMapper
             "and o.company_id =#{maps.companyId} " +
             "</if>" +
             "<if test = 'maps.isHealth != null and maps.isHealth !=  \"\"  '> " +
-            " and (o.company_id is null or o.order_type = 2 or o.order_type = 3)" +
+            " and (o.company_id is null or o.order_type = 2 or o.order_type = 3<if test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1\"> or o.order_type = 5</if>)" +
+            "</if>" +
+            "<if test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 0\">" +
+            " and IFNULL(o.order_type, 0) != 5" +
             "</if>" +
             "<if test = 'maps.notHealth != null  '> " +
             "and o.company_id is not null and o.order_type = 0 " +
@@ -334,7 +347,14 @@ public interface FsStoreOrderItemScrmMapper
             "and o.order_type =#{maps.orderType} " +
             "</if>" +
             "<if test = 'maps.orderType != null and maps.orderType == -1    '> " +
+            "<choose>" +
+            "<when test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1\">" +
+            "and o.order_type in (2, 3, 5) " +
+            "</when>" +
+            "<otherwise>" +
             "and o.order_type in (2, 3) " +
+            "</otherwise>" +
+            "</choose>" +
             "</if>" +
             "<if test = 'maps.payType != null    '> " +
             "and o.pay_type =#{maps.payType} " +

+ 22 - 2
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java

@@ -759,7 +759,10 @@ public interface FsStoreOrderScrmMapper
             "and sp_latest.pay_code like CONCAT('%', #{maps.payCode}, '%') " +
             "</if>" +
             "<if test = 'maps.isHealth != null and maps.isHealth !=  \"\"  '> " +
-            "and (o.company_id is null or o.order_type = 2 or o.order_type = 3) " +
+            "and (o.company_id is null or o.order_type = 2 or o.order_type = 3<if test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1\"> or o.order_type = 5</if>) " +
+            "</if>" +
+            "<if test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 0\">" +
+            " and IFNULL(o.order_type, 0) != 5" +
             "</if>" +
             "<if test = 'maps.notHealth != null and maps.notHealth !=  \"\"  '> " +
             "and o.company_id is not null and o.order_type = 0 " +
@@ -777,7 +780,14 @@ public interface FsStoreOrderScrmMapper
             "and o.order_type =#{maps.orderType} " +
             "</if>" +
             "<if test = 'maps.orderType != null and maps.orderType == -1    '> " +
+            "<choose>" +
+            "<when test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1\">" +
+            "and o.order_type in (2, 3, 5) " +
+            "</when>" +
+            "<otherwise>" +
             "and o.order_type in (2, 3) " +
+            "</otherwise>" +
+            "</choose>" +
             "</if>" +
             "<if test = 'maps.payType != null    '> " +
             "and o.pay_type =#{maps.payType} " +
@@ -1433,7 +1443,10 @@ public interface FsStoreOrderScrmMapper
             "and sp_latest.pay_code like CONCAT('%', #{maps.payCode}, '%') " +
             "</if>" +
             "<if test = 'maps.isHealth != null and maps.isHealth !=  \"\"  '> " +
-            "and (o.company_id is null or o.order_type = 2 or o.order_type = 3) " +
+            "and (o.company_id is null or o.order_type = 2 or o.order_type = 3<if test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1\"> or o.order_type = 5</if>) " +
+            "</if>" +
+            "<if test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 0\">" +
+            " and IFNULL(o.order_type, 0) != 5" +
             "</if>" +
             "<if test = 'maps.notHealth != null and maps.notHealth !=  \"\"  '> " +
             "and o.company_id is not null and o.order_type = 0 " +
@@ -1451,7 +1464,14 @@ public interface FsStoreOrderScrmMapper
             "and o.order_type =#{maps.orderType} " +
             "</if>" +
             "<if test = 'maps.orderType != null and maps.orderType == -1    '> " +
+            "<choose>" +
+            "<when test=\"maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1\">" +
+            "and o.order_type in (2, 3, 5) " +
+            "</when>" +
+            "<otherwise>" +
             "and o.order_type in (2, 3) " +
+            "</otherwise>" +
+            "</choose>" +
             "</if>" +
             "<if test = 'maps.payType != null    '> " +
             "and o.pay_type =#{maps.payType} " +

+ 3 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderParam.java

@@ -150,4 +150,7 @@ public class FsStoreOrderParam extends BaseEntity implements Serializable
 
     /** ID */
     private Long taskId;
+
+    /** 是否展示直播中奖订单:1展示 0不展示(由字典 store_mall_func 控制) */
+    private Integer showLiveLotteryOrder;
 }

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java

@@ -243,6 +243,11 @@ public interface IFsStoreOrderScrmService
 
     Boolean isEntityNull(FsStoreOrderParam param);
 
+    /**
+     * 健康商城订单查询:根据字典 store_mall_func 设置是否查询直播中奖订单
+     */
+    void applyHealthStoreQueryFlags(FsStoreOrderParam param);
+
     int uploadCredentials(FsStoreOrderScrm order);
 
     String getCredentialsById(Long id);

+ 27 - 23
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java

@@ -340,29 +340,7 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
      * 售后申请业务(由 {@link #applyForAfterSales} 在 Redis 分布式锁内调用,按订单号互斥)
      */
     private R doApplyForAfterSales(long userId, FsStoreAfterSalesParam storeAfterSalesParam) {
-        // 查询配置:是否删除历史售后数据
-        try {
-            String deleteAfterSalesConfig = configService.selectConfigByKey("delete_after_sales");
-            if (StringUtils.isNotEmpty(deleteAfterSalesConfig) && "true".equalsIgnoreCase(deleteAfterSalesConfig.trim())) {
-                // 查询历史售后数据,将 is_del 设置为 1
-                FsStoreAfterSalesScrm queryAfterSales = new FsStoreAfterSalesScrm();
-                queryAfterSales.setOrderCode(storeAfterSalesParam.getOrderCode());
-                List<FsStoreAfterSalesScrm> historyAfterSalesList = fsStoreAfterSalesMapper.selectFsStoreAfterSalesList(queryAfterSales);
-                if (historyAfterSalesList != null && !historyAfterSalesList.isEmpty()) {
-                    for (FsStoreAfterSalesScrm historyAfterSales : historyAfterSalesList) {
-                        if (historyAfterSales.getIsDel() != null && historyAfterSales.getIsDel() == 0) {
-                            FsStoreAfterSalesScrm updateAfterSales = new FsStoreAfterSalesScrm();
-                            updateAfterSales.setId(historyAfterSales.getId());
-                            updateAfterSales.setIsDel(1);
-                            fsStoreAfterSalesMapper.updateFsStoreAfterSales(updateAfterSales);
-                            logger.info("删除历史售后数据,售后ID:{},订单号:{}", historyAfterSales.getId(), storeAfterSalesParam.getOrderCode());
-                        }
-                    }
-                }
-            }
-        } catch (Exception e) {
-            logger.error("查询或更新历史售后数据失败", e);
-        }
+
 
         FsStoreOrderScrm order=orderService.selectFsStoreOrderByOrderCode(storeAfterSalesParam.getOrderCode());
         Integer orderStatus = order.getStatus();
@@ -390,9 +368,35 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
         if(order.getStatus()== OrderInfoEnum.STATUS_NE1.getValue()){
             return R.error("已提交申请,等待处理");
         }
+        if(order.getStatus()== OrderInfoEnum.STATUS_NE2.getValue()){
+            return R.error("订单已退款,不能重复申请");
+        }
 //        if(storeAfterSalesParam.getRefundAmount().compareTo(order.getPayPrice())==1){
 //            return R.error("退款金额不能大于支付金额");
 //        }
+        // 查询配置:是否删除历史售后数据
+        try {
+            String deleteAfterSalesConfig = configService.selectConfigByKey("delete_after_sales");
+            if (StringUtils.isNotEmpty(deleteAfterSalesConfig) && "true".equalsIgnoreCase(deleteAfterSalesConfig.trim())) {
+                // 查询历史售后数据,将 is_del 设置为 1
+                FsStoreAfterSalesScrm queryAfterSales = new FsStoreAfterSalesScrm();
+                queryAfterSales.setOrderCode(storeAfterSalesParam.getOrderCode());
+                List<FsStoreAfterSalesScrm> historyAfterSalesList = fsStoreAfterSalesMapper.selectFsStoreAfterSalesList(queryAfterSales);
+                if (historyAfterSalesList != null && !historyAfterSalesList.isEmpty()) {
+                    for (FsStoreAfterSalesScrm historyAfterSales : historyAfterSalesList) {
+                        if (historyAfterSales.getIsDel() != null && historyAfterSales.getIsDel() == 0) {
+                            FsStoreAfterSalesScrm updateAfterSales = new FsStoreAfterSalesScrm();
+                            updateAfterSales.setId(historyAfterSales.getId());
+                            updateAfterSales.setIsDel(1);
+                            fsStoreAfterSalesMapper.updateFsStoreAfterSales(updateAfterSales);
+                            logger.info("删除历史售后数据,售后ID:{},订单号:{}", historyAfterSales.getId(), storeAfterSalesParam.getOrderCode());
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            logger.error("查询或更新历史售后数据失败", e);
+        }
         //已完成订单七天后不能申请退款
         if(order.getStatus().equals(OrderInfoEnum.STATUS_3.getValue())) {
             String json=configService.selectConfigByKey("store.config");

+ 22 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -227,6 +227,9 @@ import static com.fs.hisStore.constants.UserAppsLockConstant.LOCK_KEY_PAY;
 public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
 
     private static final String STORE_ORDER_CREATE_STATUS_CHECK = "store_order_create_status_check";
+    private static final String STORE_MALL_FUNC = "store_mall_func";
+    /** 字典 dict_sort=1:是否展示直播中奖订单 */
+    private static final long DICT_SORT_SHOW_LIVE_LOTTERY_ORDER = 1L;
 
     Logger logger = LoggerFactory.getLogger(getClass());
     @Autowired
@@ -7920,6 +7923,25 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                 .anyMatch(item -> "1".equals(StringUtils.trimToEmpty(item.getDictValue())));
     }
 
+    @Override
+    public void applyHealthStoreQueryFlags(FsStoreOrderParam param) {
+        if (param == null) {
+            return;
+        }
+        param.setShowLiveLotteryOrder(isShowLiveLotteryOrderEnabled() ? 1 : 0);
+    }
+
+    private boolean isShowLiveLotteryOrderEnabled() {
+        List<SysDictData> dictList = sysDictTypeService.selectDictDataByType(STORE_MALL_FUNC);
+        if (dictList == null || dictList.isEmpty()) {
+            return false;
+        }
+        return dictList.stream()
+                .filter(item -> "0".equals(item.getStatus()))
+                .filter(item -> item.getDictSort() != null && DICT_SORT_SHOW_LIVE_LOTTERY_ORDER == item.getDictSort())
+                .anyMatch(item -> "1".equals(StringUtils.trimToEmpty(item.getDictValue())));
+    }
+
     private R validateOrderProductOnShelf(List<FsStoreCartQueryVO> carts) {
         List<String> offShelfNames = new ArrayList<>();
         for (FsStoreCartQueryVO cart : carts) {

+ 22 - 22
fs-service/src/main/java/com/fs/hisStore/service/impl/MergedOrderServiceImpl.java

@@ -354,7 +354,7 @@ public class MergedOrderServiceImpl implements IMergedOrderService
             return R.error("订单类型不能为空");
         }
 
-        if (orderType == 0) {
+//        if (orderType == 0) {
             // 商城订单
             com.fs.hisStore.domain.FsStoreOrderScrm storeOrder = storeOrderService.selectFsStoreOrderById(orderId);
             if (storeOrder == null) {
@@ -372,27 +372,27 @@ public class MergedOrderServiceImpl implements IMergedOrderService
             } else {
                 return R.error("删除失败");
             }
-        } else if (orderType == 2) {
-            // 直播订单
-            com.fs.live.domain.LiveOrder liveOrder = liveOrderService.selectLiveOrderByOrderId(String.valueOf(orderId));
-            if (liveOrder == null) {
-                return R.error("订单不存在");
-            }
-            // 检查订单是否属于当前用户
-            if (!liveOrder.getUserId().equals(userId)) {
-                return R.error("无权删除该订单");
-            }
-            // 逻辑删除:设置 isDel = "1"
-            liveOrder.setIsDel("1");
-            int result = liveOrderService.updateLiveOrder(liveOrder);
-            if (result > 0) {
-                return R.ok("删除成功");
-            } else {
-                return R.error("删除失败");
-            }
-        } else {
-            return R.error("订单类型错误");
-        }
+//        } else if (orderType == 2) {
+//            // 直播订单
+//            com.fs.live.domain.LiveOrder liveOrder = liveOrderService.selectLiveOrderByOrderId(String.valueOf(orderId));
+//            if (liveOrder == null) {
+//                return R.error("订单不存在");
+//            }
+//            // 检查订单是否属于当前用户
+//            if (!liveOrder.getUserId().equals(userId)) {
+//                return R.error("无权删除该订单");
+//            }
+//            // 逻辑删除:设置 isDel = "1"
+//            liveOrder.setIsDel("1");
+//            int result = liveOrderService.updateLiveOrder(liveOrder);
+//            if (result > 0) {
+//                return R.ok("删除成功");
+//            } else {
+//                return R.error("删除失败");
+//            }
+//        } else {
+//            return R.error("订单类型错误");
+//        }
     }
 
     @Override

+ 1 - 1
fs-service/src/main/java/com/fs/live/mapper/LiveLotteryConfMapper.java

@@ -105,6 +105,6 @@ public interface LiveLotteryConfMapper {
     @Select("select lulr.*,fu.nickname as user_name,fsp.product_name as product_name\n" +
             "        from live_user_lottery_record lulr left join fs_user fu on lulr.user_id = fu.user_id\n" +
             "        left join fs_store_product_scrm fsp on lulr.product_id = fsp.product_id \n" +
-            "        where lulr.user_id = #{userId}")
+            "        where lulr.user_id = #{userId} order by create_time desc")
     List<LiveUserLotteryRecordVo> selectLiveUserLotteryRecordByUserId(long userId);
 }

+ 4 - 0
fs-service/src/main/java/com/fs/qw/domain/QwExternalContact.java

@@ -61,6 +61,10 @@ public class QwExternalContact extends BaseEntity
     @Excel(name = "备注电话号码")
     private String remarkMobiles;
 
+    /** 联系手机号码,多个英文逗号分隔 */
+    @Excel(name = "联系手机号码")
+    private String contactMobiles;
+
     /** 备注企业名称 */
     @Excel(name = "备注企业名称")
     private String remarkCorpName;

+ 3 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

@@ -664,6 +664,9 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
             "limit 1")
     QwExternalContact queryQwUserIdIsAddContact(@Param("qwUserId") Long qwUserId, @Param("phone") String phone, @Param("addWay") int addWay);
 
+    @Update("UPDATE qw_external_contact SET contact_mobiles = #{contactMobiles} WHERE id = #{id}")
+    int updateContactMobilesById(@Param("id") Long id, @Param("contactMobiles") String contactMobiles);
+
     /**
      * 同主体下指定外部联系人的全部跟进记录
      */

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

@@ -272,6 +272,8 @@ public interface IQwExternalContactService extends IService<QwExternalContact> {
 
     int batchUpdateQwExternalContactMandatoryRegistration(List<QwMandatoryRegistrParam> batchList);
 
+    int updateContactMobilesById(Long id, String contactMobiles);
+
     /**
      * 企微用户-查询外部联系人信息
      */

+ 5 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java

@@ -6401,5 +6401,10 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         return false;
     }
 
+    @Override
+    public int updateContactMobilesById(Long id, String contactMobiles) {
+        return qwExternalContactMapper.updateContactMobilesById(id, contactMobiles);
+    }
+
 
 }

+ 13 - 2
fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java

@@ -78,6 +78,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 import javax.validation.ConstraintViolationException;
@@ -221,6 +222,8 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
     @Autowired
     private CompanyFsUserMapper companyFsUserMapper;
 
+    @Value("${cloud_host.company_name}")
+    private String signProjectName;
 
 
     @Override
@@ -1672,7 +1675,11 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
                             break;
                         case "21":
-                            if (sopLogs.getFsUserId() != null && !Long.valueOf(0L).equals(sopLogs.getFsUserId())) {
+                            boolean check = !Long.valueOf(0L).equals(sopLogs.getFsUserId());
+                            if ("济南联志健康".equals(signProjectName)) {
+                                check = true;
+                            }
+                            if (sopLogs.getFsUserId() != null && check) {
                                 addWatchLogIfNeeded(item.getSopId(), param.getVideoId(), param.getCourseId(), item.getFsUserId(), qwUserId, companyUserId, companyId,
                                         item.getExternalId(), item.getStartTime(), createTime,2);
                                 String link = createSmsShortLink(st, param.getCorpId(), createTime, param.getCourseId(), param.getVideoId(),
@@ -2627,7 +2634,11 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     }
                     break;
                 case "21":
-                    if (sopLogs.getFsUserId() != null && !Long.valueOf(0L).equals(sopLogs.getFsUserId())) {
+                    boolean check = !Long.valueOf(0L).equals(sopLogs.getFsUserId());
+                    if ("济南联志健康".equals(signProjectName)) {
+                        check = true;
+                    }
+                    if (sopLogs.getFsUserId() != null && check) {
                         addWatchLogIfNeeded(item.getSopId(), param.getVideoId(), param.getCourseId(), item.getFsUserId(), String.valueOf(qwUser.getId()), companyUserId, companyId,
                                 item.getExternalId(), item.getStartTime(), dataTime,2);
                         String link = createSmsShortLink(st, param.getCorpId(), dataTime, param.getCourseId(), param.getVideoId(),

+ 8 - 0
fs-service/src/main/resources/db/changelog/changes/20260613-live-user-add-is-del.sql

@@ -116,3 +116,11 @@ ALTER TABLE fs_course_coupon_user
 ALTER TABLE qw_external_contact
     ADD COLUMN ipad_user_id BIGINT DEFAULT NULL COMMENT 'iPad 侧 user_id';
 --rollback ALTER TABLE qw_external_contact DROP COLUMN ipad_user_id;
+
+--changeset yhq:20260630-qw-external-contact-contact-mobiles
+--preconditions onFail:MARK_RAN
+--precondition-sql-check expectedResult:1 SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'qw_external_contact'
+--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'qw_external_contact' AND column_name = 'contact_mobiles'
+ALTER TABLE qw_external_contact
+    ADD COLUMN contact_mobiles VARCHAR(500) NULL DEFAULT NULL COMMENT '联系手机号码,多个英文逗号分隔';
+--rollback ALTER TABLE qw_external_contact DROP COLUMN contact_mobiles;

+ 33 - 0
fs-service/src/main/resources/db/changelog/dictData/dict_data_update.sql

@@ -41,3 +41,36 @@ VALUES
     (10, 'APP文本', '15', 'sys_qwSopAi_contentType', NULL, 'default', 'N', '0', 'admin', NOW(), '', NULL, NULL);
 --rollback DELETE FROM sys_dict_data WHERE dict_type = 'sys_qwSopAi_contentType' AND dict_value = '15';
 
+--changeset yhq:20260625-sys-qw-external-contact-func-dict
+--preconditions onFail:MARK_RAN
+--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM sys_dict_type WHERE dict_type = 'sys_qw_external_contact_func'
+--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM sys_dict_data WHERE dict_type = 'sys_qw_external_contact_func'
+INSERT INTO `sys_dict_type`
+(`dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`)
+VALUES
+    ('外部联系人功能', 'sys_qw_external_contact_func', '0', 'admin', NOW(), '', NULL, '外部联系人相关功能开关,各字典项值为1时开启');
+
+INSERT INTO `sys_dict_data`
+(`dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`)
+VALUES
+    (1, '是否展示修改外部联系人手机号码', '0', 'sys_qw_external_contact_func', '', 'info', 'Y', '0', 'admin', NOW(), '', NULL, '值为1时展示修改外部联系人手机号码按钮,默认0不展示,sort就是id不能变'),
+    (2, '是否开启根据外部联系人手机号码发送短信', '0', 'sys_qw_external_contact_func', '', 'info', 'Y', '0', 'admin', NOW(), '', NULL, '值为1时开启根据外部联系人手机号码发送短信,默认0不开启,sort就是id不能变');
+--rollback DELETE FROM sys_dict_data WHERE dict_type = 'sys_qw_external_contact_func';
+--rollback DELETE FROM sys_dict_type WHERE dict_type = 'sys_qw_external_contact_func';
+
+--changeset yhq:20260630-store-mall-func-dict
+--preconditions onFail:MARK_RAN
+--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM sys_dict_type WHERE dict_type = 'store_mall_func'
+--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM sys_dict_data WHERE dict_type = 'store_mall_func'
+INSERT INTO `sys_dict_type`
+(`dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`)
+VALUES
+    ('商城功能相关控制', 'store_mall_func', '0', 'admin', NOW(), '', NULL, '商城相关功能开关,各字典项值为1时开启');
+
+INSERT INTO `sys_dict_data`
+(`dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`)
+VALUES
+    (1, '是否展示直播中奖订单', '0', 'store_mall_func', '', 'info', 'Y', '0', 'admin', NOW(), '', NULL, '值为1时在健康商城订单列表展示并查询直播中奖订单(order_type=5),默认0不展示,sort就是id不能变');
+--rollback DELETE FROM sys_dict_data WHERE dict_type = 'store_mall_func';
+--rollback DELETE FROM sys_dict_type WHERE dict_type = 'store_mall_func';
+

+ 88 - 15
fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml

@@ -1327,7 +1327,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             </if>
             <if test="maps.isHealth != null and maps.isHealth !=  ''   ">
                 and (o.company_id is null
-                or o.order_type = 2 or o.order_type = 3)
+                or o.order_type = 2 or o.order_type = 3<if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                    or o.order_type = 5</if>)
+            </if>
+            <if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 0">
+                and IFNULL(o.order_type, 0) != 5
             </if>
             <if test="maps.notHealth != null  ">
                 and o.company_id is not null and o.order_type = 0
@@ -1351,7 +1355,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 and o.order_type =#{maps.orderType}
             </if>
             <if test="maps.orderType != null and maps.orderType == -1">
-                and o.order_type in (2, 3)
+                <choose>
+                    <when test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                        and o.order_type in (2, 3, 5)
+                    </when>
+                    <otherwise>
+                        and o.order_type in (2, 3)
+                    </otherwise>
+                </choose>
             </if>
             <if test="maps.payType != null    ">
                 and o.pay_type =#{maps.payType}
@@ -1515,7 +1526,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             </if>
             <if test="maps.isHealth != null and maps.isHealth !=  ''   ">
                 and (o.company_id is null
-                or o.order_type = 2 or o.order_type = 3)
+                or o.order_type = 2 or o.order_type = 3<if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                    or o.order_type = 5</if>)
+            </if>
+            <if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 0">
+                and IFNULL(o.order_type, 0) != 5
             </if>
             <if test="maps.notHealth != null  ">
                 and o.company_id is not null and o.order_type = 0
@@ -1539,7 +1554,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 and o.order_type =#{maps.orderType}
             </if>
             <if test="maps.orderType != null and maps.orderType == -1">
-                and o.order_type in (2, 3)
+                <choose>
+                    <when test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                        and o.order_type in (2, 3, 5)
+                    </when>
+                    <otherwise>
+                        and o.order_type in (2, 3)
+                    </otherwise>
+                </choose>
             </if>
             <if test="maps.payType != null    ">
                 and o.pay_type =#{maps.payType}
@@ -1680,7 +1702,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             </if>
             <if test="maps.isHealth != null and maps.isHealth !=  ''   ">
                 and (o.company_id is null
-                or o.order_type = 2 or o.order_type = 3)
+                or o.order_type = 2 or o.order_type = 3<if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                    or o.order_type = 5</if>)
+            </if>
+            <if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 0">
+                and IFNULL(o.order_type, 0) != 5
             </if>
             <if test="maps.notHealth != null  ">
                 and o.company_id is not null and o.order_type = 0
@@ -1704,7 +1730,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 and o.order_type =#{maps.orderType}
             </if>
             <if test="maps.orderType != null and maps.orderType == -1">
-                and o.order_type in (2, 3)
+                <choose>
+                    <when test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                        and o.order_type in (2, 3, 5)
+                    </when>
+                    <otherwise>
+                        and o.order_type in (2, 3)
+                    </otherwise>
+                </choose>
             </if>
             <if test="maps.payType != null    ">
                 and o.pay_type =#{maps.payType}
@@ -1849,7 +1882,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             </if>
             <if test="maps.isHealth != null and maps.isHealth !=  ''   ">
                 and (o.company_id is null
-                or o.order_type = 2 or o.order_type = 3)
+                or o.order_type = 2 or o.order_type = 3<if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                    or o.order_type = 5</if>)
+            </if>
+            <if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 0">
+                and IFNULL(o.order_type, 0) != 5
             </if>
             <if test="maps.notHealth != null  ">
                 and o.company_id is not null and o.order_type = 0
@@ -1873,7 +1910,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 and o.order_type =#{maps.orderType}
             </if>
             <if test="maps.orderType != null and maps.orderType == -1">
-                and o.order_type in (2, 3)
+                <choose>
+                    <when test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                        and o.order_type in (2, 3, 5)
+                    </when>
+                    <otherwise>
+                        and o.order_type in (2, 3)
+                    </otherwise>
+                </choose>
             </if>
             <if test="maps.payType != null    ">
                 and o.pay_type =#{maps.payType}
@@ -2018,7 +2062,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             </if>
             <if test="maps.isHealth != null and maps.isHealth !=  ''   ">
                 and (o.company_id is null
-                or o.order_type = 2 or o.order_type = 3)
+                or o.order_type = 2 or o.order_type = 3<if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                    or o.order_type = 5</if>)
+            </if>
+            <if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 0">
+                and IFNULL(o.order_type, 0) != 5
             </if>
             <if test="maps.notHealth != null  ">
                 and o.company_id is not null and o.order_type = 0
@@ -2039,7 +2087,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 and o.order_type =#{maps.orderType}
             </if>
             <if test="maps.orderType != null and maps.orderType == -1">
-                and o.order_type in (2, 3)
+                <choose>
+                    <when test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                        and o.order_type in (2, 3, 5)
+                    </when>
+                    <otherwise>
+                        and o.order_type in (2, 3)
+                    </otherwise>
+                </choose>
             </if>
             <if test="maps.payType != null    ">
                 and o.pay_type =#{maps.payType}
@@ -2229,7 +2284,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             </if>
             <if test="maps.isHealth != null and maps.isHealth !=  ''   ">
                 and (o.company_id is null
-                or o.order_type = 2 or o.order_type = 3)
+                or o.order_type = 2 or o.order_type = 3<if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                    or o.order_type = 5</if>)
+            </if>
+            <if test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 0">
+                and IFNULL(o.order_type, 0) != 5
             </if>
             <if test="maps.notHealth != null  ">
                 and o.company_id is not null and o.order_type = 0
@@ -2253,7 +2312,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 and o.order_type =#{maps.orderType}
             </if>
             <if test="maps.orderType != null and maps.orderType == -1">
-                and o.order_type in (2, 3)
+                <choose>
+                    <when test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                        and o.order_type in (2, 3, 5)
+                    </when>
+                    <otherwise>
+                        and o.order_type in (2, 3)
+                    </otherwise>
+                </choose>
             </if>
             <if test="maps.payType != null    ">
                 and o.pay_type =#{maps.payType}
@@ -2429,9 +2495,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <if test="maps.orderType != null and maps.orderType != -1">
             AND o.order_type = #{maps.orderType}
         </if>
-        <if test="maps.orderType != null and maps.orderType == -1">
-            AND o.order_type in (2, 3)
-        </if>
+            <if test="maps.orderType != null and maps.orderType == -1">
+                <choose>
+                    <when test="maps.showLiveLotteryOrder != null and maps.showLiveLotteryOrder == 1">
+                        AND o.order_type in (2, 3, 5)
+                    </when>
+                    <otherwise>
+                        AND o.order_type in (2, 3)
+                    </otherwise>
+                </choose>
+            </if>
         <if test="maps.payType != null">
             AND o.pay_type = #{maps.payType}
         </if>

+ 3 - 1
fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml

@@ -17,6 +17,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="description"    column="description"    />
         <result property="tagIds"    column="tag_ids"    />
         <result property="remarkMobiles"    column="remark_mobiles"    />
+        <result property="contactMobiles"    column="contact_mobiles"    />
         <result property="remarkCorpName"    column="remark_corp_name"    />
         <result property="addWay"    column="add_way"    />
         <result property="operUserid"    column="oper_userid"    />
@@ -52,7 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <sql id="selectQwExternalContactVo">
         select id,qw_user_id,register_time,state,way_id,stage_status,first_time,open_id,is_interact,level, unionid,
                user_id,transfer_time,loss_time,del_time,transfer_num, external_user_id,transfer_status,status,create_time,
-               name, avatar, type, gender, remark, description, tag_ids, remark_mobiles, remark_corp_name, add_way, oper_userid,
+               name, avatar, type, gender, remark, description, tag_ids, remark_mobiles, contact_mobiles, remark_corp_name, add_way, oper_userid,
                corp_id, company_id, company_user_id, customer_id, fs_user_id,is_reply,fs_user_phone,add_source_type,qw_acquisition_assistant_id
         from qw_external_contact
     </sql>
@@ -344,6 +345,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="description != null">description = #{description},</if>
             <if test="tagIds != null">tag_ids = #{tagIds},</if>
             <if test="remarkMobiles != null">remark_mobiles = #{remarkMobiles},</if>
+            <if test="contactMobiles != null">contact_mobiles = #{contactMobiles},</if>
             <if test="remarkCorpName != null">remark_corp_name = #{remarkCorpName},</if>
             <if test="addWay != null">add_way = #{addWay},</if>
             <if test="operUserid != null">oper_userid = #{operUserid},</if>

+ 6 - 0
fs-service/src/main/resources/mapper/qw/QwSopSmsLogsMapper.xml

@@ -163,6 +163,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         SELECT
         id,
         fs_user_id,
+        contact_id,
+        company_id,
+        company_user_id,
         content,
         sms_template_code,
         sop_log_id,
@@ -246,6 +249,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         SELECT
         id,
         fs_user_id,
+        contact_id,
+        company_id,
+        company_user_id,
         content,
         sms_template_code,
         sop_log_id,