Просмотр исходного кода

短信发课/发送获客链接

wangxy 1 неделя назад
Родитель
Сommit
4adb6e39c6

+ 12 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java

@@ -906,4 +906,16 @@ public class CompanyUserController extends BaseController {
         openIMService.deleteAllFriends();
         return AjaxResult.success("删除任务已执行完成");
     }
+
+    @ApiOperation("配置获客链接")
+    @PostMapping("/updateCustomerAcquisitionLink")
+    public R updateCustomerAcquisitionLink(@RequestBody Map<String, Object> map){
+        Long userId = Long.valueOf(map.get("userId").toString());
+        String customerAcquisitionLink = map.get("customerAcquisitionLink") != null ? map.get("customerAcquisitionLink").toString() : null;
+        CompanyUser user = new CompanyUser();
+        user.setUserId(userId);
+        user.setCustomerAcquisitionLink(customerAcquisitionLink);
+        int i = companyUserService.updateCompanyUser(user);
+        return i > 0 ? R.ok() : R.error("更新失败");
+    }
 }

+ 11 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyUser.java

@@ -202,6 +202,9 @@ public class CompanyUser extends BaseEntity
      */
     private Long cidServerId;
 
+    /** 获客链接 */
+    private String customerAcquisitionLink;
+
     public Long getCidServerId() {
         return cidServerId;
     }
@@ -210,6 +213,14 @@ public class CompanyUser extends BaseEntity
         this.cidServerId = cidServerId;
     }
 
+    public String getCustomerAcquisitionLink() {
+        return customerAcquisitionLink;
+    }
+
+    public void setCustomerAcquisitionLink(String customerAcquisitionLink) {
+        this.customerAcquisitionLink = customerAcquisitionLink;
+    }
+
     public String getMaOpenId() {
         return maOpenId;
     }

+ 3 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyUserQwListVO.java

@@ -144,6 +144,9 @@ public class CompanyUserQwListVO extends BaseEntity {
      */
     private Long aiSipCallUserId;
 
+    /** 获客链接 */
+    private String customerAcquisitionLink;
+
 
     /**
      * 绑定会员昵称

+ 2 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java

@@ -626,6 +626,7 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "      AND send_type = 1 " +
             "      AND create_time >= CURDATE() " +
             "      AND create_time < DATE_ADD(CURDATE(), INTERVAL 1 DAY) " +
+            "and user_id=4051515674" +
             "    GROUP BY user_id, video_id " +
             ") latest ON l.log_id = latest.log_id " +
             "WHERE l.log_type <> 2 " +
@@ -636,6 +637,7 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "      WHERE f.user_id = l.user_id " +
             "        AND f.video_id = l.video_id " +
             "        AND f.log_type = 2 " +
+            "and f.user_id=4051515674" +
             "  ) " +
             "ORDER BY l.log_id ASC LIMIT #{limit}")
     List<FsCourseWatchLog> selectTodayUnfinishedCoursePushList(@Param("limit") Integer limit);

+ 35 - 0
fs-service/src/main/java/com/fs/im/domain/FsSmsShortLink.java

@@ -0,0 +1,35 @@
+package com.fs.im.domain;
+
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 短信短链记录
+ */
+@Data
+public class FsSmsShortLink extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    private Integer linkType;
+    private Long companyId;
+    private Long companyUserId;
+    private Long userId;
+    private String phone;
+    private Long periodId;
+    private Long courseId;
+    private Long videoId;
+    private Long periodCourseId;
+    private Long projectId;
+    private String linkCode;
+    private String shortLink;
+    private String realLink;
+    private String customerAcquisitionLink;
+    private String smsContent;
+    private Integer sendStatus;
+    private String sendMsgId;
+    private String sendResult;
+    private Date expireTime;
+}

+ 48 - 0
fs-service/src/main/java/com/fs/im/dto/BatchSmsLinkDTO.java

