|
@@ -10,9 +10,16 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.fs.app.domain.FsAppRole;
|
|
import com.fs.app.domain.FsAppRole;
|
|
|
import com.fs.app.mapper.FsAppRoleMapper;
|
|
import com.fs.app.mapper.FsAppRoleMapper;
|
|
|
import com.fs.common.exception.CustomException;
|
|
import com.fs.common.exception.CustomException;
|
|
|
|
|
+import com.fs.company.domain.CompanyMiniapp;
|
|
|
|
|
+import com.fs.company.service.ICompanyMiniappService;
|
|
|
import com.fs.company.service.ICompanySmsService;
|
|
import com.fs.company.service.ICompanySmsService;
|
|
|
import com.fs.core.config.WxMaConfiguration;
|
|
import com.fs.core.config.WxMaConfiguration;
|
|
|
import com.fs.core.utils.OrderCodeUtils;
|
|
import com.fs.core.utils.OrderCodeUtils;
|
|
|
|
|
+import com.fs.course.domain.FsCourseLink;
|
|
|
|
|
+import com.fs.course.domain.FsCourseRealLink;
|
|
|
|
|
+import com.fs.course.domain.FsCourseWatchLog;
|
|
|
|
|
+import com.fs.course.mapper.FsCourseLinkMapper;
|
|
|
|
|
+import com.fs.course.mapper.FsCourseWatchLogMapper;
|
|
|
import com.fs.doctor.service.IFsDoctorOnlineService;
|
|
import com.fs.doctor.service.IFsDoctorOnlineService;
|
|
|
import com.fs.fastGpt.domain.FastgptChatVoiceHomo;
|
|
import com.fs.fastGpt.domain.FastgptChatVoiceHomo;
|
|
|
import com.fs.fastGpt.mapper.FastgptChatVoiceHomoMapper;
|
|
import com.fs.fastGpt.mapper.FastgptChatVoiceHomoMapper;
|
|
@@ -30,9 +37,14 @@ import com.fs.jd.dto.CarrierInfoDTO;
|
|
|
import com.fs.jd.dto.SoQueryRequestDTO;
|
|
import com.fs.jd.dto.SoQueryRequestDTO;
|
|
|
import com.fs.jd.dto.SoQueryResponseDTO;
|
|
import com.fs.jd.dto.SoQueryResponseDTO;
|
|
|
import com.fs.jd.http.IJdHttpService;
|
|
import com.fs.jd.http.IJdHttpService;
|
|
|
|
|
+import com.fs.qw.domain.QwExternalContact;
|
|
|
|
|
+import com.fs.qw.domain.QwUser;
|
|
|
import com.fs.qw.mapper.FsSopCompanyUserTaskMapper;
|
|
import com.fs.qw.mapper.FsSopCompanyUserTaskMapper;
|
|
|
import com.fs.qw.mapper.QwExternalContactMapper;
|
|
import com.fs.qw.mapper.QwExternalContactMapper;
|
|
|
|
|
+import com.fs.qw.vo.QwSopCourseFinishTempSetting;
|
|
|
|
|
+import com.fs.sop.domain.QwSopLogs;
|
|
|
import com.fs.sop.domain.QwSopTempVoice;
|
|
import com.fs.sop.domain.QwSopTempVoice;
|
|
|
|
|
+import com.fs.sop.mapper.QwSopLogsMapper;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
|
|
import com.fs.common.core.redis.RedisCache;
|
|
import com.fs.common.core.redis.RedisCache;
|
|
@@ -94,6 +106,8 @@ import com.fs.sop.mapper.SopUserLogsInfoMapper;
|
|
|
import com.fs.sop.service.IQwSopTempVoiceService;
|
|
import com.fs.sop.service.IQwSopTempVoiceService;
|
|
|
import com.fs.system.domain.SysConfig;
|
|
import com.fs.system.domain.SysConfig;
|
|
|
import com.fs.system.mapper.SysConfigMapper;
|
|
import com.fs.system.mapper.SysConfigMapper;
|
|
|
|
|
+import com.fs.system.service.ISysConfigService;
|
|
|
|
|
+import com.fs.voice.utils.StringUtil;
|
|
|
import com.fs.wx.domain.FsWxExpressTask;
|
|
import com.fs.wx.domain.FsWxExpressTask;
|
|
|
import com.fs.wx.dto.*;
|
|
import com.fs.wx.dto.*;
|
|
|
import com.fs.wx.mapper.FsWxExpressTaskMapper;
|
|
import com.fs.wx.mapper.FsWxExpressTaskMapper;
|
|
@@ -105,24 +119,35 @@ import lombok.extern.slf4j.Slf4j;
|
|
|
import me.chanjar.weixin.common.error.WxErrorException;
|
|
import me.chanjar.weixin.common.error.WxErrorException;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
import org.slf4j.LoggerFactory;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Qualifier;
|
|
import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
|
import org.springframework.stereotype.Component;
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
import java.math.BigDecimal;
|
|
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
import java.time.LocalDateTime;
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.OffsetDateTime;
|
|
import java.time.OffsetDateTime;
|
|
|
|
|
+import java.time.ZoneId;
|
|
|
import java.time.ZoneOffset;
|
|
import java.time.ZoneOffset;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
+import static com.fs.course.utils.LinkUtil.generateRandomStringWithLock;
|
|
|
|
|
+
|
|
|
@Slf4j
|
|
@Slf4j
|
|
|
@Component("task")
|
|
@Component("task")
|
|
|
public class Task {
|
|
public class Task {
|
|
|
|
|
+ private static final String miniappRealLink = "pages_user/answerQues?";
|
|
|
|
|
+ //商城小程序appId配置常量
|
|
|
|
|
+ private static final String APP_ID_CONFIG_KEY = "appStore.appId.config";
|
|
|
|
|
|
|
|
|
|
+ private static final String DEFAULT_APP_ID = "wx50bcb040b4963a7e";
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private ISysConfigService sysConfigService;
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private FsUserInformationCollectionMapper userInformationCollectionMapper;
|
|
private FsUserInformationCollectionMapper userInformationCollectionMapper;
|
|
|
|
|
|
|
@@ -214,6 +239,9 @@ public class Task {
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private IFsCourseWatchLogService fsCourseWatchLogService;
|
|
private IFsCourseWatchLogService fsCourseWatchLogService;
|
|
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private FsCourseWatchLogMapper watchLogMapper;
|
|
|
|
|
+
|
|
|
@Autowired
|
|
@Autowired
|
|
|
ITencentCloudCosService tencentCloudCosService;
|
|
ITencentCloudCosService tencentCloudCosService;
|
|
|
@Autowired
|
|
@Autowired
|
|
@@ -252,6 +280,9 @@ public class Task {
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private FsOrderSopLogMapper fsOrderSopLogMapper;
|
|
private FsOrderSopLogMapper fsOrderSopLogMapper;
|
|
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private QwSopLogsMapper qwSopLogsMapper;
|
|
|
|
|
+
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private IQwExternalContactService qwExternalContactService;
|
|
private IQwExternalContactService qwExternalContactService;
|
|
|
|
|
|
|
@@ -321,6 +352,12 @@ public class Task {
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private XiaoShouYiOAuthService xiaoShouYiOAuthService;
|
|
private XiaoShouYiOAuthService xiaoShouYiOAuthService;
|
|
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private ICompanyMiniappService companyMiniappService;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private FsCourseLinkMapper fsCourseLinkMapper;
|
|
|
|
|
+
|
|
|
public static final String SOP_TEMP_VOICE_KEY = "sop:tempVoice";
|
|
public static final String SOP_TEMP_VOICE_KEY = "sop:tempVoice";
|
|
|
|
|
|
|
|
// sop升单客户类型
|
|
// sop升单客户类型
|
|
@@ -2626,4 +2663,215 @@ public class Task {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 发送答题SOP
|
|
|
|
|
+ */
|
|
|
|
|
+ public void sendSopLogs(){
|
|
|
|
|
+ String lockKey = "sendSopLogs:lock";
|
|
|
|
|
+ Boolean acquired = redisCache.setIfAbsent(lockKey, "1", 10, TimeUnit.MINUTES);
|
|
|
|
|
+ if (acquired == null || !acquired) {
|
|
|
|
|
+ logger.info("sendSopLogs 任务正在执行中,跳过本次执行");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<FsCourseWatchLog> watchLogs;
|
|
|
|
|
+ try {
|
|
|
|
|
+ watchLogs = watchLogMapper.selectAnswerWatchLogs();
|
|
|
|
|
+ if (CollectionUtils.isEmpty(watchLogs)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<QwSopLogs> sopLogsList = new ArrayList<>();
|
|
|
|
|
+ for (FsCourseWatchLog watchLog : watchLogs) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ QwUser qwUser = qwUserService.selectQwUserById(watchLog.getQwUserId());
|
|
|
|
|
+
|
|
|
|
|
+ if (qwUser==null||qwUser.getCompanyId()==null||qwUser.getCompanyUserId()==null){
|
|
|
|
|
+ throw new CustomException("员工未绑定 客服公司 或 客服 请先绑定");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ QwCompany qwCompany = qwCompanyService.getQwCompanyByRedis(qwUser.getCorpId());
|
|
|
|
|
+ if ( qwCompany==null ) {
|
|
|
|
|
+ throw new CustomException("企业不存在,请联系管理员");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+// List<Long> companyIds = Arrays.stream(Optional.ofNullable(qwCompany.getCompanyIds()).orElse("").split(","))
|
|
|
|
|
+// .map(String::trim)
|
|
|
|
|
+// .filter(s -> !s.isEmpty())
|
|
|
|
|
+// .map(Long::parseLong)
|
|
|
|
|
+// .collect(Collectors.toList());
|
|
|
|
|
+//
|
|
|
|
|
+// List<CompanyMiniapp> miniAppList = companyMiniappService.getMiniAppListByCompanyListTwo(companyIds);
|
|
|
|
|
+// Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap = miniAppList.stream()
|
|
|
|
|
+// .collect(Collectors.groupingBy(CompanyMiniapp::getCompanyId,
|
|
|
|
|
+// Collectors.groupingBy(CompanyMiniapp::getType)));
|
|
|
|
|
+
|
|
|
|
|
+ String finalAppId = getAppId();
|
|
|
|
|
+
|
|
|
|
|
+// if (StringUtil.strIsNullOrEmpty(finalAppId)) {
|
|
|
|
|
+// finalAppId = qwCompany.getMiniAppId();
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
|
|
+ if (StringUtil.strIsNullOrEmpty(finalAppId)) {
|
|
|
|
|
+ throw new CustomException("没有可用小程序appId");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String linkByMiniApp = createLinkByMiniApp(new Date(), watchLog.getCourseId(), watchLog.getVideoId(), qwUser, watchLog.getQwExternalContactId());
|
|
|
|
|
+
|
|
|
|
|
+ QwExternalContact qwExternalContact = qwExternalContactService.selectQwExternalContactById(watchLog.getQwExternalContactId());
|
|
|
|
|
+
|
|
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
+
|
|
|
|
|
+ QwSopLogs sopLogs = new QwSopLogs();
|
|
|
|
|
+ sopLogs.setQwUserid(qwUser.getQwUserId());
|
|
|
|
|
+ sopLogs.setExternalUserId(qwExternalContact.getExternalUserId());
|
|
|
|
|
+ sopLogs.setExternalId(qwExternalContact.getId());
|
|
|
|
|
+ sopLogs.setLogType(2);
|
|
|
|
|
+ sopLogs.setSendStatus(3L);
|
|
|
|
|
+ sopLogs.setSendTime(sdf.format(new Date()));
|
|
|
|
|
+ sopLogs.setCompanyId(qwUser.getCompanyId());
|
|
|
|
|
+ sopLogs.setReceivingStatus(0L);
|
|
|
|
|
+ sopLogs.setSopId("miniSidebar");
|
|
|
|
|
+ sopLogs.setCorpId(qwUser.getCorpId());
|
|
|
|
|
+ sopLogs.setFsUserId(watchLog.getUserId());
|
|
|
|
|
+ sopLogs.setSort(0);
|
|
|
|
|
+ sopLogs.setSendType(3);
|
|
|
|
|
+ sopLogs.setExternalUserName(qwExternalContact.getName());
|
|
|
|
|
+ sopLogs.setQwUserKey(qwUser.getId());
|
|
|
|
|
+
|
|
|
|
|
+ QwSopCourseFinishTempSetting setting = new QwSopCourseFinishTempSetting();
|
|
|
|
|
+ List<QwSopCourseFinishTempSetting.Setting> list = new ArrayList<>();
|
|
|
|
|
+ QwSopCourseFinishTempSetting.Setting st = new QwSopCourseFinishTempSetting.Setting();
|
|
|
|
|
+ st.setContentType("4");
|
|
|
|
|
+ //st.setCourseId(param.getCourseId());
|
|
|
|
|
+ st.setVideoId(watchLog.getVideoId());
|
|
|
|
|
+ st.setMiniprogramAppid(finalAppId);
|
|
|
|
|
+ String title = "答题领取";
|
|
|
|
|
+ st.setMiniprogramTitle(title);
|
|
|
|
|
+ st.setMiniprogramPicUrl("https://ysy-1329817240.cos.ap-guangzhou.myqcloud.com/ysy/20260710/9dac8618740f460a8bb60239a8913fe4.png");
|
|
|
|
|
+ st.setMiniprogramPage(linkByMiniApp);
|
|
|
|
|
+
|
|
|
|
|
+ list.add(st);
|
|
|
|
|
+ setting.setSetting(list);
|
|
|
|
|
+ setting.setType(2);
|
|
|
|
|
+ setting.setCourseType(0);
|
|
|
|
|
+ setting.setCourseId(watchLog.getCourseId().intValue());
|
|
|
|
|
+ setting.setVideoId(watchLog.getVideoId().intValue());
|
|
|
|
|
+
|
|
|
|
|
+ sopLogs.setSendMiniAppId(finalAppId);
|
|
|
|
|
+ sopLogs.setContentJson(JSON.toJSONString(setting));
|
|
|
|
|
+ sopLogsList.add(sopLogs);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("发送答题SOP异常", e);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ if (CollectionUtils.isNotEmpty(sopLogsList)) {
|
|
|
|
|
+ qwSopLogsMapper.batchInsertQwSopLogs(sopLogsList);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (CollectionUtils.isNotEmpty(watchLogs)) {
|
|
|
|
|
+ watchLogMapper.batchUpdateAnswerSendStatus(watchLogs);
|
|
|
|
|
+ }
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ redisCache.deleteObject(lockKey);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取 appId 的私有方法
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getAppId() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ String configValue = sysConfigService.selectConfigByKey(APP_ID_CONFIG_KEY);
|
|
|
|
|
+ if (StringUtils.isNotEmpty(configValue)) {
|
|
|
|
|
+ cn.hutool.json.JSONObject json = JSONUtil.parseObj(configValue);
|
|
|
|
|
+ String appId = json.getStr("appId");
|
|
|
|
|
+ if (StringUtils.isNotEmpty(appId)) {
|
|
|
|
|
+ return appId;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.warn("配置 {} 中 appId 字段为空,使用默认值", APP_ID_CONFIG_KEY);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.warn("未找到配置 {},使用默认值", APP_ID_CONFIG_KEY);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("解析 appId 配置异常,configValue={}",
|
|
|
|
|
+ sysConfigService.selectConfigByKey(APP_ID_CONFIG_KEY), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ //默认值
|
|
|
|
|
+ return DEFAULT_APP_ID;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ private String createLinkByMiniApp(Date sendTime, Long courseId, Long videoId,
|
|
|
|
|
+ QwUser qwUser, Long externalId) {
|
|
|
|
|
+
|
|
|
|
|
+ FsCourseLink link = new FsCourseLink();
|
|
|
|
|
+ link.setCompanyId(qwUser.getCompanyId());
|
|
|
|
|
+ link.setQwUserId(qwUser.getId());
|
|
|
|
|
+ link.setCompanyUserId(qwUser.getCompanyUserId());
|
|
|
|
|
+ link.setVideoId(videoId);
|
|
|
|
|
+ link.setCorpId(qwUser.getCorpId());
|
|
|
|
|
+ link.setCourseId(courseId);
|
|
|
|
|
+ link.setQwExternalId(externalId);
|
|
|
|
|
+ link.setIsRoom(0);
|
|
|
|
|
+ link.setUNo(UUID.randomUUID().toString());
|
|
|
|
|
+ link.setLinkType(3);
|
|
|
|
|
+
|
|
|
|
|
+ String randomString = generateRandomStringWithLock();
|
|
|
|
|
+ if (StringUtil.strIsNullOrEmpty(randomString)){
|
|
|
|
|
+ link.setLink(UUID.randomUUID().toString().replace("-", ""));
|
|
|
|
|
+ }else {
|
|
|
|
|
+ link.setLink(randomString);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ link.setCreateTime(sendTime);
|
|
|
|
|
+
|
|
|
|
|
+ FsCourseRealLink courseMap = new FsCourseRealLink();
|
|
|
|
|
+ BeanUtils.copyProperties(link,courseMap);
|
|
|
|
|
+
|
|
|
|
|
+ String courseJson = JSON.toJSONString(courseMap);
|
|
|
|
|
+
|
|
|
|
|
+ String realLinkFull = miniappRealLink + courseJson;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ link.setRealLink(realLinkFull);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 使用 Java 8 时间 API 计算过期时间
|
|
|
|
|
+ LocalDateTime sendDateTime = sendTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
|
|
|
|
+ LocalDateTime expireDateTime = sendDateTime.plusDays(0);
|
|
|
|
|
+ expireDateTime = expireDateTime.toLocalDate().atTime(23, 59, 59);
|
|
|
|
|
+ Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
|
|
|
|
+ link.setUpdateTime(updateTime);
|
|
|
|
|
+
|
|
|
|
|
+ //存短链-
|
|
|
|
|
+ fsCourseLinkMapper.insertFsCourseLink(link);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ return link.getRealLink();
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ private String getAppIdFromMiniMap(Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
|
|
|
|
|
+ String companyId, int sendMsgType) {
|
|
|
|
|
+ if (miniMap.isEmpty() || sendMsgType != 1) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Map<Integer, List<CompanyMiniapp>> gradeMap = miniMap.get(Long.valueOf(companyId));
|
|
|
|
|
+ if (gradeMap == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ int listIndex = 5;
|
|
|
|
|
+
|
|
|
|
|
+ List<CompanyMiniapp> miniapps = gradeMap.get(listIndex);
|
|
|
|
|
+
|
|
|
|
|
+ if (miniapps == null || miniapps.isEmpty()) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ CompanyMiniapp companyMiniapp = miniapps.get(0);
|
|
|
|
|
+ return (companyMiniapp != null && !StringUtil.strIsNullOrEmpty(companyMiniapp.getAppId()))
|
|
|
|
|
+ ? companyMiniapp.getAppId()
|
|
|
|
|
+ : null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|