Explorar o código

Merge branch 'refs/heads/test-wks' into test

wks hai 10 meses
pai
achega
55d8d87159
Modificáronse 24 ficheiros con 668 adicións e 54 borrados
  1. 13 0
      pom.xml
  2. 37 0
      src/main/java/com/ydtech/components/SendSmsComponents.java
  3. 20 2
      src/main/java/com/ydtech/components/aliyun/AliyunApiProperties.java
  4. 8 7
      src/main/java/com/ydtech/components/aliyun/AliyunSendSmsComponents.java
  5. 1 1
      src/main/java/com/ydtech/components/aliyun/SendSmsEnum.java
  6. 32 0
      src/main/java/com/ydtech/components/baidu/BaiduApiProperties.java
  7. 48 0
      src/main/java/com/ydtech/components/baidu/BaiduSendSmsComponents.java
  8. 28 0
      src/main/java/com/ydtech/components/tencent/TencentApiProperties.java
  9. 88 0
      src/main/java/com/ydtech/components/tencent/TencentSendSmsComponents.java
  10. 3 3
      src/main/java/com/ydtech/handler/CreateAndUpdateMetaObjectHandler.java
  11. 4 1
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerSendMsgOnEventRequest.java
  12. 1 1
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerSyncMsgResponse.java
  13. 5 7
      src/main/java/com/ydtech/modules/admin/controller/SysLoginController.java
  14. 18 0
      src/main/java/com/ydtech/modules/admin/dao/WorkScheduleConfigMapper.java
  15. 7 0
      src/main/java/com/ydtech/modules/admin/model/SysUser.java
  16. 113 0
      src/main/java/com/ydtech/modules/admin/model/po/WorkScheduleConfig.java
  17. 22 0
      src/main/java/com/ydtech/modules/admin/service/IWorkScheduleConfigService.java
  18. 11 0
      src/main/java/com/ydtech/modules/admin/service/SysWechatCustomerService.java
  19. 21 0
      src/main/java/com/ydtech/modules/admin/service/impl/SysWechatCustomerServiceImpl.java
  20. 87 30
      src/main/java/com/ydtech/modules/admin/service/impl/WechatCustomerServiceImpl.java
  21. 47 0
      src/main/java/com/ydtech/modules/admin/service/impl/WorkScheduleConfigServiceImpl.java
  22. 47 0
      src/main/java/com/ydtech/modules/admin/utils/WorkdayUtils.java
  23. 6 1
      src/main/resources/application-pre.yml
  24. 1 1
      src/main/resources/logback-spring.xml

+ 13 - 0
pom.xml

@@ -684,6 +684,19 @@
             <artifactId>spring-boot-starter-amqp</artifactId>
         </dependency>
 
+        <!-- 百度云短信 -->
+        <dependency>
+            <groupId>com.baidubce</groupId>
+            <artifactId>bce-java-sdk</artifactId>
+            <version>0.10.393</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.tencentcloudapi</groupId>
+            <artifactId>tencentcloud-sdk-java-sms</artifactId>
+            <version>3.1.1281</version>
+        </dependency>
+
     </dependencies>
 
 

+ 37 - 0
src/main/java/com/ydtech/components/SendSmsComponents.java

@@ -0,0 +1,37 @@
+package com.ydtech.components;
+
+
+import com.alibaba.fastjson.JSON;
+import com.ydtech.components.aliyun.AliyunSendSmsComponents;
+import com.ydtech.components.aliyun.SMSVerificationCode;
+import com.ydtech.components.aliyun.SendSmsEnum;
+import com.ydtech.components.baidu.BaiduSendSmsComponents;
+import com.ydtech.components.tencent.TencentSendSmsComponents;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class SendSmsComponents {
+
+    private final AliyunSendSmsComponents aliyunSendSmsComponents;
+
+    private final TencentSendSmsComponents tencentSendSmsComponents;
+
+    private final BaiduSendSmsComponents baiduSendSmsComponents;
+
+    public void sendSms(String phoneNumber, String captcha) throws Exception {
+        // 阿里云
+        aliyunSendSmsComponents.sendSms(phoneNumber, captcha);
+//         腾讯云
+//        tencentSendSmsComponents.sendSms(phoneNumber, captcha);
+//        // 百度
+//        baiduSendSmsComponents.sendSms(phoneNumber, captcha);
+    }
+
+
+}

+ 20 - 2
src/main/java/com/ydtech/components/aliyun/AliyunApiProperties.java

@@ -10,10 +10,28 @@ import org.springframework.stereotype.Component;
 @ConfigurationProperties(prefix = "aliyun.api")
 public class AliyunApiProperties {
 
-    //
+    /**
+     *
+     */
     private String accessKeyId;
 
-    //
+    /**
+     *
+     */
     private String accessKeySecret;
 
+    /**
+     * 链接
+     */
+    private String endpoint;
+
+    /**
+     * 签名
+     */
+    private String signName;
+
+    /**
+     * 模版
+     */
+    private String templateId;
 }

+ 8 - 7
src/main/java/com/ydtech/components/aliyun/SendSmsComponents.java → src/main/java/com/ydtech/components/aliyun/AliyunSendSmsComponents.java

@@ -1,12 +1,11 @@
 package com.ydtech.components.aliyun;
 
 
