Forráskód Böngészése

润天发送短信以及回调相关代码迁移

yjwang 2 napja
szülő
commit
562a7d1afa
21 módosított fájl, 2214 hozzáadás és 21 törlés
  1. 150 21
      fs-service/src/main/java/com/fs/common/service/impl/SmsServiceImpl.java
  2. 62 0
      fs-service/src/main/java/com/fs/company/domain/CompanyDetectionPhoneDailyStatistics.java
  3. 78 0
      fs-service/src/main/java/com/fs/company/domain/CompanyDetectionPhoneRecord.java
  4. 58 0
      fs-service/src/main/java/com/fs/company/domain/CompanySmsCommonDailyStatistics.java
  5. 98 0
      fs-service/src/main/java/com/fs/company/domain/CompanySmsCommonLogs.java
  6. 74 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyDetectionPhoneDailyStatisticsMapper.java
  7. 62 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyDetectionPhoneRecordMapper.java
  8. 87 0
      fs-service/src/main/java/com/fs/company/mapper/CompanySmsCommonLogsMapper.java
  9. 31 0
      fs-service/src/main/java/com/fs/company/param/CompanyDetectionPhoneSendLinkParam.java
  10. 34 0
      fs-service/src/main/java/com/fs/company/param/SmsSendFsUserParam.java
  11. 68 0
      fs-service/src/main/java/com/fs/company/service/ICompanyDetectionPhoneDailyStatisticsService.java
  12. 74 0
      fs-service/src/main/java/com/fs/company/service/ICompanyDetectionPhoneRecordService.java
  13. 72 0
      fs-service/src/main/java/com/fs/company/service/ICompanySmsCommonLogsService.java
  14. 180 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyDetectionPhoneDailyStatisticsServiceImpl.java
  15. 303 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyDetectionPhoneRecordServiceImpl.java
  16. 319 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanySmsCommonLogsServiceImpl.java
  17. 88 0
      fs-service/src/main/java/com/fs/company/util/DetectionPhoneModelVerifyUtil.java
  18. 13 0
      fs-service/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java
  19. 101 0
      fs-service/src/main/resources/mapper/company/CompanyDetectionPhoneDailyStatisticsMapper.xml
  20. 116 0
      fs-service/src/main/resources/mapper/company/CompanyDetectionPhoneRecordMapper.xml
  21. 146 0
      fs-service/src/main/resources/mapper/company/CompanySmsCommonLogsMapper.xml

+ 150 - 21
fs-service/src/main/java/com/fs/common/service/impl/SmsServiceImpl.java

@@ -3,6 +3,8 @@ package com.fs.common.service.impl;
 import cn.hutool.http.HttpRequest;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 
@@ -11,10 +13,7 @@ import com.fs.common.utils.StringUtils;
 import com.fs.common.vo.SmsNotifyVO;
 import com.fs.common.vo.SmsSendItemVO;
 import com.fs.common.vo.SmsSendVO;
-import com.fs.company.domain.CompanySms;
-import com.fs.company.domain.CompanySmsLogs;
-import com.fs.company.domain.CompanySmsTemp;
-import com.fs.company.domain.CompanyUser;
+import com.fs.company.domain.*;
 import com.fs.company.service.ICompanySmsLogsService;
 import com.fs.company.service.ICompanySmsService;
 import com.fs.company.service.ICompanySmsTempService;
@@ -30,8 +29,13 @@ import com.fs.his.domain.FsStoreOrder;
 import com.fs.his.mapper.FsPackageOrderMapper;
 import com.fs.his.mapper.FsStoreOrderMapper;
 import com.fs.his.vo.FsPackageOrderVO;
+import com.fs.qw.domain.QwSopSmsLogs;
+import com.fs.qw.mapper.QwSopSmsLogsMapper;
+import com.fs.qw.service.IQwSopSmsLogsService;
 import com.fs.sms.domain.SendSmsReturn;
 import com.fs.sms.service.impl.SmsTServiceImpl;
+import com.fs.sop.domain.QwSopLogs;
+import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.google.gson.Gson;
@@ -48,6 +52,7 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
+import com.fs.company.service.ICompanySmsCommonLogsService;
 
 @Service
 @Slf4j
@@ -79,6 +84,18 @@ public class SmsServiceImpl implements ISmsService
     @Autowired
     private FsPackageOrderMapper packageOrderMapper;
 
