Ver Fonte

Merge branch 'test-wks' into test

wks há 1 ano atrás
pai
commit
bb3529e871
29 ficheiros alterados com 1002 adições e 29 exclusões
  1. 2 0
      src/main/java/com/ydtech/components/TokenInterceptor.java
  2. 0 22
      src/main/java/com/ydtech/config/MvcConfig.java
  3. 61 1
      src/main/java/com/ydtech/config/WechatWorkConfig.java
  4. 26 0
      src/main/java/com/ydtech/config/properties/WechatCustomerProperties.java
  5. 106 0
      src/main/java/com/ydtech/modules/admin/components/WechatCustomerComponents.java
  6. 37 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerAddContactWayRequest.java
  7. 19 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerBaseRequest.java
  8. 26 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerGetTokenRequest.java
  9. 33 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerListRequest.java
  10. 55 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerSendMsgOnEventRequest.java
  11. 31 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerSyncMsgRequest.java
  12. 30 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerAddContactWayResponse.java
  13. 36 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerBaseResponse.java
  14. 42 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerGetTokenResponse.java
  15. 55 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerListResponse.java
  16. 42 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerSendMsgOnEventResponse.java
  17. 78 0
      src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerSyncMsgResponse.java
  18. 29 0
      src/main/java/com/ydtech/modules/admin/constants/WechatCustomerEnum.java
  19. 21 5
      src/main/java/com/ydtech/modules/admin/controller/WechatController.java
  20. 37 0
      src/main/java/com/ydtech/modules/admin/controller/wechat/WechatCustomerController.java
  21. 39 0
      src/main/java/com/ydtech/modules/admin/model/vo/wechat/WechatCustomerCallbackVo.java
  22. 24 0
      src/main/java/com/ydtech/modules/admin/model/vo/wechat/WechatEncryptVo.java
  23. 23 0
      src/main/java/com/ydtech/modules/admin/service/WechatCustomerService.java
  24. 75 0
      src/main/java/com/ydtech/modules/admin/service/impl/WechatCustomerServiceImpl.java
  25. 4 0
      src/main/java/com/ydtech/modules/order/components/InsCrawlerOrderComponents.java
  26. 5 1
      src/main/java/com/ydtech/modules/order/constants/OrderCacheConstant.java
  27. 25 0
      src/main/java/com/ydtech/utils/cache/BaseCacheConfig.java
  28. 10 0
      src/main/java/com/ydtech/utils/cache/CacheFactory.java
  29. 31 0
      src/main/java/com/ydtech/utils/cache/RedissonUtils.java

+ 2 - 0
src/main/java/com/ydtech/components/TokenInterceptor.java