+import com.alibaba.fastjson.JSON;
 import com.aliyun.dysmsapi20170525.Client;
 import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
 import com.aliyun.tea.TeaException;
 import com.aliyun.teaopenapi.models.Config;
-import com.aliyun.teautil.models.RuntimeOptions;
-import com.ydtech.exception.SystemException;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
@@ -15,7 +14,7 @@ import org.springframework.stereotype.Component;
 @Slf4j
 @Component
 @RequiredArgsConstructor
-public class SendSmsComponents {
+public class AliyunSendSmsComponents {
 
     private final AliyunApiProperties properties;
 
@@ -38,13 +37,15 @@ public class SendSmsComponents {
         return new Client(config);
     }
 
-    public void sendSms(String phoneNumber, SendSmsEnum sendSmsEnum, String templateParam) throws Exception {
+    public void sendSms(String phoneNumber, String captcha) throws Exception {
+        SMSVerificationCode smsVerificationCode = new SMSVerificationCode(String.valueOf(captcha));
+        String jsonString = JSON.toJSONString(smsVerificationCode);
         Client client = createClient();
         SendSmsRequest sendSmsRequest = new SendSmsRequest()
                 .setPhoneNumbers(phoneNumber)
-                .setSignName(sendSmsEnum.getSignName())
-                .setTemplateCode(sendSmsEnum.getCode())
-                .setTemplateParam(templateParam);
+                .setSignName(properties.getSignName())
+                .setTemplateCode(properties.getTemplateId())
+                .setTemplateParam(jsonString);
 
         sendSmsData(client, sendSmsRequest);
     }

+ 1 - 1
src/main/java/com/ydtech/components/aliyun/SendSmsEnum.java

@@ -9,7 +9,7 @@ import lombok.Getter;
 public enum SendSmsEnum {
 
     SMS_220650023("SMS_220650023", "山西掌柜鼎科技", "验证码短信"),
-    SMS_481735501("SMS_481735501", "晋掌柜", "验证码短信");
+    SMS_496835079("SMS_496835079", "山西掌柜鼎科技", "验证码");
 
     private final String code;
 

+ 32 - 0
src/main/java/com/ydtech/components/baidu/BaiduApiProperties.java

@@ -0,0 +1,32 @@
+package com.ydtech.components.baidu;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "baidu.api")
+public class BaiduApiProperties {
+
+    /**
+     * AccessKeyID
+     */
+    private String accessKeyId;
+    /**
+     * AccessKeySecret
+     */
+    private String accessKeySecret;
+    /**
+     * 链接
+     */
+    private String endpoint;
+    /**
+     * 签名
+     */
+    private String signatureId;
+    /**
+     * 模版
+     */
+    private String template;
+}

+ 48 - 0
src/main/java/com/ydtech/components/baidu/BaiduSendSmsComponents.java

@@ -0,0 +1,48 @@
+package com.ydtech.components.baidu;
+
+
+import com.baidubce.auth.DefaultBceCredentials;
+import com.baidubce.services.sms.SmsClient;
+import com.baidubce.services.sms.SmsClientConfiguration;
+import com.baidubce.services.sms.model.SendMessageV3Request;
+import com.baidubce.services.sms.model.SendMessageV3Response;
+import com.ydtech.exception.SystemException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class BaiduSendSmsComponents {
+
+    private final BaiduApiProperties baiduApiProperties;
+
+    public void sendSms(String phoneNumber, String code) {
+        String ENDPOINT = "http://smsv3.bj.baidubce.com";
+
+        SmsClientConfiguration config = new SmsClientConfiguration();
+        config.setCredentials(new DefaultBceCredentials(baiduApiProperties.getAccessKeyId(), baiduApiProperties.getAccessKeySecret()));
+        config.setEndpoint(ENDPOINT);
+        SmsClient smsClient = new SmsClient(config);
+
+        SendMessageV3Request request = new SendMessageV3Request();
+        request.setMobile(phoneNumber);
+        request.setSignatureId("sms-signQxkiwz88470");
+        request.setTemplate("sms-tmpl-awKvRY85349");
+        Map<String, String> contentVar = new HashMap<>();
+        contentVar.put("code", code);
+        request.setContentVar(contentVar);
+        SendMessageV3Response response = smsClient.sendMessage(request);
+        // 解析请求响应 response.isSuccess()为true 表示成功
+        if (response != null && response.isSuccess()) {
+            return;
+        } else {
+            throw new SystemException("发送失败");
+        }
+
+    }
+}

+ 28 - 0
src/main/java/com/ydtech/components/tencent/TencentApiProperties.java

@@ -0,0 +1,28 @@
+package com.ydtech.components.tencent;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "tencent.api")
+public class TencentApiProperties {
+
+    // SecretId
+    private String secretId;
+
+    // SecretKey
+    private String secretKey;
+
+    private String region;
+
+    private String endpoint;
+
+    private String sdkAppId;
+
+    private String signName;
+
+    private String templateId;
+
+}

+ 88 - 0
src/main/java/com/ydtech/components/tencent/TencentSendSmsComponents.java

@@ -0,0 +1,88 @@
+package com.ydtech.components.tencent;
+
+
+import com.tencentcloudapi.common.Credential;
+import com.tencentcloudapi.common.exception.TencentCloudSDKException;
+import com.tencentcloudapi.common.profile.ClientProfile;
+import com.tencentcloudapi.common.profile.HttpProfile;
+import com.tencentcloudapi.sms.v20210111.SmsClient;
+import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
+import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
+import com.tencentcloudapi.sms.v20210111.models.SendStatus;
+import com.ydtech.exception.SystemException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.retry.annotation.Retryable;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class TencentSendSmsComponents {
+
+    private final TencentApiProperties tencentApiProperties;
+
+    /**
+     * 发送短信
+     *
+     * @param phoneNumber   手机号数组(需包含国际区号,例如 "+8613711112222")
+     * @param templateParam 模板参数数组,需要与模板内容变量一一对应
+     * @return 发送请求ID,可用于日志追踪;发送失败时返回null。
+     */
+    public String sendSms(String phoneNumber, String templateParam) {
+        phoneNumber = "+86" + phoneNumber;
+        String[] phoneNumberSet = new String[]{phoneNumber};
+        String[] templateParamSet = new String[]{templateParam};
+        SmsClient client = getSmsClient();
+        SendSmsRequest req = new SendSmsRequest();
+        // 使用注入的配置设置参数
+        req.setSmsSdkAppId(tencentApiProperties.getSdkAppId());
+        req.setSignName(tencentApiProperties.getSignName());
+        req.setTemplateId(tencentApiProperties.getTemplateId());
+        req.setTemplateParamSet(templateParamSet);
+        req.setPhoneNumberSet(phoneNumberSet);
+        req.setSessionContext("");
+        req.setExtendCode("");
+        req.setSenderId("");
+        try {
+            log.info("尝试发送短信,目标手机号:{}", Arrays.toString(phoneNumberSet));
+            SendSmsResponse res = client.SendSms(req);
+            log.debug("腾讯云短信接口原始响应:{}", SendSmsResponse.toJsonString(res));
+
+            // 检查发送结果
+            if (res.getSendStatusSet() != null && res.getSendStatusSet().length > 0) {
+                SendStatus status = res.getSendStatusSet()[0];
+                if ("Ok".equals(status.getCode())) {
+                    log.info("短信发送成功!请求ID:{}, 序列号:{}", res.getRequestId(), status.getSerialNo());
+                    return res.getRequestId(); // 返回请求ID用于追踪
+                } else {
+                    log.error("短信发送失败。错误码:{}, 错误信息:{}", status.getCode(), status.getMessage());
+                }
+            }
+            return null;
+
+        } catch (TencentCloudSDKException e) {
+            log.error("调用腾讯云短信服务SDK失败,错误信息:{}", e.getMessage(), e);
+            // 重试后仍失败,抛出运行时异常或自定义异常
+            throw new SystemException("短信服务暂时不可用,请稍后重试" + e.getMessage());
+        }
+    }
+
+    private SmsClient getSmsClient() {
+        Credential cred = new Credential(tencentApiProperties.getSecretId(), tencentApiProperties.getSecretKey());
+        HttpProfile httpProfile = new HttpProfile();
+        httpProfile.setEndpoint(tencentApiProperties.getEndpoint());
+        httpProfile.setReqMethod("GET");
+        httpProfile.setConnTimeout(10);
+        httpProfile.setWriteTimeout(10);
+        httpProfile.setReadTimeout(10);
+        ClientProfile clientProfile = new ClientProfile();
+        clientProfile.setSignMethod("HmacSHA256");
+        clientProfile.setHttpProfile(httpProfile);
+        // 使用注入的配置创建客户端
+        return new SmsClient(cred, tencentApiProperties.getRegion(), clientProfile);
+    }
+
+}

+ 3 - 3
src/main/java/com/ydtech/handler/CreateAndUpdateMetaObjectHandler.java

@@ -13,7 +13,7 @@ import org.apache.ibatis.reflection.MetaObject;
 import java.util.Date;
 
 /**
- *  MP注入处理器  抄来的
+ * MP注入处理器  抄来的
  *
  * @author: lig
  * @date: 2023年06月30日 0030
@@ -55,7 +55,7 @@ public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
 //                baseEntity.setUpdateBy(username);
             }
         } catch (Exception e) {
-            throw new SystemException(StringUtils.toString(HttpStatus.HTTP_UNAUTHORIZED),"自动注入异常 => " + e.getMessage());
+            log.error(StringUtils.toString(HttpStatus.HTTP_UNAUTHORIZED), "自动注入异常 => " + e.getMessage());
         }
     }
 
@@ -77,7 +77,7 @@ public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
                 }
             }
         } catch (Exception e) {
-            throw new SystemException(StringUtils.toString(HttpStatus.HTTP_UNAUTHORIZED),"自动注入异常 => " + e.getMessage());
+            log.error(StringUtils.toString(HttpStatus.HTTP_UNAUTHORIZED), "自动注入异常 => " + e.getMessage());
         }
     }
 }

+ 4 - 1
src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerSendMsgOnEventRequest.java

@@ -55,9 +55,12 @@ public class CustomerSendMsgOnEventRequest extends CustomerBaseRequest {
     }
 
 
-    public static CustomerSendMsgOnEventRequest sendMsgOnEvent(SysUser sysUser, String msgid, String code) {
+    public static CustomerSendMsgOnEventRequest sendMsgOnEvent(SysUser sysUser, String code, boolean workingHours) {
         String messages = "你好!" + sysUser.getName() + "(" + sysUser.getId() + ")" + "很高兴为你服务";
         // 发送欢迎信息
+        if (!workingHours) {
+            messages = "您好" + sysUser.getName() + "(" + sysUser.getId() + ")" + "~当前客服暂时不在服务时间,服务时间为:周一至周五:8:30-12:00,14:00-18:30,周六至周日:9:00-12:00,14:00-18:30,当前未到服务时间,请您在服务时段内咨询~";
+        }
         CustomerSendMsgOnEventRequest customerSendMsgOnEventRequest = new CustomerSendMsgOnEventRequest();
         customerSendMsgOnEventRequest.setMsgtype("text");
         customerSendMsgOnEventRequest.setCode(code);

+ 1 - 1
src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerSyncMsgResponse.java

@@ -40,7 +40,7 @@ public class CustomerSyncMsgResponse extends CustomerBaseResponse{
         private String msgid;
 
         @JsonProperty("send_time")
-        private Integer sendTime;
+        private long sendTime;
 
         @JsonProperty("origin")
         private Integer origin;

+ 5 - 7
src/main/java/com/ydtech/modules/admin/controller/SysLoginController.java

@@ -9,8 +9,9 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.code.kaptcha.Producer;
+import com.ydtech.components.SendSmsComponents;
 import com.ydtech.components.aliyun.SMSVerificationCode;
-import com.ydtech.components.aliyun.SendSmsComponents;
+import com.ydtech.components.aliyun.AliyunSendSmsComponents;
 import com.ydtech.components.aliyun.SendSmsEnum;
 import com.ydtech.config.MasterPasswordConfig;
 import com.ydtech.constants.SysConstants;
@@ -444,8 +445,8 @@ public class SysLoginController {
 //    }
 
 
-    @Autowired
-    private SendSmsComponents sendSms;
+
+    private final SendSmsComponents sendSms;
 
     /**
      * 短信发送
@@ -469,11 +470,8 @@ public class SysLoginController {
         redisTemplate.opsForValue()
                 .set(PHONE_VERIFICATION_CODE_KEY + phone, String.valueOf(randNumber), PHONE_VERIFICATION_CODE_KEY_TIME
                         , TimeUnit.MINUTES);
-
         try {
-            SMSVerificationCode smsVerificationCode = new SMSVerificationCode(String.valueOf(randNumber));
-            String jsonString = JSON.toJSONString(smsVerificationCode);
-            sendSms.sendSms(phone, SendSmsEnum.SMS_220650023, jsonString);
+            sendSms.sendSms(phone, String.valueOf(randNumber));
             return HttpResult.ok("发送成功");
         } catch (Exception e) {
             e.printStackTrace();

+ 18 - 0
src/main/java/com/ydtech/modules/admin/dao/WorkScheduleConfigMapper.java

@@ -0,0 +1,18 @@
+package com.ydtech.modules.admin.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ydtech.modules.admin.model.po.WorkScheduleConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 工作时间配置表 Mapper 接口
+ * </p>
+ *
+ * @author author
+ * @since 2024-01-01
+ */
+@Mapper
+public interface WorkScheduleConfigMapper extends BaseMapper<WorkScheduleConfig> {
+
+}

+ 7 - 0
src/main/java/com/ydtech/modules/admin/model/SysUser.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.google.gson.annotations.SerializedName;
 import com.ydtech.exception.SystemException;
 import com.ydtech.modules.base.model.BaseEntity;
 import io.swagger.annotations.ApiModel;
@@ -104,6 +105,12 @@ public class SysUser extends BaseEntity {
     @TableField(value = "alipay")
     private String alipay;
 
+    /**
+     * 微信客服id
+     */
+    @SerializedName("external_userid")
+    private String externalUserId;
+
     /**
      * 审批意见
      */

+ 113 - 0
src/main/java/com/ydtech/modules/admin/model/po/WorkScheduleConfig.java

@@ -0,0 +1,113 @@
+package com.ydtech.modules.admin.model.po;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalTime;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 工作时间配置表
+ * </p>
+ *
+ * @author author
+ * @since 2024-01-01
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("work_schedule_config")
+public class WorkScheduleConfig implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 日期类型:工作日/休息日
+     */
+    @TableField("day_type")
+    private String dayType;
+
+    /**
+     * 时间段:上午/下午
+     */
+    @TableField("time_slot")
+    private String timeSlot;
+
+    /**
+     * 开始时间
+     */
+    @TableField("start_time")
+    @JsonFormat(pattern = "HH:mm:ss")
+    private LocalTime startTime;
+
+    /**
+     * 结束时间
+     */
+    @TableField("end_time")
+    @JsonFormat(pattern = "HH:mm:ss")
+    private LocalTime endTime;
+
+    /**
+     * 是否生效
+     */
+    @TableField("is_active")
+    private Boolean active;
+
+    /**
+     * 创建时间
+     */
+    @TableField(value = "created_at", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createdAt;
+
+    /**
+     * 更新时间
+     */
+    @TableField(value = "updated_at", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updatedAt;
+
+    /**
+     * 日期类型枚举
+     */
+    @AllArgsConstructor
+    @Getter
+    public enum DayType {
+
+        WEEKDAY("weekday", "工作日"),
+        WEEKEND("weekend", "休息日");
+
+        private final String code;
+
+        private final String desc;
+
+
+    }
+
+    /**
+     * 时间段枚举
+     */
+    @AllArgsConstructor
+    @Getter
+    public enum TimeSlot {
+        MORNING("morning", "上午"),
+        AFTERNOON("afternoon", "下午");
+
+        private final String code;
+        private final String desc;
+
+    }
+}

+ 22 - 0
src/main/java/com/ydtech/modules/admin/service/IWorkScheduleConfigService.java

@@ -0,0 +1,22 @@
+package com.ydtech.modules.admin.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ydtech.modules.admin.model.po.WorkScheduleConfig;
+
+/**
+ * <p>
+ * 工作时间配置表 服务类
+ * </p>
+ *
+ * @author author
+ * @since 2024-01-01
+ */
+public interface IWorkScheduleConfigService extends IService<WorkScheduleConfig> {
+
+    /**
+     * 判断现在的时间是否是工作时间 true 工作时间 false 休息时间
+     */
+    boolean isWorkingHours();
+
+}

+ 11 - 0
src/main/java/com/ydtech/modules/admin/service/SysWechatCustomerService.java

@@ -58,6 +58,7 @@ public interface SysWechatCustomerService extends BaseIService<SysWechatCustomer
 
     /**
      * 昵称是否存在
+     *
      * @param nickname
      */
     default void isNicknameRepeat(String nickname) {
@@ -70,4 +71,14 @@ public interface SysWechatCustomerService extends BaseIService<SysWechatCustomer
 
     SysWechatCustomer getCacheCustomerId(SysWechatCustomer sysWechatCustomer);
 
+
+    /**
+     * 判断管理人是否一致
+     *
+     * @param externalUserId 企业微信id
+     * @param sysWechatCustomer       客服
+     * @return true 一致  false 不一致
+     *
+     */
+    boolean whetherTheManagersAreConsistent(String externalUserId, SysWechatCustomer sysWechatCustomer);
 }

+ 21 - 0
src/main/java/com/ydtech/modules/admin/service/impl/SysWechatCustomerServiceImpl.java

@@ -6,11 +6,13 @@ import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.ydtech.exception.SystemException;
 import com.ydtech.modules.admin.dao.SysWechatCustomerMapper;
+import com.ydtech.modules.admin.model.SysUser;
 import com.ydtech.modules.admin.model.po.SysWechatCustomer;
 import com.ydtech.modules.admin.model.po.SysWechatCustomerDuty;
 import com.ydtech.modules.admin.model.vo.wechat.SysWechatCustomerDutyRulesVo;
 import com.ydtech.modules.admin.model.vo.wechat.SysWechatCustomerQueryVo;
 import com.ydtech.modules.admin.model.vo.wechat.SysWechatCustomerVo;
+import com.ydtech.modules.admin.service.SysUserService;
 import com.ydtech.modules.admin.service.SysWechatCustomerDutyService;
 import com.ydtech.modules.admin.service.SysWechatCustomerService;
 import com.ydtech.utils.DateTimeUtil;
@@ -21,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
 
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -40,6 +43,8 @@ public class SysWechatCustomerServiceImpl extends ServiceImpl<SysWechatCustomerM
 
     private final StringRedisTemplate redisTemplate;
 
+    private final SysUserService sysUserService;
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void deleteAllInfo(String customerId) {
@@ -141,6 +146,22 @@ public class SysWechatCustomerServiceImpl extends ServiceImpl<SysWechatCustomerM
         redisTemplate.opsForValue().set(KEY, String.valueOf(sysWechatCustomer.getId()));
         return sysWechatCustomer;
     }
+
+
+    @Override
+    public boolean whetherTheManagersAreConsistent(String externalUserId, SysWechatCustomer sysWechatCustomer) {
+        SysUser sysUser = sysUserService.lambdaQuery().eq(SysUser::getExternalUserId, externalUserId).one();
+        if (ObjectUtils.isEmpty(sysUser)) {
+            return true;
+        }
+        if(!StringUtils.hasLength(sysUser.getLeaderId())){
+            return true;
+        }
+
+        return sysWechatCustomer.getUserId().equals(sysUser.getLeaderId());
+    }
+
+
 }
 
 

+ 87 - 30
src/main/java/com/ydtech/modules/admin/service/impl/WechatCustomerServiceImpl.java

@@ -6,28 +6,32 @@ import com.ydtech.modules.admin.components.WechatCustomerComponents;
 import com.ydtech.modules.admin.components.wechat.customer.request.CustomerAddContactWayRequest;
 import com.ydtech.modules.admin.components.wechat.customer.request.CustomerListRequest;
 import com.ydtech.modules.admin.components.wechat.customer.request.CustomerSendMsgOnEventRequest;
-import com.ydtech.modules.admin.components.wechat.customer.request.CustomerSyncMsgRequest;
 import com.ydtech.modules.admin.components.wechat.customer.response.CustomerAddContactWayResponse;
 import com.ydtech.modules.admin.components.wechat.customer.response.CustomerListResponse;
-import com.ydtech.modules.admin.components.wechat.customer.response.CustomerSyncMsgResponse;
 import com.ydtech.modules.admin.model.SysUser;
 import com.ydtech.modules.admin.model.po.SysWechatCustomer;
+import com.ydtech.modules.admin.model.po.SysWechatCustomerDuty;
 import com.ydtech.modules.admin.model.vo.wechat.WechatCustomerCallbackVo;
 import com.ydtech.modules.admin.model.vo.wechat.WechatEncryptVo;
-import com.ydtech.modules.admin.service.SysUserService;
-import com.ydtech.modules.admin.service.SysWechatCustomerService;
-import com.ydtech.modules.admin.service.WechatCustomerService;
+import com.ydtech.modules.admin.service.*;
 import com.ydtech.utils.StringUtils;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.util.crypto.WxCryptUtil;
 import me.chanjar.weixin.cp.api.WxCpKfService;
+import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgListResp;
+import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgSendRequest;
 import me.chanjar.weixin.cp.bean.kf.WxCpKfServiceStateResp;
+import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfEventMsg;
+import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfTextMsg;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.util.ObjectUtils;
 
+import java.time.LocalDateTime;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -45,6 +49,11 @@ public class WechatCustomerServiceImpl implements WechatCustomerService {
 
     private final SysWechatCustomerService sysWechatCustomerService;
 
+    private final IWorkScheduleConfigService workScheduleConfigService;
+
+    private final SysWechatCustomerDutyService sysWechatCustomerDutyService;
+
+    private final StringRedisTemplate redisTemplate;
 
     @Override
     public String customerCallback(String msgSignature, String timestamp, String nonce, String echoStr) {
@@ -63,32 +72,63 @@ public class WechatCustomerServiceImpl implements WechatCustomerService {
         WechatCustomerCallbackVo wechatCustomerCallbackVo = xmlMapper.readValue(encrypt, WechatCustomerCallbackVo.class);
 
         // 获取消息
-        CustomerSyncMsgRequest customerSyncMsgRequest = new CustomerSyncMsgRequest();
-        customerSyncMsgRequest.setToken(wechatCustomerCallbackVo.getToken());
-        customerSyncMsgRequest.setOpenKfid(wechatCustomerCallbackVo.getOpenKfId());
-        CustomerSyncMsgResponse customerSyncMsgResponse = wechatCustomerComponents.syncMsg(customerSyncMsgRequest);
-        while (customerSyncMsgResponse.getHasMore() == 1) {
-            String nextCursor = customerSyncMsgResponse.getNextCursor();
-            customerSyncMsgRequest.setCursor(nextCursor);
-            customerSyncMsgResponse = wechatCustomerComponents.syncMsg(customerSyncMsgRequest);
+        WxCpKfMsgListResp wxCpKfMsgListResp = wxCpKfService.syncMsg(null, wechatCustomerCallbackVo.getToken(), null, null, wechatCustomerCallbackVo.getOpenKfId());
+
+        while (wxCpKfMsgListResp.getHasMore() == 1) {
+            String nextCursor = wxCpKfMsgListResp.getNextCursor();
+            wxCpKfMsgListResp = wxCpKfService.syncMsg(nextCursor, wechatCustomerCallbackVo.getToken(), null, null, wechatCustomerCallbackVo.getOpenKfId());
         }
-        List<CustomerSyncMsgResponse.MsgListDTO> msgList = customerSyncMsgResponse.getMsgList();
-        CustomerSyncMsgResponse.MsgListDTO msgListDTO = customerSyncMsgResponse.getMsgList().get(msgList.size() - 1);
+        List<WxCpKfMsgListResp.WxCpKfMsgItem> msgList = wxCpKfMsgListResp.getMsgList();
+        WxCpKfMsgListResp.WxCpKfMsgItem wxCpKfMsgItem = wxCpKfMsgListResp.getMsgList().get(msgList.size() - 1);
+
+
+
+
+        SysWechatCustomer sysWechatCustomer = sysWechatCustomerService.lambdaQuery().eq(SysWechatCustomer::getOpenKfId, wechatCustomerCallbackVo.getOpenKfId()).one();
+        boolean workingHours = workScheduleConfigService.isWorkingHours();
+        List<SysWechatCustomerDuty> sysWechatCustomerDuties = sysWechatCustomerDutyService.lambdaQuery()
+                .eq(SysWechatCustomerDuty::getCustomerId, sysWechatCustomer.getId())
+                .le(SysWechatCustomerDuty::getStartTime, LocalDateTime.now())
+                .ge(SysWechatCustomerDuty::getEndTime, LocalDateTime.now())
+                .list();
+
+        // 改变会话状态
+        if (wxCpKfMsgItem.getOrigin() == 3) {
+            // 1 管理人是否一致
+            // 2 休息日
+            // 3 工作时间内 安排了值班客服
+            if (!sysWechatCustomerService.whetherTheManagersAreConsistent(wxCpKfMsgItem.getExternalUserId(), sysWechatCustomer) || !workingHours || !sysWechatCustomerDuties.isEmpty()) {
+                WxCpKfServiceStateResp serviceState = wxCpKfService.getServiceState(wxCpKfMsgItem.getOpenKfid(), wxCpKfMsgItem.getExternalUserId());
+                Integer state1 = serviceState.getServiceState();
+                if (state1 == 0) {
+                    state1 = 1;
+                    wxCpKfService.transServiceState(wxCpKfMsgItem.getOpenKfid(), wxCpKfMsgItem.getExternalUserId(), state1, sysWechatCustomer.getReceptionistId());
+                    restTimeSendMsg(wxCpKfMsgItem.getExternalUserId(), wxCpKfMsgItem.getOpenKfid());
+                } else if (state1 == 1) {
+                    String s = redisTemplate.opsForValue().get(wxCpKfMsgItem.getMsgId());
+                    if (!ObjectUtils.isEmpty(s)) {
+                        return;
+                    }
+                    restTimeSendMsg(wxCpKfMsgItem.getExternalUserId(), wxCpKfMsgItem.getOpenKfid());
+                    redisTemplate.opsForValue().set(wxCpKfMsgItem.getMsgId(), wxCpKfMsgItem.getMsgId(), 1, TimeUnit.MINUTES);
+                } else if (state1 == 3) {
+                    state1 = 4;
+                    wxCpKfService.transServiceState(wxCpKfMsgItem.getOpenKfid(), wxCpKfMsgItem.getExternalUserId(), state1, sysWechatCustomer.getReceptionistId());
+                }
+            }
 
-        if (msgListDTO.getOrigin() == 3) {
-            List<CustomerSyncMsgResponse.MsgListDTO> msgListDTOList = msgList.stream()
-                    .filter(x -> "event".equals(x.getMsgtype()))
-                    .collect(Collectors.toList());
+            List<WxCpKfMsgListResp.WxCpKfMsgItem> msgListDTOList = msgList.stream().filter(x -> "event".equals(x.getMsgType())).collect(Collectors.toList());
             msgListDTOList.forEach(x -> {
                 try {
-                    CustomerSyncMsgResponse.MsgListDTO.EventDTO xEvent = x.getEvent();
-                    WxCpKfServiceStateResp serviceState = wxCpKfService.getServiceState(xEvent.getOpenKfid(), xEvent.getExternalUserid());
-                    if (serviceState.getServiceState() == 0) {
-                        List<SysWechatCustomer> list = sysWechatCustomerService.lambdaQuery()
-                                .eq(SysWechatCustomer::getOpenKfId, xEvent.getOpenKfid())
-                                .list();
-                        wxCpKfService.transServiceState(xEvent.getOpenKfid(), xEvent.getExternalUserid(), 3, list.get(0)
-                                .getReceptionistId());
+                    WxCpKfEventMsg xEvent = x.getEvent();
+                    WxCpKfServiceStateResp wxCpKfServiceStateResp = wxCpKfService.getServiceState(xEvent.getOpenKfid(), xEvent.getExternalUserId());
+                    Integer state = wxCpKfServiceStateResp.getServiceState();
+                    // 休息时间
+                    List<SysWechatCustomer> list = sysWechatCustomerService.lambdaQuery().eq(SysWechatCustomer::getOpenKfId, xEvent.getOpenKfid()).list();
+                    String receptionistId = list.get(0).getReceptionistId();
+                    if (workScheduleConfigService.isWorkingHours() && (state == 0 || state == 1)) {
+                        state = 3;
+                        wxCpKfService.transServiceState(xEvent.getOpenKfid(), xEvent.getExternalUserId(), state, receptionistId);
                     }
                 } catch (WxErrorException e) {
                     throw new RuntimeException(e);
@@ -96,7 +136,7 @@ public class WechatCustomerServiceImpl implements WechatCustomerService {
             });
         }
 
-        CustomerSyncMsgResponse.MsgListDTO.EventDTO event = msgListDTO.getEvent();
+        WxCpKfEventMsg event = wxCpKfMsgItem.getEvent();
         if (ObjectUtils.isEmpty(event)) {
             return;
         }
@@ -105,14 +145,31 @@ public class WechatCustomerServiceImpl implements WechatCustomerService {
         if (StringUtils.isEmpty(scene)) {
             return;
         }
-        if (msgListDTO.getOrigin() == 4) {
+
+        // 发送欢迎语
+        if (wxCpKfMsgItem.getOrigin() == 4) {
             SysUser sysUser = sysUserService.getById(scene);
-            CustomerSendMsgOnEventRequest customerSendMsgOnEventRequest = CustomerSendMsgOnEventRequest.sendMsgOnEvent(sysUser, msgListDTO.getMsgid(), event.getWelcomeCode());
+            sysUser.setExternalUserId(event.getExternalUserId());
+            sysUserService.updateById(sysUser);
+            CustomerSendMsgOnEventRequest customerSendMsgOnEventRequest = CustomerSendMsgOnEventRequest.sendMsgOnEvent(sysUser, event.getWelcomeCode(), workingHours);
             wechatCustomerComponents.sendMsgOnEvent(customerSendMsgOnEventRequest);
         }
 
     }
 
+    private void restTimeSendMsg(String externalUserId, String openKfId) throws WxErrorException {
+        String message = "您咨询的客服目前暂时不在服务时间,请您前往“晋掌柜App”点击“微信客服”,将为您安排新的客服为您提供服务~(客服服务时间:周一至周五  8:30-18:30,周六至周日  8:30-18:30,午间 12:00-14:00 休息)";
+        WxCpKfMsgSendRequest wxCpKfMsgSendRequest = new WxCpKfMsgSendRequest();
+        wxCpKfMsgSendRequest.setToUser(externalUserId);
+        wxCpKfMsgSendRequest.setOpenKfid(openKfId);
+        wxCpKfMsgSendRequest.setMsgType("text");
+        WxCpKfTextMsg wxCpKfTextMsg = new WxCpKfTextMsg();
+        wxCpKfTextMsg.setContent(message);
+        wxCpKfMsgSendRequest.setText(wxCpKfTextMsg);
+        wxCpKfService.sendMsg(wxCpKfMsgSendRequest);
+    }
+
+
     @Override
     public List<CustomerListResponse.AccountListDTO> list() {
         CustomerListRequest customerListResponse = new CustomerListRequest(0, 100);

+ 47 - 0
src/main/java/com/ydtech/modules/admin/service/impl/WorkScheduleConfigServiceImpl.java

@@ -0,0 +1,47 @@
+package com.ydtech.modules.admin.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ydtech.modules.admin.constants.DataPermissionsEnum;
+import com.ydtech.modules.admin.dao.WorkScheduleConfigMapper;
+import com.ydtech.modules.admin.model.po.WorkScheduleConfig;
+import com.ydtech.modules.admin.service.IWorkScheduleConfigService;
+import com.ydtech.modules.admin.utils.WorkdayUtils;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalTime;
+import java.util.List;
+
+@Service
+public class WorkScheduleConfigServiceImpl extends ServiceImpl<WorkScheduleConfigMapper, WorkScheduleConfig> implements IWorkScheduleConfigService {
+
+    @Override
+    public boolean isWorkingHours() {
+        WorkScheduleConfig.DayType dayType = WorkdayUtils.isTodayWeekend() ? WorkScheduleConfig.DayType.WEEKEND : WorkScheduleConfig.DayType.WEEKDAY;
+        List<WorkScheduleConfig> list = lambdaQuery().eq(WorkScheduleConfig::getDayType, dayType.getCode()).list();
+        LocalTime now = LocalTime.now();
+        for (WorkScheduleConfig workScheduleConfig : list) {
+            if (isTimeInRange(now, workScheduleConfig.getStartTime(), workScheduleConfig.getEndTime())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean isTimeInRange(LocalTime checkTime, LocalTime startTime, LocalTime endTime) {
+        if (startTime.equals(endTime)) {
+            // 开始时间等于结束时间,表示全天
+            return true;
+        }
+        if (startTime.isBefore(endTime)) {
+            // 正常情况:开始时间在结束时间之前(同一天内)
+            return !checkTime.isBefore(startTime) && !checkTime.isAfter(endTime);
+        } else {
+            // 跨天情况:开始时间在结束时间之后(如 22:00 - 06:00)
+            return !checkTime.isBefore(startTime) || !checkTime.isAfter(endTime);
+        }
+    }
+
+}
+
+
+

+ 47 - 0
src/main/java/com/ydtech/modules/admin/utils/WorkdayUtils.java

@@ -0,0 +1,47 @@
+package com.ydtech.modules.admin.utils;
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+
+/**
+ * 工作日工具类
+ * 默认规则:周一至周五为工作日,周六周日为休息日
+ */
+public class WorkdayUtils {
+
+    /**
+     * 判断指定日期是否为工作日
+     * @param date 日期
+     * @return true-工作日, false-休息日
+     */
+    public static boolean isWorkday(LocalDate date) {
+        DayOfWeek dayOfWeek = date.getDayOfWeek();
+        return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY;
+    }
+
+    /**
+     * 判断指定日期是否为休息日
+     * @param date 日期
+     * @return true-休息日, false-工作日
+     */
+    public static boolean isWeekend(LocalDate date) {
+        return !isWorkday(date);
+    }
+
+    /**
+     * 判断当前日期是否为工作日
+     * @return true-工作日, false-休息日
+     */
+    public static boolean isTodayWorkday() {
+        return isWorkday(LocalDate.now());
+    }
+
+    /**
+     * 判断当前日期是否为休息日
+     * @return true-休息日, false-工作日
+     */
+    public static boolean isTodayWeekend() {
+        return isWeekend(LocalDate.now());
+    }
+
+
+}

+ 6 - 1
src/main/resources/application-pre.yml

@@ -488,4 +488,9 @@ jzg:
   referrerId: "99140121M09D01001"
 lg-ip:
   dateServer: http://192.168.0.59:8081/system/record/receiveDistributeServices
-  withdrawal: http://192.168.0.59:8081/withdrawal/record/acceptPushWithdrawalData
+  withdrawal: http://192.168.0.59:8081/withdrawal/record/acceptPushWithdrawalData
+
+
+#新合伙人配置佣金过期天数
+partner:
+  expireDays: 30

+ 1 - 1
src/main/resources/logback-spring.xml

@@ -226,7 +226,7 @@
 
 
     <!--开发环境:打印控制台-->
-    <springProfile name="dev,test,prod,pre">
+    <springProfile name="dev,test,prod,pre,local">
         <root level="info">
             <appender-ref ref="console"/>
             <appender-ref ref="file_console"/>