diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml
index 8c59a54..9fed7c0 100644
--- a/ruoyi-common/ruoyi-common-core/pom.xml
+++ b/ruoyi-common/ruoyi-common-core/pom.xml
@@ -133,6 +133,18 @@
core
3.4.1
+
+
+ cn.hutool
+ hutool-all
+ 5.1.0
+
+
+
+ com.baomidou
+ mybatis-plus-extension
+ 3.5.5
+
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/sensitive/Sensitive.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/sensitive/Sensitive.java
new file mode 100644
index 0000000..b23b89f
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/sensitive/Sensitive.java
@@ -0,0 +1,28 @@
+package com.ruoyi.common.core.sensitive;
+
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 脱敏注解
+ *
+ * @author JooLun
+ * @Author: https://www.cnblogs.com/xiluonanfeng/p/10183926.html
+ **/
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+@JacksonAnnotationsInside
+@JsonSerialize(using = SensitiveSerialize.class)
+public @interface Sensitive {
+
+ /**
+ * 脱敏数据类型
+ */
+ SensitiveTypeEnum type();
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/sensitive/SensitiveSerialize.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/sensitive/SensitiveSerialize.java
new file mode 100644
index 0000000..08109d6
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/sensitive/SensitiveSerialize.java
@@ -0,0 +1,68 @@
+package com.ruoyi.common.core.sensitive;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.ruoyi.common.core.utils.SensitiveUtils;
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * @author JooLun
+ * @Author: https://www.cnblogs.com/xiluonanfeng/p/10183926.html
+ * 脱敏序列化
+ */
+@NoArgsConstructor
+@AllArgsConstructor
+public class SensitiveSerialize extends JsonSerializer implements ContextualSerializer {
+
+ private SensitiveTypeEnum type;
+
+ @Override
+ public void serialize(final String originStr, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider) throws IOException {
+ switch (type) {
+ case CHINESE_NAME:
+ jsonGenerator.writeString(SensitiveUtils.chineseName(originStr));
+ break;
+ case MOBILE_PHONE:
+ jsonGenerator.writeString(SensitiveUtils.mobilePhone(originStr));
+ break;
+ case EMAIL:
+ jsonGenerator.writeString(SensitiveUtils.email(originStr));
+ break;
+ case PASSWORD:
+ jsonGenerator.writeString(SensitiveUtils.password(originStr));
+ break;
+ case KEY:
+ jsonGenerator.writeString(SensitiveUtils.key(originStr));
+ break;
+ default:
+ throw new IllegalArgumentException("未定义的敏感信息枚举类" + type);
+ }
+ }
+
+ @Override
+ public JsonSerializer> createContextual(final SerializerProvider serializerProvider, final BeanProperty beanProperty) throws JsonMappingException {
+ if (beanProperty != null) {
+ if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
+ Sensitive sensitive = beanProperty.getAnnotation(Sensitive.class);
+ if (sensitive == null) {
+ sensitive = beanProperty.getContextAnnotation(Sensitive.class);
+ }
+ if (sensitive != null) {
+ return new SensitiveSerialize(sensitive.type());
+ }
+ }
+ return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
+ }
+ return serializerProvider.findNullValueSerializer(null);
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/sensitive/SensitiveTypeEnum.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/sensitive/SensitiveTypeEnum.java
new file mode 100644
index 0000000..284b7ec
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/sensitive/SensitiveTypeEnum.java
@@ -0,0 +1,32 @@
+package com.ruoyi.common.core.sensitive;
+
+/**
+ * 敏感信息枚举类
+ *
+ * @author JooLun
+ * @Author: https://www.cnblogs.com/xiluonanfeng/p/10183926.html
+ **/
+public enum SensitiveTypeEnum {
+
+ /**
+ * 用户名, 李*天, 张*
+ */
+ CHINESE_NAME,
+ /**
+ * 手机号, 185****1653
+ */
+ MOBILE_PHONE,
+ /**
+ * 电子邮件, r*****o@qq.com
+ */
+ EMAIL,
+ /**
+ * 密码, ******
+ */
+ PASSWORD,
+ /**
+ * 密钥, 最后三位其他都是***
+ */
+ KEY
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/typehandler/ArrayLongTypeHandler.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/typehandler/ArrayLongTypeHandler.java
new file mode 100644
index 0000000..6002fcb
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/typehandler/ArrayLongTypeHandler.java
@@ -0,0 +1,48 @@
+package com.ruoyi.common.core.typehandler;
+
+import cn.hutool.json.JSONUtil;
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * 存储到数据库, 将LONG数组转换成字符串;
+ * 从数据库获取数据, 将字符串转为LONG数组.
+ */
+@MappedTypes({Long[].class})
+@MappedJdbcTypes({JdbcType.VARCHAR})
+public class ArrayLongTypeHandler extends BaseTypeHandler {
+
+ private static Long[] l = new Long[]{};
+
+ @Override
+ public void setNonNullParameter(PreparedStatement ps, int i,
+ Long[] parameter, JdbcType jdbcType) throws SQLException {
+ ps.setString(i, JSONUtil.toJsonStr(parameter));
+ }
+
+ @Override
+ public Long[] getNullableResult(ResultSet rs, String columnName)
+ throws SQLException {
+ return JSONUtil.parseArray(rs.getString(columnName)).toArray(l);
+ }
+
+ @Override
+ public Long[] getNullableResult(ResultSet rs, int columnIndex)
+ throws SQLException {
+ return JSONUtil.parseArray(rs.getString(columnIndex)).toArray(l);
+ }
+
+ @Override
+ public Long[] getNullableResult(CallableStatement cs, int columnIndex)
+ throws SQLException {
+ return JSONUtil.parseArray(cs.getString(columnIndex)).toArray(l);
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/typehandler/ArrayStringTypeHandler.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/typehandler/ArrayStringTypeHandler.java
new file mode 100644
index 0000000..6dab677
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/typehandler/ArrayStringTypeHandler.java
@@ -0,0 +1,48 @@
+package com.ruoyi.common.core.typehandler;
+
+import cn.hutool.json.JSONUtil;
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * 存储到数据库, 将String数组转换成字符串;
+ * 从数据库获取数据, 将字符串转为LONG数组.
+ */
+@MappedTypes({String[].class})
+@MappedJdbcTypes({JdbcType.VARCHAR})
+public class ArrayStringTypeHandler extends BaseTypeHandler {
+
+ private static String[] l = new String[]{};
+
+ @Override
+ public void setNonNullParameter(PreparedStatement ps, int i,
+ String[] parameter, JdbcType jdbcType) throws SQLException {
+ ps.setString(i, JSONUtil.toJsonStr(parameter));
+ }
+
+ @Override
+ public String[] getNullableResult(ResultSet rs, String columnName)
+ throws SQLException {
+ return JSONUtil.parseArray(rs.getString(columnName)).toArray(l);
+ }
+
+ @Override
+ public String[] getNullableResult(ResultSet rs, int columnIndex)
+ throws SQLException {
+ return JSONUtil.parseArray(rs.getString(columnIndex)).toArray(l);
+ }
+
+ @Override
+ public String[] getNullableResult(CallableStatement cs, int columnIndex)
+ throws SQLException {
+ return JSONUtil.parseArray(cs.getString(columnIndex)).toArray(l);
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/typehandler/JsonTypeHandler.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/typehandler/JsonTypeHandler.java
new file mode 100644
index 0000000..b013bed
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/typehandler/JsonTypeHandler.java
@@ -0,0 +1,50 @@
+package com.ruoyi.common.core.typehandler;
+
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * 存储到数据库, 将JSON对象转换成字符串;
+ * 从数据库获取数据, 将字符串转为JSON对象.
+ */
+@MappedTypes({JSONObject.class})
+@MappedJdbcTypes({JdbcType.VARCHAR})
+public class JsonTypeHandler extends BaseTypeHandler {
+
+ @Override
+ public void setNonNullParameter(PreparedStatement ps, int i, JSONObject parameter,
+ JdbcType jdbcType) throws SQLException {
+
+ ps.setString(i, JSONUtil.toJsonStr(parameter));
+ }
+
+ @Override
+ public JSONObject getNullableResult(ResultSet rs, String columnName)
+ throws SQLException {
+
+ return JSONUtil.parseObj(rs.getString(columnName)).toBean(JSONObject.class);
+ }
+
+ @Override
+ public JSONObject getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
+
+ return JSONUtil.parseObj(rs.getString(columnIndex)).toBean(JSONObject.class);
+ }
+
+ @Override
+ public JSONObject getNullableResult(CallableStatement cs, int columnIndex)
+ throws SQLException {
+
+ return JSONUtil.parseObj(cs.getString(columnIndex)).toBean(JSONObject.class);
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SensitiveUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SensitiveUtils.java
new file mode 100644
index 0000000..0412156
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SensitiveUtils.java
@@ -0,0 +1,153 @@
+package com.ruoyi.common.core.utils;
+
+import cn.hutool.core.util.StrUtil;
+
+/**
+ * 数据脱敏工具类
+ *
+ * @Author: JooLun
+ * @Author: https://www.cnblogs.com/xiluonanfeng/p/10183926.html
+ * @Date: 2021/7/19 16:21
+ */
+public class SensitiveUtils {
+
+ /**
+ * 默认填充字符
+ */
+ public static final String DEFAULT_PAD_STR = "*";
+
+ /**
+ * 数据脱敏
+ *
+ */
+ public static String process(String data) {
+ return process(data, 2, 1, DEFAULT_PAD_STR);
+ }
+
+ /**
+ * 数据脱敏
+ *
+ */
+ public static String process(String data, Integer leftLen, Integer rightLen) {
+ return process(data, leftLen, rightLen, DEFAULT_PAD_STR);
+ }
+
+ /**
+ * 对字符串进行脱敏操作
+ * @param originStr 原始字符串
+ * @param prefixNoMaskLen 左侧需要保留几位明文字段
+ * @param suffixNoMaskLen 右侧需要保留几位明文字段
+ * @param maskStr 用于遮罩的字符串, 如'*'
+ * @return 脱敏后结果
+ */
+ public static String process(String originStr, int prefixNoMaskLen, int suffixNoMaskLen, String maskStr) {
+ if (originStr == null) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0, n = originStr.length(); i < n; i++) {
+ if (i < prefixNoMaskLen) {
+ sb.append(originStr.charAt(i));
+ continue;
+ }
+ if (i > (n - suffixNoMaskLen - 1)) {
+ sb.append(originStr.charAt(i));
+ continue;
+ }
+ sb.append(maskStr);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 中文姓名只显示最后一个汉字
+ * @param fullName 姓名
+ * @return
+ */
+ public static String chineseName(String fullName) {
+ if (fullName == null) {
+ return null;
+ }
+ return process(fullName, 0, 1, DEFAULT_PAD_STR);
+ }
+
+ /**
+ * 手机号码前三位,后四位,如186****2356
+ * @param num 手机号码
+ * @return
+ */
+ public static String mobilePhone(String num) {
+ return process(num, 0, 4, DEFAULT_PAD_STR);
+ }
+
+ /**
+ * 地址只显示到地区
+ * @param address 地址
+ * @return
+ */
+ public static String address(String address) {
+ return process(address, 6, 0, DEFAULT_PAD_STR);
+ }
+
+ /**
+ * 电子邮箱 仅显示第一个字母,@后面的地址显示,比如:r**@qq.com
+ * @param email 电子邮箱
+ * @return
+ */
+ public static String email(String email) {
+ if (email == null) {
+ return null;
+ }
+ int index = StrUtil.indexOf(email, '@');
+ if (index <= 1) {
+ return email;
+ }
+ String preEmail = process(email.substring(0, index), 1, 0, DEFAULT_PAD_STR);
+ return preEmail + email.substring(index);
+
+ }
+
+ /**
+ * 密码的全部字符,如:******
+ * @param password 密码
+ * @return
+ */
+ public static String password(String password) {
+ if (password == null) {
+ return null;
+ }
+ return "******";
+ }
+
+ /**
+ * 密钥除了最后三位,全部,比如:***klo
+ * @param key 密钥
+ * @return 结果
+ */
+ public static String key(String key) {
+ if (key == null) {
+ return null;
+ }
+ int viewLength = 6;
+ StringBuilder tmpKey = new StringBuilder(process(key, 0, 3, DEFAULT_PAD_STR));
+ if (tmpKey.length() > viewLength) {
+ return tmpKey.substring(tmpKey.length() - viewLength);
+ }
+ else if (tmpKey.length() < viewLength) {
+ int buffLength = viewLength - tmpKey.length();
+ for (int i = 0; i < buffLength; i++) {
+ tmpKey.insert(0, DEFAULT_PAD_STR);
+ }
+ return tmpKey.toString();
+ }
+ else {
+ return tmpKey.toString();
+ }
+ }
+
+ public static void main(String[] args) {
+ String s = mobilePhone("18653653621");
+ System.out.println(s);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/http/HttpHelper.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/http/HttpHelper.java
new file mode 100644
index 0000000..17532db
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/http/HttpHelper.java
@@ -0,0 +1,57 @@
+package com.ruoyi.common.core.utils.http;
+
+
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletRequest;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 通用http工具封装
+ *
+ * @author ruoyi
+ */
+public class HttpHelper
+{
+ private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class);
+
+ public static String getBodyString(ServletRequest request)
+ {
+ StringBuilder sb = new StringBuilder();
+ BufferedReader reader = null;
+ try (InputStream inputStream = request.getInputStream())
+ {
+ reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+ String line = "";
+ while ((line = reader.readLine()) != null)
+ {
+ sb.append(line);
+ }
+ }
+ catch (IOException e)
+ {
+ LOGGER.warn("getBodyString出现问题!");
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (IOException e)
+ {
+ LOGGER.error(ExceptionUtils.getMessage(e));
+ }
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/http/HttpUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/http/HttpUtils.java
new file mode 100644
index 0000000..b43e18f
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/http/HttpUtils.java
@@ -0,0 +1,267 @@
+package com.ruoyi.common.core.utils.http;
+
+
+import com.ruoyi.common.core.constant.Constants;
+import com.ruoyi.common.core.utils.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.X509Certificate;
+
+/**
+ * 通用http发送方法
+ *
+ * @author ruoyi
+ */
+public class HttpUtils
+{
+ private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
+
+ /**
+ * 向指定 URL 发送GET方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @return 所代表远程资源的响应结果
+ */
+ public static String sendGet(String url)
+ {
+ return sendGet(url, StringUtils.EMPTY);
+ }
+
+ /**
+ * 向指定 URL 发送GET方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+ * @return 所代表远程资源的响应结果
+ */
+ public static String sendGet(String url, String param)
+ {
+ return sendGet(url, param, Constants.UTF8);
+ }
+
+ /**
+ * 向指定 URL 发送GET方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+ * @param contentType 编码类型
+ * @return 所代表远程资源的响应结果
+ */
+ public static String sendGet(String url, String param, String contentType)
+ {
+ StringBuilder result = new StringBuilder();
+ BufferedReader in = null;
+ try
+ {
+ String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
+ log.info("sendGet - {}", urlNameString);
+ URL realUrl = new URL(urlNameString);
+ URLConnection connection = realUrl.openConnection();
+ connection.setRequestProperty("accept", "*/*");
+ connection.setRequestProperty("connection", "Keep-Alive");
+ connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
+ connection.connect();
+ in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
+ String line;
+ while ((line = in.readLine()) != null)
+ {
+ result.append(line);
+ }
+ log.info("recv - {}", result);
+ }
+ catch (ConnectException e)
+ {
+ log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
+ }
+ catch (SocketTimeoutException e)
+ {
+ log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
+ }
+ catch (IOException e)
+ {
+ log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
+ }
+ catch (Exception e)
+ {
+ log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
+ }
+ finally
+ {
+ try
+ {
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ catch (Exception ex)
+ {
+ log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 向指定 URL 发送POST方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+ * @return 所代表远程资源的响应结果
+ */
+ public static String sendPost(String url, String param)
+ {
+ PrintWriter out = null;
+ BufferedReader in = null;
+ StringBuilder result = new StringBuilder();
+ try
+ {
+ log.info("sendPost - {}", url);
+ URL realUrl = new URL(url);
+ URLConnection conn = realUrl.openConnection();
+ conn.setRequestProperty("accept", "*/*");
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
+ conn.setRequestProperty("Accept-Charset", "utf-8");
+ conn.setRequestProperty("contentType", "utf-8");
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+ out = new PrintWriter(conn.getOutputStream());
+ out.print(param);
+ out.flush();
+ in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
+ String line;
+ while ((line = in.readLine()) != null)
+ {
+ result.append(line);
+ }
+ log.info("recv - {}", result);
+ }
+ catch (ConnectException e)
+ {
+ log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
+ }
+ catch (SocketTimeoutException e)
+ {
+ log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+ }
+ catch (IOException e)
+ {
+ log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
+ }
+ catch (Exception e)
+ {
+ log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
+ }
+ finally
+ {
+ try
+ {
+ if (out != null)
+ {
+ out.close();
+ }
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ catch (IOException ex)
+ {
+ log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+ }
+ }
+ return result.toString();
+ }
+
+ public static String sendSSLPost(String url, String param)
+ {
+ StringBuilder result = new StringBuilder();
+ String urlNameString = url + "?" + param;
+ try
+ {
+ log.info("sendSSLPost - {}", urlNameString);
+ SSLContext sc = SSLContext.getInstance("SSL");
+ sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
+ URL console = new URL(urlNameString);
+ HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
+ conn.setRequestProperty("accept", "*/*");
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
+ conn.setRequestProperty("Accept-Charset", "utf-8");
+ conn.setRequestProperty("contentType", "utf-8");
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+
+ conn.setSSLSocketFactory(sc.getSocketFactory());
+ conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
+ conn.connect();
+ InputStream is = conn.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ String ret = "";
+ while ((ret = br.readLine()) != null)
+ {
+ if (ret != null && !"".equals(ret.trim()))
+ {
+ result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
+ }
+ }
+ log.info("recv - {}", result);
+ conn.disconnect();
+ br.close();
+ }
+ catch (ConnectException e)
+ {
+ log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
+ }
+ catch (SocketTimeoutException e)
+ {
+ log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+ }
+ catch (IOException e)
+ {
+ log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
+ }
+ catch (Exception e)
+ {
+ log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
+ }
+ return result.toString();
+ }
+
+ private static class TrustAnyTrustManager implements X509TrustManager
+ {
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ {
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers()
+ {
+ return new X509Certificate[] {};
+ }
+ }
+
+ private static class TrustAnyHostnameVerifier implements HostnameVerifier
+ {
+ @Override
+ public boolean verify(String hostname, SSLSession session)
+ {
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/intc-weixin/README.md b/ruoyi-modules/intc-weixin/README.md
new file mode 100644
index 0000000..d76f839
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/README.md
@@ -0,0 +1 @@
+特定业务模块
\ No newline at end of file
diff --git a/ruoyi-modules/intc-weixin/pom.xml b/ruoyi-modules/intc-weixin/pom.xml
new file mode 100644
index 0000000..f6d2720
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/pom.xml
@@ -0,0 +1,214 @@
+
+
+
+ com.ruoyi
+ ruoyi-modules
+ 3.6.3
+
+ 4.0.0
+
+ intc-weixin
+
+
+ intc-weixin 微信公众号
+
+
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-sentinel
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
+
+
+
+
+ io.springfox
+ springfox-swagger-ui
+ ${swagger.fox.version}
+
+
+
+
+
+
+
+
+
+
+ com.ruoyi
+ ruoyi-common-datasource
+ 3.6.3
+
+
+
+
+ com.ruoyi
+ ruoyi-common-datascope
+ 3.6.3
+
+
+
+ org.postgresql
+ postgresql
+ 42.2.22
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+ com.ruoyi
+ ruoyi-common-log
+
+
+
+
+ com.ruoyi
+ ruoyi-common-swagger
+
+
+ ws.schild
+ jave-core
+ 2.4.6
+
+
+ ws.schild
+ jave-native-win64
+ 2.4.6
+
+
+ ws.schild
+ jave-native-linux64
+ 2.4.6
+
+
+ org.springframework
+ spring-test
+ 5.3.3
+ compile
+
+
+
+ com.baomidou
+ mybatis-plus-extension
+ 3.5.5
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ 3.5.5
+
+
+
+ com.github.binarywang
+ weixin-java-common
+ 4.6.2.B
+
+
+
+ com.github.binarywang
+ weixin-java-mp
+ 4.6.2.B
+
+
+
+ com.github.binarywang
+ weixin-java-miniapp
+ 4.6.2.B
+
+
+
+ com.github.binarywang
+ weixin-java-pay
+ 4.6.2.B
+
+
+
+ com.alibaba
+ transmittable-thread-local
+ 2.11.4
+
+
+ cn.hutool
+ hutool-all
+ 5.1.0
+
+
+
+ pro.fessional
+ kaptcha
+ 2.3.3
+
+
+ servlet-api
+ javax.servlet
+
+
+
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ 4.0.4
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ 3.3.4
+
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/IntcWeixinApplication.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/IntcWeixinApplication.java
new file mode 100644
index 0000000..ea17975
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/IntcWeixinApplication.java
@@ -0,0 +1,25 @@
+package com.ruoyi;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import com.ruoyi.common.security.annotation.EnableCustomConfig;
+import com.ruoyi.common.security.annotation.EnableRyFeignClients;
+import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
+
+/**
+ * 系统模块
+ *
+ * @author ruoyi
+ */
+@EnableCustomConfig
+@EnableCustomSwagger2
+@EnableRyFeignClients
+@SpringBootApplication
+public class IntcWeixinApplication
+{
+ public static void main(String[] args)
+ {
+ SpringApplication.run(IntcWeixinApplication.class, args);
+ System.out.println("(♥◠‿◠)ノ゙ 智聪微信业务模块启动成功 ლ(´ڡ`ლ)゙");
+ }
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/config/ActuatorSecurityConfig.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/config/ActuatorSecurityConfig.java
new file mode 100644
index 0000000..05433e1
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/config/ActuatorSecurityConfig.java
@@ -0,0 +1,44 @@
+package com.ruoyi.config;
+
+import com.ruoyi.common.core.utils.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.web.SecurityFilterChain;
+
+/**
+ * @ClassName ActuatorSecurityConfig
+ * @Author YaphetS
+ * @Date 2023/3/14 9:18
+ * @Version 1.0
+ * @Description TODO
+ */
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class ActuatorSecurityConfig {
+
+ @Autowired
+ Environment env;
+
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ String contextPath = env.getProperty("management.endpoints.web.base-path");
+ if(StringUtils.isEmpty(contextPath)) {
+ contextPath = "/actuator";
+ }
+ http.csrf().disable();
+ http.authorizeRequests()
+ .antMatchers("/**"+contextPath+"/**")
+ .authenticated()
+ .anyRequest()
+ .permitAll()
+ .and()
+ .httpBasic();
+ return http.build();
+ }
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/builder/AbstractBuilder.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/builder/AbstractBuilder.java
new file mode 100644
index 0000000..4b5ca58
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/builder/AbstractBuilder.java
@@ -0,0 +1,40 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.builder;
+
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Binary Wang(https://github.com/binarywang)
+ */
+public abstract class AbstractBuilder {
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ public abstract WxMpXmlOutMessage build(String content,
+ WxMpXmlMessage wxMessage, WxMpService service);
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/builder/ImageBuilder.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/builder/ImageBuilder.java
new file mode 100644
index 0000000..9eefca8
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/builder/ImageBuilder.java
@@ -0,0 +1,47 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.builder;
+
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+
+/**
+ * @author Binary Wang(https://github.com/binarywang)
+ */
+public class ImageBuilder extends AbstractBuilder {
+
+ @Override
+ public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage,
+ WxMpService service) {
+
+ WxMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId(content)
+ .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
+ .build();
+
+ return m;
+ }
+
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/builder/TextBuilder.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/builder/TextBuilder.java
new file mode 100644
index 0000000..470ff0e
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/builder/TextBuilder.java
@@ -0,0 +1,45 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.builder;
+
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage;
+
+/**
+ * @author Binary Wang(https://github.com/binarywang)
+ */
+public class TextBuilder extends AbstractBuilder {
+
+ @Override
+ public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage,
+ WxMpService service) {
+ WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content(content)
+ .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
+ .build();
+ return m;
+ }
+
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/CommonConstants.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/CommonConstants.java
new file mode 100644
index 0000000..f1ca61a
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/CommonConstants.java
@@ -0,0 +1,47 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.config;
+
+/**
+ * @author
+ */
+public interface CommonConstants {
+ /**
+ * 是
+ */
+ String YES = "1";
+ /**
+ * 否
+ */
+ String NO = "0";
+
+ /**
+ * 树形父类ID
+ */
+ String PARENT_ID = "0";
+ /**
+ * 编码
+ */
+ String UTF8 = "UTF-8";
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WebConfig.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WebConfig.java
new file mode 100644
index 0000000..9563332
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WebConfig.java
@@ -0,0 +1,34 @@
+package com.ruoyi.weixin.config;
+
+import com.ruoyi.weixin.interceptor.ThirdSessionInterceptor;
+import lombok.AllArgsConstructor;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * web配置
+ */
+@Configuration
+@AllArgsConstructor
+public class WebConfig implements WebMvcConfigurer {
+ private final RedisTemplate redisTemplate;
+
+ /**
+ * 拦截器
+ * @param registry
+ */
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ /**
+ * 进入ThirdSession拦截器
+ */
+ registry.addInterceptor(new ThirdSessionInterceptor(redisTemplate))
+ .addPathPatterns("/weixin/api/**")//拦截/api/**接口
+ .excludePathPatterns("/weixin/api/ma/wxuser/login",
+ "/weixin/api/ma/orderinfo/notify-order",
+ "/weixin/api/ma/orderinfo/notify-logisticsr",
+ "/weixin/api/ma/orderinfo/notify-refunds");//放行接口
+ }
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMaConfiguration.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMaConfiguration.java
new file mode 100644
index 0000000..efe95d6
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMaConfiguration.java
@@ -0,0 +1,148 @@
+package com.ruoyi.weixin.config;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
+import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
+import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import cn.binarywang.wx.miniapp.message.WxMaMessageHandler;
+import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author Binary Wang
+ */
+@Slf4j
+@Configuration
+@EnableConfigurationProperties(com.ruoyi.weixin.config.WxMaProperties.class)
+public class WxMaConfiguration {
+ private final com.ruoyi.weixin.config.WxMaProperties properties;
+
+ private static final Map routers = Maps.newHashMap();
+ private static Map maServices;
+
+ @Autowired
+ public WxMaConfiguration(com.ruoyi.weixin.config.WxMaProperties properties) {
+ this.properties = properties;
+ }
+
+ public static WxMaService getMaService(String appId) {
+ WxMaService wxService = maServices.get(appId);
+ if (wxService == null) {
+ throw new IllegalArgumentException(String.format("未找到对应appId=[%s]的配置,请核实!", appId));
+ }
+
+ return wxService;
+ }
+
+ public static WxMaMessageRouter getRouter(String appId) {
+ return routers.get(appId);
+ }
+
+ @PostConstruct
+ public void init() {
+ List configs = this.properties.getConfigs();
+ if (configs == null) {
+ throw new RuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!");
+ }
+
+ maServices = configs.stream()
+ .map(a -> {
+ WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
+ // 使用上面的配置时,需要同时引入jedis-lock的依赖,否则会报类无法找到的异常
+ config.setAppid(a.getAppId());
+ config.setSecret(a.getSecret());
+ config.setToken(a.getToken());
+ config.setAesKey(a.getAesKey());
+ config.setMsgDataFormat(a.getMsgDataFormat());
+
+ WxMaService service = new WxMaServiceImpl();
+ service.setWxMaConfig(config);
+ routers.put(a.getAppId(), this.newRouter(service));
+ return service;
+ }).collect(Collectors.toMap(s -> s.getWxMaConfig().getAppid(), a -> a));
+ }
+
+ private WxMaMessageRouter newRouter(WxMaService service) {
+ final WxMaMessageRouter router = new WxMaMessageRouter(service);
+ router
+ .rule().handler(logHandler).next()
+ .rule().async(false).content("订阅消息").handler(subscribeMsgHandler).end()
+ .rule().async(false).content("文本").handler(textHandler).end()
+ .rule().async(false).content("图片").handler(picHandler).end()
+ .rule().async(false).content("二维码").handler(qrcodeHandler).end();
+ return router;
+ }
+
+ private final WxMaMessageHandler subscribeMsgHandler = (wxMessage, context, service, sessionManager) -> {
+ service.getMsgService().sendSubscribeMsg(WxMaSubscribeMessage.builder()
+ .templateId("此处更换为自己的模板id")
+ .data(Lists.newArrayList(
+ new WxMaSubscribeMessage.MsgData("keyword1", "339208499")))
+ .toUser(wxMessage.getFromUser())
+ .build());
+ return null;
+ };
+
+ private final WxMaMessageHandler logHandler = (wxMessage, context, service, sessionManager) -> {
+ log.info("收到消息:" + wxMessage.toString());
+ service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为:" + wxMessage.toJson())
+ .toUser(wxMessage.getFromUser()).build());
+ return null;
+ };
+
+ private final WxMaMessageHandler textHandler = (wxMessage, context, service, sessionManager) -> {
+ service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息")
+ .toUser(wxMessage.getFromUser()).build());
+ return null;
+ };
+
+ private final WxMaMessageHandler picHandler = (wxMessage, context, service, sessionManager) -> {
+ try {
+ WxMediaUploadResult uploadResult = service.getMediaService()
+ .uploadMedia("image", "png",
+ ClassLoader.getSystemResourceAsStream("tmp.png"));
+ service.getMsgService().sendKefuMsg(
+ WxMaKefuMessage
+ .newImageBuilder()
+ .mediaId(uploadResult.getMediaId())
+ .toUser(wxMessage.getFromUser())
+ .build());
+ } catch (WxErrorException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ };
+
+ private final WxMaMessageHandler qrcodeHandler = (wxMessage, context, service, sessionManager) -> {
+ try {
+ final File file = service.getQrcodeService().createQrcode("123", 430);
+ WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file);
+ service.getMsgService().sendKefuMsg(
+ WxMaKefuMessage
+ .newImageBuilder()
+ .mediaId(uploadResult.getMediaId())
+ .toUser(wxMessage.getFromUser())
+ .build());
+ } catch (WxErrorException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ };
+
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMaProperties.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMaProperties.java
new file mode 100644
index 0000000..750873f
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMaProperties.java
@@ -0,0 +1,80 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.List;
+
+/**
+ * @author Binary Wang
+ */
+@Data
+@ConfigurationProperties(prefix = "wx.ma")
+public class WxMaProperties {
+
+ private List configs;
+
+ @Data
+ public static class Config {
+ /**
+ * 设置微信小程序的appid
+ */
+ private String appId;
+
+ /**
+ * 设置微信小程序的Secret
+ */
+ private String secret;
+
+ /**
+ * 设置微信小程序消息服务器配置的token
+ */
+ private String token;
+
+ /**
+ * 设置微信小程序消息服务器配置的EncodingAESKey
+ */
+ private String aesKey;
+
+ /**
+ * 消息格式,XML或者JSON
+ */
+ private String msgDataFormat;
+ /**
+ * 微信支付商户号
+ */
+ private String mchId;
+ /**
+ * 微信支付商户密钥
+ */
+ private String mchKey;
+ /**
+ * p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
+ */
+ private String keyPath;
+ }
+
+}
\ No newline at end of file
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMpConfiguration.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMpConfiguration.java
new file mode 100644
index 0000000..d17e967
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMpConfiguration.java
@@ -0,0 +1,132 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.config;
+
+import com.ruoyi.weixin.handler.*;
+import lombok.AllArgsConstructor;
+import me.chanjar.weixin.mp.api.WxMpMessageRouter;
+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.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import java.util.List;
+import java.util.stream.Collectors;
+import static me.chanjar.weixin.common.api.WxConsts.EventType;
+import static me.chanjar.weixin.common.api.WxConsts.EventType.SUBSCRIBE;
+import static me.chanjar.weixin.common.api.WxConsts.EventType.UNSUBSCRIBE;
+import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
+import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType.EVENT;
+import static me.chanjar.weixin.mp.constant.WxMpEventConstants.CustomerService.*;
+import static me.chanjar.weixin.mp.constant.WxMpEventConstants.POI_CHECK_NOTIFY;
+
+/**
+ * wechat mp configuration
+ *
+ * @author Binary Wang(https://github.com/binarywang)
+ */
+@AllArgsConstructor
+@Configuration
+@EnableConfigurationProperties(WxMpProperties.class)
+public class WxMpConfiguration {
+ private final LogHandler logHandler;
+ private final NullHandler nullHandler;
+ private final KfSessionHandler kfSessionHandler;
+ private final StoreCheckNotifyHandler storeCheckNotifyHandler;
+ private final LocationHandler locationHandler;
+ private final MenuHandler menuHandler;
+ private final MsgHandler msgHandler;
+ private final UnsubscribeHandler unsubscribeHandler;
+ private final SubscribeHandler subscribeHandler;
+ private final ScanHandler scanHandler;
+ private final WxMpProperties properties;
+
+ @Bean
+ public WxMpService wxMpService() {
+ // 代码里 getConfigs()处报错的同学,请注意仔细阅读项目说明,你的IDE需要引入lombok插件!!!!
+ final List configs = this.properties.getConfigs();
+ if (configs == null) {
+ throw new RuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!");
+ }
+
+ WxMpService service = new WxMpServiceImpl();
+ service.setMultiConfigStorages(configs
+ .stream().map(a -> {
+ WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
+ configStorage.setAppId(a.getAppId());
+ configStorage.setSecret(a.getSecret());
+ configStorage.setToken(a.getToken());
+ configStorage.setAesKey(a.getAesKey());
+ return configStorage;
+ }).collect(Collectors.toMap(WxMpDefaultConfigImpl::getAppId, a -> a, (o, n) -> o)));
+ return service;
+ }
+
+ @Bean
+ public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
+ final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
+
+ // 记录所有事件的日志 (异步执行)
+ newRouter.rule().handler(this.logHandler).next();
+
+ // 接收客服会话管理事件
+ newRouter.rule().async(false).msgType(EVENT).event(KF_CREATE_SESSION)
+ .handler(this.kfSessionHandler).end();
+ newRouter.rule().async(false).msgType(EVENT).event(KF_CLOSE_SESSION)
+ .handler(this.kfSessionHandler).end();
+ newRouter.rule().async(false).msgType(EVENT).event(KF_SWITCH_SESSION)
+ .handler(this.kfSessionHandler).end();
+
+ // 门店审核事件
+ newRouter.rule().async(false).msgType(EVENT).event(POI_CHECK_NOTIFY).handler(this.storeCheckNotifyHandler).end();
+
+ // 自定义菜单事件
+ newRouter.rule().async(false).msgType(EVENT).event(EventType.CLICK).handler(this.menuHandler).end();
+
+ // 点击菜单连接事件
+ newRouter.rule().async(false).msgType(EVENT).event(EventType.VIEW).handler(this.nullHandler).end();
+
+ // 关注事件
+ newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end();
+
+ // 取消关注事件
+ newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.unsubscribeHandler).end();
+
+ // 上报地理位置事件
+ newRouter.rule().async(false).msgType(EVENT).event(EventType.LOCATION).handler(this.locationHandler).end();
+
+ // 接收地理位置消息
+ newRouter.rule().async(false).msgType(XmlMsgType.LOCATION).handler(this.locationHandler).end();
+
+ // 扫码事件
+ newRouter.rule().async(false).msgType(EVENT).event(EventType.SCAN).handler(this.scanHandler).end();
+
+ // 默认
+ newRouter.rule().async(false).handler(this.msgHandler).end();
+
+ return newRouter;
+ }
+
+}
\ No newline at end of file
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMpProperties.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMpProperties.java
new file mode 100644
index 0000000..f5171dd
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxMpProperties.java
@@ -0,0 +1,69 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.config;
+
+import com.ruoyi.weixin.utils.JsonUtils;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.List;
+
+/**
+ * wechat mp properties
+ *
+ * @author Binary Wang(https://github.com/binarywang)
+ */
+@Data
+@ConfigurationProperties(prefix = "wx.mp")
+public class WxMpProperties {
+ private List configs;
+
+ @Data
+ public static class MpConfig {
+ /**
+ * 设置微信公众号的appid
+ */
+ private String appId;
+
+ /**
+ * 设置微信公众号的app secret
+ */
+ private String secret;
+
+ /**
+ * 设置微信公众号的token
+ */
+ private String token;
+
+ /**
+ * 设置微信公众号的EncodingAESKey
+ */
+ private String aesKey;
+ }
+
+ @Override
+ public String toString() {
+ return JsonUtils.toJson(this);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxPayConfiguration.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxPayConfiguration.java
new file mode 100644
index 0000000..dab45a9
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/config/WxPayConfiguration.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2018-2019
+ * All rights reserved, Designed By www.joolun.com
+ * 注意:
+ * 本软件为www.joolun.com开发研制,项目使用请保留此说明
+ */
+package com.ruoyi.weixin.config;
+
+import cn.hutool.core.util.StrUtil;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 微信支付Configuration
+ * @author www.joolun.com
+ *
+ */
+@Slf4j
+@Configuration
+public class WxPayConfiguration {
+
+ private static WxMaProperties wxMaProperties;
+
+ @Autowired
+ public WxPayConfiguration(WxMaProperties wxMaProperties) {
+ this.wxMaProperties = wxMaProperties;
+ }
+
+ /**
+ * 获取WxMpService
+ * @return
+ */
+ public static WxPayService getPayService() {
+ WxPayService wxPayService = null;
+ WxPayConfig payConfig = new WxPayConfig();
+ payConfig.setAppId(wxMaProperties.getConfigs().get(0).getAppId());
+ payConfig.setMchId(wxMaProperties.getConfigs().get(0).getMchId());
+ payConfig.setMchKey(wxMaProperties.getConfigs().get(0).getMchKey());
+ payConfig.setKeyPath(wxMaProperties.getConfigs().get(0).getKeyPath());
+ // 可以指定是否使用沙箱环境
+ payConfig.setUseSandboxEnv(false);
+ wxPayService = new WxPayServiceImpl();
+ wxPayService.setConfig(payConfig);
+ return wxPayService;
+ }
+
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/ConfigConstant.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/ConfigConstant.java
new file mode 100644
index 0000000..9d3f3d0
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/ConfigConstant.java
@@ -0,0 +1,106 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.constant;
+
+/**
+ * 全局常量
+ * @author www.joolun.com
+ * 2019年1月21日
+ */
+public interface ConfigConstant {
+
+ //订阅状态(0:已订阅;1:未订阅;2:网页授权用户)
+ /**
+ * 0:未订阅,取消订阅
+ */
+ String SUBSCRIBE_TYPE_NO = "0";
+ /**
+ * 1:已订阅
+ */
+ String SUBSCRIBE_TYPE_YES = "1";
+ /**
+ * 2:网页授权用户
+ */
+ String SUBSCRIBE_TYPE_WEBLICENS = "2";
+
+ /**
+ * 应用类型 1:小程序
+ */
+ String WX_APP_TYPE_1 = "1";
+ /**
+ * 应用类型 2:公众号
+ */
+ String WX_APP_TYPE_2 = "2";
+
+ /**
+ * 消息自动回复类型(1、关注时回复;2、消息回复;3、关键词回复)
+ */
+ String WX_AUTO_REPLY_TYPE_1 = "1";
+ String WX_AUTO_REPLY_TYPE_2 = "2";
+ String WX_AUTO_REPLY_TYPE_3 = "3";
+
+ /**
+ * 回复类型文本匹配类型(1、全匹配,2、半匹配)
+ */
+ String WX_REP_MATE_1 = "1";
+ String WX_REP_MATE_2 = "2";
+
+ /**
+ * 消息分类(1、用户发给公众号;2、公众号发给用户;)
+ */
+ String WX_MSG_TYPE_1 = "1";
+ String WX_MSG_TYPE_2 = "2";
+
+ /**
+ * 群发消息发送类型(1、分组发;2、选择用户发)
+ */
+ String WX_MASS_SEND_TYPE_1 = "1";
+ String WX_MASS_SEND_TYPE_2 = "2";
+
+ /**
+ * 群发消息发送后的状态(SUB_SUCCESS:提交成功,SUB_FAIL:提交失败,SEND_SUCCESS:发送成功,SENDING:发送中,SEND_FAIL:发送失败,DELETE:已删除)
+ */
+ String WX_MASS_STATUS_SUB_SUCCESS = "SUB_SUCCESS";
+ String WX_MASS_STATUS_SUB_FAIL = "SUB_FAIL";
+ String WX_MASS_STATUS_SEND_SUCCESS = "SEND_SUCCESS";
+ String WX_MASS_STATUS_SENDING = "SENDING";
+ String WX_MASS_STATUS_SEND_FAIL = "SEND_FAIL";
+ String WX_MASS_STATUS_DELETE = "DELETE";
+
+ /**
+ * 菜单类型(1:普通菜单,2:个性化菜单)
+ */
+ String WX_MENU_TYPE_1 = "1";
+ String WX_MENU_TYPE_2 = "2";
+
+ /**
+ * header中的app-id
+ */
+ String HEADER_APP_ID = "app-id";
+
+ /**
+ * header中的third-session
+ */
+ String HEADER_THIRDSESSION = "third-session";
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/MyReturnCode.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/MyReturnCode.java
new file mode 100644
index 0000000..9adb5c6
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/MyReturnCode.java
@@ -0,0 +1,75 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.constant;
+
+/**
+ * 全局返回码
+ * 小程序用6开头,例60001
+ * @author www.joolun.com
+ * 2019年7月25日
+ */
+public enum MyReturnCode {
+
+ ERR_60000(60000, "系统错误,请稍候再试"){},//其它错误
+ ERR_60001(60001, "登录超时,请重新登录"){},
+ ERR_60002(60002, "session不能为空"){},
+
+ ERR_70001(70001, "该状态订单不允许操作"){},
+ ERR_70002(70002, "请选择付款方式"){},
+ ERR_70003(70003, "没有符合下单条件的规格商品,商品已下架或库存不足"){},
+ ERR_70004(70004, "只有未支付的详单能发起支付"){},
+ ERR_70005(70005, "无效订单"){},
+
+ ERR_80004(80004, "该商品已删除"){},
+
+ ;
+ ;
+
+ MyReturnCode(int code, String msg) {
+ this.code = code;
+ this.msg = msg;
+ }
+
+ private int code;
+ private String msg;
+
+ public int getCode() {
+ return code;
+ }
+ public void setCode(int code) {
+ this.code = code;
+ }
+ public String getMsg() {
+ return msg;
+ }
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ @Override
+ public String toString() {
+ return "MyReturnCode{" + "code='" + code + '\'' + "msg='" + msg + '\'' + '}';
+ }
+
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/WebSocketConstant.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/WebSocketConstant.java
new file mode 100644
index 0000000..5f93971
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/WebSocketConstant.java
@@ -0,0 +1,34 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.constant;
+
+/**
+ * @author www.joolun.com
+ */
+public interface WebSocketConstant {
+
+ String USER_DESTINATION_PREFIX = "/weixin/";
+ String WX_MSG = "wx_msg";
+
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/WxMaConstants.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/WxMaConstants.java
new file mode 100644
index 0000000..63ab32a
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/WxMaConstants.java
@@ -0,0 +1,39 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.constant;
+
+/**
+ * @author
+ */
+public interface WxMaConstants {
+
+ /**
+ * redis中3rd_session过期时间(单位:小时)
+ */
+ long TIME_OUT_SESSION = 6;
+ /**
+ * redis中3rd_session拼接前缀
+ */
+ String THIRD_SESSION_BEGIN = "wx:ma:3rd_session";
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/WxReturnCode.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/WxReturnCode.java
new file mode 100644
index 0000000..cf466b1
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/constant/WxReturnCode.java
@@ -0,0 +1,259 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.constant;
+
+/**
+ * 微信接口全局返回码
+ * @author www.joolun.com
+ *
+ */
+public enum WxReturnCode {
+
+ SUC_0("0", "请求成功"){},
+ ERR_1("-1", "系统繁忙,此时请开发者稍候再试"){},
+ ERR_10001("10001", "涉嫌广告 "){},
+ ERR_20001("20001", "涉嫌政治 "){},
+ ERR_20002("20002", "涉嫌色情 "){},
+ ERR_20004("20004", "涉嫌社会 "){},
+ ERR_20006("20006", "涉嫌违法犯罪 "){},
+ ERR_20008("20008", "涉嫌欺诈 "){},
+ ERR_20013("20013", "涉嫌版权 "){},
+ ERR_21000("21000", "涉嫌其他 "){},
+ ERR_22000("22000", "涉嫌互推(互相宣传) "){},
+ ERR_30001("30001", "原创校验出现系统错误且用户选择了被判为转载就不群发 "){},
+ ERR_30002("30002", "原创校验被判定为不能群发 "){},
+ ERR_30003("30003", "原创校验被判定为转载文且用户选择了被判为转载就不群发 "){},
+ ERR_40001("40001", "获取access_token时AppSecret错误,或者access_token无效。请开发者认真比对AppSecret的正确性,或查看是否正在为恰当的公众号调用接口"){},
+ ERR_40002("40002", "不合法的凭证类型"){},
+ ERR_40003("40003", "不合法的OpenID,请开发者确认OpenID(该用户)是否已关注公众号,或是否是其他公众号的OpenID"){},
+ ERR_40004("40004", "不合法的媒体文件类型"){},
+ ERR_40005("40005", "不合法的文件类型"){},
+ ERR_40006("40006", "不合法的文件大小"){},
+ ERR_40007("40007", "不合法的媒体文件id"){},
+ ERR_40008("40008", "不合法的消息类型"){},
+ ERR_40009("40009", "不合法的图片文件大小"){},
+ ERR_40010("40010", "不合法的语音文件大小"){},
+ ERR_40011("40011", "不合法的视频文件大小"){},
+ ERR_40012("40012", "不合法的缩略图文件大小"){},
+ ERR_40013("40013", "不合法的AppID,请开发者检查AppID的正确性,避免异常字符,注意大小写"){},
+ ERR_40014("40014", "不合法的access_token,请开发者认真比对access_token的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口"){},
+ ERR_40015("40015", "不合法的菜单类型"){},
+ ERR_40016("40016", "不合法的按钮个数"){},
+ ERR_40017("40017", "不合法的按钮个数"){},
+ ERR_40018("40018", "不合法的按钮名字长度"){},
+ ERR_40019("40019", "不合法的按钮KEY长度"){},
+ ERR_40020("40020", "不合法的按钮URL长度"){},
+ ERR_40021("40021", "不合法的菜单版本号"){},
+ ERR_40022("40022", "不合法的子菜单级数"){},
+ ERR_40023("40023", "不合法的子菜单按钮个数"){},
+ ERR_40024("40024", "不合法的子菜单按钮类型"){},
+ ERR_40025("40025", "不合法的子菜单按钮名字长度"){},
+ ERR_40026("40026", "不合法的子菜单按钮KEY长度"){},
+ ERR_40027("40027", "不合法的子菜单按钮URL长度"){},
+ ERR_40028("40028", "不合法的自定义菜单使用用户"){},
+ ERR_40029("40029", "不合法的oauth_code"){},
+ ERR_40030("40030", "不合法的refresh_token"){},
+ ERR_40031("40031", "不合法的openid列表"){},
+ ERR_40032("40032", "不合法的openid列表个数"){},
+ ERR_40033("40033", "不合法的请求字符,不能包含xxxx格式的字符"){},
+ ERR_40035("40035", "不合法的参数"){},
+ ERR_40055("40055", "不完整的url,前面要加http://"){},
+ ERR_40037("40037", "template_id不正确"){},
+ ERR_40038("40038", "不合法的请求格式"){},
+ ERR_40039("40039", "不合法的URL长度"){},
+ ERR_40050("40050", "不合法的分组id"){},
+ ERR_40051("40051", "分组名字不合法"){},
+ ERR_40062("40062", "标题长度不合法"){},
+ ERR_40097("40097", "参数不合法"){},
+ ERR_40113("40113", "文件名称不合法,需包含正确后缀"){},
+ ERR_40117("40117", "分组名字不合法"){},
+ ERR_40118("40118", "media_id大小不合法"){},
+ ERR_40119("40119", "button类型错误"){},
+ ERR_40120("40120", "button类型错误"){},
+ ERR_40121("40121", "不合法的media_id类型"){},
+ ERR_40125("40125", "不合法的AppSecret,请开发者检查AppSecret的正确性,避免异常字符,注意大小写"){},
+ ERR_40130("40130", "至少需要同时发送两个用户"){},
+ ERR_40132("40132", "微信号不合法"){},
+ ERR_40137("40137", "不支持的图片格式"){},
+ ERR_40164("40164", "调用接口的IP地址不在白名单中,请在接口IP白名单中进行设置"){},
+ ERR_41001("41001", "缺少access_token参数"){},
+ ERR_41002("41002", "缺少appid参数"){},
+ ERR_41003("41003", "缺少refresh_token参数"){},
+ ERR_41004("41004", "缺少secret参数"){},
+ ERR_41005("41005", "缺少多媒体文件数据"){},
+ ERR_41006("41006", "缺少media_id参数"){},
+ ERR_41007("41007", "缺少子菜单数据"){},
+ ERR_41008("41008", "缺少oauth code"){},
+ ERR_41009("41009", "缺少openid"){},
+ ERR_41028("41028", "form_id不正确,或者过期"){},
+ ERR_41029("41029", "form_id已被使用"){},
+ ERR_41030("41030", "page不正确"){},
+ ERR_42001("42001", "access_token超时,请检查access_token的有效期,请参考基础支持-获取access_token中,对access_token的详细机制说明"){},
+ ERR_42002("42002", "refresh_token超时"){},
+ ERR_42003("42003", "oauth_code超时"){},
+ ERR_43001("43001", "需要GET请求"){},
+ ERR_43002("43002", "需要POST请求"){},
+ ERR_43003("43003", "需要HTTPS请求"){},
+ ERR_43004("43004", "需要接收者关注"){},
+ ERR_43005("43005", "需要好友关系"){},
+ ERR_44001("44001", "多媒体文件为空"){},
+ ERR_44002("44002", "POST的数据包为空"){},
+ ERR_44003("44003", "图文消息内容为空"){},
+ ERR_44004("44004", "文本消息内容为空"){},
+ ERR_45001("45001", "多媒体文件大小超过限制"){},
+ ERR_45002("45002", "消息内容超过限制"){},
+ ERR_45003("45003", "标题字段超过限制"){},
+ ERR_45004("45004", "描述字段超过限制"){},
+ ERR_45005("45005", "链接字段超过限制"){},
+ ERR_45006("45006", "图片链接字段超过限制"){},
+ ERR_45007("45007", "语音播放时间超过限制"){},
+ ERR_45008("45008", "图文消息超过限制"){},
+ ERR_45009("45009", "接口调用超过限制"){},
+ ERR_45010("45010", "创建菜单个数超过限制"){},
+ ERR_45015("45015", "回复时间超过限制"){},
+ ERR_45016("45016", "系统分组,不允许修改"){},
+ ERR_45017("45017", "分组名字过长"){},
+ ERR_45018("45018", "分组数量超过上限"){},
+ ERR_45028("45028", "没有群发的配额,配额已经用完"){},
+ ERR_45047("45047", "客服下行消息超过上限"){},
+ ERR_45157("45157", "标签名非法,请注意不能和其他标签重名"){},
+ ERR_45158("45158", "标签名长度超过30个字节"){},
+ ERR_45056("45056", "创建的标签数过多,请注意不能超过100个"){},
+ ERR_45058("45058", "不能修改0/1/2这三个系统默认保留的标签"){},
+ ERR_45057("45057", "该标签下粉丝数超过10w,不允许直接删除"){},
+ ERR_45059("45059", "有粉丝身上的标签数已经超过限制"){},
+ ERR_45159("45159", "非法的tag_id"){},
+ ERR_46001("46001", "不存在媒体数据"){},
+ ERR_46002("46002", "不存在的菜单版本"){},
+ ERR_46003("46003", "不存在的菜单数据"){},
+ ERR_46004("46004", "不存在的用户"){},
+ ERR_47001("47001", "解析JSON/XML内容错误"){},
+ ERR_48001("48001", "api功能未授权,请确认公众号已获得该接口,可以在公众平台官网-开发者中心页中查看接口权限"){},
+ ERR_48002("48002", "粉丝拒收消息(粉丝在公众号选项中,关闭了 “ 接收消息 ” )"){},
+ ERR_48004("48004", "api 接口被封禁,请登录 admin.weixin.qq.com 查看详情"){},
+ ERR_48005("48005", "api 禁止删除被自动回复和自定义菜单引用的素材"){},
+ ERR_48006("48006", "api 禁止清零调用次数,因为清零次数达到上限"){},
+ ERR_48008("48008", "没有该类型消息的发送权限"){},
+ ERR_49003("49003", "传入的openid不属于此AppID"){},
+ ERR_50001("50001", "用户未授权该api"){},
+ ERR_50002("50002", "用户受限,可能是违规后接口被封禁"){},
+ ERR_50005("50005", "用户未关注公众号"){},
+ ERR_61003("61003", "请确认是否取消授权(第三方平台授权)"){},
+ ERR_61004("61004", "当前ip未在白名单中,直接获取本地ip添加"){},
+ ERR_61005("61005", " 组件 ticket已失效,重新接受授权url反馈的ticket"){},
+ ERR_61006("61006", "获取componentTicket为null"){},
+ ERR_61007("61007", "当前公众号或者小程序已在公众平台解绑"){},
+ ERR_61009("61009", "授权码失效,重新授权"){},
+ ERR_61451("61451", "参数错误(invalid parameter)"){},
+ ERR_61452("61452", "无效客服账号(invalid kf_account)"){},
+ ERR_61453("61453", "客服帐号已存在(kf_account exsited)"){},
+ ERR_61454("61454", "客服帐号名长度超过限制(仅允许10个英文字符,不包括@及@后的公众号的微信号)(invalid kf_acount length)"){},
+ ERR_61455("61455", "客服帐号名包含非法字符(仅允许英文+数字)(illegal character in kf_account)"){},
+ ERR_61456("61456", "客服帐号个数超过限制(10个客服账号)(kf_account count exceeded)"){},
+ ERR_61457("61457", "无效头像文件类型(invalid file type)"){},
+ ERR_61450("61450", "系统错误(system error)"){},
+ ERR_61500("61500", "日期格式错误"){},
+ ERR_61501("61501", "日期范围错误"){},
+ ERR_65400("65400", "API不可用,即没有开通/升级到新版客服功能"){},
+ ERR_65401("65401", "无效客服帐号"){},
+ ERR_65403("65403", "客服昵称不合法"){},
+ ERR_65404("65404", "客服帐号不合法"){},
+ ERR_65405("65405", "帐号数目已达到上限,不能继续添加"){},
+ ERR_65406("65406", "已经存在的客服帐号"){},
+ ERR_65407("65407", "邀请对象已经是该公众号客服"){},
+ ERR_65408("65408", "本公众号已经有一个邀请给该微信"){},
+ ERR_65409("65409", "无效的微信号"){},
+ ERR_65410("65410", "邀请对象绑定公众号客服数达到上限(目前每个微信号可以绑定5个公众号客服帐号)"){},
+ ERR_65411("65411", "该帐号已经有一个等待确认的邀请,不能重复邀请"){},
+ ERR_65412("65412", "该帐号已经绑定微信号,不能进行邀请"){},
+ ERR_99999("99999", "无法获取到文件名"){},
+ ERR_9001001("9001001", "POST数据参数不合法"){},
+ ERR_9001002("9001002", "远端服务不可用"){},
+ ERR_9001003("9001003", "Ticket不合法"){},
+ ERR_9001004("9001004", "获取摇周边用户信息失败"){},
+ ERR_9001005("9001005", "获取商户信息失败"){},
+ ERR_9001006("9001006", "获取OpenID失败"){},
+ ERR_9001007("9001007", "上传文件缺失"){},
+ ERR_9001008("9001008", "上传素材的文件类型不合法"){},
+ ERR_9001009("9001009", "上传素材的文件尺寸不合法"){},
+ ERR_9001010("9001010", "上传失败"){},
+ ERR_9001020("9001020", "帐号不合法"){},
+ ERR_9001021("9001021", "已有设备激活率低于50%,不能新增设备"){},
+ ERR_9001022("9001022", "设备申请数不合法,必须为大于0的数字"){},
+ ERR_9001023("9001023", "已存在审核中的设备ID申请"){},
+ ERR_9001024("9001024", "一次查询设备ID数量不能超过50"){},
+ ERR_9001025("9001025", "设备ID不合法"){},
+ ERR_9001026("9001026", "页面ID不合法"){},
+ ERR_9001027("9001027", "页面参数不合法"){},
+ ERR_9001028("9001028", "一次删除页面ID数量不能超过10"){},
+ ERR_9001029("9001029", "页面已应用在设备中,请先解除应用关系再删除"){},
+ ERR_9001030("9001030", "一次查询页面ID数量不能超过50"){},
+ ERR_9001031("9001031", "时间区间不合法"){},
+ ERR_9001032("9001032", "保存设备与页面的绑定关系参数错误"){},
+ ERR_9001033("9001033", "门店ID不合法"){},
+ ERR_9001034("9001034", "设备备注信息过长"){},
+ ERR_9001035("9001035", "设备申请参数不合法"){},
+ ERR_9001036("9001036", "查询起始值begin不合法"){}
+ ;
+
+ WxReturnCode(String code, String msg) {
+ this.code = code;
+ this.msg = msg;
+ }
+
+ private String code;
+ private String msg;
+
+ /**
+ * 通过code获取msg
+ * @param code
+ * @return
+ */
+ public static String getMsg(String code) {
+ try {
+ return WxReturnCode.valueOf(code).getMsg();
+ }catch (IllegalArgumentException e){
+ return "未定义的返回码:"+code;
+ }
+ }
+
+ public String getCode() {
+ return code;
+ }
+ public void setCode(String code) {
+ this.code = code;
+ }
+ public String getMsg() {
+ return msg;
+ }
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ @Override
+ public String toString() {
+ return "WxReturnCode{" + "code='" + code + '\'' + "msg='" + msg + '\'' + '}';
+ }
+
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WeChatController.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WeChatController.java
new file mode 100644
index 0000000..0c45e8e
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WeChatController.java
@@ -0,0 +1,58 @@
+package com.ruoyi.weixin.controller;
+
+import com.ruoyi.weixin.utils.CheckUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * 业务模块
+ *
+ * @author tianyongbao
+ * @date 2025/1/8
+ */
+
+/**
+ * 服务器验证
+ *
+ * @author tianyongbao
+ * @date 2025/1/8
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/api")
+@Api(tags = "对接微信模块")
+public class WeChatController {
+ /**
+ * 功能描述: 签名校验
+ */
+ @GetMapping("/wx")
+ @ApiOperation(value = "签名校验", notes = "签名校验")
+ public void login(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
+ request.setCharacterEncoding("UTF-8");
+ String signature = request.getParameter("signature");
+ String timestamp = request.getParameter("timestamp");
+ String nonce = request.getParameter("nonce");
+ String echostr = request.getParameter("echostr");
+ PrintWriter out = null;
+ try {
+ out = response.getWriter();
+ if (CheckUtil.checkSignature(signature, timestamp, nonce)) {
+ out.write(echostr);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ out.close();
+ }
+ }
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxAutoReplyController.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxAutoReplyController.java
new file mode 100644
index 0000000..54675b2
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxAutoReplyController.java
@@ -0,0 +1,148 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.controller;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.security.annotation.RequiresPermissions;
+import com.ruoyi.weixin.constant.ConfigConstant;
+import com.ruoyi.weixin.entity.WxAutoReply;
+import com.ruoyi.weixin.service.WxAutoReplyService;
+import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 消息自动回复
+ *
+ * @author www.joolun.com
+ * @date 2019-04-18 15:40:39
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/wxautoreply")
+public class WxAutoReplyController extends BaseController {
+
+ private final WxAutoReplyService wxAutoReplyService;
+
+ /**
+ * 分页查询
+ * @param page 分页对象
+ * @param wxAutoReply 消息自动回复
+ * @return
+ */
+ @GetMapping("/page")
+ @RequiresPermissions("wxmp:wxautoreply:index")
+ public AjaxResult getWxAutoReplyPage(Page page, WxAutoReply wxAutoReply) {
+ return AjaxResult.success(wxAutoReplyService.page(page,Wrappers.query(wxAutoReply)));
+ }
+
+
+ /**
+ * 通过id查询消息自动回复
+ * @param id id
+ * @return R
+ */
+ @GetMapping("/{id}")
+ @RequiresPermissions("wxmp:wxautoreply:get")
+ public AjaxResult getById(@PathVariable("id") String id){
+ return AjaxResult.success(wxAutoReplyService.getById(id));
+ }
+
+ /**
+ * 新增消息自动回复
+ * @param wxAutoReply 消息自动回复
+ * @return R
+ */
+ @PostMapping
+ @RequiresPermissions("wxmp:wxautoreply:add")
+ public AjaxResult save(@RequestBody WxAutoReply wxAutoReply){
+ this.jude(wxAutoReply);
+ return AjaxResult.success(wxAutoReplyService.save(wxAutoReply));
+ }
+
+ /**
+ * 修改消息自动回复
+ * @param wxAutoReply 消息自动回复
+ * @return R
+ */
+ @PutMapping
+ @RequiresPermissions("wxmp:wxautoreply:edit")
+ public AjaxResult updateById(@RequestBody WxAutoReply wxAutoReply){
+ this.jude(wxAutoReply);
+ return AjaxResult.success(wxAutoReplyService.updateById(wxAutoReply));
+ }
+
+ /**
+ * 通过id删除消息自动回复
+ * @param id id
+ * @return R
+ */
+ @DeleteMapping("/{id}")
+ @RequiresPermissions("wxmp:wxautoreply:del")
+ public AjaxResult removeById(@PathVariable String id){
+ return AjaxResult.success(wxAutoReplyService.removeById(id));
+ }
+
+ /**
+ * //校验参数
+ * @param wxAutoReply
+ */
+ public void jude(WxAutoReply wxAutoReply){
+ if(ConfigConstant.WX_AUTO_REPLY_TYPE_2.equals(wxAutoReply.getType())){
+ Wrapper queryWrapper = Wrappers.lambdaQuery()
+ .eq(WxAutoReply::getReqType,wxAutoReply.getReqType());
+ List list = wxAutoReplyService.list(queryWrapper);
+ if(StringUtils.isNotBlank(wxAutoReply.getId())){
+ if(list != null && list.size() == 1){
+ if(!list.get(0).getId().equals(wxAutoReply.getId())){
+ throw new RuntimeException("请求消息类型重复");
+ }
+ }
+ if(list != null && list.size()>1){
+ throw new RuntimeException("请求消息类型重复");
+ }
+ }
+ }
+ if(ConfigConstant.WX_AUTO_REPLY_TYPE_3.equals(wxAutoReply.getType())){
+ Wrapper queryWrapper = Wrappers.lambdaQuery()
+ .eq(WxAutoReply::getReqKey,wxAutoReply.getReqKey())
+ .eq(WxAutoReply::getRepType,wxAutoReply.getRepMate());
+ List list = wxAutoReplyService.list(queryWrapper);
+ if(list != null && list.size() == 1){
+ if(!list.get(0).getId().equals(wxAutoReply.getId())){
+ throw new RuntimeException("关键词重复");
+ }
+ }
+ if(list != null && list.size()>1){
+ throw new RuntimeException("关键词重复");
+ }
+ }
+ }
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxDraftController.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxDraftController.java
new file mode 100644
index 0000000..42d02b6
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxDraftController.java
@@ -0,0 +1,141 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.controller;
+
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.security.annotation.RequiresPermissions;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.api.WxMpDraftService;
+import me.chanjar.weixin.mp.api.WxMpFreePublishService;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.draft.WxMpAddDraft;
+import me.chanjar.weixin.mp.bean.draft.WxMpDraftArticles;
+import me.chanjar.weixin.mp.bean.draft.WxMpUpdateDraft;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 微信草稿箱
+ *
+ * @author www.joolun.com
+ * @date 2022-03-10 21:26:35
+ */
+@Slf4j
+@RestController
+@AllArgsConstructor
+@RequestMapping("/wxdraft")
+public class WxDraftController extends BaseController {
+ private final WxMpService wxService;
+
+ /**
+ * 新增图文消息
+ *
+ * @param data
+ * @return
+ */
+ @PostMapping
+ @RequiresPermissions("wxmp:wxdraft:add")
+ public AjaxResult add(@RequestBody JSONObject data) throws Exception {
+ JSONArray jSONArray = data.getJSONArray("articles");
+ List articles = jSONArray.toList(WxMpDraftArticles.class);
+ WxMpAddDraft wxMpAddDraft = new WxMpAddDraft();
+ wxMpAddDraft.setArticles(articles);
+ WxMpDraftService wxMpDraftService = wxService.getDraftService();
+ String rs = wxMpDraftService.addDraft(wxMpAddDraft);
+ return AjaxResult.success(rs);
+ }
+
+ /**
+ * 修改微信草稿箱
+ *
+ * @param data
+ * @return
+ */
+ @PutMapping
+ @RequiresPermissions("wxmp:wxdraft:edit")
+ public AjaxResult edit(@RequestBody JSONObject data) throws Exception {
+ String mediaId = data.getStr("mediaId");
+ JSONArray jSONArray = data.getJSONArray("articles");
+ List articles = jSONArray.toList(WxMpDraftArticles.class);
+ WxMpDraftService wxMpDraftService = wxService.getDraftService();
+ WxMpUpdateDraft wxMpUpdateDraft = new WxMpUpdateDraft();
+ wxMpUpdateDraft.setMediaId(mediaId);
+ int index = 0;
+ for (WxMpDraftArticles article : articles) {
+ wxMpUpdateDraft.setIndex(index);
+ wxMpUpdateDraft.setArticles(article);
+ wxMpDraftService.updateDraft(wxMpUpdateDraft);
+ index++;
+ }
+ return AjaxResult.success();
+ }
+
+ /**
+ * 通过id删除微信草稿箱
+ *
+ * @param
+ * @return R
+ */
+ @DeleteMapping
+ @RequiresPermissions("wxmp:wxdraft:del")
+ public AjaxResult del(String id) throws Exception {
+ WxMpDraftService wxMpDraftService = wxService.getDraftService();
+ return AjaxResult.success(wxMpDraftService.delDraft(id));
+ }
+
+ /**
+ * 分页查询
+ *
+ * @param page 分页对象
+ * @param
+ * @return
+ */
+ @GetMapping("/page")
+ @RequiresPermissions("wxmp:wxdraft:index")
+ public AjaxResult getPage(Page page) throws Exception {
+ WxMpDraftService wxMpDraftService = wxService.getDraftService();
+ int count = (int) page.getSize();
+ int offset = (int) page.getCurrent() * count - count;
+ return AjaxResult.success(wxMpDraftService.listDraft(offset, count));
+ }
+
+ /**
+ * 发布草稿箱
+ * @param id
+ * @return
+ */
+ @PostMapping("/publish/{id}")
+ @RequiresPermissions("wxmp:wxdraft:publish")
+ public AjaxResult publish(@PathVariable String id) throws Exception {
+ WxMpFreePublishService wxMpFreePublishService = wxService.getFreePublishService();
+ wxMpFreePublishService.submit(id);
+ return AjaxResult.success();
+ }
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxFreePublishController.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxFreePublishController.java
new file mode 100644
index 0000000..8f624cb
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxFreePublishController.java
@@ -0,0 +1,81 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.security.annotation.RequiresPermissions;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.api.WxMpFreePublishService;
+import me.chanjar.weixin.mp.api.WxMpService;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 微信发布
+ *
+ * @author www.joolun.com
+ * @date 2022-03-10 21:26:35
+ */
+@Slf4j
+@RestController
+@AllArgsConstructor
+@RequestMapping("/freepublish")
+public class WxFreePublishController extends BaseController {
+ private final WxMpService wxService;
+
+ /**
+ * 删除发布
+ *
+ * @param
+ * @return R
+ */
+ @DeleteMapping
+ @RequiresPermissions("wxmp:wxfreepublish:del")
+ public AjaxResult del(String id) throws Exception {
+ WxMpFreePublishService wxMpFreePublishService = wxService.getFreePublishService();
+ return AjaxResult.success(wxMpFreePublishService.deletePushAllArticle(id));
+ }
+
+ /**
+ * 获取成功发布列表
+ *
+ * @param page 获取成功发布列表
+ * @param
+ * @return
+ */
+ @GetMapping("/page")
+ @RequiresPermissions("wxmp:wxfreepublish:index")
+ public AjaxResult getPage(Page page) throws Exception {
+ WxMpFreePublishService wxMpFreePublishService = wxService.getFreePublishService();
+ int count = (int) page.getSize();
+ int offset = (int) page.getCurrent() * count - count;
+ return AjaxResult.success(wxMpFreePublishService.getPublicationRecords(offset, count));
+ }
+
+}
diff --git a/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxMaterialController.java b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxMaterialController.java
new file mode 100644
index 0000000..5c093ee
--- /dev/null
+++ b/ruoyi-modules/intc-weixin/src/main/java/com/ruoyi/weixin/controller/WxMaterialController.java
@@ -0,0 +1,266 @@
+/*
+MIT License
+
+Copyright (c) 2020 www.joolun.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+package com.ruoyi.weixin.controller;
+
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.security.annotation.RequiresPermissions;
+import com.ruoyi.weixin.entity.ImageManager;
+import com.ruoyi.weixin.utils.FileUtils;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpMaterialService;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.material.WxMediaImgUploadResult;
+import me.chanjar.weixin.mp.bean.material.WxMpMaterial;
+import me.chanjar.weixin.mp.bean.material.WxMpMaterialFileBatchGetResult;
+import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 微信素材
+ *
+ * @author www.joolun.com
+ * @date 2019-03-23 21:26:35
+ */
+@Slf4j
+@RestController
+@AllArgsConstructor
+@RequestMapping("/wxmaterial")
+public class WxMaterialController extends BaseController {
+
+ private final WxMpService wxService;
+
+ /**
+ * 上传非图文微信素材
+ * @param mulFile
+ * @param mediaType
+ * @return
+ */
+ @PostMapping("/materialFileUpload")
+ // @PreAuthorize("@ss.hasPermi('wxmp:wxmaterial:add')")
+ public AjaxResult materialFileUpload(@RequestParam("file") MultipartFile mulFile,
+ @RequestParam("title") String title,
+ @RequestParam("introduction") String introduction,
+ @RequestParam("mediaType") String mediaType) {
+ try {
+ WxMpMaterial material = new WxMpMaterial();
+ material.setName(mulFile.getOriginalFilename());
+ if(WxConsts.MediaFileType.VIDEO.equals(mediaType)){
+ material.setVideoTitle(title);
+ material.setVideoIntroduction(introduction);
+ }
+ File file = FileUtils.multipartFileToFile(mulFile);
+ material.setFile(file);
+ WxMpMaterialService wxMpMaterialService = wxService.getMaterialService();
+ WxMpMaterialUploadResult wxMpMaterialUploadResult = wxMpMaterialService.materialFileUpload(mediaType,material);
+ WxMpMaterialFileBatchGetResult.WxMaterialFileBatchGetNewsItem wxMpMaterialFileBatchGetResult = new WxMpMaterialFileBatchGetResult.WxMaterialFileBatchGetNewsItem();
+ wxMpMaterialFileBatchGetResult.setName(file.getName());
+ wxMpMaterialFileBatchGetResult.setMediaId(wxMpMaterialUploadResult.getMediaId());
+ wxMpMaterialFileBatchGetResult.setUrl(wxMpMaterialUploadResult.getUrl());
+ return AjaxResult.success(wxMpMaterialFileBatchGetResult);
+ } catch (WxErrorException e) {
+ e.printStackTrace();
+ log.error("上传非图文微信素材失败" + e);
+ return AjaxResult.error(e.getMessage());
+ } catch (Exception e) {
+ e.printStackTrace();
+ log.error("上传失败", e);
+ return AjaxResult.error(e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * 上传图文消息内的图片获取URL
+ * @param mulFile
+ * @return
+ */
+ @PostMapping("/newsImgUpload")
+ // @PreAuthorize("@ss.hasPermi('wxmp:wxmaterial:add')")
+ public String newsImgUpload(@RequestParam("file") MultipartFile mulFile) throws Exception {
+ File file = FileUtils.multipartFileToFile(mulFile);
+ WxMpMaterialService wxMpMaterialService = wxService.getMaterialService();
+ WxMediaImgUploadResult wxMediaImgUploadResult = wxMpMaterialService.mediaImgUpload(file);
+ Map