@@ -83,6 +83,8 @@ public class TokenInterceptor {
         //企业微信绑定
         notMatchList.add("/wxWork/verifyBind");
         notMatchList.add("/wxWork/bind");
+        // 微信客服
+        notMatchList.add("/wechat/customer/callback");
 
         // 平安相关屏蔽地址
         notMatchList.add("/order/pingAnApi/getApplyVerifyCodeAndRecoverApplyVerifyCode");

+ 0 - 22
src/main/java/com/ydtech/config/MvcConfig.java

@@ -31,8 +31,6 @@ import java.util.stream.Collectors;
 @Configuration
 public class MvcConfig implements WebMvcConfigurer {
 
-    @Resource
-    private WechatOpenProperties wechatOpenProperties;
 
     @Value("${apk.path}")
     private String apkPath;
@@ -97,26 +95,6 @@ public class MvcConfig implements WebMvcConfigurer {
         registry.addResourceHandler(uploadFileUrl + "**").addResourceLocations("file:" + uploadFilePath);
     }
 
-    @Bean
-    public Map<String, WxMpService> wxMpService() {
-        Map<String, WxMpDefaultConfigImpl> collect = wechatOpenProperties.getProperties().keySet()
-                .stream()
-                .collect(Collectors.toMap(x->x, x -> {
-                    WechatOpenProperties.PropertiesDto propertiesDto = wechatOpenProperties.getProperties().get(x);
-                    WxMpDefaultConfigImpl wxMpDefaultConfig = new WxMpDefaultConfigImpl();
-                    wxMpDefaultConfig.setAppId(propertiesDto.getComponentAppId());
-                    wxMpDefaultConfig.setSecret(propertiesDto.getComponentSecret());
-                    return wxMpDefaultConfig;
-                }));
-
-        return collect.keySet().stream().collect(Collectors.toMap(x -> x, x -> {
-            WxMpServiceImpl wxMpService = new WxMpServiceImpl();
-            WxMpDefaultConfigImpl wxMpDefaultConfig = collect.get(x);
-            wxMpService.setWxMpConfigStorage(wxMpDefaultConfig);
-            return wxMpService;
-        }));
-    }
-
     @Bean
     public PaginationInnerInterceptor paginationInnerInterceptor() {
         PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();

+ 61 - 1
src/main/java/com/ydtech/config/WechatWorkConfig.java

@@ -1,12 +1,30 @@
 package com.ydtech.config;
 
+import com.ydtech.config.properties.WechatCustomerProperties;
+import com.ydtech.config.properties.WechatOpenProperties;
+import me.chanjar.weixin.cp.api.WxCpKfService;
 import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.api.impl.WxCpKfServiceImpl;
 import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
 import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
+import javax.annotation.Resource;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+/**
+ * @version
+ * @description: 微信相关配置
+ * @author: wenks
+ * @date: 2024/11/27 15:01
+ **/
 @Configuration
 public class WechatWorkConfig {
 
@@ -17,13 +35,55 @@ public class WechatWorkConfig {
     @Value("${wechat.cp.agent-id}")
     private Integer agentId;
 
+    @Resource
+    private WechatOpenProperties wechatOpenProperties;
+
+    @Resource
+    private WechatCustomerProperties wechatCustomerProperties;
+
     @Bean
     public WxCpService wxCpService() {
         WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();
         config.setCorpId(corpId);
         config.setCorpSecret(secret);
         config.setAgentId(agentId);
-
+        WxCpServiceImpl wxCpService = new WxCpServiceImpl();
+        wxCpService.setWxCpConfigStorage(config);
         return new WxCpServiceImpl();
     }
+
+    @Bean
+    public Map<String, WxMpService> wxMpService() {
+        Map<String, WxMpDefaultConfigImpl> collect = wechatOpenProperties.getProperties().keySet()
+                .stream()
+                .collect(Collectors.toMap(x->x, x -> {
+                    WechatOpenProperties.PropertiesDto propertiesDto = wechatOpenProperties.getProperties().get(x);
+                    WxMpDefaultConfigImpl wxMpDefaultConfig = new WxMpDefaultConfigImpl();
+                    wxMpDefaultConfig.setAppId(propertiesDto.getComponentAppId());
+                    wxMpDefaultConfig.setSecret(propertiesDto.getComponentSecret());
+                    return wxMpDefaultConfig;
+                }));
+
+        return collect.keySet().stream().collect(Collectors.toMap(x -> x, x -> {
+            WxMpServiceImpl wxMpService = new WxMpServiceImpl();
+            WxMpDefaultConfigImpl wxMpDefaultConfig = collect.get(x);
+            wxMpService.setWxMpConfigStorage(wxMpDefaultConfig);
+            return wxMpService;
+        }));
+    }
+
+
+    @Bean
+    public WxCpKfService wxCpKfService(){
+        WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();
+        config.setCorpId(corpId);
+        config.setCorpSecret(wechatCustomerProperties.getSecret());
+        config.setToken(wechatCustomerProperties.getToken());
+        config.setAesKey(wechatCustomerProperties.getEncodingAesKey());
+        WxCpServiceImpl wxCpService = new WxCpServiceImpl();
+        wxCpService.setWxCpConfigStorage(config);
+        return new WxCpKfServiceImpl(wxCpService());
+    }
+
+
 }

+ 26 - 0
src/main/java/com/ydtech/config/properties/WechatCustomerProperties.java

@@ -0,0 +1,26 @@
+package com.ydtech.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @version
+ * @description: 企业微信客服接口
+ * @author: wenks
+ * @date: 2024/11/26 10:22
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "wechat.customer")
+public class WechatCustomerProperties {
+
+    private String corpId;
+
+    private String secret;
+
+    private String token;
+
+    private String encodingAesKey;
+
+}

+ 106 - 0
src/main/java/com/ydtech/modules/admin/components/WechatCustomerComponents.java

@@ -0,0 +1,106 @@
+package com.ydtech.modules.admin.components;
+
+
+import com.ydtech.config.properties.WechatCustomerProperties;
+import com.ydtech.modules.admin.components.wechat.customer.request.*;
+import com.ydtech.modules.admin.components.wechat.customer.response.*;
+import com.ydtech.modules.admin.constants.WechatCustomerEnum;
+import com.ydtech.modules.order.components.InsCrawlerOrderComponents;
+import com.ydtech.modules.order.constants.OrderCacheConstant;
+import com.ydtech.modules.protocol.utils.AssertionUtils;
+import com.ydtech.utils.cache.BaseCacheConfig;
+import com.ydtech.utils.cache.RedissonUtils;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.util.crypto.WxCryptUtil;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @description: 【微信客服】 发送请求实体
+ * @author: wenks
+ * @date: 2024/11/28 15:35
+ **/
+@Component
+@RequiredArgsConstructor
+public class WechatCustomerComponents {
+
+    private final RestTemplate restTemplate;
+
+    private final WechatCustomerProperties wechatCustomerProperties;
+
+    private final RedissonUtils redissonUtils;
+
+    private final InsCrawlerOrderComponents insCrawlerOrderComponents;
+
+    private <T, B extends CustomerBaseResponse> B request(WechatCustomerEnum wechatCustomer, T requestBody, Class<B> responseType, HttpHeaders httpHeaders) {
+        String token = getToken();
+        String url = WechatCustomerEnum.URL + wechatCustomer.getUri() + "?access_token=" + token;
+        return insCrawlerOrderComponents.request(url, "wechatCustomer", wechatCustomer.getDesc(), requestBody, responseType, httpHeaders);
+    }
+
+    public WxCryptUtil getWxCryptUtil(){
+        return new WxCryptUtil(wechatCustomerProperties.getToken(),  wechatCustomerProperties.getEncodingAesKey(), wechatCustomerProperties.getCorpId());
+    }
+
+    /**
+     * 获取调用凭证access_token
+     *
+     * @return access_token
+     */
+    private String getToken() {
+        return redissonUtils.getFromRedis(() -> {
+            CustomerGetTokenRequest customerGetTokenRequest = new CustomerGetTokenRequest(wechatCustomerProperties.getCorpId(), wechatCustomerProperties.getSecret());
+            String url = WechatCustomerEnum.URL + customerGetTokenRequest.getUrlParams();
+            CustomerGetTokenResponse customerGetTokenResponse = restTemplate.getForObject(url, CustomerGetTokenResponse.class);
+            AssertionUtils.isEmpty(customerGetTokenResponse, "获取调用凭证access_token异常");
+            String accessToken = customerGetTokenResponse.getAccessToken();
+            return new BaseCacheConfig<String>().setExpiresIn(customerGetTokenResponse.getExpiresIn()).setTimeUnit(TimeUnit.SECONDS).setData(accessToken);
+        }, OrderCacheConstant.WECHAT_CUSTOMER_TOKEN);
+    }
+
+
+
+    /**
+     * 获取客服账号列表
+     *
+     * @param customerListRequest 请求实体
+     * @return 客服账号列表
+     */
+    public CustomerListResponse list(CustomerListRequest customerListRequest) {
+        return request(WechatCustomerEnum.LIST, customerListRequest, CustomerListResponse.class, HttpHeaders.EMPTY);
+    }
+
+    /**
+     * 获取客服账号链接
+     *
+     * @param customerAddContactWayRequest 请求实体
+     * @return 客服账号链接
+     */
+    public CustomerAddContactWayResponse addContactWay(CustomerAddContactWayRequest customerAddContactWayRequest) {
+        return request(WechatCustomerEnum.ADD_CONTACT_WAY, customerAddContactWayRequest, CustomerAddContactWayResponse.class, HttpHeaders.EMPTY);
+    }
+
+    /**
+     * 获取消息
+     *
+     * @param customerSendMsgOnEventRequest 请求实体
+     * @return 消息信息
+     */
+    public CustomerSendMsgOnEventResponse sendMsgOnEvent(CustomerSendMsgOnEventRequest customerSendMsgOnEventRequest) {
+        return request(WechatCustomerEnum.LIST, customerSendMsgOnEventRequest, CustomerSendMsgOnEventResponse.class, HttpHeaders.EMPTY);
+    }
+
+    /**
+     * 发送客服欢迎语
+     *
+     * @param customerSyncMsgRequest 请求实体
+     * @return 成功失败
+     */
+    public CustomerSyncMsgResponse syncMsg(CustomerSyncMsgRequest customerSyncMsgRequest) {
+        return request(WechatCustomerEnum.SYNC_MSG, customerSyncMsgRequest, CustomerSyncMsgResponse.class, HttpHeaders.EMPTY);
+    }
+
+}

+ 37 - 0
src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerAddContactWayRequest.java

@@ -0,0 +1,37 @@
+package com.ydtech.modules.admin.components.wechat.customer.request;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+
+/**
+ * @description: 【微信客服】 获取客服账号链接
+ * @author: wenks
+ * @date: 2024/11/28 15:14
+ **/
+@EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CustomerAddContactWayRequest extends CustomerBaseRequest {
+
+    /**
+     * 客服账号ID
+     */
+    @JsonProperty("open_kfid")
+    private String openKfid;
+
+    /**
+     * 场景值,字符串类型,由开发者自定义。
+     * 不多于32字节
+     * 字符串取值范围(正则表达式):[0-9a-zA-Z_-]*
+     */
+    @JsonProperty("scene")
+    private String scene;
+
+
+}

+ 19 - 0
src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerBaseRequest.java

@@ -0,0 +1,19 @@
+package com.ydtech.modules.admin.components.wechat.customer.request;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+
+/**
+ * @description: 【微信客服】 请求基类
+ * @author: wenks
+ * @date: 2024/11/28 14:51
+ **/
+@Data
+@NoArgsConstructor
+public class CustomerBaseRequest {
+
+
+}

+ 26 - 0
src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerGetTokenRequest.java

@@ -0,0 +1,26 @@
+package com.ydtech.modules.admin.components.wechat.customer.request;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @description: 【微信客服】 获取调用凭证access_token
+ * @author: wenks
+ * @date: 2024/11/27 18:03
+ **/
+@NoArgsConstructor
+@AllArgsConstructor
+@Data
+public class CustomerGetTokenRequest {
+
+    private String corpId;
+
+    private String corpSecret;
+
+    public String getUrlParams() {
+        return "?corpid=" + corpId + "&corpsecret=" + corpSecret;
+    }
+
+
+}

+ 33 - 0
src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerListRequest.java

@@ -0,0 +1,33 @@
+package com.ydtech.modules.admin.components.wechat.customer.request;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+
+/**
+ * @description: 【微信客服】 获取客服账号列表
+ * @author: wenks
+ * @date: 2024/11/28 15:15
+ **/
+@EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CustomerListRequest extends CustomerBaseRequest {
+
+    /**
+     * 分页,偏移量, 默认为0
+     */
+    @JsonProperty("offset")
+    private Integer offset;
+
+    /**
+     * 分页,预期请求的数据量,默认为100,取值范围 1 ~ 100
+     */
+    @JsonProperty("limit")
+    private Integer limit;
+
+}

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

@@ -0,0 +1,55 @@
+package com.ydtech.modules.admin.components.wechat.customer.request;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+
+/**
+ * @description: 【微信客服】 发送客服欢迎语
+ * @author: wenks
+ * @date: 2024/11/28 15:34
+ **/
+@EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CustomerSendMsgOnEventRequest extends CustomerBaseRequest {
+
+    /**
+     * 事件响应消息对应的code。通过事件回调下发,仅可使用一次。
+     */
+    @JsonProperty("code")
+    private String code;
+
+    /**
+     * 消息ID。如果请求参数指定了msgid,则原样返回,否则系统自动生成并返回。
+     * 不多于32字节
+     * 字符串取值范围(正则表达式):[0-9a-zA-Z_-]*
+     */
+    @JsonProperty("msgid")
+    private String msgid;
+
+    /**
+     * 消息类型。对不同的msgtype,有相应的结构描述,详见消息类型
+     */
+    @JsonProperty("msgtype")
+    private String msgtype;
+
+    @JsonProperty("text")
+    private TextDTO text;
+
+    @NoArgsConstructor
+    @Data
+    @AllArgsConstructor
+    public static class TextDTO {
+
+        @JsonProperty("content")
+        private String content;
+
+    }
+
+}

+ 31 - 0
src/main/java/com/ydtech/modules/admin/components/wechat/customer/request/CustomerSyncMsgRequest.java

@@ -0,0 +1,31 @@
+package com.ydtech.modules.admin.components.wechat.customer.request;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@EqualsAndHashCode(callSuper = true)
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class CustomerSyncMsgRequest extends CustomerBaseRequest {
+
+    @JsonProperty("cursor")
+    private String cursor;
+
+    @JsonProperty("token")
+    private String token;
+
+    @JsonProperty("limit")
+    private Integer limit;
+
+    @JsonProperty("voice_format")
+    private Integer voiceFormat;
+
+    @JsonProperty("open_kfid")
+    private String openKfid;
+
+}

+ 30 - 0
src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerAddContactWayResponse.java

@@ -0,0 +1,30 @@
+package com.ydtech.modules.admin.components.wechat.customer.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+
+/**
+ * @description: 【微信客服】 获取客服账号链接
+ * @author: wenks
+ * @date: 2024/11/28 15:14
+ **/
+@EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CustomerAddContactWayResponse extends CustomerBaseResponse {
+
+    private static final long serialVersionUID = 3481360032833130444L;
+
+    /**
+     * 客服链接,开发者可将该链接嵌入到H5页面中,用户点击链接即可向对应
+     * 的微信客服账号发起咨询。开发者也可根据该url自行生成需要的二维码图片
+     */
+    @JsonProperty("url")
+    private String url;
+
+}

+ 36 - 0
src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerBaseResponse.java

@@ -0,0 +1,36 @@
+package com.ydtech.modules.admin.components.wechat.customer.response;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+
+/**
+ * @description: 【微信客服】 请求基类
+ * @author: wenks
+ * @date: 2024/11/28 14:54
+ **/
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CustomerBaseResponse implements Serializable {
+
+    private static final long serialVersionUID = -3778665006292913282L;
+
+    /**
+     * 返回码
+     */
+    @JsonProperty("errcode")
+    private Integer errcode;
+
+    /**
+     * 错误码描述
+     */
+    @JsonProperty("errmsg")
+    private String errmsg;
+
+}

+ 42 - 0
src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerGetTokenResponse.java

@@ -0,0 +1,42 @@
+package com.ydtech.modules.admin.components.wechat.customer.response;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+
+/**
+ * @description: 【微信客服】  获取调用凭证access_token
+ * @author: wenks
+ * @date: 2024/11/27 18:01
+ **/
+@NoArgsConstructor
+@Data
+public class CustomerGetTokenResponse {
+
+    /**
+     * 出错返回码,为0表示成功,非0表示调用失败
+     */
+    @JsonProperty("errcode")
+    private Integer errcode;
+
+    /**
+     * 返回码提示语
+     */
+    @JsonProperty("errmsg")
+    private String errmsg;
+
+    /**
+     * 获取到的凭证,最长为512字节
+     */
+    @JsonProperty("access_token")
+    private String accessToken;
+
+    /**
+     * 凭证的有效时间(秒)
+     */
+    @JsonProperty("expires_in")
+    private Integer expiresIn;
+
+}

+ 55 - 0
src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerListResponse.java

@@ -0,0 +1,55 @@
+package com.ydtech.modules.admin.components.wechat.customer.response;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * @description: 【微信客服】 获取客服账号列表
+ * @author: wenks
+ * @date: 2024/11/28 15:36
+ **/
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@AllArgsConstructor
+@Data
+public class CustomerListResponse extends CustomerBaseResponse {
+
+    private static final long serialVersionUID = -4750295003681323623L;
+
+    @JsonProperty("account_list")
+    private List<AccountListDTO> accountList;
+
+    @NoArgsConstructor
+    @Data
+    public static class AccountListDTO implements Serializable {
+
+        private static final long serialVersionUID = 1186740344093413070L;
+
+        /**
+         * 客服账号ID
+         */
+        @JsonProperty("open_kfid")
+        private String openKfid;
+
+        /**
+         * 客服账号名称
+         */
+        @JsonProperty("name")
+        private String name;
+
+        /**
+         * 客服头像URL
+         */
+        @JsonProperty("avatar")
+        private String avatar;
+
+    }
+}

+ 42 - 0
src/main/java/com/ydtech/modules/admin/components/wechat/customer/response/CustomerSendMsgOnEventResponse.java

@@ -0,0 +1,42 @@
+package com.ydtech.modules.admin.components.wechat.customer.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+/**
+ * @version
+ * @description: 【微信客服】 发送客服欢迎语
+ * @author: wenks
+ * @date: 2024/11/28 16:00
+ **/
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@AllArgsConstructor
+@Data
+public class CustomerSendMsgOnEventResponse extends CustomerBaseResponse {
+
+    private static final long serialVersionUID = -3439241873552051173L;
+
+    @JsonProperty("code")
+    private String code;
+
+    @JsonProperty("msgid")
+    private String msgid;
+
+    @JsonProperty("msgtype")
+    private String msgtype;
+
+    @JsonProperty("text")
+    private TextDTO text;
+
+    @NoArgsConstructor
+    @Data
+    public static class TextDTO {
+        @JsonProperty("content")
+        private String content;
+    }
+
+}

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

@@ -0,0 +1,78 @@
+package com.ydtech.modules.admin.components.wechat.customer.response;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * @version
+ * @description: TODO
+ * @author: wenks
+ * @date: 2024/11/28 16:00
+ **/
+@EqualsAndHashCode(callSuper = true)
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class CustomerSyncMsgResponse extends CustomerBaseResponse{
+
+    private static final long serialVersionUID = 7675662937493615248L;
+
+
+    @JsonProperty("next_cursor")
+    private String nextCursor;
+
+    @JsonProperty("msg_list")
+    private List<MsgListDTO> msgList;
+
+    @JsonProperty("has_more")
+    private Integer hasMore;
+
+    @NoArgsConstructor
+    @Data
+    public static class MsgListDTO {
+        @JsonProperty("msgid")
+        private String msgid;
+
+        @JsonProperty("send_time")
+        private Integer sendTime;
+
+        @JsonProperty("origin")
+        private Integer origin;
+
+        @JsonProperty("msgtype")
+        private String msgtype;
+
+        @JsonProperty("event")
+        private EventDTO event;
+
+        @NoArgsConstructor
+        @Data
+        public static class EventDTO {
+
+            @JsonProperty("event_type")
+            private String eventType;
+
+            @JsonProperty("scene")
+            private String scene;
+
+            @JsonProperty("open_kfid")
+            private String openKfid;
+
+            @JsonProperty("external_userid")
+            private String externalUserid;
+
+            @JsonProperty("scene_param")
+            private String sceneParam;
+
+            @JsonProperty("welcome_code")
+            private String welcomeCode;
+
+        }
+    }
+}

+ 29 - 0
src/main/java/com/ydtech/modules/admin/constants/WechatCustomerEnum.java

@@ -0,0 +1,29 @@
+package com.ydtech.modules.admin.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @description: 微信客服api
+ * @author: wenks
+ * @date: 2024/11/27 17:41
+ **/
+@Getter
+@AllArgsConstructor
+public enum WechatCustomerEnum {
+
+
+    GET_ACCESS_TOKEN("获取调用凭证access_token", "/cgi-bin/gettoken"),
+    LIST("获取客服账号列表", "/cgi-bin/kf/account/list"),
+    ADD_CONTACT_WAY("获取客服账号链接", "/cgi-bin/kf/add_contact_way"),
+    SYNC_MSG("获取消息", "/cgi-bin/kf/sync_msg"),
+    SEND_MSG_ON_EVENT("发送客服欢迎语", "/cgi-bin/kf/send_msg_on_event"),
+    ;
+
+    private final String desc;
+
+    private final String uri;
+
+    public final static String URL = "https://qyapi.weixin.qq.com";
+
+}

+ 21 - 5
src/main/java/com/ydtech/modules/admin/controller/WechatController.java

@@ -1,21 +1,37 @@
 package com.ydtech.modules.admin.controller;
 
+import cn.hutool.core.util.XmlUtil;
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
 import com.ydtech.core.page.HttpResult;
 import com.ydtech.exception.SystemException;
 import com.ydtech.modules.admin.model.vo.WechatBindVo;
 import com.ydtech.modules.admin.model.vo.WechatLoginVo;
 import com.ydtech.modules.admin.model.vo.WechatVo;
+import com.ydtech.modules.admin.model.vo.wechat.WechatCustomerCallbackVo;
+import com.ydtech.modules.admin.model.vo.wechat.WechatEncryptVo;
 import com.ydtech.modules.admin.service.WechatService;
 import com.ydtech.modules.base.controller.BaseController;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.models.Xml;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+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.util.crypto.WxCpCryptUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
 
 @Api(tags = "微信业务")
 @Slf4j
@@ -39,7 +55,7 @@ public class WechatController extends BaseController {
 
     @PostMapping("/bind")
     @ApiOperation(value = "工号绑定微信")
-    HttpResult<Object> bind(@RequestBody WechatBindVo wechatBindVo) throws WxErrorException {
+    public HttpResult<Object> bind(@RequestBody WechatBindVo wechatBindVo) throws WxErrorException {
         try {
             String userId = getUserId();
             return wechatService.bind(wechatBindVo, userId);

+ 37 - 0
src/main/java/com/ydtech/modules/admin/controller/wechat/WechatCustomerController.java

@@ -0,0 +1,37 @@
+package com.ydtech.modules.admin.controller.wechat;
+
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.ydtech.modules.admin.service.WechatCustomerService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+
+@Api(tags = "微信业务")
+@Slf4j
+@RestController
+@RequestMapping("/wechat/customer")
+@RequiredArgsConstructor
+public class WechatCustomerController {
+
+    private final WechatCustomerService customerService;
+
+    @GetMapping(path = "/callback")
+    @ApiOperation(value = "客服回调")
+    public String customerCallback(@RequestParam(value = "msg_signature") String msgSignature,
+                                   @RequestParam(value = "timestamp") String timestamp,
+                                   @RequestParam(value = "nonce") String nonce,
+                                   @RequestParam(value = "echostr") String echoStr) {
+        return customerService.customerCallback(msgSignature, timestamp, nonce, echoStr);
+    }
+
+    @PostMapping(path = "/callback", consumes = MediaType.TEXT_XML_VALUE, produces = MediaType.TEXT_XML_VALUE)
+    @ApiOperation(value = "客服回调(处理事件发送欢迎语)")
+    public void customerCallback(@RequestBody String wechatEncrypt) throws JsonProcessingException {
+        customerService.callbackSendMsgOnEvent(wechatEncrypt);
+    }
+
+}

+ 39 - 0
src/main/java/com/ydtech/modules/admin/model/vo/wechat/WechatCustomerCallbackVo.java

@@ -0,0 +1,39 @@
+package com.ydtech.modules.admin.model.vo.wechat;
+
+
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @description: 微信客服 回调实体
+ * @author: wenks
+ * @date: 2024/11/26 10:37
+ **/
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@JacksonXmlRootElement(localName = "xml")
+public class WechatCustomerCallbackVo {
+
+    @JacksonXmlProperty(localName = "ToUserName")
+    private String toUserName;
+
+    @JacksonXmlProperty(localName = "CreateTime")
+    private String createTime;
+
+    @JacksonXmlProperty(localName = "MsgType")
+    private String msgType;
+
+    @JacksonXmlProperty(localName = "Event")
+    private String event;
+
+    @JacksonXmlProperty(localName = "Token")
+    private String token;
+
+    @JacksonXmlProperty(localName = "OpenKfId")
+    private String openKfId;
+
+}

+ 24 - 0
src/main/java/com/ydtech/modules/admin/model/vo/wechat/WechatEncryptVo.java

@@ -0,0 +1,24 @@
+package com.ydtech.modules.admin.model.vo.wechat;
+
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@JacksonXmlRootElement(localName = "xml")
+public class WechatEncryptVo {
+
+    @JacksonXmlProperty(localName = "ToUserName")
+    private String toUserName;
+
+    @JacksonXmlProperty(localName = "Encrypt")
+    private String encrypt;
+
+    @JacksonXmlProperty(localName = "AgentID")
+    private String agentID;
+
+}

+ 23 - 0
src/main/java/com/ydtech/modules/admin/service/WechatCustomerService.java

@@ -0,0 +1,23 @@
+package com.ydtech.modules.admin.service;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+public interface WechatCustomerService {
+
+    /**
+     * 配置微信客服回调信息
+     *
+     * @param msgSignature
+     * @param timestamp 时间戳
+     * @param nonce
+     * @param echoStr 加密后的参数
+     * @return 校验参数
+     */
+    String customerCallback(String msgSignature, String timestamp, String nonce, String echoStr);
+
+    /**
+     * 回调事件
+     * @param wechatEncrypt
+     */
+    void callbackSendMsgOnEvent(String wechatEncrypt) throws JsonProcessingException;
+}

+ 75 - 0
src/main/java/com/ydtech/modules/admin/service/impl/WechatCustomerServiceImpl.java

@@ -0,0 +1,75 @@
+package com.ydtech.modules.admin.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import com.ydtech.modules.admin.components.WechatCustomerComponents;
+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.CustomerSyncMsgResponse;
+import com.ydtech.modules.admin.model.SysDept;
+import com.ydtech.modules.admin.model.SysUser;
+import com.ydtech.modules.admin.model.vo.wechat.WechatCustomerCallbackVo;
+import com.ydtech.modules.admin.model.vo.wechat.WechatEncryptVo;
+import com.ydtech.modules.admin.service.SysDeptService;
+import com.ydtech.modules.admin.service.SysUserService;
+import com.ydtech.modules.admin.service.WechatCustomerService;
+import com.ydtech.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.util.crypto.WxCryptUtil;
+import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgListResp;
+import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgSendRequest;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class WechatCustomerServiceImpl implements WechatCustomerService {
+
+    private final WechatCustomerComponents wechatCustomerComponents;
+
+    private final SysUserService sysUserService;
+
+    private final SysDeptService sysDeptService;
+
+    @Override
+    public String customerCallback(String msgSignature, String timestamp, String nonce, String echoStr) {
+        System.out.println(msgSignature + " " + timestamp + " " + nonce + " " + echoStr);
+        WxCryptUtil wxCryptUtil = wechatCustomerComponents.getWxCryptUtil();
+        return wxCryptUtil.decrypt(echoStr);
+    }
+
+    @Override
+    public void callbackSendMsgOnEvent(String wechatEncrypt) throws JsonProcessingException {
+        XmlMapper xmlMapper = new XmlMapper();
+        WechatEncryptVo wechatEncryptVo = xmlMapper.readValue(wechatEncrypt, WechatEncryptVo.class);
+        WxCryptUtil wxCryptUtil = wechatCustomerComponents.getWxCryptUtil();
+        String encrypt = wxCryptUtil.decrypt(wechatEncryptVo.getEncrypt());
+        WechatCustomerCallbackVo wechatCustomerCallbackVo = xmlMapper.readValue(encrypt, WechatCustomerCallbackVo.class);
+
+        // 获取消息
+        CustomerSyncMsgRequest customerSyncMsgRequest = new CustomerSyncMsgRequest();
+        customerSyncMsgRequest.setToken(wechatCustomerCallbackVo.getToken());
+        customerSyncMsgRequest.setOpenKfid(wechatCustomerCallbackVo.getOpenKfId());
+        CustomerSyncMsgResponse customerSyncMsgResponse = wechatCustomerComponents.syncMsg(customerSyncMsgRequest);
+        List<CustomerSyncMsgResponse.MsgListDTO> msgList = customerSyncMsgResponse.getMsgList();
+        CustomerSyncMsgResponse.MsgListDTO msgListDTO = customerSyncMsgResponse.getMsgList().get(msgList.size() - 1);
+
+        CustomerSyncMsgResponse.MsgListDTO.EventDTO event = msgListDTO.getEvent();
+        String sceneParam = event.getSceneParam();
+        if (StringUtils.isEmpty(sceneParam)) {
+            return;
+        }
+
+        SysUser sysUser = sysUserService.getById(sceneParam);
+        String messages = "你好!" + sysUser.getName() + "(" + sysUser.getId() + ")" + "很高兴为你服务";
+
+        // 发送欢迎信息
+        CustomerSendMsgOnEventRequest customerSendMsgOnEventRequest = new CustomerSendMsgOnEventRequest();
+        customerSendMsgOnEventRequest.setMsgtype("text");
+        customerSendMsgOnEventRequest.setMsgid(msgListDTO.getMsgid());
+        customerSendMsgOnEventRequest.setCode(event.getWelcomeCode());
+        customerSendMsgOnEventRequest.setText(new CustomerSendMsgOnEventRequest.TextDTO(messages));
+        wechatCustomerComponents.sendMsgOnEvent(customerSendMsgOnEventRequest);
+    }
+}

+ 4 - 0
src/main/java/com/ydtech/modules/order/components/InsCrawlerOrderComponents.java

@@ -56,5 +56,9 @@ public class InsCrawlerOrderComponents {
         return this.post(url, insuranceEnum.getPinyin(), message, requestBody, responseType, httpHeaders);
     }
 
+    public <T, B> B request(String url, String logId, String message, T requestBody, Class<B> responseType, HttpHeaders httpHeaders) {
+        return this.post(url, logId, message, requestBody, responseType, httpHeaders);
+    }
+
 }
 

+ 5 - 1
src/main/java/com/ydtech/modules/order/constants/OrderCacheConstant.java

@@ -99,7 +99,6 @@ public class OrderCacheConstant {
     public static final String CT_ACCESS_LOCK = "ct:accessLock:";
 
 
-
     /**
      * 紫金财险 为接入方提供获取token时间
      */
@@ -110,4 +109,9 @@ public class OrderCacheConstant {
      */
     public static final long CT_ACCESS_TOKEN_TIME = 238;
 
+    /**
+     * 微信客服token
+     */
+    public static final String WECHAT_CUSTOMER_TOKEN = "WECHAT:CUSTOMER:TOKEN";
+
 }

+ 25 - 0
src/main/java/com/ydtech/utils/cache/BaseCacheConfig.java

@@ -0,0 +1,25 @@
+package com.ydtech.utils.cache;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.util.concurrent.TimeUnit;
+
+
+@Data
+@Accessors(chain = true)
+@AllArgsConstructor
+@NoArgsConstructor
+public class BaseCacheConfig<T> {
+
+    private Integer expiresIn;
+
+    private TimeUnit timeUnit;
+
+    private T data;
+
+}

+ 10 - 0
src/main/java/com/ydtech/utils/cache/CacheFactory.java

@@ -0,0 +1,10 @@
+package com.ydtech.utils.cache;
+
+import org.apache.poi.ss.formula.functions.T;
+
+@FunctionalInterface
+public interface CacheFactory <F extends BaseCacheConfig<?>>{
+
+    F getCache();
+
+}

+ 31 - 0
src/main/java/com/ydtech/utils/cache/RedissonUtils.java

@@ -0,0 +1,31 @@
+package com.ydtech.utils.cache;
+
+
+import com.alibaba.fastjson.JSON;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RedissonClient;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class RedissonUtils {
+
+    private final RedissonClient redisson;
+
+    public <T, F extends BaseCacheConfig<T>> T getFromRedis(CacheFactory<F> cacheFactory, String redisKey) {
+        Object cache = redisson.getBucket(redisKey).get();
+        if (cache != null) {
+            log.info("key:{} cache:{}", redisKey, JSON.toJSONString(cache));
+        }
+
+        F result = cacheFactory.getCache();
+        if (result == null) {
+            return null;
+        }
+        redisson.getBucket(redisKey).set(result.getData(), result.getExpiresIn(), result.getTimeUnit());
+        return result.getData();
+    }
+
+}