From 52d56c1ee26617faba8194814ebf5d401a7ca1c8 Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Fri, 18 Jul 2025 13:15:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=99=BB=E5=BD=95=EF=BC=88?= =?UTF-8?q?=E5=BE=85=E6=B5=8B=E8=AF=95=E7=99=BB=E5=BD=95=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E6=88=90=E5=8A=9F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/compiler.xml | 2 + .idea/encodings.xml | 1 + .idea/misc.xml | 2 +- README-Applet-Filter.md | 144 +++++++++++ README-Applet-Token-Config.md | 216 ++++++++++++++++ README-AppletUser-Config.md | 234 +++++++++++++++++ .../org/jeecg/common/system/vo/AppletUser.java | 169 +++++++++++++ .../main/java/org/jeecg/config/Swagger3Config.java | 41 +-- .../org/jeecg/config/shiro/AppletJwtFilter.java | 141 +++++++++++ .../org/jeecg/config/shiro/AppletJwtToken.java | 28 +++ .../org/jeecg/config/shiro/AppletShiroRealm.java | 198 +++++++++++++++ .../java/org/jeecg/config/shiro/ShiroConfig.java | 34 ++- .../jeecgboot-boot-applet/README-Applet-Module.md | 272 ++++++++++++++++++++ .../applet/controller/AppletLoginController.java | 105 ++++++++ .../controller/AppletUserInfoController.java | 100 ++++++++ .../modules/applet/service/AppletLoginService.java | 183 ++++++++++++++ .../modules/applet/service/AppletUserService.java | 136 ++++++++++ .../modules/applet/service/WxAppletService.java | 172 +++++++++++++ .../controller/AppletUserController.java | 165 ++++++++++++ .../appletUser/mapper/AppletUserMapper.java | 14 ++ .../appletUser/mapper/xml/AppletUserMapper.xml | 5 + .../appletUser/service/IAppletUserService.java | 14 ++ .../service/impl/AppletUserServiceImpl.java | 19 ++ .../appletUser/uniapp/AppletUserForm.vue | 113 +++++++++ .../appletUser/uniapp/AppletUserList.vue | 44 ++++ .../appletUser/uniapp3/AppletUserData.ts | 34 +++ .../appletUser/uniapp3/AppletUserForm.vue | 276 +++++++++++++++++++++ .../appletUser/uniapp3/AppletUserList.vue | 148 +++++++++++ .../appletUser/vue3Native/AppletUser.api.ts | 72 ++++++ .../appletUser/vue3Native/AppletUser.data.ts | 48 ++++ .../appletUser/vue3Native/AppletUserList.vue | 249 +++++++++++++++++++ .../V20250717_1__menu_insert_AppletUser.sql | 26 ++ .../vue3Native/components/AppletUserForm.vue | 180 ++++++++++++++ .../vue3Native/components/AppletUserModal.vue | 81 ++++++ .../modules/common/wxUtils/WxHttpClientUtil.java | 264 ++++++++++++++++++++ .../jeecg/modules/common/wxUtils/WxHttpUtils.java | 96 +++++++ .../modules/healthApi/HealthManagerController.java | 28 --- .../src/main/resources/application-dev.yml | 14 ++ 38 files changed, 4021 insertions(+), 47 deletions(-) create mode 100644 README-Applet-Filter.md create mode 100644 README-Applet-Token-Config.md create mode 100644 README-AppletUser-Config.md create mode 100644 jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/AppletUser.java create mode 100644 jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/AppletJwtFilter.java create mode 100644 jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/AppletJwtToken.java create mode 100644 jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/AppletShiroRealm.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/README-Applet-Module.md create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletLoginController.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletUserInfoController.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletLoginService.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletUserService.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/WxAppletService.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/controller/AppletUserController.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/mapper/AppletUserMapper.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/mapper/xml/AppletUserMapper.xml create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/service/IAppletUserService.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/service/impl/AppletUserServiceImpl.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp/AppletUserForm.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp/AppletUserList.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserData.ts create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserForm.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/uniapp3/AppletUserList.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUser.api.ts create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUser.data.ts create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/AppletUserList.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/V20250717_1__menu_insert_AppletUser.sql create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/components/AppletUserForm.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/appletBackground/appletUser/vue3Native/components/AppletUserModal.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/common/wxUtils/WxHttpClientUtil.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/common/wxUtils/WxHttpUtils.java delete mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/healthApi/HealthManagerController.java 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); + +// + + 返回 + 用户 + + + +
+ + + 昵称: + + + + + + 第三方认证id: + + + + + + 手机号: + + + + + + 体总指数: + + + + + + 脂肪: + + + + + + 头像: + + + + + + +
+
+ + + + 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', +}, +} + + + + + + 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>() {}); + + String accessToken = map.get("access_token"); + if (accessToken == null || accessToken.isEmpty()) { + throw new RuntimeException("获取access_token失败,响应: " + response); + } + + return accessToken; + } catch (Exception e) { + throw new RuntimeException("获取微信access_token失败: " + e.getMessage(), e); + } + } + + + public String getPhoneNumber(String code) throws Exception { + URL url = new URL(GET_USER_PHONE_NUMBER + "?access_token=" + this.getAccessToken()); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json; utf-8"); + conn.setRequestProperty("Accept", "application/json"); + conn.setDoOutput(true); + + JSONObject jsonInput = new JSONObject(); + jsonInput.put("code", code); + + try (DataOutputStream os = new DataOutputStream(conn.getOutputStream())) { + byte[] input = jsonInput.toString().getBytes(StandardCharsets.UTF_8); + os.write(input, 0, input.length); + } + + try (BufferedReader br = new BufferedReader( + new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { + StringBuilder response = new StringBuilder(); + String responseLine; + while ((responseLine = br.readLine()) != null) { + response.append(responseLine.trim()); + } + //获取手机号码 + return response.toString(); + } + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/healthApi/HealthManagerController.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/healthApi/HealthManagerController.java deleted file mode 100644 index e963d7a..0000000 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/healthApi/HealthManagerController.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.jeecg.modules.healthApi; - -import lombok.extern.slf4j.Slf4j; -import org.jeecg.common.api.vo.Result; -import org.jeecg.config.shiro.IgnoreAuth; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * 健康管理小程序模块控制器 - * - * @author jeecg - * @since 2024-01-01 - */ -@RestController -@RequestMapping("/applet/health") -@Slf4j -public class HealthManagerController { - - /** - * 健康检查接口 - */ - @GetMapping("/health") - public Result health() { - return Result.OK("健康管理小程序模块运行正常"); - } -} \ No newline at end of file diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml index 223727f..2b73b8f 100644 --- a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml @@ -325,3 +325,17 @@ justauth: type: default prefix: 'demo::' timeout: 1h + +##配置微信##爱简收旧衣按件回收小程序 +wechat: + mpAppId: wx2bd656ae9704dbee # 微信小程序appid + mpAppSecret: b07f82b16e9598bc23de604f49c57e23 # 微信小程序密钥 + merchantId: 1701841654 # 商户号 + privateKeyPath: module-system/src/main/resources/apiclient_key.pem #本地私钥路径 + publicKeyPath: module-system/src/main/resources/pub_key.pem #本地公钥路径 +# privateKeyPath: /data/app-test/hly/cerFile/apiclient_key.pem #线上私钥路径 +# publicKeyPath: /data/app-test/hly/cerFile/pub_key.pem #线上公钥路径 + publicKeyId: PUB_KEY_ID_0117018416542025022100395100001649 #公钥 + merchantSerialNumber: 525971050851A99F315A970D3055192779652E03 # 商户API证书序列号 + apiV3Key: # 商户APIV3密钥 + refundNotifyUrl: # 退款通知地址(正式环境) \ No newline at end of file