diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 6529e3a..03095d2 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -8,6 +8,7 @@
+
@@ -29,6 +30,7 @@
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index d6fb4db..0946b03 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -7,6 +7,7 @@
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index a4c32d8..aaa3022 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -8,7 +8,7 @@
-
+
\ No newline at end of file
diff --git a/README-Applet-Filter.md b/README-Applet-Filter.md
new file mode 100644
index 0000000..9669dd6
--- /dev/null
+++ b/README-Applet-Filter.md
@@ -0,0 +1,144 @@
+# 小程序登录拦截器分离配置说明
+
+## 配置目标
+将系统的登录拦截器分成后台管理系统和小程序两个独立的拦截器,实现不同的认证策略。
+
+## 实现方案
+
+### 1. 创建小程序专用拦截器
+
+#### 1.1 新增文件
+- `jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/filters/AppletJwtFilter.java`
+
+#### 1.2 核心特性
+```java
+@Override
+protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
+ try {
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ String requestPath = httpRequest.getServletPath();
+
+ // 只处理以/applet开头的请求
+ if (!requestPath.startsWith("/applet")) {
+ return true; // 不是applet请求,直接放行
+ }
+
+ // 判断当前路径是不是注解了@IngoreAuth路径,如果是,则放开验证
+ if (InMemoryIgnoreAuth.contains(requestPath)) {
+ return true;
+ }
+
+ executeLogin(request, response);
+ return true;
+ } catch (Exception e) {
+ JwtUtil.responseError(response,401,CommonConstant.TOKEN_IS_INVALID_MSG);
+ return false;
+ }
+}
+```
+
+### 2. 修改Shiro配置
+
+#### 2.1 修改文件
+- `jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java`
+
+#### 2.2 配置变更
+```java
+// 添加自己的过滤器并且取名为jwt和applet
+Map filterMap = new HashMap(2);
+//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
+Object cloudServer = env.getProperty(CommonConstant.CLOUD_SERVER_KEY);
+filterMap.put("jwt", new JwtFilter(cloudServer==null));
+filterMap.put("applet", new AppletJwtFilter(cloudServer==null));
+shiroFilterFactoryBean.setFilters(filterMap);
+
+//
+
+ 返回
+ 用户
+
+
+
+
+
+
+
+
+
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp/AppletUserList.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp/AppletUserList.vue
new file mode 100644
index 0000000..b1611c4
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp/AppletUserList.vue
@@ -0,0 +1,44 @@
+
+
+
+
+ 返回
+ 用户
+
+
+
+
+
+
+
+
+
+
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserData.ts b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserData.ts
new file mode 100644
index 0000000..7919028
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserData.ts
@@ -0,0 +1,34 @@
+import { render } from '@/common/renderUtils';
+//列表数据
+export const columns = [
+ {
+ title: '昵称',
+ align:"center",
+ dataIndex: 'name'
+ },
+ {
+ title: '第三方认证id',
+ align:"center",
+ dataIndex: 'openid'
+ },
+ {
+ title: '手机号',
+ align:"center",
+ dataIndex: 'phone'
+ },
+ {
+ title: '体总指数',
+ align:"center",
+ dataIndex: 'bmi'
+ },
+ {
+ title: '脂肪',
+ align:"center",
+ dataIndex: 'fat'
+ },
+ {
+ title: '头像',
+ align:"center",
+ dataIndex: 'avatar'
+ },
+];
\ No newline at end of file
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserForm.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserForm.vue
new file mode 100644
index 0000000..e2b8ccc
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserForm.vue
@@ -0,0 +1,276 @@
+
+{
+layout: 'default',
+style: {
+navigationStyle: 'custom',
+navigationBarTitleText: '用户',
+},
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserList.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserList.vue
new file mode 100644
index 0000000..174a77b
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserList.vue
@@ -0,0 +1,148 @@
+
+{
+layout: 'default',
+style: {
+navigationBarTitleText: '用户',
+navigationStyle: 'custom',
+},
+}
+
+
+
+
+
+
+
+
+
+
+ {{ cItem.title }}
+ {{ item[cItem.dataIndex] }}
+
+
+
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUser.api.ts b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUser.api.ts
new file mode 100644
index 0000000..7129b96
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUser.api.ts
@@ -0,0 +1,72 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from "/@/hooks/web/useMessage";
+
+const { createConfirm } = useMessage();
+
+enum Api {
+ list = '/appletUser/appletUser/list',
+ save='/appletUser/appletUser/add',
+ edit='/appletUser/appletUser/edit',
+ deleteOne = '/appletUser/appletUser/delete',
+ deleteBatch = '/appletUser/appletUser/deleteBatch',
+ importExcel = '/appletUser/appletUser/importExcel',
+ exportXls = '/appletUser/appletUser/exportXls',
+}
+
+/**
+ * 导出api
+ * @param params
+ */
+export const getExportUrl = Api.exportXls;
+
+/**
+ * 导入api
+ */
+export const getImportUrl = Api.importExcel;
+
+/**
+ * 列表接口
+ * @param params
+ */
+export const list = (params) => defHttp.get({ url: Api.list, params });
+
+/**
+ * 删除单个
+ * @param params
+ * @param handleSuccess
+ */
+export const deleteOne = (params,handleSuccess) => {
+ return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
+ handleSuccess();
+ });
+}
+
+/**
+ * 批量删除
+ * @param params
+ * @param handleSuccess
+ */
+export const batchDelete = (params, handleSuccess) => {
+ createConfirm({
+ iconType: 'warning',
+ title: '确认删除',
+ content: '是否删除选中数据',
+ okText: '确认',
+ cancelText: '取消',
+ onOk: () => {
+ return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
+ handleSuccess();
+ });
+ }
+ });
+}
+
+/**
+ * 保存或者更新
+ * @param params
+ * @param isUpdate
+ */
+export const saveOrUpdate = (params, isUpdate) => {
+ let url = isUpdate ? Api.edit : Api.save;
+ return defHttp.post({ url: url, params }, { isTransformResponse: false });
+}
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUser.data.ts b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUser.data.ts
new file mode 100644
index 0000000..cbcf507
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUser.data.ts
@@ -0,0 +1,48 @@
+import {BasicColumn} from '/@/components/Table';
+import {FormSchema} from '/@/components/Table';
+import { rules} from '/@/utils/helper/validator';
+import { render } from '/@/utils/common/renderUtils';
+import { getWeekMonthQuarterYear } from '/@/utils';
+//列表数据
+export const columns: BasicColumn[] = [
+ {
+ title: '昵称',
+ align: "center",
+ dataIndex: 'name'
+ },
+ {
+ title: '第三方认证id',
+ align: "center",
+ dataIndex: 'openid'
+ },
+ {
+ title: '手机号',
+ align: "center",
+ dataIndex: 'phone'
+ },
+ {
+ title: '体总指数',
+ align: "center",
+ dataIndex: 'bmi'
+ },
+ {
+ title: '脂肪',
+ align: "center",
+ dataIndex: 'fat'
+ },
+ {
+ title: '头像',
+ align: "center",
+ dataIndex: 'avatar'
+ },
+];
+
+// 高级查询数据
+export const superQuerySchema = {
+ name: {title: '昵称',order: 0,view: 'text', type: 'string',},
+ openid: {title: '第三方认证id',order: 1,view: 'text', type: 'string',},
+ phone: {title: '手机号',order: 2,view: 'text', type: 'string',},
+ bmi: {title: '体总指数',order: 3,view: 'number', type: 'number',},
+ fat: {title: '脂肪',order: 4,view: 'number', type: 'number',},
+ avatar: {title: '头像',order: 5,view: 'text', type: 'string',},
+};
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUserList.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUserList.vue
new file mode 100644
index 0000000..06ee674
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUserList.vue
@@ -0,0 +1,249 @@
+
+
+
+
+
+
+
+
+ 新增
+ 导出
+ 导入
+
+
+
+
+
+ 删除
+
+
+
+ 批量操作
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/V20250717_1__menu_insert_AppletUser.sql b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/V20250717_1__menu_insert_AppletUser.sql
new file mode 100644
index 0000000..449ee13
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/V20250717_1__menu_insert_AppletUser.sql
@@ -0,0 +1,26 @@
+-- 注意:该页面对应的前台目录为views/appletUser文件夹下
+-- 如果你想更改到其他目录,请修改sql中component字段对应的值
+
+
+INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external)
+VALUES ('2025071707392390060', NULL, '用户', '/appletUser/appletUserList', 'appletUser/AppletUserList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2025-07-17 19:39:06', NULL, NULL, 0);
+
+-- 权限控制sql
+-- 新增
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025071707392390061', '2025071707392390060', '添加用户', NULL, NULL, 0, NULL, NULL, 2, 'appletUser:applet_user:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-17 19:39:06', NULL, NULL, 0, 0, '1', 0);
+-- 编辑
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025071707392390062', '2025071707392390060', '编辑用户', NULL, NULL, 0, NULL, NULL, 2, 'appletUser:applet_user:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-17 19:39:06', NULL, NULL, 0, 0, '1', 0);
+-- 删除
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025071707392390063', '2025071707392390060', '删除用户', NULL, NULL, 0, NULL, NULL, 2, 'appletUser:applet_user:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-17 19:39:06', NULL, NULL, 0, 0, '1', 0);
+-- 批量删除
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025071707392390064', '2025071707392390060', '批量删除用户', NULL, NULL, 0, NULL, NULL, 2, 'appletUser:applet_user:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-17 19:39:06', NULL, NULL, 0, 0, '1', 0);
+-- 导出excel
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025071707392390065', '2025071707392390060', '导出excel_用户', NULL, NULL, 0, NULL, NULL, 2, 'appletUser:applet_user:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-17 19:39:06', NULL, NULL, 0, 0, '1', 0);
+-- 导入excel
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025071707392390066', '2025071707392390060', '导入excel_用户', NULL, NULL, 0, NULL, NULL, 2, 'appletUser:applet_user:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-17 19:39:06', NULL, NULL, 0, 0, '1', 0);
\ No newline at end of file
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/components/AppletUserForm.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/components/AppletUserForm.vue
new file mode 100644
index 0000000..e6e0645
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/components/AppletUserForm.vue
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/components/AppletUserModal.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/components/AppletUserModal.vue
new file mode 100644
index 0000000..06c17d8
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/components/AppletUserModal.vue
@@ -0,0 +1,81 @@
+
+
+
+
+ 取消
+ 确认
+
+
+
+
+
+
+
+
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/common/wxUtils/WxHttpClientUtil.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/common/wxUtils/WxHttpClientUtil.java
new file mode 100644
index 0000000..0b1d4da
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/common/wxUtils/WxHttpClientUtil.java
@@ -0,0 +1,264 @@
+package org.jeecg.modules.common.wxUtils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLContextBuilder;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.net.URI;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+/**
+ * 微信API专用HTTP客户端工具类
+ * 具有超时配置、重试机制和异常处理
+ *
+ * @author system
+ * @date 2025-01-25
+ */
+@Slf4j
+public class WxHttpClientUtil {
+
+ // 超时配置常量
+ private static final int CONNECTION_REQUEST_TIMEOUT = 10000; // 10秒
+ private static final int CONNECT_TIMEOUT = 15000; // 15秒
+ private static final int SOCKET_TIMEOUT = 30000; // 30秒
+ private static final int MAX_RETRY_COUNT = 3; // 最大重试次数
+
+ /**
+ * 创建带超时配置的SSL客户端
+ */
+ private static CloseableHttpClient createWxHttpClient() {
+ try {
+ // SSL配置 - 信任所有证书
+ SSLContext sslContext = new SSLContextBuilder()
+ .loadTrustMaterial(null, new TrustStrategy() {
+ @Override
+ public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ return true;
+ }
+ }).build();
+
+ SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
+ sslContext,
+ SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
+ );
+
+ // 请求配置
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
+ .setConnectTimeout(CONNECT_TIMEOUT)
+ .setSocketTimeout(SOCKET_TIMEOUT)
+ .build();
+
+ // 重试处理器
+ DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(MAX_RETRY_COUNT, true);
+
+ return HttpClients.custom()
+ .setSSLSocketFactory(sslsf)
+ .setDefaultRequestConfig(requestConfig)
+ .setRetryHandler(retryHandler)
+ .build();
+
+ } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
+ log.error("创建SSL客户端失败: {}", e.getMessage(), e);
+ // 返回默认客户端作为后备
+ return HttpClients.createDefault();
+ }
+ }
+
+ /**
+ * 执行GET请求(微信API专用)
+ * @param url 请求URL
+ * @param params 请求参数
+ * @return 响应字符串
+ */
+ public static String doGet(String url, Map params) {
+ return doGetWithRetry(url, params, 0);
+ }
+
+ /**
+ * 执行GET请求(微信API专用)
+ * @param url 请求URL
+ * @return 响应字符串
+ */
+ public static String doGet(String url) {
+ return doGet(url, null);
+ }
+
+ /**
+ * 带重试机制的GET请求
+ */
+ private static String doGetWithRetry(String url, Map params, int retryCount) {
+ CloseableHttpClient httpClient = null;
+ CloseableHttpResponse response = null;
+
+ try {
+ log.info("开始请求微信API: {}, 重试次数: {}", url, retryCount);
+
+ httpClient = createWxHttpClient();
+
+ // 构建URI
+ URIBuilder builder = new URIBuilder(url);
+ if (params != null) {
+ for (Map.Entry entry : params.entrySet()) {
+ builder.addParameter(entry.getKey(), entry.getValue());
+ }
+ }
+ URI uri = builder.build();
+
+ // 创建GET请求
+ HttpGet httpGet = new HttpGet(uri);
+ httpGet.setHeader("User-Agent", "WxHttpClient/1.0");
+ httpGet.setHeader("Accept", "application/json, text/plain, */*");
+
+ // 执行请求
+ response = httpClient.execute(httpGet);
+
+ // 检查响应状态
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode == HttpStatus.SC_OK) {
+ String result = EntityUtils.toString(response.getEntity(), "UTF-8");
+ log.info("微信API请求成功: {}", url);
+ return result;
+ } else {
+ log.warn("微信API返回非200状态码: {}, URL: {}", statusCode, url);
+ throw new RuntimeException("HTTP状态码异常: " + statusCode);
+ }
+
+ } catch (Exception e) {
+ log.error("微信API请求失败: {}, 错误: {}, 重试次数: {}", url, e.getMessage(), retryCount);
+
+ // 如果还有重试机会,进行重试
+ if (retryCount < MAX_RETRY_COUNT) {
+ log.info("准备进行第{}次重试...", retryCount + 1);
+ try {
+ Thread.sleep(1000 * (retryCount + 1)); // 递增延迟
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ }
+ return doGetWithRetry(url, params, retryCount + 1);
+ }
+
+ // 重试次数用尽,抛出异常
+ throw new RuntimeException("微信API请求失败,已重试" + MAX_RETRY_COUNT + "次: " + e.getMessage(), e);
+
+ } finally {
+ // 关闭资源
+ if (response != null) {
+ try {
+ response.close();
+ } catch (IOException e) {
+ log.error("关闭响应失败: {}", e.getMessage());
+ }
+ }
+ if (httpClient != null) {
+ try {
+ httpClient.close();
+ } catch (IOException e) {
+ log.error("关闭客户端失败: {}", e.getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * 执行POST请求(微信API专用)
+ * @param url 请求URL
+ * @param jsonBody JSON请求体
+ * @return 响应字符串
+ */
+ public static String doPost(String url, String jsonBody) {
+ return doPostWithRetry(url, jsonBody, 0);
+ }
+
+ /**
+ * 带重试机制的POST请求
+ */
+ private static String doPostWithRetry(String url, String jsonBody, int retryCount) {
+ CloseableHttpClient httpClient = null;
+ CloseableHttpResponse response = null;
+
+ try {
+ log.info("开始POST请求微信API: {}, 重试次数: {}", url, retryCount);
+
+ httpClient = createWxHttpClient();
+
+ // 创建POST请求
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.setHeader("Content-Type", "application/json; charset=UTF-8");
+ httpPost.setHeader("User-Agent", "WxHttpClient/1.0");
+
+ // 设置请求体
+ if (jsonBody != null) {
+ StringEntity entity = new StringEntity(jsonBody, "UTF-8");
+ httpPost.setEntity(entity);
+ }
+
+ // 执行请求
+ response = httpClient.execute(httpPost);
+
+ // 检查响应状态
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode == HttpStatus.SC_OK) {
+ String result = EntityUtils.toString(response.getEntity(), "UTF-8");
+ log.info("微信API POST请求成功: {}", url);
+ return result;
+ } else {
+ log.warn("微信API POST返回非200状态码: {}, URL: {}", statusCode, url);
+ throw new RuntimeException("HTTP状态码异常: " + statusCode);
+ }
+
+ } catch (Exception e) {
+ log.error("微信API POST请求失败: {}, 错误: {}, 重试次数: {}", url, e.getMessage(), retryCount);
+
+ // 如果还有重试机会,进行重试
+ if (retryCount < MAX_RETRY_COUNT) {
+ log.info("准备进行第{}次重试...", retryCount + 1);
+ try {
+ Thread.sleep(1000 * (retryCount + 1)); // 递增延迟
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ }
+ return doPostWithRetry(url, jsonBody, retryCount + 1);
+ }
+
+ // 重试次数用尽,抛出异常
+ throw new RuntimeException("微信API POST请求失败,已重试" + MAX_RETRY_COUNT + "次: " + e.getMessage(), e);
+
+ } finally {
+ // 关闭资源
+ if (response != null) {
+ try {
+ response.close();
+ } catch (IOException e) {
+ log.error("关闭响应失败: {}", e.getMessage());
+ }
+ }
+ if (httpClient != null) {
+ try {
+ httpClient.close();
+ } catch (IOException e) {
+ log.error("关闭客户端失败: {}", e.getMessage());
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/common/wxUtils/WxHttpUtils.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/common/wxUtils/WxHttpUtils.java
new file mode 100644
index 0000000..6934bf8
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/common/wxUtils/WxHttpUtils.java
@@ -0,0 +1,96 @@
+package org.jeecg.modules.common.wxUtils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.TypeReference;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+@Component
+public class WxHttpUtils {
+
+ @Value("${wechat.mpAppId}")
+ private String appid;
+ @Value("${wechat.mpAppSecret}")
+ private String secret;//
+ @Value("${wechat.merchantId}")
+ private String mchId;//
+ private static String shipmentUrl = "https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=";
+ private static final String GET_USER_PHONE_NUMBER = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";
+
+ /**
+ * 获取appid
+ */
+ public String getAppid() {
+ return appid;
+ }
+
+ /**
+ * 获取secret
+ */
+ public String getSecret() {
+ return secret;
+ }
+
+
+ /**
+ * 获取令牌
+ *
+ * @return
+ */
+ public String getAccessToken() {
+ String requestUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret;
+
+ try {
+ // 使用增强版HTTP客户端,具有超时配置和重试机制
+ String response = WxHttpClientUtil.doGet(requestUrl);
+ Map map = JSON.parseObject(response, new TypeReference