+    @Autowired
+    private ICompanySmsCommonLogsService smsCommonLogsService;
+
+    @Autowired
+    private IQwSopSmsLogsService qwSopSmsLogsService;
+
+    @Autowired
+    private QwSopLogsMapper qwSopLogsMapper;
+
+    @Autowired
+    private QwSopSmsLogsMapper qwSopSmsLogsMapper;
+
     @Override
     public R sendTSms(String mobile, String code) {
 //        try{
@@ -157,40 +174,80 @@ public class SmsServiceImpl implements ISmsService
 
     @Override
     public String smsNotify(String json) {
+        log.info("短信回调参数:{}", json);
         List<SmsNotifyVO> list = JSONUtil.parseArray(json).toList(SmsNotifyVO.class);
-        if(list!=null){
-            for(SmsNotifyVO vo:list){
-                if(vo.getFlag()!=null&&vo.getFlag()==0){
+        if (list != null) {
+            for (SmsNotifyVO vo : list) {
+                if (vo.getFlag() != null && vo.getFlag() == 0) {
                     //手机用户上行
-                    CompanySmsLogs logs=smsLogsService.selectCompanySmsLogsByMobile(vo.getMobile());
-                    if(logs!=null){
+                    CompanySmsLogs logs = smsLogsService.selectCompanySmsLogsByMobile(vo.getMobile());
+                    if (logs != null) {
                         logs.setReplyContent(vo.getContent());
                         smsLogsService.updateCompanySmsLogs(logs);
+                        if(logs.getSopSmsLogId() != null){
+                            updateQwSopSmsLogsByUuid(logs.getSopSmsLogId(), null, null, vo.getContent(),logs.getSmsIndex());
+                        }
                     }
-                }
-                else if(vo.getFlag()!=null&&vo.getFlag()==1){
-                    CompanySmsLogs logs=smsLogsService.selectCompanySmsLogsByMid(vo.getMid());
-                    if(logs!=null&&logs.getStatus().equals(0)){
+                    CompanySmsCommonLogs commonLogs = smsCommonLogsService.selectCompanySmsCommonLogsByMobile(vo.getMobile());
+                    if (commonLogs != null) {
+                        commonLogs.setReplyContent(vo.getContent());
+                        smsCommonLogsService.updateCompanySmsCommonLogs(commonLogs);
+                    }
+                } else if (vo.getFlag() != null && vo.getFlag() == 1) {
+                    CompanySmsLogs logs = smsLogsService.selectCompanySmsLogsByMid(vo.getMid());
+                    if (logs != null && logs.getStatus().equals(0)) {
                         logs.setStat(vo.getStat());
-                        if(vo.getTime()!=null){
+                        if (vo.getTime() != null) {
                             try {
-                                SimpleDateFormat format = new   SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                                 logs.setSendTime(format.parse(vo.getTime()));
-                            }
-                            catch (Exception e){
+                            } catch (Exception e) {
 
                             }
                         }
                         //状态报告
-                        if(vo.getStat().equals("DELIVRD")){
+                        if (vo.getStat().equals("DELIVRD")) {
                             logs.setStatus(1);
-                        }
-                        else{
+                        } else {
                             logs.setStatus(-1);
-                            companySmsService.addCompanySms(logs.getCompanyId(),logs.getNumber());
+                            companySmsService.addCompanySms(logs.getCompanyId(), logs.getNumber());
                         }
                         smsLogsService.updateCompanySmsLogs(logs);
                     }
+
+                    if(logs != null && logs.getSopSmsLogId() != null){
+                        Integer sopStatus = "DELIVRD".equals(vo.getStat()) ? 2 : 3;
+                        Date sopSendTime = null;
+                        if (vo.getTime() != null) {
+                            try {
+                                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                                sopSendTime = format.parse(vo.getTime());
+                            } catch (Exception ignored) { }
+                        }
+                        updateQwSopSmsLogsByUuid(logs.getSopSmsLogId(), sopStatus, sopSendTime, null,logs.getSmsIndex());
+                    }
+
+                    CompanySmsCommonLogs commonLogs = smsCommonLogsService.selectCompanySmsCommonLogsByMid(vo.getMid());
+                    if (commonLogs != null && commonLogs.getStatus().equals(0)) {
+                        commonLogs.setStat(vo.getStat());
+                        if (vo.getTime() != null) {
+                            try {
+                                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                                commonLogs.setSendTime(format.parse(vo.getTime()));
+                            } catch (Exception e) {
+
+                            }
+                        }
+                        //状态报告
+                        if (vo.getStat().equals("DELIVRD")) {
+                            commonLogs.setStatus(1);
+                        } else {
+                            commonLogs.setStatus(-1);
+                            companySmsService.addCompanySms(commonLogs.getCompanyId(), commonLogs.getNumber());
+                        }
+                        smsCommonLogsService.updateCompanySmsCommonLogs(commonLogs);
+
+                    }
                 }
             }
         }
@@ -979,4 +1036,76 @@ public class SmsServiceImpl implements ISmsService
             }
         }
     }
+
+    /**
+     * 根据 uuid(QwSopSmsLogs.id)和索引更新子记录,并检查主记录完成状态
+     */
+    private void updateQwSopSmsLogsByUuid(Long sopSmsLogId, Integer status, Date sendTime, String replyContent, Integer smsIndex) {
+        if (sopSmsLogId == null) return;
+        QwSopSmsLogs update = new QwSopSmsLogs();
+        update.setSopLogId(sopSmsLogId);
+        update.setSmsIndex(smsIndex);
+        if (status != null) {
+            update.setStatus(status);
+        }
+        if (sendTime != null) {
+            update.setSendTime(sendTime);
+        }
+        if (replyContent != null) {
+            update.setRemark(replyContent);
+        }
+
+        qwSopSmsLogsService.updateSmsNotifyInfo(update);
+        QwSopLogs mainLog = qwSopLogsMapper.selectoneByUid(sopSmsLogId);
+        if (mainLog == null) return;
+        JSONObject jsonObject = JSONObject.parseObject(mainLog.getContentJson());
+        JSONArray jsonArray = jsonObject.getJSONArray("setting");
+        int total = jsonArray.size();
+
+        Integer processed = qwSopSmsLogsMapper.countProcessed(sopSmsLogId);
+
+        if (processed != null && processed >= total) {
+            int successCount = 0;
+            int failCount = 0;
+
+            List<QwSopSmsLogs> list = qwSopSmsLogsMapper.getSmsListBySopLogId(sopSmsLogId);
+            //更新对应content_json信息
+            JSONArray newArray = new JSONArray();
+            for (QwSopSmsLogs sub : list) {
+                JSONObject json = jsonArray.getJSONObject(sub.getSmsIndex());
+                if (sub.getStatus() == 2) {
+                    json.put("sendStatus","1");
+                    successCount++;
+                } else if (sub.getStatus() == 3) {
+                    json.put("sendStatus","0");
+                    failCount++;
+                }
+                newArray.add(json);
+            }
+
+            boolean allSuccess = (successCount == list.size());
+            boolean allFail = (failCount == list.size());
+
+            QwSopLogs updateLog = new QwSopLogs();
+            updateLog.setSmsLogsId(sopSmsLogId);
+            jsonObject.put("setting", newArray);
+            updateLog.setContentJson(jsonObject.toJSONString());
+            if (allSuccess) {
+                updateLog.setSendStatus(1L);
+                log.info("主记录 {} 全部成功", sopSmsLogId);
+            } else if (allFail) {
+                updateLog.setSendStatus(0L);
+                log.info("主记录 {} 全部失败", sopSmsLogId);
+            } else {
+                updateLog.setRemark("部分成功!");
+                updateLog.setSendStatus(0L); // 部分成功
+                log.info("主记录 {} 部分成功(成功{}条,失败{}条)",
+                        sopSmsLogId, successCount, failCount);
+            }
+
+
+            qwSopLogsMapper.updateQwSopLogsBySmsLogsId(updateLog);
+        }
+    }
+
 }

+ 62 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyDetectionPhoneDailyStatistics.java

@@ -0,0 +1,62 @@
+package com.fs.company.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 机型检测日统计对象 company_detection_phone_daily_statistics
+ *
+ * @author fs
+ * @date 2025-12-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyDetectionPhoneDailyStatistics extends BaseEntity{
+
+    /** 日统计id */
+    private Long id;
+
+    /** 销售公司id */
+    @Excel(name = "销售公司id")
+    private Long companyId;
+
+    /** 销售公司名称 */
+    @Excel(name = "销售公司名称")
+    private String companyName;
+
+    /** 销售id */
+    @Excel(name = "销售id")
+    private Long companyUserId;
+
+    /** 销售名称 */
+    @Excel(name = "销售名称")
+    private String companyUserName;
+
+    /** 检测时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "检测时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date detectionTime;
+
+    /** 安卓次数 */
+    @Excel(name = "安卓次数")
+    private Integer azCount;
+
+    /** 苹果次数 */
+    @Excel(name = "苹果次数")
+    private Integer iosCount;
+
+    /** 未知次数 */
+    @Excel(name = "未知次数")
+    private Integer unknownCount;
+
+    /** 总计 */
+    @Excel(name = "总计")
+    private Integer totalCount;
+
+
+}

+ 78 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyDetectionPhoneRecord.java

@@ -0,0 +1,78 @@
+package com.fs.company.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.*;
+
+import java.util.Date;
+
+/**
+ * 机型检测记录对象 company_detection_phone_record
+ *
+ * @author fs
+ * @date 2025-12-17
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class CompanyDetectionPhoneRecord extends BaseEntity{
+
+    /** 检测记录id */
+    private Long id;
+
+    /** 企业id */
+    @Excel(name = "企业id")
+    private Long companyId;
+
+    /** 企业名称 */
+    @Excel(name = "企业名称")
+    private String companyName;
+
+    /** 销售id */
+    @Excel(name = "销售id")
+    private Long companyUserId;
+
+    /** 销售名称 */
+    @Excel(name = "销售名称")
+    private String companyUserName;
+    /**
+     * 客户id
+     */
+    @Excel(name = "客户id")
+    private Long userId;
+    /**
+     * 客户昵称
+     */
+    @Excel(name = "客户昵称")
+    private String nickName;
+
+    /** 检测手机 */
+    @Excel(name = "检测手机")
+    private String phoneNumber;
+
+    /** 检测系统 */
+    @Excel(name = "检测系统")
+    private Integer detectionSystem;
+
+    /** 检测机型 */
+    @Excel(name = "检测机型")
+    private String detectionModel;
+
+    /** 检测时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "检测时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date detectionTime;
+
+    /** 检测状态 0:失败;1:成功 */
+    @Excel(name = "检测状态 0:失败;1:成功")
+    private Long status;
+
+    /** 备注 */
+    @Excel(name = "备注")
+    private String remark;
+
+
+}

+ 58 - 0
fs-service/src/main/java/com/fs/company/domain/CompanySmsCommonDailyStatistics.java

@@ -0,0 +1,58 @@
+package com.fs.company.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 短信通用日统计对象 company_sms_common_daily_statistics
+ *
+ * @author fs
+ * @date 2025-12-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanySmsCommonDailyStatistics extends BaseEntity{
+
+    /** id */
+    private Long id;
+
+    /** 公司ID */
+    @Excel(name = "公司ID")
+    private Long companyId;
+
+    /** 销售ID */
+    @Excel(name = "销售ID")
+    private Long companyUserId;
+
+    /** 销售名称 */
+    @Excel(name = "销售名称")
+    private String companyUserName;
+
+    /** 发送类型 4:会员app下载链接 */
+    @Excel(name = "发送类型 4:会员app下载链接")
+    private Long sendType;
+
+    /** 最后一次发送时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "最后一次发送时间 ", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date lastSendTime;
+    /** 失败次数 */
+    @Excel(name = "失败次数")
+    private Integer failCount;
+    /** 发送中次数 */
+    @Excel(name = "发送中次数")
+    private Integer pendingCount;
+    /** 成功次数 */
+    @Excel(name = "成功次数")
+    private Integer successCount;
+    /** 总计 */
+    @Excel(name = "总计")
+    private Integer totalCount;
+
+
+}

+ 98 - 0
fs-service/src/main/java/com/fs/company/domain/CompanySmsCommonLogs.java

@@ -0,0 +1,98 @@
+package com.fs.company.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 短信通用发送记录对象 company_sms_common_logs
+ *
+ * @author fs
+ * @date 2025-12-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanySmsCommonLogs extends BaseEntity{
+
+    /** ID */
+    private Long logsId;
+
+    /** 公司ID */
+    @Excel(name = "公司ID")
+    private Long companyId;
+
+    /** 销售ID */
+    @Excel(name = "销售ID")
+    private Long companyUserId;
+    /** 销售ID */
+    @Excel(name = "销售昵称")
+    private String companyUserName;
+
+    /** 收信人ID */
+    @Excel(name = "收信人ID")
+    private Long recipientId;
+
+    /** 收信人名称 */
+    @Excel(name = "收信人名称")
+    private String recipientName;
+
+    /** 发送类型 4:会员app下载链接 */
+    @Excel(name = "发送类型 4:会员app下载链接")
+    private Integer sendType;
+
+    /** 模板ID */
+    @Excel(name = "模板ID")
+    private Long tempId;
+
+    /** 模板CODE */
+    @Excel(name = "模板CODE")
+    private String tempCode;
+
+    /** 手机号 */
+    @Excel(name = "手机号")
+    private String phone;
+
+    /** 短信内容 */
+    @Excel(name = "短信内容")
+    private String content;
+
+    /** 发送时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "发送时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date sendTime;
+
+    /** 状态 */
+    @Excel(name = "状态")
+    private Integer status;
+
+    /** $column.columnComment */
+    @Excel(name = "状态")
+    private String mid;
+
+    /** $column.columnComment */
+    @Excel(name = "状态")
+    private String stat;
+
+    /** $column.columnComment */
+    @Excel(name = "状态")
+    private String replyContent;
+
+    /** 短信数量 */
+    @Excel(name = "短信数量")
+    private Integer number;
+
+    /** 发送短信服务商 */
+    @Excel(name = "发送短信服务商")
+    private String type;
+
+    private Integer isReply;
+
+    /**
+     * sop短信发送记录uuid
+     * **/
+    private Long uuid;
+}

+ 74 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyDetectionPhoneDailyStatisticsMapper.java

@@ -0,0 +1,74 @@
+package com.fs.company.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyDetectionPhoneDailyStatistics;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 机型检测日统计Mapper接口
+ * 
+ * @author fs
+ * @date 2025-12-17
+ */
+public interface CompanyDetectionPhoneDailyStatisticsMapper extends BaseMapper<CompanyDetectionPhoneDailyStatistics> {
+    /**
+     * 查询机型检测日统计
+     * 
+     * @param id 机型检测日统计主键
+     * @return 机型检测日统计
+     */
+    CompanyDetectionPhoneDailyStatistics selectCompanyDetectionPhoneDailyStatisticsById(Long id);
+
+    /**
+     * 查询机型检测日统计列表
+     * 
+     * @param companyDetectionPhoneDailyStatistics 机型检测日统计
+     * @return 机型检测日统计集合
+     */
+    List<CompanyDetectionPhoneDailyStatistics> selectCompanyDetectionPhoneDailyStatisticsList(CompanyDetectionPhoneDailyStatistics companyDetectionPhoneDailyStatistics);
+
+    /**
+     * 新增机型检测日统计
+     * 
+     * @param companyDetectionPhoneDailyStatistics 机型检测日统计
+     * @return 结果
+     */
+    int insertCompanyDetectionPhoneDailyStatistics(CompanyDetectionPhoneDailyStatistics companyDetectionPhoneDailyStatistics);
+
+    /**
+     * 修改机型检测日统计
+     * 
+     * @param companyDetectionPhoneDailyStatistics 机型检测日统计
+     * @return 结果
+     */
+    int updateCompanyDetectionPhoneDailyStatistics(CompanyDetectionPhoneDailyStatistics companyDetectionPhoneDailyStatistics);
+
+    /**
+     * 删除机型检测日统计
+     * 
+     * @param id 机型检测日统计主键
+     * @return 结果
+     */
+    int deleteCompanyDetectionPhoneDailyStatisticsById(Long id);
+
+    /**
+     * 批量删除机型检测日统计
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyDetectionPhoneDailyStatisticsByIds(Long[] ids);
+    /**
+     * 根据销售id和创建时间查询公司用户
+     * @param companyUserId 销售id
+     * @param startDate 起始时间
+     * @return 结果
+     */
+    @Select("select * from company_detection_phone_daily_statistics where company_user_id=#{companyUserId} and DATE(create_time) =DATE(#{startDate})")
+    CompanyDetectionPhoneDailyStatistics selectByCompanyUserAndDate(@Param("companyUserId")Long companyUserId, @Param("startDate")Date startDate);
+}

+ 62 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyDetectionPhoneRecordMapper.java

@@ -0,0 +1,62 @@
+package com.fs.company.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyDetectionPhoneRecord;
+
+import java.util.List;
+
+/**
+ * 机型检测记录Mapper接口
+ * 
+ * @author fs
+ * @date 2025-12-17
+ */
+public interface CompanyDetectionPhoneRecordMapper extends BaseMapper<CompanyDetectionPhoneRecord>{
+    /**
+     * 查询机型检测记录
+     * 
+     * @param id 机型检测记录主键
+     * @return 机型检测记录
+     */
+    CompanyDetectionPhoneRecord selectCompanyDetectionPhoneRecordById(Long id);
+
+    /**
+     * 查询机型检测记录列表
+     * 
+     * @param companyDetectionPhoneRecord 机型检测记录
+     * @return 机型检测记录集合
+     */
+    List<CompanyDetectionPhoneRecord> selectCompanyDetectionPhoneRecordList(CompanyDetectionPhoneRecord companyDetectionPhoneRecord);
+
+    /**
+     * 新增机型检测记录
+     * 
+     * @param companyDetectionPhoneRecord 机型检测记录
+     * @return 结果
+     */
+    int insertCompanyDetectionPhoneRecord(CompanyDetectionPhoneRecord companyDetectionPhoneRecord);
+
+    /**
+     * 修改机型检测记录
+     * 
+     * @param companyDetectionPhoneRecord 机型检测记录
+     * @return 结果
+     */
+    int updateCompanyDetectionPhoneRecord(CompanyDetectionPhoneRecord companyDetectionPhoneRecord);
+
+    /**
+     * 删除机型检测记录
+     * 
+     * @param id 机型检测记录主键
+     * @return 结果
+     */
+    int deleteCompanyDetectionPhoneRecordById(Long id);
+
+    /**
+     * 批量删除机型检测记录
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyDetectionPhoneRecordByIds(Long[] ids);
+}

+ 87 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanySmsCommonLogsMapper.java

@@ -0,0 +1,87 @@
+package com.fs.company.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanySmsCommonDailyStatistics;
+import com.fs.company.domain.CompanySmsCommonLogs;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/**
+ * 短信通用发送记录Mapper接口
+ * 
+ * @author fs
+ * @date 2025-12-17
+ */
+public interface CompanySmsCommonLogsMapper extends BaseMapper<CompanySmsCommonLogs>{
+    /**
+     * 查询短信通用发送记录
+     * 
+     * @param logsId 短信通用发送记录主键
+     * @return 短信通用发送记录
+     */
+    CompanySmsCommonLogs selectCompanySmsCommonLogsByLogsId(Long logsId);
+
+    /**
+     * 查询短信通用发送记录列表
+     * 
+     * @param companySmsCommonLogs 短信通用发送记录
+     * @return 短信通用发送记录集合
+     */
+    List<CompanySmsCommonLogs> selectCompanySmsCommonLogsList(CompanySmsCommonLogs companySmsCommonLogs);
+
+    /**
+     * 新增短信通用发送记录
+     * 
+     * @param companySmsCommonLogs 短信通用发送记录
+     * @return 结果
+     */
+    int insertCompanySmsCommonLogs(CompanySmsCommonLogs companySmsCommonLogs);
+
+    /**
+     * 修改短信通用发送记录
+     * 
+     * @param companySmsCommonLogs 短信通用发送记录
+     * @return 结果
+     */
+    int updateCompanySmsCommonLogs(CompanySmsCommonLogs companySmsCommonLogs);
+
+    /**
+     * 删除短信通用发送记录
+     * 
+     * @param logsId 短信通用发送记录主键
+     * @return 结果
+     */
+    int deleteCompanySmsCommonLogsByLogsId(Long logsId);
+
+    /**
+     * 批量删除短信通用发送记录
+     * 
+     * @param logsIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanySmsCommonLogsByLogsIds(Long[] logsIds);
+
+    @Select("select * from company_sms_common_logs where mid=#{msgid} ")
+    CompanySmsCommonLogs selectCompanySmsCommonLogsByMid(@Param("msgid") String msgid);
+    @Select("select * from company_sms_common_logs where phone=#{phone} order by logs_id desc limit 1")
+    CompanySmsCommonLogs selectCompanySmsCommonLogsByMobile(@Param("phone") String phone);
+
+    @Select("<script>" +
+            "SELECT " +
+            "    company_id, " +
+            "    company_user_id, " +
+            "    company_user_name, " +
+            "    send_type, " +
+            "    MAX(send_time) AS last_send_time, " +
+            "    COUNT(*) AS total_count, " +
+            "    SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS pendingCount, " +
+            "    SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS successCount, " +
+            "    SUM(CASE WHEN status = -1 THEN 1 ELSE 0 END) AS failCount " +
+            "FROM company_sms_common_logs " +
+            "WHERE DATE(send_time) = DATE_SUB(CURDATE(), INTERVAL 1 DAY) " +
+            "GROUP BY company_user_id, send_type" +
+            "</script>")
+    List<CompanySmsCommonDailyStatistics> yesterdayStatistics();
+}

+ 31 - 0
fs-service/src/main/java/com/fs/company/param/CompanyDetectionPhoneSendLinkParam.java

@@ -0,0 +1,31 @@
+package com.fs.company.param;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @Author:peicj
+ * @Description: 机型检测参数
+ * @Date:2025/12/17 11:18
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class CompanyDetectionPhoneSendLinkParam {
+
+    /**
+     * 会员id
+     */
+    private Long userId;
+    /**
+     * 会员名称
+     */
+    private String nickName;
+    /**
+     * 手机号码
+     */
+    private String mobile;
+}

+ 34 - 0
fs-service/src/main/java/com/fs/company/param/SmsSendFsUserParam.java

@@ -0,0 +1,34 @@
+package com.fs.company.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * @Author:peicj
+ * @Description: 发送给会员用户短信入参
+ * @Date:2025/12/17 16:03
+ */
+@Data
+public class SmsSendFsUserParam implements Serializable {
+
+//    @NotNull(message = "会员ID不能为空")
+    private Long  userId;
+//    @NotNull(message = "会员IDS不能为空")
+    private Long[] userIds;
+    @NotNull(message = "模板CODE不能为空")
+    private String tempCode;
+    private Long  companyId;
+    private Long  companyUserId;
+    private Integer smsType;//短信类型 1 CRM客户端信
+    @NotNull(message = "手机号不能为空")
+    private String mobile;
+    @NotNull(message = "短信内容不能为空")
+    private String content;
+
+    private String cardUrl; //名片链接
+    private String workUrl; //名片链接
+    private String downloaAppdUrl;//下载链接
+}
+

+ 68 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyDetectionPhoneDailyStatisticsService.java

@@ -0,0 +1,68 @@
+package com.fs.company.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.CompanyDetectionPhoneDailyStatistics;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 机型检测日统计Service接口
+ * 
+ * @author fs
+ * @date 2025-12-17
+ */
+public interface ICompanyDetectionPhoneDailyStatisticsService extends IService<CompanyDetectionPhoneDailyStatistics>{
+    /**
+     * 查询机型检测日统计
+     * 
+     * @param id 机型检测日统计主键
+     * @return 机型检测日统计
+     */
+    CompanyDetectionPhoneDailyStatistics selectCompanyDetectionPhoneDailyStatisticsById(Long id);
+
+    /**
+     * 查询机型检测日统计列表
+     * 
+     * @param companyDetectionPhoneDailyStatistics 机型检测日统计
+     * @return 机型检测日统计集合
+     */
+    List<CompanyDetectionPhoneDailyStatistics> selectCompanyDetectionPhoneDailyStatisticsList(CompanyDetectionPhoneDailyStatistics companyDetectionPhoneDailyStatistics);
+
+    /**
+     * 新增机型检测日统计
+     * 
+     * @param companyDetectionPhoneDailyStatistics 机型检测日统计
+     * @return 结果
+     */
+    int insertCompanyDetectionPhoneDailyStatistics(CompanyDetectionPhoneDailyStatistics companyDetectionPhoneDailyStatistics);
+
+    /**
+     * 修改机型检测日统计
+     * 
+     * @param companyDetectionPhoneDailyStatistics 机型检测日统计
+     * @return 结果
+     */
+    int updateCompanyDetectionPhoneDailyStatistics(CompanyDetectionPhoneDailyStatistics companyDetectionPhoneDailyStatistics);
+
+    /**
+     * 批量删除机型检测日统计
+     * 
+     * @param ids 需要删除的机型检测日统计主键集合
+     * @return 结果
+     */
+    int deleteCompanyDetectionPhoneDailyStatisticsByIds(Long[] ids);
+
+    /**
+     * 删除机型检测日统计信息
+     * 
+     * @param id 机型检测日统计主键
+     * @return 结果
+     */
+    int deleteCompanyDetectionPhoneDailyStatisticsById(Long id);
+    /**
+     * 重新计算统计数据
+     * @param detectionSystemType 1苹果 2安卓 3其他
+     */
+    void recalculateStatistics(Long companyId, String companyName, Long companyUserId, String companyUserName, Date detectionTime, int detectionSystemType);
+}

+ 74 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyDetectionPhoneRecordService.java

@@ -0,0 +1,74 @@
+package com.fs.company.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.CompanyDetectionPhoneRecord;
+import com.fs.company.param.CompanyDetectionPhoneSendLinkParam;
+
+import java.util.List;
+
+/**
+ * 机型检测记录Service接口
+ * 
+ * @author fs
+ * @date 2025-12-17
+ */
+public interface ICompanyDetectionPhoneRecordService extends IService<CompanyDetectionPhoneRecord>{
+    /**
+     * 查询机型检测记录
+     * 
+     * @param id 机型检测记录主键
+     * @return 机型检测记录
+     */
+    CompanyDetectionPhoneRecord selectCompanyDetectionPhoneRecordById(Long id);
+
+    /**
+     * 查询机型检测记录列表
+     * 
+     * @param companyDetectionPhoneRecord 机型检测记录
+     * @return 机型检测记录集合
+     */
+    List<CompanyDetectionPhoneRecord> selectCompanyDetectionPhoneRecordList(CompanyDetectionPhoneRecord companyDetectionPhoneRecord);
+
+    /**
+     * 新增机型检测记录
+     * 
+     * @param companyDetectionPhoneRecord 机型检测记录
+     * @return 结果
+     */
+    int insertCompanyDetectionPhoneRecord(CompanyDetectionPhoneRecord companyDetectionPhoneRecord);
+
+    /**
+     * 修改机型检测记录
+     * 
+     * @param companyDetectionPhoneRecord 机型检测记录
+     * @return 结果
+     */
+    int updateCompanyDetectionPhoneRecord(CompanyDetectionPhoneRecord companyDetectionPhoneRecord);
+
+    /**
+     * 批量删除机型检测记录
+     * 
+     * @param ids 需要删除的机型检测记录主键集合
+     * @return 结果
+     */
+    int deleteCompanyDetectionPhoneRecordByIds(Long[] ids);
+
+    /**
+     * 删除机型检测记录信息
+     * 
+     * @param id 机型检测记录主键
+     * @return 结果
+     */
+    int deleteCompanyDetectionPhoneRecordById(Long id);
+    /**
+     * 机型检测发送链接
+     *
+     * @param sendLinkParam 机型检测发送链接参数
+     * @param companyId 企业 id
+     * @param companyName 企业名称
+     * @param companyUserId 销售
+     * @param companyUserName 销售名称
+     * @return 结果
+     */
+    String getDownloadAppLink(CompanyDetectionPhoneSendLinkParam sendLinkParam, Long companyId,String companyName,Long companyUserId,String companyUserName);
+}

+ 72 - 0
fs-service/src/main/java/com/fs/company/service/ICompanySmsCommonLogsService.java

@@ -0,0 +1,72 @@
+package com.fs.company.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
+import com.fs.company.domain.CompanySmsCommonLogs;
+import com.fs.company.param.SmsSendFsUserParam;
+
+import java.util.List;
+
+/**
+ * 短信通用发送记录Service接口
+ * 
+ * @author fs
+ * @date 2025-12-17
+ */
+public interface ICompanySmsCommonLogsService extends IService<CompanySmsCommonLogs>{
+    /**
+     * 查询短信通用发送记录
+     * 
+     * @param logsId 短信通用发送记录主键
+     * @return 短信通用发送记录
+     */
+    CompanySmsCommonLogs selectCompanySmsCommonLogsByLogsId(Long logsId);
+
+    /**
+     * 查询短信通用发送记录列表
+     * 
+     * @param companySmsCommonLogs 短信通用发送记录
+     * @return 短信通用发送记录集合
+     */
+    List<CompanySmsCommonLogs> selectCompanySmsCommonLogsList(CompanySmsCommonLogs companySmsCommonLogs);
+
+    /**
+     * 新增短信通用发送记录
+     * 
+     * @param companySmsCommonLogs 短信通用发送记录
+     * @return 结果
+     */
+    int insertCompanySmsCommonLogs(CompanySmsCommonLogs companySmsCommonLogs);
+
+    /**
+     * 修改短信通用发送记录
+     * 
+     * @param companySmsCommonLogs 短信通用发送记录
+     * @return 结果
+     */
+    int updateCompanySmsCommonLogs(CompanySmsCommonLogs companySmsCommonLogs);
+
+    /**
+     * 批量删除短信通用发送记录
+     * 
+     * @param logsIds 需要删除的短信通用发送记录主键集合
+     * @return 结果
+     */
+    int deleteCompanySmsCommonLogsByLogsIds(Long[] logsIds);
+
+    /**
+     * 删除短信通用发送记录信息
+     * 
+     * @param logsId 短信通用发送记录主键
+     * @return 结果
+     */
+    int deleteCompanySmsCommonLogsByLogsId(Long logsId);
+
+    R sendUserDownloadAppMsg(SmsSendFsUserParam param);
+
+    CompanySmsCommonLogs selectCompanySmsCommonLogsByMid(String msgid);
+
+    CompanySmsCommonLogs selectCompanySmsCommonLogsByMobile(String phone);
+
+    R batchPushAppLinks(SmsSendFsUserParam param, Long companyId, String companyName, Long userId, String userName);
+}

+ 180 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyDetectionPhoneDailyStatisticsServiceImpl.java

@@ -0,0 +1,180 @@
+package com.fs.company.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.DateUtils;
+import com.fs.company.domain.CompanyDetectionPhoneDailyStatistics;
+import com.fs.company.mapper.CompanyDetectionPhoneDailyStatisticsMapper;
+import com.fs.company.service.ICompanyDetectionPhoneDailyStatisticsService;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 机型检测日统计Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-12-17
+ */
+@Service
+public class CompanyDetectionPhoneDailyStatisticsServiceImpl extends ServiceImpl<CompanyDetectionPhoneDailyStatisticsMapper, CompanyDetectionPhoneDailyStatistics> implements ICompanyDetectionPhoneDailyStatisticsService {
+
+    /**
+     * 查询机型检测日统计
+     * 
+     * @param id 机型检测日统计主键
+     * @return 机型检测日统计
+     */
+    @Override
+    public CompanyDetectionPhoneDailyStatistics selectCompanyDetectionPhoneDailyStatisticsById(Long id)
+    {
+        return baseMapper.selectCompanyDetectionPhoneDailyStatisticsById(id);
+    }
+
+    /**
+     * 查询机型检测日统计列表
+     * 
+     * @param companyDetectionPhoneDailyStatistics 机型检测日统计
+     * @return 机型检测日统计
+     */
+    @Override
+    public List<CompanyDetectionPhoneDailyStatistics> selectCompanyDetectionPhoneDailyStatisticsList(CompanyDetectionPhoneDailyStatistics companyDetectionPhoneDailyStatistics)
+    {
+        return baseMapper.selectCompanyDetectionPhoneDailyStatisticsList(companyDetectionPhoneDailyStatistics);
+    }
+
+    /**
+     * 新增机型检测日统计
+     * 
+     * @param companyDetectionPhoneDailyStatistics 机型检测日统计
+     * @return 结果
+     */
+    @Override
+    public int insertCompanyDetectionPhoneDailyStatistics(CompanyDetectionPhoneDailyStatistics companyDetectionPhoneDailyStatistics)
+    {
+        companyDetectionPhoneDailyStatistics.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanyDetectionPhoneDailyStatistics(companyDetectionPhoneDailyStatistics);
+    }
+
+    /**
+     * 修改机型检测日统计
+     * 
+     * @param companyDetectionPhoneDailyStatistics 机型检测日统计
+     * @return 结果
+     */
+    @Override
+    public int updateCompanyDetectionPhoneDailyStatistics(CompanyDetectionPhoneDailyStatistics companyDetectionPhoneDailyStatistics)
+    {
+        return baseMapper.updateCompanyDetectionPhoneDailyStatistics(companyDetectionPhoneDailyStatistics);
+    }
+
+    /**
+     * 批量删除机型检测日统计
+     * 
+     * @param ids 需要删除的机型检测日统计主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDetectionPhoneDailyStatisticsByIds(Long[] ids)
+    {
+        return baseMapper.deleteCompanyDetectionPhoneDailyStatisticsByIds(ids);
+    }
+
+    /**
+     * 删除机型检测日统计信息
+     * 
+     * @param id 机型检测日统计主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDetectionPhoneDailyStatisticsById(Long id)
+    {
+        return baseMapper.deleteCompanyDetectionPhoneDailyStatisticsById(id);
+    }
+
+    /**
+     * 重新计算统计数据
+     * @param detectionSystemType 1苹果 2安卓 3其他
+     */
+    @Override
+    public void recalculateStatistics(Long companyId, String companyName, Long companyUserId, String companyUserName, Date detectionTime, int detectionSystemType) {
+        // 1. 查询当天是否已有统计记录
+        String dateStr = DateUtils.parseDateToStr("yyyy-MM-dd", detectionTime);
+        Date toDay = DateUtils.dateTime(DateUtils.YYYY_MM_DD, dateStr);
+        CompanyDetectionPhoneDailyStatistics existingStatictics = baseMapper.selectByCompanyUserAndDate(
+                companyUserId, toDay);
+        if(existingStatictics != null){
+            //更新操作
+            existingStatictics.setCompanyId(companyId);
+            existingStatictics.setCompanyName(companyName);
+            existingStatictics.setCompanyUserName(companyUserName);
+            updateExistingStatistics(existingStatictics, detectionSystemType);
+            baseMapper.updateCompanyDetectionPhoneDailyStatistics(existingStatictics);
+        }else{
+            //新增
+            CompanyDetectionPhoneDailyStatistics newStats = createNewStatistics(
+                    companyId, companyName, companyUserId, companyUserName, detectionTime, detectionSystemType);
+            baseMapper.insertCompanyDetectionPhoneDailyStatistics(newStats);
+
+        }
+    }
+
+    /**
+     * 创建新的统计记录
+     */
+    private CompanyDetectionPhoneDailyStatistics createNewStatistics(Long companyId, String companyName,
+                                                                     Long companyUserId, String companyUserName,
+                                                                     Date detectionTime, int detectionSystemType) {
+        CompanyDetectionPhoneDailyStatistics statistics = new CompanyDetectionPhoneDailyStatistics();
+        statistics.setCompanyId(companyId);
+        statistics.setCompanyName(companyName);
+        statistics.setCompanyUserId(companyUserId);
+        statistics.setCompanyUserName(companyUserName);
+        statistics.setDetectionTime(detectionTime);
+
+        // 根据检测类型初始化计数
+        int azCount = 0, iosCount = 0, unknownCount = 0;
+        switch (detectionSystemType) {
+            case 1: // 苹果
+                iosCount = 1;
+                break;
+            case 2: // 安卓
+                azCount = 1;
+                break;
+            default: // 其他
+                unknownCount = 1;
+                break;
+        }
+        statistics.setAzCount(azCount);
+        statistics.setIosCount(iosCount);
+        statistics.setUnknownCount(unknownCount);
+        statistics.setTotalCount(azCount + iosCount + unknownCount);
+        statistics.setCreateTime(new Date());
+        return statistics;
+    }
+
+
+    /**
+     * 更新现有统计记录
+     */
+    private void updateExistingStatistics(CompanyDetectionPhoneDailyStatistics statistics, int detectionSystemType) {
+        // 根据检测类型增加对应计数
+        switch (detectionSystemType) {
+            case 1: // 苹果
+                statistics.setIosCount(statistics.getIosCount() + 1);
+                break;
+            case 2: // 安卓
+                statistics.setAzCount(statistics.getAzCount() + 1);
+                break;
+            default: // 其他
+                statistics.setUnknownCount(statistics.getUnknownCount() + 1);
+                break;
+        }
+        // 重新计算总数,保证 az_count + ios_count + unknown_count = total_count
+        int newTotal = statistics.getAzCount() + statistics.getIosCount() + statistics.getUnknownCount();
+        statistics.setTotalCount(newTotal);
+
+        // 更新最后检测时间
+        statistics.setDetectionTime(new Date());
+    }
+}

+ 303 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyDetectionPhoneRecordServiceImpl.java

@@ -0,0 +1,303 @@
+package com.fs.company.service.impl;
+
+import cn.hutool.http.HttpResponse;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.DateUtils;
+import com.fs.company.domain.CompanyDetectionPhoneRecord;
+import com.fs.company.mapper.CompanyDetectionPhoneRecordMapper;
+import com.fs.company.param.CompanyDetectionPhoneSendLinkParam;
+import com.fs.company.service.ICompanyDetectionPhoneDailyStatisticsService;
+import com.fs.company.service.ICompanyDetectionPhoneRecordService;
+import com.fs.company.util.DetectionPhoneModelVerifyUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 机型检测记录Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-12-17
+ */
+@Slf4j
+@Service
+public class CompanyDetectionPhoneRecordServiceImpl extends ServiceImpl<CompanyDetectionPhoneRecordMapper, CompanyDetectionPhoneRecord> implements ICompanyDetectionPhoneRecordService {
+
+    @Autowired
+    private ICompanyDetectionPhoneDailyStatisticsService companyDetectionPhoneDailyStatisticsService;
+
+    /**
+     * 查询机型检测记录
+     * 
+     * @param id 机型检测记录主键
+     * @return 机型检测记录
+     */
+    @Override
+    public CompanyDetectionPhoneRecord selectCompanyDetectionPhoneRecordById(Long id)
+    {
+        return baseMapper.selectCompanyDetectionPhoneRecordById(id);
+    }
+
+    /**
+     * 查询机型检测记录列表
+     * 
+     * @param companyDetectionPhoneRecord 机型检测记录
+     * @return 机型检测记录
+     */
+    @Override
+    public List<CompanyDetectionPhoneRecord> selectCompanyDetectionPhoneRecordList(CompanyDetectionPhoneRecord companyDetectionPhoneRecord)
+    {
+        return baseMapper.selectCompanyDetectionPhoneRecordList(companyDetectionPhoneRecord);
+    }
+
+    /**
+     * 新增机型检测记录
+     * 
+     * @param companyDetectionPhoneRecord 机型检测记录
+     * @return 结果
+     */
+    @Override
+    public int insertCompanyDetectionPhoneRecord(CompanyDetectionPhoneRecord companyDetectionPhoneRecord)
+    {
+        companyDetectionPhoneRecord.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanyDetectionPhoneRecord(companyDetectionPhoneRecord);
+    }
+
+    /**
+     * 修改机型检测记录
+     * 
+     * @param companyDetectionPhoneRecord 机型检测记录
+     * @return 结果
+     */
+    @Override
+    public int updateCompanyDetectionPhoneRecord(CompanyDetectionPhoneRecord companyDetectionPhoneRecord)
+    {
+        return baseMapper.updateCompanyDetectionPhoneRecord(companyDetectionPhoneRecord);
+    }
+
+    /**
+     * 批量删除机型检测记录
+     * 
+     * @param ids 需要删除的机型检测记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDetectionPhoneRecordByIds(Long[] ids)
+    {
+        return baseMapper.deleteCompanyDetectionPhoneRecordByIds(ids);
+    }
+
+    /**
+     * 删除机型检测记录信息
+     * 
+     * @param id 机型检测记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDetectionPhoneRecordById(Long id)
+    {
+        return baseMapper.deleteCompanyDetectionPhoneRecordById(id);
+    }
+
+    /**
+     * 机型检测发送链接
+     *
+     * @param sendLinkParam 机型检测发送链接参数
+     * @param companyId 企业 id
+     * @param companyName 企业名称
+     * @param companyUserId 销售
+     * @param companyUserName 销售名称
+     * @return 检测结果对应的链接
+     */
+    @Override
+    public String getDownloadAppLink(CompanyDetectionPhoneSendLinkParam sendLinkParam, Long companyId, String companyName,
+                          Long companyUserId, String companyUserName) {
+        String requestBody = DetectionPhoneModelVerifyUtil.getRequestBody(sendLinkParam.getMobile());
+        log.info("校验机型发送请求体:" + requestBody);
+        Date now = new Date();
+
+        try {
+            HttpResponse response = HttpUtil.createPost(DetectionPhoneModelVerifyUtil.VERIFY_URL)
+                    .setConnectionTimeout(5000)
+                    .setReadTimeout(10000)
+                    .body(requestBody)
+                    .execute();
+
+            if (!response.isOk()) {
+                String errorMsg = "请求失败,通讯状态码: " + response.getStatus();
+                saveErrorRecord(companyId, companyName, companyUserId, companyUserName, sendLinkParam,
+                              4, null, now, 0L, errorMsg);
+                throw new CustomException(errorMsg);
+            }
+            String responseBody = response.body();
+
+
+            //===========  测试代码begin ==========
+            //ios数据
+//            String iosData = "{\"Code\":200,\"Data\":[{\"IsCharge\":1,\"Mobile\":\"18323465069\",\"State\":\"1\",\"Firm\":\"0\"}]}";
+//            //android数据
+//            String androidData = "{\"Code\":200,\"Data\":[{\"IsCharge\":1,\"Mobile\":\"18580336425\",\"State\":\"3\",\"Firm\":\"3\"}]}";
+//            //库无
+//            String noData = "{\"Code\":200,\"Data\":[{\"IsCharge\":0,\"Mobile\":\"15779601354\",\"State\":\"4\",\"Firm\":\"\"}]}";
+//            String[] data = new String[]{iosData, androidData, noData};
+//            //随机拿个数据处理
+//            int codeIndex = ThreadLocalRandom.current().nextInt(0,data.length);
+//            String responseBody = data[codeIndex];
+            //===========  测试代码end ==========
+
+
+            log.info("机型校验接口返回数据: " + responseBody);
+            JSONObject json = JSONObject.parseObject(responseBody);
+            int code = json.getIntValue("Code");
+            if (code != 200) {
+                log.info("接口返回错误信息:" + responseBody);
+                return null;
+            }
+            JSONArray dataArray = json.getJSONArray("Data");
+            if (dataArray == null || dataArray.isEmpty()) {
+                log.info("解析返回信息为空:" + responseBody);
+                return null;
+            }
+
+            JSONObject firstItem = dataArray.getJSONObject(0);
+            String state = firstItem.getString("State");
+            String firm = firstItem.getString("Firm");
+            Integer myState = getState(state);
+
+            // 保存检测记录
+            int saveResult = saveDetectionPhoneRecord(companyId, companyName, companyUserId, companyUserName,
+                    sendLinkParam.getUserId(), sendLinkParam.getNickName(), sendLinkParam.getMobile(),
+                    myState, getFirm(state, firm), now, 1L, null);
+            if (saveResult <= 0) {
+                String errorMsg = "新增失败插入失败数据";
+                saveErrorRecord(companyId, companyName, companyUserId, companyUserName, sendLinkParam,
+                              myState, null, now, 0L, errorMsg);
+            }
+            // 更新统计
+            companyDetectionPhoneDailyStatisticsService.recalculateStatistics(companyId, companyName,
+                    companyUserId, companyUserName, now, myState);
+
+            // 返回对应链接
+            return getDetectionUrl(myState);
+        } catch (CustomException e) {
+            log.info("系统处理异常:"+e);
+            return null;
+        } catch (Exception e) {
+            log.info("系统逻辑处理异常:" + e);
+            return null;
+        }
+    }
+
+    /**
+     * 根据检测状态获取对应链接
+     */
+    private String getDetectionUrl(Integer state) {
+        switch (state) {
+            case 1: // 苹果
+                return DetectionPhoneModelVerifyUtil.IOS_URL;
+            case 2: // 安卓
+                return DetectionPhoneModelVerifyUtil.ANDROID_URL;
+            default:
+                log.info("无法识别的系统:" + state + ",无法发送链接");
+                return null;
+        }
+    }
+
+    //类型state=1||2 时 0 苹果  State=3时0其他 1其他 2 honor  3 vivo  4 oppo  5 xiaomi  6 huawei
+    private String getFirm(String state, String firm) {
+        // 苹果系列处理
+        if ("1".equals(state) || "2".equals(state)) {
+            return "0".equals(firm) ? "苹果" : "未知机型[State=" + state + ",Firm=" + firm+"]";
+        }
+        // 安卓系列处理
+        if ("3".equals(state)) {
+            switch (firm) {
+                case "2": return "honor";
+                case "3": return "vivo";
+                case "4": return "oppo";
+                case "5": return "xiaomi";
+                case "6": return "huawei";
+                default: return "其他";
+            }
+        }
+        // 其他未知状态
+        return "未知机型[State=" + state + ",Firm=" + firm+"]";
+    }
+    /**
+     * 将外部机型检测状态转换为内部状态
+     * 对方机型状态: 1-苹果90%, 2-疑似苹果75%, 3-不是苹果90%, 4-库无, -1-异常
+     * 我方内部状态: 0-异常, 1-苹果, 2-安卓, 3-库无,4-获取失败
+     * @param state 外部状态码
+     * @return 内部状态码
+     */
+    private Integer getState(String state) {
+        switch (state) {
+            case "1":
+            case "2":
+                return 1;  // 苹果类型
+            case "3":
+                return 2;  // 安卓类型
+            case "4":
+                return 3;  // 库无
+            default:
+                return 0;  // 异常状态
+        }
+    }
+
+    /**
+     * 保存机型检测记录
+     *
+     * @param companyId 企业ID
+     * @param companyName 企业名称
+     * @param companyUserId 销售人员ID
+     * @param companyUserName 销售人员姓名
+     * @param userId 会员ID
+     * @param nickName 会员昵称
+     * @param phoneNumber 手机号码
+     * @param detectionSystem 检测系统类型
+     * @param detectionModel 检测机型
+     * @param detectionTime 检测时间
+     * @param status 状态
+     * @param remark 备注
+     * @return 保存结果
+     */
+    private int saveDetectionPhoneRecord(Long companyId, String companyName, Long companyUserId, String companyUserName,
+                                         Long userId, String nickName,
+                                         String phoneNumber, Integer detectionSystem, String detectionModel,Date detectionTime,
+                                         Long status, String remark) {
+        CompanyDetectionPhoneRecord record = CompanyDetectionPhoneRecord.builder()
+                .companyId(companyId)
+                .companyName(companyName)
+                .companyUserId(companyUserId)
+                .companyUserName(companyUserName)
+                .userId(userId)
+                .nickName(nickName)
+                .phoneNumber(phoneNumber)
+                .detectionSystem(detectionSystem)
+                .detectionModel(detectionModel)
+                .detectionTime(detectionTime)
+                .status(status)
+                .remark(remark)
+                .build();
+        return baseMapper.insertCompanyDetectionPhoneRecord(record);
+    }
+
+    /**
+     * 保存错误记录
+     */
+    private void saveErrorRecord(Long companyId, String companyName, Long companyUserId, String companyUserName,
+                                 CompanyDetectionPhoneSendLinkParam param, Integer systemType, String model,
+                                 Date detectionTime, Long status, String remark) {
+        saveDetectionPhoneRecord(companyId, companyName, companyUserId, companyUserName,
+                param.getUserId(), param.getNickName(), param.getMobile(),
+                systemType, model, detectionTime, status, remark);
+    }
+
+}

+ 319 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanySmsCommonLogsServiceImpl.java

@@ -0,0 +1,319 @@
+package com.fs.company.service.impl;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.core.domain.R;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.vo.SmsSendItemVO;
+import com.fs.common.vo.SmsSendVO;
+import com.fs.company.domain.CompanySms;
+import com.fs.company.domain.CompanySmsCommonLogs;
+import com.fs.company.domain.CompanySmsTemp;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanySmsCommonLogsMapper;
+import com.fs.company.param.CompanyDetectionPhoneSendLinkParam;
+import com.fs.company.param.SmsSendFsUserParam;
+import com.fs.company.service.ICompanySmsCommonLogsService;
+import com.fs.company.service.ICompanySmsService;
+import com.fs.company.service.ICompanySmsTempService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.his.config.FsSmsConfig;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
+import com.fs.his.utils.PhoneUtil;
+import com.fs.sms.domain.SendSmsReturn;
+import com.fs.sms.service.impl.SmsTServiceImpl;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 短信通用发送记录Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-12-17
+ */
+@Slf4j
+@Service
+public class CompanySmsCommonLogsServiceImpl extends ServiceImpl<CompanySmsCommonLogsMapper, CompanySmsCommonLogs> implements ICompanySmsCommonLogsService {
+
+    @Autowired
+    private ICompanySmsTempService smsTempService;
+    @Autowired
+    private SmsTServiceImpl smsTService;
+    @Autowired
+    private ICompanySmsService companySmsService;
+    @Autowired
+    private ICompanyUserService companyUserService;
+    @Autowired
+    SysConfigMapper sysConfigMapper;
+    @Autowired
+    private IFsUserService fsUserService;
+    @Autowired
+    private CompanyDetectionPhoneRecordServiceImpl companyDetectionPhoneRecordService;
+
+    /**
+     * 查询短信通用发送记录
+     * 
+     * @param logsId 短信通用发送记录主键
+     * @return 短信通用发送记录
+     */
+    @Override
+    public CompanySmsCommonLogs selectCompanySmsCommonLogsByLogsId(Long logsId)
+    {
+        return baseMapper.selectCompanySmsCommonLogsByLogsId(logsId);
+    }
+
+    /**
+     * 查询短信通用发送记录列表
+     * 
+     * @param companySmsCommonLogs 短信通用发送记录
+     * @return 短信通用发送记录
+     */
+    @Override
+    public List<CompanySmsCommonLogs> selectCompanySmsCommonLogsList(CompanySmsCommonLogs companySmsCommonLogs)
+    {
+        return baseMapper.selectCompanySmsCommonLogsList(companySmsCommonLogs);
+    }
+
+    /**
+     * 新增短信通用发送记录
+     * 
+     * @param companySmsCommonLogs 短信通用发送记录
+     * @return 结果
+     */
+    @Override
+    public int insertCompanySmsCommonLogs(CompanySmsCommonLogs companySmsCommonLogs)
+    {
+        companySmsCommonLogs.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanySmsCommonLogs(companySmsCommonLogs);
+    }
+
+    /**
+     * 修改短信通用发送记录
+     * 
+     * @param companySmsCommonLogs 短信通用发送记录
+     * @return 结果
+     */
+    @Override
+    public int updateCompanySmsCommonLogs(CompanySmsCommonLogs companySmsCommonLogs)
+    {
+        return baseMapper.updateCompanySmsCommonLogs(companySmsCommonLogs);
+    }
+
+    /**
+     * 批量删除短信通用发送记录
+     * 
+     * @param logsIds 需要删除的短信通用发送记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanySmsCommonLogsByLogsIds(Long[] logsIds)
+    {
+        return baseMapper.deleteCompanySmsCommonLogsByLogsIds(logsIds);
+    }
+
+    /**
+     * 删除短信通用发送记录信息
+     * 
+     * @param logsId 短信通用发送记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanySmsCommonLogsByLogsId(Long logsId)
+    {
+        return baseMapper.deleteCompanySmsCommonLogsByLogsId(logsId);
+    }
+
+    @Override
+    public R sendUserDownloadAppMsg(SmsSendFsUserParam param) {
+        // 获取模板
+        CompanySmsTemp temp = smsTempService.selectCompanySmsTempByCode(param.getTempCode());
+        if (temp == null || !temp.getStatus().equals(1) || !temp.getIsAudit().equals(1)) {
+            R.error("模板未审核");
+        }
+        // 获取公司短信配置
+        CompanySms csms = companySmsService.selectCompanySmsByCompanyId(param.getCompanyId());
+        if (csms == null) {
+            R.error("请充值");
+        }
+        if (csms.getRemainSmsCount() <= 0) {
+            R.error("剩余短信数量不足,请充值");
+        }
+        FsUser fsUser = fsUserService.selectFsUserByUserId(param.getUserId());
+        if (fsUser == null) {
+            return R.error("没有此会员");
+        }
+        if (fsUser.getPhone() == null) {
+            return R.error("电话不能为空");
+        }
+        String phone = PhoneUtil.decryptPhone(fsUser.getPhone());
+        String name = fsUser.getNickName();
+        return templateSplicing(phone,name,param,temp,4);
+    }
+
+    @Override
+    public CompanySmsCommonLogs selectCompanySmsCommonLogsByMid(String msgid) {
+        return baseMapper.selectCompanySmsCommonLogsByMid(msgid);
+    }
+
+    @Override
+    public CompanySmsCommonLogs selectCompanySmsCommonLogsByMobile(String phone) {
+        return baseMapper.selectCompanySmsCommonLogsByMobile(phone);
+    }
+
+    @Override
+    public R batchPushAppLinks(SmsSendFsUserParam param, Long companyId, String companyName, Long companyUserId, String userName) {
+        int successCount = 0;
+        int failCount = 0;
+
+        for (Long userId : param.getUserIds()) {
+            try {
+                FsUser fsUser = fsUserService.selectFsUserByUserId(userId);
+                if (fsUser == null || fsUser.getPhone() == null) {
+                    failCount++;
+                    continue;
+                }
+
+                String phone = PhoneUtil.decryptPhone(fsUser.getPhone());
+                String nickName = fsUser.getNickName();
+
+                CompanyDetectionPhoneSendLinkParam linkParam = CompanyDetectionPhoneSendLinkParam.builder()
+                        .userId(userId)
+                        .mobile(phone)
+                        .nickName(nickName)
+                        .build();
+
+                String linkUrl = companyDetectionPhoneRecordService.getDownloadAppLink(
+                        linkParam, companyId, companyName, companyUserId, userName);
+
+                if (linkUrl != null && !linkUrl.isEmpty()) {
+                    // 设置当前用户ID并发送短信
+                    param.setUserId(userId);
+                    param.setDownloaAppdUrl(linkUrl);
+                    try {
+                        sendUserDownloadAppMsg(param);
+                        successCount++;
+                    }catch (Exception e){
+                        failCount++;
+                        log.error("为用户 {} 发送短信时发生异常", userName, e);
+                    }
+                } else {
+                    failCount++;
+                }
+            } catch (Exception e) {
+                failCount++;
+                log.error("为用户 {} 批量推送APP链接时发生异常", userName, e);
+            }
+        }
+        return R.ok(String.format("批量推送完成,成功: %d,失败: %d", successCount, failCount));
+    }
+
+
+
+    /**
+     * 组装模板短信及发送
+     */
+    private R templateSplicing(String phone,String name,SmsSendFsUserParam param,CompanySmsTemp temp,Integer sendType){
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(param.getCompanyUserId());
+        String content;
+        content = param.getContent();
+        if (StringUtils.isNotEmpty(name)) {
+            content = content.replace("${sms.csName}", name);
+        }
+        if (StringUtils.isNotEmpty(param.getCardUrl())) {
+            content = content.replace("${sms.cardUrl}", param.getCardUrl());
+        }
+        if (companyUser != null && StringUtils.isNotEmpty(companyUser.getPhonenumber())) {
+            content = content.replace("${sms.phoneNumber}", companyUser.getPhonenumber());
+        }
+        if(StringUtils.isNotEmpty(param.getDownloaAppdUrl())){
+            content = content.replace("${sms.downloadAppLink}", param.getDownloaAppdUrl());
+        }
+        String urls = null;
+        SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.sms");
+        FsSmsConfig sms = JSON.parseObject(sysConfig.getConfigValue(), FsSmsConfig.class);
+        if (sms.getType().equals("rf")) {
+            try {
+                if (temp.getTempType().equals(1)) {
+                    urls = sms.getRfUrl1() + "sms?action=send&account=" + sms.getRfAccount1() + "&password=" + sms.getRfPassword1() + "&mobile=" + phone + "&content=" + URLEncoder.encode(sms.getRfSign() + content, "UTF-8") + "&extno=" + sms.getRfCode1() + "&rt=json";
+                } else if (temp.getTempType().equals(2)) {
+                    urls = sms.getRfUrl2() + "sms?action=send&account=" + sms.getRfAccount2() + "&password=" + sms.getRfPassword2() + "&mobile=" + phone + "&content=" + URLEncoder.encode(sms.getRfSign() + content + "拒收请回复R", "UTF-8") + "&extno=" + sms.getRfCode2() + "&rt=json";
+                }
+            } catch (UnsupportedEncodingException e) {
+                e.printStackTrace();
+            }
+            String post = HttpRequest.get(urls)
+//                            .body(String.valueOf(jsonObject))
+                    .execute().body();
+            SmsSendVO vo = JSONUtil.toBean(post, SmsSendVO.class);
+            if (vo.getStatus().equals(0)) {
+                for (SmsSendItemVO itemVO : vo.getList()) {
+                    if (itemVO.getResult().equals("0")) {
+                        recordSmsLog(param,temp,content,phone,name,sms,itemVO.getMid(),sendType,companyUser.getUserName());
+                    }
+                }
+            }
+        } else if (sms.getType().equals("dh")) {
+            SendSmsReturn sendSmsReturn = null;
+            if (temp.getTempType().equals(1)) {
+                sendSmsReturn = smsTService.sendSms(sms.getDhAccount1(), sms.getDhPassword1(), content, phone);
+            } else if (temp.getTempType().equals(2)) {
+                sendSmsReturn = smsTService.sendSms(sms.getDhAccount2(), sms.getDhPassword2(), content + "拒收请回复R", phone);
+            }
+            log.info("数据:{}", sendSmsReturn);
+            if (sendSmsReturn != null) {
+                if (sendSmsReturn.getResult() != null && sendSmsReturn.getResult().equals("0")) {
+                    recordSmsLog(param,temp,content,phone,name,sms,sendSmsReturn.getMsgid(),sendType,companyUser.getUserName());
+                }
+            }
+        }
+        return R.ok("短信提交成功,正在发送中...");
+    }
+
+    /**
+     * 记录发送短信日志
+     */
+    private void recordSmsLog(SmsSendFsUserParam param, CompanySmsTemp temp,String content,String phone,String name,FsSmsConfig sms,String mid,Integer sendType,String companyUserName) {
+        CompanySmsCommonLogs logs = new CompanySmsCommonLogs();
+        logs.setCompanyId(param.getCompanyId());
+        logs.setContent(content);
+        logs.setTempCode(temp.getTempCode());
+        logs.setCompanyUserId(param.getCompanyUserId());
+        logs.setCompanyUserName(companyUserName);
+        logs.setTempId(temp.getTempId());
+
+        //=======区别参数
+        logs.setRecipientId(param.getUserId());
+        logs.setRecipientName(name);
+        logs.setSendType(sendType);
+        //=======区别参数
+
+        logs.setPhone(phone);
+        logs.setSendTime(new Date());
+        logs.setStatus(0);
+        logs.setType(sms.getType());
+        logs.setMid(mid);
+        Integer counts = logs.getContent().length() / 67;
+        if (logs.getContent().length() % 67 > 0) {
+            counts = counts + 1;
+        }
+        if (counts == 0) {
+            counts = 1;
+        }
+        logs.setNumber(counts);
+        logs.setCreateTime(new Date());
+        baseMapper.insertCompanySmsCommonLogs(logs);
+        companySmsService.subCompanySms(logs.getCompanyId(), logs.getNumber());
+    }
+}

+ 88 - 0
fs-service/src/main/java/com/fs/company/util/DetectionPhoneModelVerifyUtil.java

@@ -0,0 +1,88 @@
+package com.fs.company.util;
+
+import com.alibaba.fastjson.JSONObject;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author:peicj
+ * @Description: 机型检测接口调用工具类
+ * @Date:2025/12/17 11:23
+ */
+public class DetectionPhoneModelVerifyUtil {
+
+    //appid
+    public static final long VERIFY_APPID = 100712;
+    //secretkey
+    public static final String SECRET_KEY = "faGf4dQNG3Tg";
+    //机型校验地址
+    public static final String VERIFY_URL = "http://zhijian.h12321.com:8086/mobile/model/check";
+    //苹果链接
+    public static String IOS_URL = "https://apps.apple.com/cn/app/%E8%8A%B3%E5%8D%8E%E6%9C%AA%E6%9D%A5/id6738688148";
+    //安卓链接  芳华未来App应用商店下载链接 华为/小米/oppo/vivo/荣耀 https://m.malink.cn/s/2UZjMz
+    public static String ANDROID_URL = "https://m.malink.cn/s/2UZjMz";
+
+
+    /**
+     * 获取请求体
+     * @param phone 手机号
+     * @return 请求体
+     */
+    public static String getRequestBody(String phone) {
+            String timestamp = System.currentTimeMillis() + "";
+            Map<String,Object> map = new HashMap<>();
+            map.put("appId", VERIFY_APPID);
+            map.put("timestamp", timestamp);
+            map.put("sign", getSign(VERIFY_APPID, SECRET_KEY, timestamp));
+            map.put("phones", phone);
+        return JSONObject.toJSONString(map);
+    }
+
+    /**
+     * 生成MD5签名字符串
+     * @param input 输入字符串
+     * @return 32位小写MD5字符串
+     */
+    public static String generateMD5(String input) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] digest = md.digest(input.getBytes());
+            return bytesToHex(digest);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("MD5 algorithm not found", e);
+        }
+    }
+
+    /**
+     * 字节数组转十六进制字符串
+     * @param bytes 字节数组
+     * @return 十六进制字符串(小写)
+     */
+    private static String bytesToHex(byte[] bytes) {
+        StringBuilder sb = new StringBuilder();
+        for (byte b : bytes) {
+            String hex = Integer.toHexString(0xff & b);
+            if (hex.length() == 1) {
+                sb.append('0');
+            }
+            sb.append(hex);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 获取签名
+     * @param appid appid
+     * @param secretkey secretkey
+     * @param timestamp 时间戳
+     * @Description 签名md5小写32位加密字符串,Md5(appId+secretkey+timestamp)
+     * @return String 签名
+     */
+    private static String getSign(long appid,String secretkey,String timestamp){
+        return generateMD5(appid + secretkey + timestamp);
+    }
+
+}

+ 13 - 0
fs-service/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java

@@ -364,6 +364,19 @@ public interface QwSopLogsMapper extends BaseMapper<QwSopLogs> {
     @MapKey("id")
     Map<String, SopUserLogs> queryAllPeriodNew(StatsWatchLogPageListDTO param);
 
+    @DataSource(DataSourceType.SOP)
+    QwSopLogs selectoneByUid(@Param("sopSmsLogId") Long sopSmsLogId);
+
+
+    /**
+     * 根据短信执行记录表修改企业微信SOP
+     *
+     * @param qwSopLogs 企业微信SOP  定时任务
+     * @return 结果
+     */
+    @DataSource(DataSourceType.SOP)
+    public int updateQwSopLogsBySmsLogsId(@Param("data") QwSopLogs qwSopLogs);
+
     /**
      * 批量更执行记录表
      * @param list 更新对象

+ 101 - 0
fs-service/src/main/resources/mapper/company/CompanyDetectionPhoneDailyStatisticsMapper.xml

@@ -0,0 +1,101 @@
+<?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.company.mapper.CompanyDetectionPhoneDailyStatisticsMapper">
+    
+    <resultMap type="CompanyDetectionPhoneDailyStatistics" id="CompanyDetectionPhoneDailyStatisticsResult">
+        <result property="id"    column="id"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="companyName"    column="company_name"    />
+        <result property="companyUserId"    column="company_user_id"    />
+        <result property="companyUserName"    column="company_user_name"    />
+        <result property="detectionTime"    column="detection_time"    />
+        <result property="azCount"    column="az_count"    />
+        <result property="iosCount"    column="ios_count"    />
+        <result property="unknownCount"    column="unknown_count"    />
+        <result property="totalCount"    column="total_count"    />
+        <result property="createTime"    column="create_time"    />
+    </resultMap>
+
+    <sql id="selectCompanyDetectionPhoneDailyStatisticsVo">
+        select id, company_id, company_name, company_user_id, company_user_name, detection_time, az_count, ios_count, unknown_count, total_count, create_time from company_detection_phone_daily_statistics
+    </sql>
+
+    <select id="selectCompanyDetectionPhoneDailyStatisticsList" parameterType="CompanyDetectionPhoneDailyStatistics" resultMap="CompanyDetectionPhoneDailyStatisticsResult">
+        <include refid="selectCompanyDetectionPhoneDailyStatisticsVo"/>
+        <where>  
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="companyName != null  and companyName != ''"> and company_name like concat('%', #{companyName}, '%')</if>
+            <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
+            <if test="companyUserName != null  and companyUserName != ''"> and company_user_name like concat('%', #{companyUserName}, '%')</if>
+            <if test="detectionTime != null "> and DATE(detection_time) = DATE(#{detectionTime})</if>
+            <if test="azCount != null "> and az_count = #{azCount}</if>
+            <if test="iosCount != null "> and ios_count = #{iosCount}</if>
+            <if test="unknownCount != null "> and unknown_count = #{unknownCount}</if>
+            <if test="totalCount != null "> and total_count = #{totalCount}</if>
+        </where>
+        order by id desc
+    </select>
+    
+    <select id="selectCompanyDetectionPhoneDailyStatisticsById" parameterType="Long" resultMap="CompanyDetectionPhoneDailyStatisticsResult">
+        <include refid="selectCompanyDetectionPhoneDailyStatisticsVo"/>
+        where id = #{id}
+    </select>
+        
+    <insert id="insertCompanyDetectionPhoneDailyStatistics" parameterType="CompanyDetectionPhoneDailyStatistics" useGeneratedKeys="true" keyProperty="id">
+        insert into company_detection_phone_daily_statistics
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">company_id,</if>
+            <if test="companyName != null">company_name,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="companyUserName != null">company_user_name,</if>
+            <if test="detectionTime != null">detection_time,</if>
+            <if test="azCount != null">az_count,</if>
+            <if test="iosCount != null">ios_count,</if>
+            <if test="unknownCount != null">unknown_count,</if>
+            <if test="totalCount != null">total_count,</if>
+            <if test="createTime != null">create_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyName != null">#{companyName},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="companyUserName != null">#{companyUserName},</if>
+            <if test="detectionTime != null">#{detectionTime},</if>
+            <if test="azCount != null">#{azCount},</if>
+            <if test="iosCount != null">#{iosCount},</if>
+            <if test="unknownCount != null">#{unknownCount},</if>
+            <if test="totalCount != null">#{totalCount},</if>
+            <if test="createTime != null">#{createTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateCompanyDetectionPhoneDailyStatistics" parameterType="CompanyDetectionPhoneDailyStatistics">
+        update company_detection_phone_daily_statistics
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="companyName != null">company_name = #{companyName},</if>
+            <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
+            <if test="companyUserName != null">company_user_name = #{companyUserName},</if>
+            <if test="detectionTime != null">detection_time = #{detectionTime},</if>
+            <if test="azCount != null">az_count = #{azCount},</if>
+            <if test="iosCount != null">ios_count = #{iosCount},</if>
+            <if test="unknownCount != null">unknown_count = #{unknownCount},</if>
+            <if test="totalCount != null">total_count = #{totalCount},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCompanyDetectionPhoneDailyStatisticsById" parameterType="Long">
+        delete from company_detection_phone_daily_statistics where id = #{id}
+    </delete>
+
+    <delete id="deleteCompanyDetectionPhoneDailyStatisticsByIds" parameterType="String">
+        delete from company_detection_phone_daily_statistics where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 116 - 0
fs-service/src/main/resources/mapper/company/CompanyDetectionPhoneRecordMapper.xml

@@ -0,0 +1,116 @@
+<?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.company.mapper.CompanyDetectionPhoneRecordMapper">
+    
+    <resultMap type="CompanyDetectionPhoneRecord" id="CompanyDetectionPhoneRecordResult">
+        <result property="id"    column="id"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="companyName"    column="company_name"    />
+        <result property="companyUserId"    column="company_user_id"    />
+        <result property="companyUserName"    column="company_user_name"    />
+        <result property="userId"    column="user_id"    />
+        <result property="nickName"    column="nick_name"    />
+        <result property="phoneNumber"    column="phone_number"    />
+        <result property="detectionSystem"    column="detection_system"    />
+        <result property="detectionModel"    column="detection_model"    />
+        <result property="detectionTime"    column="detection_time"    />
+        <result property="status"    column="status"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="remark"    column="remark"    />
+    </resultMap>
+
+    <sql id="selectCompanyDetectionPhoneRecordVo">
+        select id, company_id, company_name, company_user_id, company_user_name,user_id,nick_name, phone_number, detection_system, detection_model, detection_time, status, create_time,remark from company_detection_phone_record
+    </sql>
+
+    <select id="selectCompanyDetectionPhoneRecordList" parameterType="CompanyDetectionPhoneRecord" resultMap="CompanyDetectionPhoneRecordResult">
+        <include refid="selectCompanyDetectionPhoneRecordVo"/>
+        <where>  
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="companyName != null  and companyName != ''"> and company_name like concat('%', #{companyName}, '%')</if>
+            <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
+            <if test="companyUserName != null  and companyUserName != ''"> and company_user_name like concat('%', #{companyUserName}, '%')</if>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="nickName != null  and nickName != ''"> and nick_name like concat('%', #{nickName}, '%')</if>
+            <if test="phoneNumber != null  and phoneNumber != ''"> and phone_number like concat('%', #{phoneNumber}, '%')</if>
+            <if test="detectionSystem != null "> and detection_system = #{detectionSystem}</if>
+            <if test="detectionModel != null  and detectionModel != ''"> and detection_model = #{detectionModel}</if>
+            <if test="detectionTime != null "> and DATE(detection_time) = DATE(#{detectionTime})</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="remark != null  and remark != ''"> and remark like concat('%', #{remark}, '%')</if>
+        </where>
+        order by id desc
+    </select>
+    
+    <select id="selectCompanyDetectionPhoneRecordById" parameterType="Long" resultMap="CompanyDetectionPhoneRecordResult">
+        <include refid="selectCompanyDetectionPhoneRecordVo"/>
+        where id = #{id}
+    </select>
+        
+    <insert id="insertCompanyDetectionPhoneRecord" parameterType="CompanyDetectionPhoneRecord" useGeneratedKeys="true" keyProperty="id">
+        insert into company_detection_phone_record
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">company_id,</if>
+            <if test="companyName != null">company_name,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="companyUserName != null">company_user_name,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="nickName != null">nick_name,</if>
+            <if test="phoneNumber != null">phone_number,</if>
+            <if test="detectionSystem != null">detection_system,</if>
+            <if test="detectionModel != null">detection_model,</if>
+            <if test="detectionTime != null">detection_time,</if>
+            <if test="status != null">status,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="remark != null"> remark,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyName != null">#{companyName},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="companyUserName != null">#{companyUserName},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="nickName != null">#{nickName},</if>
+            <if test="phoneNumber != null">#{phoneNumber},</if>
+            <if test="detectionSystem != null">#{detectionSystem},</if>
+            <if test="detectionModel != null">#{detectionModel},</if>
+            <if test="detectionTime != null">#{detectionTime},</if>
+            <if test="status != null">#{status},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="remark != null"> #{remark},</if>
+         </trim>
+    </insert>
+
+    <update id="updateCompanyDetectionPhoneRecord" parameterType="CompanyDetectionPhoneRecord">
+        update company_detection_phone_record
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="companyName != null">company_name = #{companyName},</if>
+            <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
+            <if test="companyUserName != null">company_user_name = #{companyUserName},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="nickName != null">nick_name = #{nickName},</if>
+            <if test="phoneNumber != null">phone_number = #{phoneNumber},</if>
+            <if test="detectionSystem != null">detection_system = #{detectionSystem},</if>
+            <if test="detectionModel != null">detection_model = #{detectionModel},</if>
+            <if test="detectionTime != null">detection_time = #{detectionTime},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="remark != null">remark = #{remark},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCompanyDetectionPhoneRecordById" parameterType="Long">
+        delete from company_detection_phone_record where id = #{id}
+    </delete>
+
+    <delete id="deleteCompanyDetectionPhoneRecordByIds" parameterType="String">
+        delete from company_detection_phone_record where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 146 - 0
fs-service/src/main/resources/mapper/company/CompanySmsCommonLogsMapper.xml

@@ -0,0 +1,146 @@
+<?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.company.mapper.CompanySmsCommonLogsMapper">
+
+    <resultMap type="CompanySmsCommonLogs" id="CompanySmsCommonLogsResult">
+        <result property="logsId"    column="logs_id"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="companyUserId"    column="company_user_id"    />
+        <result property="recipientId"    column="recipient_id"    />
+        <result property="recipientName"    column="recipient_name"    />
+        <result property="sendType"    column="send_type"    />
+        <result property="tempId"    column="temp_id"    />
+        <result property="tempCode"    column="temp_code"    />
+        <result property="phone"    column="phone"    />
+        <result property="content"    column="content"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="sendTime"    column="send_time"    />
+        <result property="status"    column="status"    />
+        <result property="mid"    column="mid"    />
+        <result property="stat"    column="stat"    />
+        <result property="replyContent"    column="reply_content"    />
+        <result property="number"    column="number"    />
+        <result property="type"    column="type"    />
+        <result property="companyUserName"    column="company_user_name"    />
+
+    </resultMap>
+
+    <sql id="selectCompanySmsCommonLogsVo">
+        select logs_id, company_id, company_user_id, recipient_id, recipient_name, send_type, temp_id, temp_code, phone, content, create_time, send_time, status, mid, stat, reply_content, number, type,company_user_name from company_sms_common_logs
+    </sql>
+
+    <select id="selectCompanySmsCommonLogsList" parameterType="CompanySmsCommonLogs" resultMap="CompanySmsCommonLogsResult">
+        <include refid="selectCompanySmsCommonLogsVo"/>
+        <where>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
+            <if test="recipientId != null "> and recipient_id = #{recipientId}</if>
+            <if test="recipientName != null "> and recipient_name like concat('%', #{recipientName}, '%')</if>
+            <if test="sendType != null "> and send_type = #{sendType}</if>
+            <if test="tempId != null "> and temp_id = #{tempId}</if>
+            <if test="tempCode != null  and tempCode != ''"> and temp_code = #{tempCode}</if>
+            <if test="phone != null  and phone != ''"> and phone = #{phone}</if>
+            <if test="content != null  and content != ''"> and content = #{content}</if>
+            <if test="sendTime != null "> and send_time = #{sendTime}</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="mid != null  and mid != ''"> and mid = #{mid}</if>
+            <if test="stat != null  and stat != ''"> and stat = #{stat}</if>
+            <if test="replyContent != null  and replyContent != ''"> and reply_content = #{replyContent}</if>
+            <if test="number != null "> and number = #{number}</if>
+            <if test="type != null  and type != ''"> and type = #{type}</if>
+            <if test="companyUserName != null  and companyUserName != ''"> and company_user_name = #{companyUserName}</if>
+            <if test="beginTime != null  and beginTime != '' "> and send_time &gt;= #{beginTime}</if>
+            <if test="endTime != null  and endTime != '' "> and send_time &lt; #{endTime}</if>
+            <if test="isReply != null  and isReply == 0 "> and content is not null</if>
+            <if test="isReply != null  and isReply == 1 "> and content is null</if>
+        </where>
+        order by logs_id desc
+    </select>
+
+    <select id="selectCompanySmsCommonLogsByLogsId" parameterType="Long" resultMap="CompanySmsCommonLogsResult">
+        <include refid="selectCompanySmsCommonLogsVo"/>
+        where logs_id = #{logsId}
+    </select>
+
+    <insert id="insertCompanySmsCommonLogs" parameterType="CompanySmsCommonLogs" useGeneratedKeys="true" keyProperty="logsId">
+        insert into company_sms_common_logs
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">company_id,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="recipientId != null">recipient_id,</if>
+            <if test="recipientName != null">recipient_name,</if>
+            <if test="sendType != null">send_type,</if>
+            <if test="tempId != null">temp_id,</if>
+            <if test="tempCode != null">temp_code,</if>
+            <if test="phone != null">phone,</if>
+            <if test="content != null">content,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="sendTime != null">send_time,</if>
+            <if test="status != null">status,</if>
+            <if test="mid != null">mid,</if>
+            <if test="stat != null">stat,</if>
+            <if test="replyContent != null">reply_content,</if>
+            <if test="number != null">number,</if>
+            <if test="type != null">type,</if>
+            <if test="companyUserName != null">company_user_name,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="recipientId != null">#{recipientId},</if>
+            <if test="recipientName != null">#{recipientName},</if>
+            <if test="sendType != null">#{sendType},</if>
+            <if test="tempId != null">#{tempId},</if>
+            <if test="tempCode != null">#{tempCode},</if>
+            <if test="phone != null">#{phone},</if>
+            <if test="content != null">#{content},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="sendTime != null">#{sendTime},</if>
+            <if test="status != null">#{status},</if>
+            <if test="mid != null">#{mid},</if>
+            <if test="stat != null">#{stat},</if>
+            <if test="replyContent != null">#{replyContent},</if>
+            <if test="number != null">#{number},</if>
+            <if test="type != null">#{type},</if>
+            <if test="companyUserName != null">#{companyUserName},</if>
+         </trim>
+    </insert>
+
+    <update id="updateCompanySmsCommonLogs" parameterType="CompanySmsCommonLogs">
+        update company_sms_common_logs
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
+            <if test="recipientId != null">recipient_id = #{recipientId},</if>
+            <if test="recipientName != null">recipient_name = #{recipientName},</if>
+            <if test="sendType != null">send_type = #{sendType},</if>
+            <if test="tempId != null">temp_id = #{tempId},</if>
+            <if test="tempCode != null">temp_code = #{tempCode},</if>
+            <if test="phone != null">phone = #{phone},</if>
+            <if test="content != null">content = #{content},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="sendTime != null">send_time = #{sendTime},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="mid != null">mid = #{mid},</if>
+            <if test="stat != null">stat = #{stat},</if>
+            <if test="replyContent != null">reply_content = #{replyContent},</if>
+            <if test="number != null">number = #{number},</if>
+            <if test="type != null">type = #{type},</if>
+            <if test="companyUserName != null">company_user_name = #{companyUserName},</if>
+        </trim>
+        where logs_id = #{logsId}
+    </update>
+
+    <delete id="deleteCompanySmsCommonLogsByLogsId" parameterType="Long">
+        delete from company_sms_common_logs where logs_id = #{logsId}
+    </delete>
+
+    <delete id="deleteCompanySmsCommonLogsByLogsIds" parameterType="String">
+        delete from company_sms_common_logs where logs_id in
+        <foreach item="logsId" collection="array" open="(" separator="," close=")">
+            #{logsId}
+        </foreach>
+    </delete>
+</mapper>