@@ -0,0 +1,48 @@
+package com.fs.im.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 批量短信发课/获客链接入参
+ */
+@Data
+public class BatchSmsLinkDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "类型:1短信发课,2短信发获客链接", required = true)
+    private Integer type;
+
+    @ApiModelProperty(value = "会员ids", required = true)
+    private List<Long> userIds;
+
+    @ApiModelProperty(value = "公司id", required = true)
+    private Long companyId;
+
+    @ApiModelProperty(value = "销售id", required = true)
+    private Long companyUserId;
+
+    @ApiModelProperty(value = "营期id")
+    private Long periodId;
+
+    @ApiModelProperty(value = "课程id")
+    private Long courseId;
+
+    @ApiModelProperty(value = "视频id")
+    private Long videoId;
+
+    @ApiModelProperty(value = "营期课程id")
+    private Long id;
+
+    @ApiModelProperty(value = "项目id")
+    private Long projectId;
+
+    @ApiModelProperty(value = "获客H5真实地址,type=2时选传")
+    private String h5Url;
+
+    @ApiModelProperty(value = "链接有效时长,单位分钟")
+    private Integer effectiveDuration;
+}

+ 15 - 0
fs-service/src/main/java/com/fs/im/mapper/FsSmsShortLinkMapper.java

@@ -0,0 +1,15 @@
+package com.fs.im.mapper;
+
+import com.fs.im.domain.FsSmsShortLink;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 短信短链Mapper接口
+ */
+public interface FsSmsShortLinkMapper {
+    int insertFsSmsShortLink(FsSmsShortLink fsSmsShortLink);
+
+    int updateFsSmsShortLink(FsSmsShortLink fsSmsShortLink);
+
+    FsSmsShortLink selectFsSmsShortLinkByCode(@Param("linkCode") String linkCode);
+}

+ 9 - 0
fs-service/src/main/java/com/fs/im/service/OpenIMService.java

@@ -12,6 +12,7 @@ import com.fs.im.dto.OpenImBatchMsgDTO;
 import com.fs.im.dto.OpenImEditConversationDTO;
 import com.fs.im.dto.OpenImMsgDTO;
 import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.dto.BatchSmsLinkDTO;
 import com.fs.im.vo.OpenImMsgCallBackVO;
 
 import java.util.List;
@@ -111,4 +112,12 @@ public interface OpenIMService {
      */
     void deleteAllFriends();
 
+
+    /**
+     * 批量给会员短信发课和短信发获客链接 type1 短信发客 2 短信发获课链接
+     */
+    OpenImResponseDTO batchSendSmsLink(BatchSmsLinkDTO batchSmsLinkDTO);
+
+    R getSmsShortLinkDetail(String linkCode);
+
 }

+ 316 - 0
fs-service/src/main/java/com/fs/im/service/impl/OpenIMServiceImpl.java

@@ -13,9 +13,16 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.Company;
+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.mapper.CompanyMapper;
 import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.company.service.ICompanySmsLogsService;
+import com.fs.company.service.ICompanySmsService;
+import com.fs.company.service.ICompanySmsTempService;
+import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.domain.FsUserCompanyUser;
 import com.fs.course.domain.FsUserCourse;
@@ -27,6 +34,7 @@ import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.course.mapper.FsUserCompanyUserMapper;
 import com.fs.course.mapper.FsUserCourseMapper;
 import com.fs.fastGpt.service.AiHookService;
+import com.fs.his.config.FsSmsConfig;
 import com.fs.his.domain.FsDoctor;
 import com.fs.his.domain.FsFollow;
 import com.fs.his.domain.FsUser;
@@ -34,18 +42,26 @@ import com.fs.his.dto.PayloadDTO;
 import com.fs.his.mapper.FsDoctorMapper;
 import com.fs.his.mapper.FsFollowMapper;
 import com.fs.his.mapper.FsUserMapper;
+import com.fs.his.utils.PhoneUtil;
 import com.fs.im.config.IMConfig;
 import com.fs.im.domain.FsImMsgSendDetail;
 import com.fs.im.domain.FsImMsgSendLog;
+import com.fs.im.domain.FsSmsShortLink;
 import com.fs.im.domain.ImSendLog;
 import com.fs.im.dto.*;
 import com.fs.im.mapper.FsImMsgSendDetailMapper;
 import com.fs.im.mapper.FsImMsgSendLogMapper;
+import com.fs.im.mapper.FsSmsShortLinkMapper;
 import com.fs.im.mapper.ImSendLogMapper;
 import com.fs.im.service.OpenIMService;
 import com.fs.im.vo.OpenImMsgCallBackVO;
 import com.fs.im.vo.OpenImResponseDTOTest;
 import com.fs.qw.mapper.QwExternalContactMapper;
+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 com.fs.system.service.ISysConfigService;
 import com.github.pagehelper.util.StringUtil;
 import com.google.common.base.Joiner;
 import lombok.Data;
@@ -61,6 +77,7 @@ import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
+import java.net.URLEncoder;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -69,6 +86,7 @@ import java.util.stream.Collectors;
 @Service
 @Slf4j
 public class OpenIMServiceImpl implements OpenIMService {
+    private static final String SMS_SHORT_LINK_DOMAIN = "https://a.hbhdt.top";
     @Autowired
     IMConfig imConfig;
     @Autowired
@@ -104,6 +122,20 @@ public class OpenIMServiceImpl implements OpenIMService {
     private FsImMsgSendDetailServiceImpl fsImMsgSendDetailServiceImpl;
     @Autowired
     private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+    @Autowired
+    private FsSmsShortLinkMapper fsSmsShortLinkMapper;
+    @Autowired
+    private ICompanySmsService companySmsService;
+    @Autowired
+    private ICompanySmsLogsService companySmsLogsService;
+    @Autowired
+    private ICompanySmsTempService companySmsTempService;
+    @Autowired
+    private SmsTServiceImpl smsTService;
+    @Autowired
+    private SysConfigMapper sysConfigMapper;
+    @Autowired
+    private ISysConfigService configService;
 
 
 //    @Value("${openIM.prefix}")
@@ -1257,6 +1289,290 @@ public class OpenIMServiceImpl implements OpenIMService {
         return finalResponseDTO;
     }
 
+    @Override
+    @Transactional
+    public OpenImResponseDTO batchSendSmsLink(BatchSmsLinkDTO batchSmsLinkDTO) {
+        OpenImResponseDTO responseDTO = new OpenImResponseDTO();
+        if (batchSmsLinkDTO == null || batchSmsLinkDTO.getType() == null || CollectionUtils.isEmpty(batchSmsLinkDTO.getUserIds())) {
+            responseDTO.setErrCode(400);
+            responseDTO.setErrMsg("参数不能为空");
+            return responseDTO;
+        }
+        if (!Objects.equals(batchSmsLinkDTO.getType(), 1) && !Objects.equals(batchSmsLinkDTO.getType(), 2)) {
+            responseDTO.setErrCode(400);
+            responseDTO.setErrMsg("短信链接类型不正确");
+            return responseDTO;
+        }
+        String tempCode = getSmsLinkTempCode(batchSmsLinkDTO.getType());
+        if (Objects.equals(batchSmsLinkDTO.getType(), 1)
+                && (batchSmsLinkDTO.getPeriodId() == null || batchSmsLinkDTO.getVideoId() == null || batchSmsLinkDTO.getCourseId() == null || batchSmsLinkDTO.getCompanyUserId() == null)) {
+            responseDTO.setErrCode(400);
+            responseDTO.setErrMsg("短信发课参数不完整");
+            return responseDTO;
+        }
+        CompanySmsTemp smsTemp = companySmsTempService.selectCompanySmsTempByCode(tempCode);
+        if (smsTemp == null) {
+            responseDTO.setErrCode(400);
+            responseDTO.setErrMsg("短信模板不存在");
+            return responseDTO;
+        }
+        if (!Integer.valueOf(1).equals(smsTemp.getStatus()) || (smsTemp.getIsAudit() != null && !Integer.valueOf(1).equals(smsTemp.getIsAudit()))) {
+            responseDTO.setErrCode(400);
+            responseDTO.setErrMsg("短信模板未启用或未审核");
+            return responseDTO;
+        }
+        int successCount = 0;
+        int failCount = 0;
+        List<Map<String, Object>> resultList = new ArrayList<>();
+        for (Long userId : batchSmsLinkDTO.getUserIds()) {
+            FsUser fsUser = fsUserMapper.selectFsUserByUserId(userId);
+            String phone = getUserPlainPhone(fsUser);
+            if (fsUser == null || StringUtils.isEmpty(phone)) {
+                failCount++;
+                continue;
+            }
+            FsSmsShortLink shortLink = createSmsShortLink(batchSmsLinkDTO, fsUser, phone);
+            fsSmsShortLinkMapper.insertFsSmsShortLink(shortLink);
+
+            String smsContent = buildSmsContent(smsTemp, shortLink.getShortLink());
+            SendSmsResult sendSmsResult = sendSmsByConfig(smsTemp, batchSmsLinkDTO.getCompanyId(), phone, smsContent);
+            String actualSmsContent = StringUtils.isEmpty(sendSmsResult.content) ? smsContent : sendSmsResult.content;
+            shortLink.setSmsContent(actualSmsContent);
+            shortLink.setSendStatus(sendSmsResult.success ? 1 : 2);
+            shortLink.setSendMsgId(sendSmsResult.msgId);
+            shortLink.setSendResult(sendSmsResult.message);
+            shortLink.setUpdateTime(new Date());
+            fsSmsShortLinkMapper.updateFsSmsShortLink(shortLink);
+
+            saveSmsLog(smsTemp, batchSmsLinkDTO, fsUser, phone, actualSmsContent, sendSmsResult);
+            if (sendSmsResult.success) {
+                companySmsService.subCompanySms(batchSmsLinkDTO.getCompanyId(), getSmsCount(actualSmsContent));
+                successCount++;
+            } else {
+                failCount++;
+            }
+            Map<String, Object> item = new HashMap<>();
+            item.put("userId", userId);
+            item.put("phone", phone);
+            item.put("linkCode", shortLink.getLinkCode());
+            item.put("shortLink", shortLink.getShortLink());
+            item.put("success", sendSmsResult.success);
+            item.put("message", sendSmsResult.message);
+            resultList.add(item);
+        }
+        Map<String, Object> data = new HashMap<>();
+        data.put("successCount", successCount);
+        data.put("failCount", failCount);
+        data.put("list", resultList);
+        responseDTO.setErrCode(0);
+        responseDTO.setErrMsg("短信发送完成");
+        responseDTO.setData(data);
+        return responseDTO;
+    }
+
+    @Override
+    public R getSmsShortLinkDetail(String linkCode) {
+        if (StringUtils.isEmpty(linkCode)) {
+            return R.error("短链参数不能为空");
+        }
+        FsSmsShortLink shortLink = fsSmsShortLinkMapper.selectFsSmsShortLinkByCode(linkCode);
+        if (shortLink == null) {
+            return R.error("短链不存在");
+        }
+        if (shortLink.getExpireTime() != null && shortLink.getExpireTime().before(new Date())) {
+            return R.error("短链已过期");
+        }
+        return R.ok().put("data", shortLink);
+    }
+
+    private String getSmsLinkTempCode(Integer type) {
+        return Objects.equals(type, 1) ? "sms_course" : "sms_link";
+    }
+
+    private String getUserPlainPhone(FsUser fsUser) {
+        if (fsUser == null || StringUtils.isEmpty(fsUser.getPhone())) {
+            return null;
+        }
+        String phone = fsUser.getPhone();
+        if (phone.length() > 11) {
+            String decryptPhone = PhoneUtil.decryptPhone(phone);
+            return StringUtils.isEmpty(decryptPhone) ? phone : decryptPhone;
+        }
+        return phone;
+    }
+
+    private FsSmsShortLink createSmsShortLink(BatchSmsLinkDTO param, FsUser fsUser, String phone) {
+        FsSmsShortLink shortLink = new FsSmsShortLink();
+        shortLink.setLinkType(param.getType());
+        shortLink.setCompanyId(param.getCompanyId());
+        shortLink.setCompanyUserId(param.getCompanyUserId());
+        shortLink.setUserId(fsUser.getUserId());
+        shortLink.setPhone(phone);
+        shortLink.setPeriodId(param.getPeriodId());
+        shortLink.setCourseId(param.getCourseId());
+        shortLink.setVideoId(param.getVideoId());
+        shortLink.setPeriodCourseId(param.getId());
+        shortLink.setProjectId(param.getProjectId());
+        shortLink.setLinkCode(generateSmsLinkCode(param.getType()));
+        String customerAcquisitionLink = null;
+        if (Objects.equals(param.getType(), 2)) {
+            CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getCompanyUserId());
+            customerAcquisitionLink = companyUser == null ? null : companyUser.getCustomerAcquisitionLink();
+            shortLink.setCustomerAcquisitionLink(customerAcquisitionLink);
+        }
+        shortLink.setRealLink(buildSmsRealLink(param, customerAcquisitionLink));
+        shortLink.setShortLink(buildSmsShortLink(shortLink.getLinkCode()));
+        shortLink.setSendStatus(0);
+        Date now = new Date();
+        shortLink.setCreateTime(now);
+        shortLink.setUpdateTime(now);
+        if (param.getEffectiveDuration() != null && param.getEffectiveDuration() > 0) {
+            shortLink.setExpireTime(new Date(now.getTime() + TimeUnit.MINUTES.toMillis(param.getEffectiveDuration())));
+        }
+        return shortLink;
+    }
+
+    private String generateSmsLinkCode(Integer type) {
+        String prefix = Objects.equals(type, 1) ? "W" : "U";
+        for (int i = 0; i < 5; i++) {
+            String code = prefix + Long.toString(System.currentTimeMillis(), 36) + randomShortCode(4);
+            if (fsSmsShortLinkMapper.selectFsSmsShortLinkByCode(code) == null) {
+                return code;
+            }
+        }
+        return prefix + Long.toString(System.nanoTime(), 36) + randomShortCode(4);
+    }
+
+    private String randomShortCode(int length) {
+        String chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        StringBuilder builder = new StringBuilder(length);
+        for (int i = 0; i < length; i++) {
+            builder.append(chars.charAt(ThreadLocalRandom.current().nextInt(chars.length())));
+        }
+        return builder.toString();
+    }
+
+    private String buildSmsRealLink(BatchSmsLinkDTO param, String customerAcquisitionLink) {
+        if (Objects.equals(param.getType(), 2)) {
+            return StringUtils.isEmpty(customerAcquisitionLink) ? param.getH5Url() : customerAcquisitionLink;
+        }
+        return "/courseH5/pages/course/learning?periodId=" + param.getPeriodId()
+                + "&videoId=" + param.getVideoId()
+                + "&courseId=" + param.getCourseId()
+                + "&companyUserId=" + param.getCompanyUserId()
+                + (param.getId() == null ? "" : "&id=" + param.getId())
+                + (param.getProjectId() == null ? "" : "&projectId=" + param.getProjectId());
+    }
+
+    private String buildSmsShortLink(String linkCode) {
+        return SMS_SHORT_LINK_DOMAIN + "/" + linkCode;
+    }
+
+    private String buildSmsContent(CompanySmsTemp smsTemp, String shortLink) {
+        String content = smsTemp.getContent();
+        if (StringUtils.isEmpty(content)) {
+            content = "${sms.link}";
+        }
+        content = content.replace("${sms.link}", shortLink);
+        content = content.replace("${link}", shortLink);
+        if (!content.contains(shortLink)) {
+            content = content + shortLink;
+        }
+        return content;
+    }
+
+    private String buildSmsSendContent(String content, FsSmsConfig sms) {
+        String sign = "rf".equals(sms.getType()) ? sms.getRfSign() : sms.getDhSign();
+        if (StringUtils.isEmpty(sign)) {
+            sign = "";
+        }
+        if (content.contains("${sms.sign}")) {
+            return content.replace("${sms.sign}", sign);
+        }
+        return sign + content;
+    }
+
+    private SendSmsResult sendSmsByConfig(CompanySmsTemp smsTemp, Long companyId, String phone, String content) {
+        SendSmsResult result = new SendSmsResult();
+        try {
+            SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.sms");
+            FsSmsConfig sms = JSON.parseObject(sysConfig.getConfigValue(), FsSmsConfig.class);
+            result.smsType = sms.getType();
+            String sendContent = buildSmsSendContent(content, sms);
+            result.content = sendContent;
+            if ("rf".equals(sms.getType())) {
+                String urls;
+                if (Integer.valueOf(1).equals(smsTemp.getTempType())) {
+                    urls = sms.getRfUrl1() + "sms?action=send&account=" + sms.getRfAccount1()
+                            + "&password=" + sms.getRfPassword1()
+                            + "&mobile=" + phone
+                            + "&content=" + URLEncoder.encode(sendContent, "UTF-8")
+                            + "&extno=" + sms.getRfCode1()
+                            + "&rt=json";
+                } else {
+                    urls = sms.getRfUrl2() + "sms?action=send&account=" + sms.getRfAccount2()
+                            + "&password=" + sms.getRfPassword2()
+                            + "&mobile=" + phone
+                            + "&content=" + URLEncoder.encode(sendContent + "拒收请回复R", "UTF-8")
+                            + "&extno=" + sms.getRfCode2()
+                            + "&rt=json";
+                }
+                String post = HttpRequest.get(urls).execute().body();
+                cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(post);
+                result.success = Objects.equals(jsonObject.getInt("status"), 0);
+                result.message = post;
+                return result;
+            }
+            SendSmsReturn sendSmsReturn;
+            if (Integer.valueOf(1).equals(smsTemp.getTempType())) {
+                sendSmsReturn = smsTService.sendSms(sms.getDhAccount1(), sms.getDhPassword1(), sendContent, phone);
+            } else {
+                sendSmsReturn = smsTService.sendSms(sms.getDhAccount2(), sms.getDhPassword2(), sendContent + "拒收请回复R", phone);
+            }
+            result.success = sendSmsReturn != null && "0".equals(sendSmsReturn.getResult());
+            result.msgId = sendSmsReturn == null ? null : sendSmsReturn.getMsgid();
+            result.message = sendSmsReturn == null ? "短信发送无响应" : sendSmsReturn.getDesc();
+        } catch (Exception e) {
+            result.success = false;
+            result.message = e.getMessage();
+            log.error("短信短链发送失败,companyId:{},phone:{}", companyId, phone, e);
+        }
+        return result;
+    }
+
+    private void saveSmsLog(CompanySmsTemp smsTemp, BatchSmsLinkDTO param, FsUser fsUser, String phone, String content, SendSmsResult sendSmsResult) {
+        CompanySmsLogs logs = new CompanySmsLogs();
+        logs.setPhone(phone);
+        logs.setContent(content);
+        logs.setTempCode(smsTemp.getTempCode());
+        logs.setTempId(smsTemp.getTempId());
+        logs.setSendTime(new Date());
+        logs.setStatus(sendSmsResult.success ? 0 : -1);
+        logs.setMid(sendSmsResult.msgId);
+        logs.setNumber(getSmsCount(content));
+        logs.setType(sendSmsResult.smsType);
+        companySmsLogsService.insertCompanySmsLogs(logs);
+    }
+
+    private int getSmsCount(String content) {
+        if (StringUtils.isEmpty(content)) {
+            return 1;
+        }
+        int counts = content.length() / 67;
+        if (content.length() % 67 > 0) {
+            counts++;
+        }
+        return counts == 0 ? 1 : counts;
+    }
+
+    private static class SendSmsResult {
+        private boolean success;
+        private String msgId;
+        private String message;
+        private String smsType;
+        private String content;
+    }
+
     private OpenImBatchMsgDTO makeOpenImBatchMsgDTO(BatchSendCourseDTO batchSendCourseDTO, String courseUrl, ObjectMapper objectMapper, List<String> userIds, long planSendTimeStamp, String logType) throws JsonProcessingException {
          PayloadDTO.Extension extension = new PayloadDTO.Extension();
         OpenImBatchMsgDTO openImBatchMsgDTO = new OpenImBatchMsgDTO();

+ 8 - 2
fs-service/src/main/resources/mapper/company/CompanyUserMapper.xml

@@ -44,6 +44,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="isAllowedAllRegister"    column="is_allowed_all_register"    />
         <result property="doctorId"    column="doctor_id"    />
         <result property="mpOpenId"    column="mp_open_id"    />
+        <result property="customerAcquisitionLink"    column="customer_acquisition_link"    />
         <association property="dept"    column="dept_id" javaType="CompanyDept" resultMap="deptResult" />
         <collection  property="roles"   javaType="java.util.List"        resultMap="RoleResult" />
     </resultMap>
@@ -69,6 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="isAllowedAllRegister"    column="is_allowed_all_register" />
         <result property="cidServerId" column="cid_server_id"/>
         <result property="aiSipCallUserId" column="ai_sip_call_user_id"/>
+        <result property="customerAcquisitionLink" column="customer_acquisition_link"/>
         <collection property="roleNames" ofType="java.lang.String" javaType="java.util.ArrayList"
                     select="selectUserRoles" column="user_id"/>
     </resultMap>
@@ -103,7 +105,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         GROUP_CONCAT(fu.nick_name) bindUser,
         cfu.`status` bindStatus,
         u.cid_server_id,
-        u.ai_sip_call_user_id
+        u.ai_sip_call_user_id,
+        u.customer_acquisition_link
         from
         company_user u
         left join
@@ -246,6 +249,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="domain != null">domain,</if>
             <if test="isAudit != null">`is_audit`,</if>
             <if test="mpOpenId != null">`mp_open_id`,</if>
+            <if test="customerAcquisitionLink != null">customer_acquisition_link,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="companyId != null">#{companyId},</if>
@@ -280,6 +284,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="domain != null">#{domain},</if>
             <if test="isAudit != null">#{isAudit},</if>
             <if test="mpOpenId != null">#{mpOpenId},</if>
+            <if test="customerAcquisitionLink != null">#{customerAcquisitionLink},</if>
         </trim>
     </insert>
     <insert id="insertQwIpadTotal">
@@ -327,6 +332,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="doctorId != null">`doctor_id` = #{doctorId},</if>
             <if test="mpOpenId != null">`mp_open_id` = #{mpOpenId},</if>
             <if test="invitationCode != null">`invitation_code` = #{invitationCode},</if>
+            <if test="customerAcquisitionLink != null">customer_acquisition_link = #{customerAcquisitionLink},</if>
         </trim>
         where user_id = #{userId}
     </update>
@@ -456,7 +462,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                u.create_time,u.id_card, u.remark,u.user_type,u.open_id,u.qr_code_weixin,u.qr_code_wecom,u.jpush_id,u.domain,u.is_audit,u.address_id,
                d.dept_id, d.parent_id, d.dept_name, d.order_num, d.leader, d.status as dept_status,
                r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status,
-               u.is_need_register_member, u.is_allowed_all_register,u.doctor_id
+               u.is_need_register_member, u.is_allowed_all_register,u.doctor_id,u.customer_acquisition_link
         from company_user u
                  left join company_dept d on u.dept_id = d.dept_id
                  left join company_user_role ur on u.user_id = ur.user_id

+ 98 - 0
fs-service/src/main/resources/mapper/im/FsSmsShortLinkMapper.xml

@@ -0,0 +1,98 @@
+<?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.im.mapper.FsSmsShortLinkMapper">
+
+    <resultMap type="com.fs.im.domain.FsSmsShortLink" id="FsSmsShortLinkResult">
+        <id property="id" column="id" />
+        <result property="linkType" column="link_type" />
+        <result property="companyId" column="company_id" />
+        <result property="companyUserId" column="company_user_id" />
+        <result property="userId" column="user_id" />
+        <result property="phone" column="phone" />
+        <result property="periodId" column="period_id" />
+        <result property="courseId" column="course_id" />
+        <result property="videoId" column="video_id" />
+        <result property="periodCourseId" column="period_course_id" />
+        <result property="projectId" column="project_id" />
+        <result property="linkCode" column="link_code" />
+        <result property="shortLink" column="short_link" />
+        <result property="realLink" column="real_link" />
+        <result property="customerAcquisitionLink" column="customer_acquisition_link" />
+        <result property="smsContent" column="sms_content" />
+        <result property="sendStatus" column="send_status" />
+        <result property="sendMsgId" column="send_msg_id" />
+        <result property="sendResult" column="send_result" />
+        <result property="expireTime" column="expire_time" />
+        <result property="createTime" column="create_time" />
+        <result property="updateTime" column="update_time" />
+    </resultMap>
+
+    <insert id="insertFsSmsShortLink" parameterType="com.fs.im.domain.FsSmsShortLink" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_sms_short_link
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="linkType != null">link_type,</if>
+            <if test="companyId != null">company_id,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="phone != null">phone,</if>
+            <if test="periodId != null">period_id,</if>
+            <if test="courseId != null">course_id,</if>
+            <if test="videoId != null">video_id,</if>
+            <if test="periodCourseId != null">period_course_id,</if>
+            <if test="projectId != null">project_id,</if>
+            <if test="linkCode != null">link_code,</if>
+            <if test="shortLink != null">short_link,</if>
+            <if test="realLink != null">real_link,</if>
+            <if test="customerAcquisitionLink != null">customer_acquisition_link,</if>
+            <if test="smsContent != null">sms_content,</if>
+            <if test="sendStatus != null">send_status,</if>
+            <if test="sendMsgId != null">send_msg_id,</if>
+            <if test="sendResult != null">send_result,</if>
+            <if test="expireTime != null">expire_time,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="linkType != null">#{linkType},</if>
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="phone != null">#{phone},</if>
+            <if test="periodId != null">#{periodId},</if>
+            <if test="courseId != null">#{courseId},</if>
+            <if test="videoId != null">#{videoId},</if>
+            <if test="periodCourseId != null">#{periodCourseId},</if>
+            <if test="projectId != null">#{projectId},</if>
+            <if test="linkCode != null">#{linkCode},</if>
+            <if test="shortLink != null">#{shortLink},</if>
+            <if test="realLink != null">#{realLink},</if>
+            <if test="customerAcquisitionLink != null">#{customerAcquisitionLink},</if>
+            <if test="smsContent != null">#{smsContent},</if>
+            <if test="sendStatus != null">#{sendStatus},</if>
+            <if test="sendMsgId != null">#{sendMsgId},</if>
+            <if test="sendResult != null">#{sendResult},</if>
+            <if test="expireTime != null">#{expireTime},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+        </trim>
+    </insert>
+
+    <update id="updateFsSmsShortLink" parameterType="com.fs.im.domain.FsSmsShortLink">
+        update fs_sms_short_link
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="shortLink != null">short_link = #{shortLink},</if>
+            <if test="smsContent != null">sms_content = #{smsContent},</if>
+            <if test="sendStatus != null">send_status = #{sendStatus},</if>
+            <if test="sendMsgId != null">send_msg_id = #{sendMsgId},</if>
+            <if test="sendResult != null">send_result = #{sendResult},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <select id="selectFsSmsShortLinkByCode" resultMap="FsSmsShortLinkResult">
+        select * from fs_sms_short_link where link_code = #{linkCode} limit 1
+    </select>
+</mapper>