From e735f768b58dcee2818286eeeaa5924751c499c4 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 11 Feb 2026 11:35:04 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E7=A7=BB=E9=99=A4=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E7=99=BB=E5=BD=95=E5=92=8CAI=20=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 20 +- sql/mysql/youlai_admin.sql | 46 +-- .../boot/auth/controller/AuthController.java | 26 -- .../auth/model/dto/WxMiniAppCodeLoginDTO.java | 21 -- .../model/dto/WxMiniAppPhoneLoginDTO.java | 27 -- .../youlai/boot/auth/service/AuthService.java | 26 -- .../auth/service/impl/AuthServiceImpl.java | 71 ---- .../youlai/boot/config/SecurityConfig.java | 24 -- .../youlai/boot/config/WxMiniAppConfig.java | 41 --- .../platform/ai/config/SpringAiConfig.java | 49 --- .../ai/controller/AiAssistantController.java | 79 ----- .../ai/mapper/AiAssistantRecordMapper.java | 22 -- .../ai/model/dto/AiExecuteRequestDTO.java | 48 --- .../ai/model/dto/AiExecuteResponseDTO.java | 59 ---- .../ai/model/dto/AiFunctionCallDTO.java | 36 --- .../ai/model/dto/AiParseRequestDTO.java | 34 -- .../ai/model/dto/AiParseResponseDTO.java | 55 ---- .../ai/model/entity/AiAssistantRecord.java | 80 ----- .../ai/model/query/AiAssistantPageQuery.java | 44 --- .../ai/model/query/AiAssistantQuery.java | 44 --- .../ai/model/vo/AiAssistantRecordVO.java | 91 ------ .../ai/service/AiAssistantRecordService.java | 52 --- .../impl/AiAssistantRecordServiceImpl.java | 302 ------------------ .../boot/platform/ai/tools/UserTools.java | 49 --- .../WxMiniAppCodeAuthenticationToken.java | 69 ---- .../WxMiniAppPhoneAuthenticationToken.java | 89 ------ .../WxMiniAppCodeAuthenticationProvider.java | 95 ------ .../WxMiniAppPhoneAuthenticationProvider.java | 115 ------- .../youlai/boot/system/mapper/UserMapper.java | 12 - .../youlai/boot/system/model/entity/User.java | 7 - .../boot/system/model/form/UserForm.java | 3 - .../boot/system/service/UserService.java | 38 --- .../system/service/impl/UserServiceImpl.java | 153 --------- src/main/resources/application-dev.yml | 52 ++- src/main/resources/application-prod.yml | 51 ++- .../mapper/ai/AiAssistantRecordMapper.xml | 95 ------ .../resources/mapper/system/UserMapper.xml | 18 -- 37 files changed, 39 insertions(+), 2104 deletions(-) delete mode 100644 src/main/java/com/youlai/boot/auth/model/dto/WxMiniAppCodeLoginDTO.java delete mode 100644 src/main/java/com/youlai/boot/auth/model/dto/WxMiniAppPhoneLoginDTO.java delete mode 100644 src/main/java/com/youlai/boot/config/WxMiniAppConfig.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/config/SpringAiConfig.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/controller/AiAssistantController.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/mapper/AiAssistantRecordMapper.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteRequestDTO.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteResponseDTO.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/model/dto/AiFunctionCallDTO.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/model/dto/AiParseRequestDTO.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/model/dto/AiParseResponseDTO.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/model/entity/AiAssistantRecord.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/model/query/AiAssistantPageQuery.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/model/query/AiAssistantQuery.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/model/vo/AiAssistantRecordVO.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/service/AiAssistantRecordService.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/service/impl/AiAssistantRecordServiceImpl.java delete mode 100644 src/main/java/com/youlai/boot/platform/ai/tools/UserTools.java delete mode 100644 src/main/java/com/youlai/boot/security/model/WxMiniAppCodeAuthenticationToken.java delete mode 100644 src/main/java/com/youlai/boot/security/model/WxMiniAppPhoneAuthenticationToken.java delete mode 100644 src/main/java/com/youlai/boot/security/provider/WxMiniAppCodeAuthenticationProvider.java delete mode 100644 src/main/java/com/youlai/boot/security/provider/WxMiniAppPhoneAuthenticationProvider.java delete mode 100644 src/main/resources/mapper/ai/AiAssistantRecordMapper.xml diff --git a/pom.xml b/pom.xml index e24bbfe4..0663d1ca 100644 --- a/pom.xml +++ b/pom.xml @@ -58,15 +58,10 @@ 4.7.6 2.2.1 - - 4.7.7.B 2.9.3 2.14.5 - - - 2.0.0-M1 @@ -269,12 +264,6 @@ ${aliyun.java.sdk.dysmsapi.version} - - com.github.binarywang - weixin-java-miniapp - ${weixin-java.version} - - com.github.ben-manes.caffeine @@ -288,14 +277,7 @@ dynamic-datasource-spring-boot3-starter ${dynamic-datasource.version} --> - - - - org.springframework.ai - spring-ai-starter-model-openai - ${spring-ai-openai.version} - - + diff --git a/sql/mysql/youlai_admin.sql b/sql/mysql/youlai_admin.sql index 194d6d6e..8c4de406 100644 --- a/sql/mysql/youlai_admin.sql +++ b/sql/mysql/youlai_admin.sql @@ -136,10 +136,9 @@ CREATE TABLE `sys_menu` ( -- ---------------------------- -- Records of sys_menu -- ---------------------------- --- 顶级目录(1-9):系统/代码生成/AI助手/文档/接口文档/组件/演示/多级/路由 +-- 顶级目录(1-9):系统/代码生成/文档/接口文档/组件/演示/多级/路由 INSERT INTO `sys_menu` VALUES (1, 0, '0', '系统管理', 'C', '', '/system', 'Layout', NULL, NULL, NULL, 1, 1, 'system', '/system/user', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (2, 0, '0', '代码生成', 'C', '', '/codegen', 'Layout', NULL, NULL, NULL, 1, 2, 'code', '/codegen/index', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (3, 0, '0', 'AI助手', 'C', '', '/ai', 'Layout', NULL, NULL, NULL, 1, 3, 'ai', '/ai/command-record', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (4, 0, '0', '平台文档', 'C', '', '/doc', 'Layout', NULL, NULL, NULL, 1, 4, 'document', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (5, 0, '0', '接口文档', 'C', '', '/api', 'Layout', NULL, NULL, NULL, 1, 5, 'api', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (6, 0, '0', '组件封装', 'C', '', '/component', 'Layout', NULL, NULL, NULL, 1, 6, 'menu', '', now(), now(), NULL); @@ -208,9 +207,6 @@ INSERT INTO `sys_menu` VALUES (2806, 280, '0,1,280', '通知撤回', 'B', NULL, -- 代码生成 INSERT INTO `sys_menu` VALUES (310, 2, '0,2', '代码生成', 'M', 'Codegen', 'codegen', 'codegen/index', NULL, NULL, 1, 1, 1, 'code', NULL, now(), now(), NULL); --- AI 助手 -INSERT INTO `sys_menu` VALUES (401, 3, '0,3', 'AI命令记录', 'M', 'ai', 'ai', 'ai/index', NULL, NULL, 1, 1, 1, 'document', NULL, now(), now(), NULL); - -- 平台文档(外链通过 route_path 识别) INSERT INTO `sys_menu` VALUES (501, 4, '0,4', '平台文档(外链)', 'M', NULL, 'https://juejin.cn/post/7228990409909108793', '', NULL, NULL, NULL, 1, 1, 'document', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (502, 4, '0,4', '后端文档', 'M', NULL, 'https://youlai.blog.csdn.net/article/details/145178880', '', NULL, NULL, NULL, 1, 2, 'document', '', now(), now(), NULL); @@ -298,7 +294,7 @@ CREATE TABLE `sys_role_menu` ( -- ============================================ -- 系统管理员角色菜单权限(role_id=2) -- 顶级目录 -INSERT INTO `sys_role_menu` VALUES (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9); +INSERT INTO `sys_role_menu` VALUES (2, 1), (2, 2), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9); -- 系统管理 INSERT INTO `sys_role_menu` VALUES (2, 210), (2, 2101), (2, 2102), (2, 2103), (2, 2104), (2, 2105), (2, 2106), (2, 2107); INSERT INTO `sys_role_menu` VALUES (2, 220), (2, 2201), (2, 2202), (2, 2203), (2, 2204), (2, 2205); @@ -311,8 +307,6 @@ INSERT INTO `sys_role_menu` VALUES (2, 270), (2, 2701), (2, 2702), (2, 2703), (2 INSERT INTO `sys_role_menu` VALUES (2, 280), (2, 2801), (2, 2802), (2, 2803), (2, 2804), (2, 2805), (2, 2806); -- 代码生成 INSERT INTO `sys_role_menu` VALUES (2, 310); --- AI 助手 -INSERT INTO `sys_role_menu` VALUES (2, 401); -- 平台文档 INSERT INTO `sys_role_menu` VALUES (2, 501), (2, 502), (2, 503), (2, 504); -- 接口文档 @@ -344,7 +338,6 @@ CREATE TABLE `sys_user` ( `update_time` datetime COMMENT '更新时间', `update_by` bigint COMMENT '修改人ID', `is_deleted` tinyint(1) DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)', - `openid` char(28) COMMENT '微信 openid', PRIMARY KEY (`id`) USING BTREE, UNIQUE KEY `uk_username` (`username`) ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '系统用户表'; @@ -531,38 +524,3 @@ INSERT INTO `sys_user_notice` VALUES (7, 7, 2, 1, NULL, now(), now(), 0); INSERT INTO `sys_user_notice` VALUES (8, 8, 2, 1, NULL, now(), now(), 0); INSERT INTO `sys_user_notice` VALUES (9, 9, 2, 1, NULL, now(), now(), 0); INSERT INTO `sys_user_notice` VALUES (10, 10, 2, 1, NULL, now(), now(), 0); - --- ---------------------------- --- AI 命令记录表 --- ---------------------------- -DROP TABLE IF EXISTS `ai_assistant_record`; -CREATE TABLE `ai_assistant_record` ( - `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', - `user_id` bigint DEFAULT NULL COMMENT '用户ID', - `username` varchar(64) DEFAULT NULL COMMENT '用户名', - `original_command` text COMMENT '原始命令', - `ai_provider` varchar(32) DEFAULT NULL COMMENT 'AI 供应商(qwen/openai/deepseek/gemini等)', - `ai_model` varchar(64) DEFAULT NULL COMMENT 'AI 模型名称(qwen-plus/qwen-max/gpt-4-turbo等)', - `parse_status` tinyint DEFAULT '0' COMMENT '解析是否成功(0-失败, 1-成功)', - `function_calls` text COMMENT '解析出的函数调用列表(JSON)', - `explanation` varchar(500) DEFAULT NULL COMMENT 'AI的理解说明', - `confidence` decimal(3,2) DEFAULT NULL COMMENT '置信度(0.00-1.00)', - `parse_error_message` text COMMENT '解析错误信息', - `input_tokens` int DEFAULT NULL COMMENT '输入Token数量', - `output_tokens` int DEFAULT NULL COMMENT '输出Token数量', - `parse_duration_ms` int DEFAULT NULL COMMENT '解析耗时(毫秒)', - `function_name` varchar(255) DEFAULT NULL COMMENT '执行的函数名称', - `function_arguments` text COMMENT '函数参数(JSON)', - `execute_status` tinyint(1) DEFAULT NULL COMMENT '执行状态(0-待执行, 1-成功, -1-失败)', - `execute_error_message` text COMMENT '执行错误信息', - `ip_address` varchar(128) DEFAULT NULL COMMENT 'IP地址', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `idx_user_id` (`user_id`), - KEY `idx_create_time` (`create_time`), - KEY `idx_provider` (`ai_provider`), - KEY `idx_model` (`ai_model`), - KEY `idx_parse_status` (`parse_status`), - KEY `idx_execute_status` (`execute_status`) -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='AI 助手行为记录表(解析、执行、审计)'; diff --git a/src/main/java/com/youlai/boot/auth/controller/AuthController.java b/src/main/java/com/youlai/boot/auth/controller/AuthController.java index 3e640527..93a75a76 100644 --- a/src/main/java/com/youlai/boot/auth/controller/AuthController.java +++ b/src/main/java/com/youlai/boot/auth/controller/AuthController.java @@ -1,7 +1,5 @@ package com.youlai.boot.auth.controller; -import com.youlai.boot.auth.model.dto.WxMiniAppCodeLoginDTO; -import com.youlai.boot.auth.model.dto.WxMiniAppPhoneLoginDTO; import com.youlai.boot.auth.model.vo.CaptchaVO; import com.youlai.boot.auth.model.dto.LoginRequest; import com.youlai.boot.common.enums.LogModuleEnum; @@ -69,30 +67,6 @@ public class AuthController { return Result.success(); } - @Operation(summary = "微信授权登录(Web)") - @PostMapping("/login/wechat") - @Log(value = "微信登录", module = LogModuleEnum.LOGIN) - public Result loginByWechat( - @Parameter(description = "微信授权码", example = "code") @RequestParam String code - ) { - AuthenticationToken loginResult = authService.loginByWechat(code); - return Result.success(loginResult); - } - - @Operation(summary = "微信小程序登录(Code)") - @PostMapping("/wx/miniapp/code-login") - public Result loginByWxMiniAppCode(@RequestBody @Valid WxMiniAppCodeLoginDTO loginDto) { - AuthenticationToken token = authService.loginByWxMiniAppCode(loginDto); - return Result.success(token); - } - - @Operation(summary = "微信小程序登录(手机号)") - @PostMapping("/wx/miniapp/phone-login") - public Result loginByWxMiniAppPhone(@RequestBody @Valid WxMiniAppPhoneLoginDTO loginDto) { - AuthenticationToken token = authService.loginByWxMiniAppPhone(loginDto); - return Result.success(token); - } - @Operation(summary = "退出登录") @DeleteMapping("/logout") @Log(value = "退出登录", module = LogModuleEnum.LOGIN) diff --git a/src/main/java/com/youlai/boot/auth/model/dto/WxMiniAppCodeLoginDTO.java b/src/main/java/com/youlai/boot/auth/model/dto/WxMiniAppCodeLoginDTO.java deleted file mode 100644 index ff58242f..00000000 --- a/src/main/java/com/youlai/boot/auth/model/dto/WxMiniAppCodeLoginDTO.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.youlai.boot.auth.model.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotBlank; - -/** - *微信小程序Code登录请求参数 - */ -@Schema(description = "微信小程序Code登录请求参数") -@Data -public class WxMiniAppCodeLoginDTO { - - @Schema(description = "微信小程序登录时获取的code", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "code不能为空") - private String code; - -} - - diff --git a/src/main/java/com/youlai/boot/auth/model/dto/WxMiniAppPhoneLoginDTO.java b/src/main/java/com/youlai/boot/auth/model/dto/WxMiniAppPhoneLoginDTO.java deleted file mode 100644 index 8dafaa1e..00000000 --- a/src/main/java/com/youlai/boot/auth/model/dto/WxMiniAppPhoneLoginDTO.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.youlai.boot.auth.model.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotBlank; - -/** - * 微信小程序手机号登录请求参数 - */ -@Schema(description = "微信小程序手机号登录请求参数") -@Data -public class WxMiniAppPhoneLoginDTO { - - @Schema(description = "微信小程序登录时获取的code", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "code不能为空") - private String code; - - @Schema(description = "包括敏感数据在内的完整用户信息的加密数据") - private String encryptedData; - - @Schema(description = "加密算法的初始向量") - private String iv; - -} - - diff --git a/src/main/java/com/youlai/boot/auth/service/AuthService.java b/src/main/java/com/youlai/boot/auth/service/AuthService.java index 2b1cb2b4..8a7d47cb 100644 --- a/src/main/java/com/youlai/boot/auth/service/AuthService.java +++ b/src/main/java/com/youlai/boot/auth/service/AuthService.java @@ -1,9 +1,7 @@ package com.youlai.boot.auth.service; import com.youlai.boot.auth.model.vo.CaptchaVO; -import com.youlai.boot.auth.model.dto.WxMiniAppPhoneLoginDTO; import com.youlai.boot.security.model.AuthenticationToken; -import com.youlai.boot.auth.model.dto.WxMiniAppCodeLoginDTO; /** * 认证服务接口 @@ -42,30 +40,6 @@ public interface AuthService { */ AuthenticationToken refreshToken(String refreshToken); - /** - * 微信小程序登录 - * - * @param code 微信登录code - * @return 登录结果 - */ - AuthenticationToken loginByWechat(String code); - - /** - * 微信小程序Code登录 - * - * @param loginDto 登录参数 - * @return 访问令牌 - */ - AuthenticationToken loginByWxMiniAppCode(WxMiniAppCodeLoginDTO loginDto); - - /** - * 微信小程序手机号登录 - * - * @param loginDto 登录参数 - * @return 访问令牌 - */ - AuthenticationToken loginByWxMiniAppPhone(WxMiniAppPhoneLoginDTO loginDto); - /** * 发送短信验证码 * diff --git a/src/main/java/com/youlai/boot/auth/service/impl/AuthServiceImpl.java b/src/main/java/com/youlai/boot/auth/service/impl/AuthServiceImpl.java index 66d7cd1d..44ad2260 100644 --- a/src/main/java/com/youlai/boot/auth/service/impl/AuthServiceImpl.java +++ b/src/main/java/com/youlai/boot/auth/service/impl/AuthServiceImpl.java @@ -5,8 +5,6 @@ import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; -import com.youlai.boot.auth.model.dto.WxMiniAppCodeLoginDTO; -import com.youlai.boot.auth.model.dto.WxMiniAppPhoneLoginDTO; import com.youlai.boot.auth.model.vo.CaptchaVO; import com.youlai.boot.auth.service.AuthService; import com.youlai.boot.common.constant.RedisConstants; @@ -17,8 +15,6 @@ import com.youlai.boot.platform.sms.enums.SmsTypeEnum; import com.youlai.boot.platform.sms.service.SmsService; import com.youlai.boot.security.model.AuthenticationToken; import com.youlai.boot.security.model.SmsAuthenticationToken; -import com.youlai.boot.security.model.WxMiniAppCodeAuthenticationToken; -import com.youlai.boot.security.model.WxMiniAppPhoneAuthenticationToken; import com.youlai.boot.security.token.TokenManager; import com.youlai.boot.security.util.SecurityUtils; import lombok.RequiredArgsConstructor; @@ -84,27 +80,6 @@ public class AuthServiceImpl implements AuthService { return authenticationTokenResponse; } - /** - * 微信一键授权登录 - * - * @param code 微信登录code - * @return 访问令牌 - */ - @Override - public AuthenticationToken loginByWechat(String code) { - // 1. 创建用户微信认证的令牌(未认证) - WxMiniAppCodeAuthenticationToken authenticationToken = new WxMiniAppCodeAuthenticationToken(code); - - // 2. 执行认证(认证中) - Authentication authentication = authenticationManager.authenticate(authenticationToken); - - // 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证) - AuthenticationToken token = tokenManager.generateToken(authentication); - SecurityContextHolder.getContext().setAuthentication(authentication); - - return token; - } - /** * 发送登录短信验证码 * @@ -224,50 +199,4 @@ public class AuthServiceImpl implements AuthService { return tokenManager.refreshToken(refreshToken); } - /** - * 微信小程序Code登录 - * - * @param loginDto 登录参数 - * @return 访问令牌 - */ - @Override - public AuthenticationToken loginByWxMiniAppCode(WxMiniAppCodeLoginDTO loginDto) { - // 1. 创建微信小程序认证令牌(未认证) - WxMiniAppCodeAuthenticationToken authenticationToken = new WxMiniAppCodeAuthenticationToken(loginDto.getCode()); - - // 2. 执行认证(认证中) - Authentication authentication = authenticationManager.authenticate(authenticationToken); - - // 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证) - AuthenticationToken token = tokenManager.generateToken(authentication); - SecurityContextHolder.getContext().setAuthentication(authentication); - - return token; - } - - /** - * 微信小程序手机号登录 - * - * @param loginDto 登录参数 - * @return 访问令牌 - */ - @Override - public AuthenticationToken loginByWxMiniAppPhone(WxMiniAppPhoneLoginDTO loginDto) { - // 创建微信小程序手机号认证Token - WxMiniAppPhoneAuthenticationToken authenticationToken = new WxMiniAppPhoneAuthenticationToken( - loginDto.getCode(), - loginDto.getEncryptedData(), - loginDto.getIv() - ); - - // 执行认证 - Authentication authentication = authenticationManager.authenticate(authenticationToken); - - // 认证成功后生成JWT令牌,并存入Security上下文 - AuthenticationToken token = tokenManager.generateToken(authentication); - SecurityContextHolder.getContext().setAuthentication(authentication); - - return token; - } - } diff --git a/src/main/java/com/youlai/boot/config/SecurityConfig.java b/src/main/java/com/youlai/boot/config/SecurityConfig.java index bab3ccaa..fe3539ce 100644 --- a/src/main/java/com/youlai/boot/config/SecurityConfig.java +++ b/src/main/java/com/youlai/boot/config/SecurityConfig.java @@ -1,6 +1,5 @@ package com.youlai.boot.config; -import cn.binarywang.wx.miniapp.api.WxMaService; import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.util.ArrayUtil; import com.youlai.boot.config.property.SecurityProperties; @@ -10,8 +9,6 @@ import com.youlai.boot.security.filter.TokenAuthenticationFilter; import com.youlai.boot.security.handler.MyAccessDeniedHandler; import com.youlai.boot.security.handler.MyAuthenticationEntryPoint; import com.youlai.boot.security.provider.SmsAuthenticationProvider; -import com.youlai.boot.security.provider.WxMiniAppCodeAuthenticationProvider; -import com.youlai.boot.security.provider.WxMiniAppPhoneAuthenticationProvider; import com.youlai.boot.security.token.TokenManager; import com.youlai.boot.security.service.SysUserDetailsService; import com.youlai.boot.system.service.ConfigService; @@ -50,7 +47,6 @@ public class SecurityConfig { private final PasswordEncoder passwordEncoder; private final TokenManager tokenManager; - private final WxMaService wxMaService; private final UserService userService; private final SysUserDetailsService userDetailsService; @@ -124,22 +120,6 @@ public class SecurityConfig { return daoAuthenticationProvider; } - /** - * 微信小程序Code认证Provider - */ - @Bean - public WxMiniAppCodeAuthenticationProvider wxMiniAppCodeAuthenticationProvider() { - return new WxMiniAppCodeAuthenticationProvider(userService, wxMaService); - } - - /** - * 微信小程序手机号认证Provider - */ - @Bean - public WxMiniAppPhoneAuthenticationProvider wxMiniAppPhoneAuthenticationProvider() { - return new WxMiniAppPhoneAuthenticationProvider(userService, wxMaService); - } - /** * 短信验证码认证 Provider */ @@ -154,14 +134,10 @@ public class SecurityConfig { @Bean public AuthenticationManager authenticationManager( DaoAuthenticationProvider daoAuthenticationProvider, - WxMiniAppCodeAuthenticationProvider wxMiniAppCodeAuthenticationProvider, - WxMiniAppPhoneAuthenticationProvider wxMiniAppPhoneAuthenticationProvider, SmsAuthenticationProvider smsAuthenticationProvider ) { return new ProviderManager( daoAuthenticationProvider, - wxMiniAppCodeAuthenticationProvider, - wxMiniAppPhoneAuthenticationProvider, smsAuthenticationProvider ); } diff --git a/src/main/java/com/youlai/boot/config/WxMiniAppConfig.java b/src/main/java/com/youlai/boot/config/WxMiniAppConfig.java deleted file mode 100644 index bbce096c..00000000 --- a/src/main/java/com/youlai/boot/config/WxMiniAppConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.youlai.boot.config; - -import cn.binarywang.wx.miniapp.api.WxMaService; -import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; -import cn.binarywang.wx.miniapp.config.WxMaConfig; -import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * 配置微信 appId 和 appSecret - * - * @author wangtao - * @since 2024/11/26 17:28 - */ -@Setter -@ConfigurationProperties(prefix = "wx.miniapp") -@Configuration -public class WxMiniAppConfig { - - private String appId; - - private String appSecret; - - @Bean - public WxMaConfig wxMaConfig() { - WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl(); - config.setAppid(appId); - config.setSecret(appSecret); - return config; - } - - @Bean - public WxMaService wxMaService(WxMaConfig wxMaConfig) { - WxMaService service = new WxMaServiceImpl(); - service.setWxMaConfig(wxMaConfig); - return service; - } -} diff --git a/src/main/java/com/youlai/boot/platform/ai/config/SpringAiConfig.java b/src/main/java/com/youlai/boot/platform/ai/config/SpringAiConfig.java deleted file mode 100644 index 699f3140..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/config/SpringAiConfig.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.youlai.boot.platform.ai.config; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.chat.client.ChatClient; -import org.springframework.ai.openai.OpenAiChatModel; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import com.youlai.boot.platform.ai.tools.UserTools; - -/** - * Spring AI 配置类 - * - * 使用 Spring AI 自动配置,支持: - * - OpenAI - * - 通义千问(DashScope 兼容 OpenAI 协议) - * - DeepSeek(兼容 OpenAI 协议) - * - 其他兼容 OpenAI 协议的模型 - * - * 配置方式: - * spring.ai.openai.api-key: xxx - * spring.ai.openai.base-url: xxx - * spring.ai.openai.chat.options.model: xxx - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Slf4j -@Configuration -@ConditionalOnProperty(prefix = "spring.ai.openai.chat", name = "enabled", havingValue = "true", matchIfMissing = false) -public class SpringAiConfig { - - /** - * 创建 ChatClient(Spring AI 核心客户端) - *

- * OpenAiChatModel 由 Spring AI 自动配置创建 - * 根据 spring.ai.openai.* 配置自动初始化 - */ - @Bean - public ChatClient chatClient(OpenAiChatModel chatModel, UserTools userTools) { - log.info("✅ Spring AI ChatClient 初始化成功"); - log.info("📋 当前配置 - 模型: {}", chatModel.getDefaultOptions().getModel()); - // 将 UserTools 注册为默认工具,所有调用都可使用 - return ChatClient.builder(chatModel) - .defaultTools(userTools) - .build(); - } -} - diff --git a/src/main/java/com/youlai/boot/platform/ai/controller/AiAssistantController.java b/src/main/java/com/youlai/boot/platform/ai/controller/AiAssistantController.java deleted file mode 100644 index 792ae741..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/controller/AiAssistantController.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.youlai.boot.platform.ai.controller; - -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.youlai.boot.core.web.PageResult; -import com.youlai.boot.core.web.Result; -import com.youlai.boot.platform.ai.model.dto.AiExecuteRequestDTO; -import com.youlai.boot.platform.ai.model.dto.AiParseRequestDTO; -import com.youlai.boot.platform.ai.model.dto.AiParseResponseDTO; -import com.youlai.boot.platform.ai.model.query.AiAssistantQuery; -import com.youlai.boot.platform.ai.model.vo.AiAssistantRecordVO; -import com.youlai.boot.platform.ai.service.AiAssistantRecordService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.*; - -/** - * AI 助手控制器 - *

- * 负责 AI 命令的解析、执行与记录查询。 - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Tag(name = "13.AI 助手接口") -@RestController -@RequestMapping("/api/v1/ai/assistant") -@RequiredArgsConstructor -@Slf4j -public class AiAssistantController { - - private final AiAssistantRecordService aiAssistantRecordService; - - @Operation(summary = "解析自然语言命令") - @PostMapping("/parse") - public Result parseCommand( - @RequestBody AiParseRequestDTO request, - HttpServletRequest httpRequest - ) { - log.info("收到 AI 命令解析请求: {}", request.getCommand()); - try { - AiParseResponseDTO response = aiAssistantRecordService.parseCommand(request, httpRequest); - return Result.success(response); - } catch (Exception e) { - log.error("命令解析失败", e); - return Result.success(AiParseResponseDTO.builder() - .success(false) - .error(e.getMessage()) - .build()); - } - } - - @Operation(summary = "执行已解析的命令") - @PostMapping("/execute") - public Result executeCommand( - @RequestBody AiExecuteRequestDTO request, - HttpServletRequest httpRequest - ) { - log.info("收到 AI 命令执行请求: {}", request.getFunctionCall().getName()); - try { - Object result = aiAssistantRecordService.executeCommand(request, httpRequest); - return Result.success(result); - } catch (Exception e) { - log.error("命令执行失败", e); - return Result.failed(e.getMessage()); - } - } - - @Operation(summary = "获取 AI 命令记录分页列表") - @GetMapping("/records") - public PageResult getRecordPage(AiAssistantQuery queryParams) { - IPage page = aiAssistantRecordService.getRecordPage(queryParams); - return PageResult.success(page); - } - - // 记录类接口按需扩展,当前开放 parse/execute/records -} diff --git a/src/main/java/com/youlai/boot/platform/ai/mapper/AiAssistantRecordMapper.java b/src/main/java/com/youlai/boot/platform/ai/mapper/AiAssistantRecordMapper.java deleted file mode 100644 index ca6668c5..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/mapper/AiAssistantRecordMapper.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.youlai.boot.platform.ai.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.youlai.boot.platform.ai.model.entity.AiAssistantRecord; -import com.youlai.boot.platform.ai.model.query.AiAssistantQuery; -import com.youlai.boot.platform.ai.model.vo.AiAssistantRecordVO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface AiAssistantRecordMapper extends BaseMapper { - - /** - * AI 助手行为记录分页列表 - * - * @param page 分页参数 - * @param queryParams 查询参数 - * @return 分页结果 - */ - IPage getRecordPage(Page page, AiAssistantQuery queryParams); -} diff --git a/src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteRequestDTO.java b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteRequestDTO.java deleted file mode 100644 index c22b5dc7..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteRequestDTO.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.youlai.boot.platform.ai.model.dto; - -import lombok.Data; - -/** - * AI 命令执行请求 Dto - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Data -public class AiExecuteRequestDTO { - - /** - * 关联的解析日志ID - */ - private String parseLogId; - - /** - * 原始命令(用于审计) - */ - private String originalCommand; - - /** - * 要执行的函数调用 - */ - private AiFunctionCallDTO functionCall; - - /** - * 确认模式:auto=自动执行, manual=需要用户确认 - */ - private String confirmMode; - - /** - * 用户确认标志 - */ - private Boolean userConfirmed; - - /** - * 幂等性令牌(防止重复执行) - */ - private String idempotencyKey; - - /** - * 当前页面路由 - */ - private String currentRoute; -} diff --git a/src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteResponseDTO.java b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteResponseDTO.java deleted file mode 100644 index f90173a0..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteResponseDTO.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.youlai.boot.platform.ai.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * AI 命令执行响应 Dto - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AiExecuteResponseDTO { - - /** - * 是否执行成功 - */ - private Boolean success; - - /** - * 执行结果数据 - */ - private Object data; - - /** - * 执行结果说明 - */ - private String message; - - /** - * 影响的记录数 - */ - private Integer affectedRows; - - /** - * 错误信息 - */ - private String error; - - /** - * 记录ID(用于追踪) - */ - private Long recordId; - - /** - * 需要用户确认 - */ - private Boolean requiresConfirmation; - - /** - * 确认提示信息 - */ - private String confirmationPrompt; -} diff --git a/src/main/java/com/youlai/boot/platform/ai/model/dto/AiFunctionCallDTO.java b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiFunctionCallDTO.java deleted file mode 100644 index 5e1dc9b0..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/model/dto/AiFunctionCallDTO.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.youlai.boot.platform.ai.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import java.util.Map; - -/** - * AI 函数调用 Dto - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AiFunctionCallDTO { - - /** - * 函数名称 - */ - private String name; - - /** - * 函数描述 - */ - private String description; - - /** - * 参数对象 - */ - private Map arguments; -} - diff --git a/src/main/java/com/youlai/boot/platform/ai/model/dto/AiParseRequestDTO.java b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiParseRequestDTO.java deleted file mode 100644 index f41cea59..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/model/dto/AiParseRequestDTO.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.youlai.boot.platform.ai.model.dto; - -import lombok.Data; -import java.util.Map; - -/** - * AI 解析请求 Dto - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Data -public class AiParseRequestDTO { - - /** - * 用户输入的自然语言命令 - */ - private String command; - - /** - * 当前页面路由(用于上下文) - */ - private String currentRoute; - - /** - * 当前激活的组件名称 - */ - private String currentComponent; - - /** - * 额外上下文信息 - */ - private Map context; -} diff --git a/src/main/java/com/youlai/boot/platform/ai/model/dto/AiParseResponseDTO.java b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiParseResponseDTO.java deleted file mode 100644 index 783756ac..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/model/dto/AiParseResponseDTO.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.youlai.boot.platform.ai.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import java.util.List; - -/** - * AI 解析响应 Dto - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AiParseResponseDTO { - - /** - * 解析日志ID(用于关联执行记录) - */ - private Long parseLogId; - - /** - * 是否成功解析 - */ - private Boolean success; - - /** - * 解析后的函数调用列表 - */ - private List functionCalls; - - /** - * AI 的理解和说明 - */ - private String explanation; - - /** - * 置信度 (0-1) - */ - private Double confidence; - - /** - * 错误信息 - */ - private String error; - - /** - * 原始 LLM 响应(用于调试) - */ - private String rawResponse; -} diff --git a/src/main/java/com/youlai/boot/platform/ai/model/entity/AiAssistantRecord.java b/src/main/java/com/youlai/boot/platform/ai/model/entity/AiAssistantRecord.java deleted file mode 100644 index 6fa37b12..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/model/entity/AiAssistantRecord.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.youlai.boot.platform.ai.model.entity; - -import com.baomidou.mybatisplus.annotation.TableName; -import com.youlai.boot.common.base.BaseEntity; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.math.BigDecimal; - -/** - * AI 助手行为记录实体 - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@TableName("ai_assistant_record") -public class AiAssistantRecord extends BaseEntity { - - /** 用户ID */ - private Long userId; - - /** 用户名 */ - private String username; - - /** 原始命令 */ - private String originalCommand; - - // ==================== 解析相关字段 ==================== - - /** AI 供应商(qwen/openai/deepseek等) */ - private String aiProvider; - - /** AI 模型(qwen-plus/qwen-max/gpt-4-turbo等) */ - private String aiModel; - - /** 解析状态(0-失败, 1-成功) */ - private Integer parseStatus; - - /** 解析出的函数调用列表(JSON) */ - private String functionCalls; - - /** AI 的理解说明 */ - private String explanation; - - /** 置信度(0.00-1.00) */ - private BigDecimal confidence; - - /** 解析错误信息 */ - private String parseErrorMessage; - - /** 输入 Token 数量 */ - private Integer inputTokens; - - /** 输出 Token 数量 */ - private Integer outputTokens; - - /** 解析耗时(毫秒) */ - private Integer parseDurationMs; - - // ==================== 执行相关字段 ==================== - - /** 执行的函数名称 */ - private String functionName; - - /** 函数参数(JSON) */ - private String functionArguments; - - /** 执行状态(0-待执行, 1-成功, -1-失败) */ - private Integer executeStatus; - - /** 执行错误信息 */ - private String executeErrorMessage; - - // ==================== 通用字段 ==================== - - /** IP 地址 */ - private String ipAddress; -} diff --git a/src/main/java/com/youlai/boot/platform/ai/model/query/AiAssistantPageQuery.java b/src/main/java/com/youlai/boot/platform/ai/model/query/AiAssistantPageQuery.java deleted file mode 100644 index 4fc4083a..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/model/query/AiAssistantPageQuery.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.youlai.boot.platform.ai.model.query; - -import com.youlai.boot.common.base.BaseQuery; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -/** - * AI 助手行为记录分页查询对象 - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Schema(description = "AI 助手行为记录分页查询对象") -@Getter -@Setter -public class AiAssistantPageQuery extends BaseQuery { - - @Schema(description = "关键字(原始命令/函数名称/用户名)") - private String keywords; - - @Schema(description = "执行状态(0-待执行, 1-成功, -1-失败)") - private Integer executeStatus; - - @Schema(description = "用户ID") - private Long userId; - - @Schema(description = "解析状态(0-失败, 1-成功)") - private Integer parseStatus; - - @Schema(description = "创建时间范围") - private List createTime; - - @Schema(description = "函数名称") - private String functionName; - - @Schema(description = "AI供应商") - private String aiProvider; - - @Schema(description = "AI模型") - private String aiModel; -} diff --git a/src/main/java/com/youlai/boot/platform/ai/model/query/AiAssistantQuery.java b/src/main/java/com/youlai/boot/platform/ai/model/query/AiAssistantQuery.java deleted file mode 100644 index 03966ea7..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/model/query/AiAssistantQuery.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.youlai.boot.platform.ai.model.query; - -import com.youlai.boot.common.base.BaseQuery; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -/** - * AI 助手行为记录查询对象 - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Schema(description = "AI 助手行为记录查询对象") -@Getter -@Setter -public class AiAssistantQuery extends BaseQuery { - - @Schema(description = "关键字(原始命令/函数名称/用户名)") - private String keywords; - - @Schema(description = "执行状态(0-待执行, 1-成功, -1-失败)") - private Integer executeStatus; - - @Schema(description = "用户ID") - private Long userId; - - @Schema(description = "解析状态(0-失败, 1-成功)") - private Integer parseStatus; - - @Schema(description = "创建时间范围") - private List createTime; - - @Schema(description = "函数名称") - private String functionName; - - @Schema(description = "AI供应商") - private String aiProvider; - - @Schema(description = "AI模型") - private String aiModel; -} diff --git a/src/main/java/com/youlai/boot/platform/ai/model/vo/AiAssistantRecordVO.java b/src/main/java/com/youlai/boot/platform/ai/model/vo/AiAssistantRecordVO.java deleted file mode 100644 index 95249fc9..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/model/vo/AiAssistantRecordVO.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.youlai.boot.platform.ai.model.vo; - -import com.fasterxml.jackson.annotation.JsonFormat; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * AI 助手行为记录Vo - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Data -@Schema(description = "AI 助手行为记录Vo") -public class AiAssistantRecordVO implements Serializable { - - @Schema(description = "主键ID") - private String id; - - @Schema(description = "用户ID") - private Long userId; - - @Schema(description = "用户名") - private String username; - - @Schema(description = "原始命令") - private String originalCommand; - - // ==================== 解析相关字段 ==================== - - @Schema(description = "AI供应商") - private String aiProvider; - - @Schema(description = "AI模型") - private String aiModel; - - @Schema(description = "解析状态(0-失败, 1-成功)") - private Integer parseStatus; - - @Schema(description = "解析出的函数调用列表(JSON)") - private String functionCalls; - - @Schema(description = "AI的理解说明") - private String explanation; - - @Schema(description = "置信度") - private BigDecimal confidence; - - @Schema(description = "解析错误信息") - private String parseErrorMessage; - - @Schema(description = "输入Token数量") - private Integer inputTokens; - - @Schema(description = "输出Token数量") - private Integer outputTokens; - - @Schema(description = "解析耗时(毫秒)") - private Integer parseDurationMs; - - // ==================== 执行相关字段 ==================== - - @Schema(description = "执行的函数名称") - private String functionName; - - @Schema(description = "函数参数(JSON)") - private String functionArguments; - - @Schema(description = "执行状态(0-待执行, 1-成功, -1-失败)") - private Integer executeStatus; - - @Schema(description = "执行错误信息") - private String executeErrorMessage; - - // ==================== 通用字段 ==================== - - @Schema(description = "IP地址") - private String ipAddress; - - @Schema(description = "创建时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; - - @Schema(description = "更新时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; -} diff --git a/src/main/java/com/youlai/boot/platform/ai/service/AiAssistantRecordService.java b/src/main/java/com/youlai/boot/platform/ai/service/AiAssistantRecordService.java deleted file mode 100644 index 38340bdb..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/service/AiAssistantRecordService.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.youlai.boot.platform.ai.service; - -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.service.IService; -import com.youlai.boot.platform.ai.model.dto.AiExecuteRequestDTO; -import com.youlai.boot.platform.ai.model.dto.AiParseRequestDTO; -import com.youlai.boot.platform.ai.model.dto.AiParseResponseDTO; -import com.youlai.boot.platform.ai.model.entity.AiAssistantRecord; -import com.youlai.boot.platform.ai.model.query.AiAssistantQuery; -import com.youlai.boot.platform.ai.model.vo.AiAssistantRecordVO; -import jakarta.servlet.http.HttpServletRequest; - -import java.util.List; - -/** - * AI 助手行为记录服务接口 - * - * 负责 AI 助手指令的解析/执行审计记录的分页查询、删除与回滚。 - * - * @author Ray.Hao - * @since 3.0.0 - */ -public interface AiAssistantRecordService extends IService { - - /** - * 解析自然语言命令。 - * - * @param request 解析请求参数 - * @param httpRequest HTTP 请求(用于获取 IP 等上下文) - * @return 解析结果(包含 functionCalls 等信息) - */ - AiParseResponseDTO parseCommand(AiParseRequestDTO request, HttpServletRequest httpRequest); - - /** - * 执行已解析的命令。 - * - * @param request 执行请求参数 - * @param httpRequest HTTP 请求(用于获取 IP 等上下文) - * @return 执行结果 - * @throws Exception 执行异常 - */ - Object executeCommand(AiExecuteRequestDTO request, HttpServletRequest httpRequest) throws Exception; - - /** - * 获取 AI 助手行为记录分页列表 - * - * @param queryParams 查询参数 - * @return 分页列表 - */ - IPage getRecordPage(AiAssistantQuery queryParams); - -} diff --git a/src/main/java/com/youlai/boot/platform/ai/service/impl/AiAssistantRecordServiceImpl.java b/src/main/java/com/youlai/boot/platform/ai/service/impl/AiAssistantRecordServiceImpl.java deleted file mode 100644 index 7791a323..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/service/impl/AiAssistantRecordServiceImpl.java +++ /dev/null @@ -1,302 +0,0 @@ -package com.youlai.boot.platform.ai.service.impl; - -import cn.hutool.core.lang.TypeReference; -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.servlet.JakartaServletUtil; -import cn.hutool.json.JSONArray; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.youlai.boot.platform.ai.mapper.AiAssistantRecordMapper; -import com.youlai.boot.platform.ai.model.dto.AiExecuteRequestDTO; -import com.youlai.boot.platform.ai.model.dto.AiFunctionCallDTO; -import com.youlai.boot.platform.ai.model.dto.AiParseRequestDTO; -import com.youlai.boot.platform.ai.model.dto.AiParseResponseDTO; -import com.youlai.boot.platform.ai.model.entity.AiAssistantRecord; -import com.youlai.boot.platform.ai.model.query.AiAssistantQuery; -import com.youlai.boot.platform.ai.model.vo.AiAssistantRecordVO; -import com.youlai.boot.platform.ai.service.AiAssistantRecordService; -import com.youlai.boot.platform.ai.tools.UserTools; -import com.youlai.boot.security.util.SecurityUtils; -import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.chat.client.ChatClient; -import org.springframework.ai.chat.model.ChatResponse; -import org.springframework.stereotype.Service; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** - * AI 助手行为记录服务实现类 - * - * @author Ray.Hao - * @since 3.0.0 - */ -@Service -@Slf4j -@RequiredArgsConstructor -public class AiAssistantRecordServiceImpl - extends ServiceImpl - implements AiAssistantRecordService { - - private static final String SYSTEM_PROMPT = """ - 你是一个智能的企业操作助手,需要将用户的自然语言命令解析成标准的函数调用。 - 请返回严格的 JSON 格式,包含字段: - - success: boolean - - explanation: string - - confidence: number (0-1) - - error: string - - provider: string - - model: string - - functionCalls: 数组,每个元素包含 name、description、arguments(对象) - 当无法识别命令时,success=false,并给出 error。 - """; - - private final UserTools userTools; - private final ChatClient chatClient; - - @Override - public AiParseResponseDTO parseCommand(AiParseRequestDTO request, HttpServletRequest httpRequest) { - long startTime = System.currentTimeMillis(); - String command = Optional.ofNullable(request.getCommand()).orElse("").trim(); - - if (StrUtil.isBlank(command)) { - return AiParseResponseDTO.builder() - .success(false) - .error("命令不能为空") - .functionCalls(Collections.emptyList()) - .build(); - } - - Long userId = SecurityUtils.getUserId(); - String username = SecurityUtils.getUsername(); - String ipAddress = JakartaServletUtil.getClientIP(httpRequest); - - AiAssistantRecord commandRecord = new AiAssistantRecord(); - commandRecord.setUserId(userId); - commandRecord.setUsername(username); - commandRecord.setOriginalCommand(command); - commandRecord.setIpAddress(ipAddress); - commandRecord.setAiProvider("spring-ai"); - commandRecord.setAiModel("auto"); - - String systemPrompt = buildSystemPrompt(); - String userPrompt = buildUserPrompt(request); - - try { - log.info("📤 发送命令至 AI 模型: {}", command); - ChatResponse chatResponse = chatClient.prompt() - .system(systemPrompt) - .user(userPrompt) - .call().chatResponse(); - - String rawContent = Optional.ofNullable(chatResponse.getResult()) - .map(result -> result.getOutput().getText()) - .orElse(""); - - ParseResult parseResult = parseAiResponse(rawContent); - - commandRecord.setAiProvider(StrUtil.emptyToDefault(parseResult.provider(), "spring-ai")); - commandRecord.setAiModel(StrUtil.emptyToDefault(parseResult.model(), "auto")); - commandRecord.setParseStatus(parseResult.success() ? 1 : 0); - commandRecord.setExplanation(parseResult.explanation()); - commandRecord.setFunctionCalls(JSONUtil.toJsonStr(parseResult.functionCalls())); - commandRecord.setConfidence(parseResult.confidence() != null ? BigDecimal.valueOf(parseResult.confidence()) : null); - commandRecord.setParseErrorMessage(parseResult.success() ? null : StrUtil.emptyToDefault(parseResult.error(), "解析失败")); - long duration = System.currentTimeMillis() - startTime; - commandRecord.setParseDurationMs((int) duration); - - this.save(commandRecord); - - return AiParseResponseDTO.builder() - .parseLogId(commandRecord.getId()) - .success(parseResult.success()) - .functionCalls(parseResult.functionCalls()) - .explanation(parseResult.explanation()) - .confidence(parseResult.confidence()) - .error(parseResult.error()) - .rawResponse(rawContent) - .build(); - } catch (Exception e) { - long duration = System.currentTimeMillis() - startTime; - commandRecord.setParseStatus(0); - commandRecord.setFunctionCalls(JSONUtil.toJsonStr(Collections.emptyList())); - commandRecord.setParseErrorMessage(e.getMessage()); - commandRecord.setParseDurationMs((int) duration); - this.save(commandRecord); - - log.error("❌ 解析命令失败: {}", e.getMessage(), e); - throw new RuntimeException("解析命令失败: " + e.getMessage(), e); - } - } - - private String buildSystemPrompt() { - return SYSTEM_PROMPT; - } - - private String buildUserPrompt(AiParseRequestDTO request) { - JSONObject payload = JSONUtil.createObj() - .set("command", request.getCommand()) - .set("currentRoute", request.getCurrentRoute()) - .set("currentComponent", request.getCurrentComponent()) - .set("context", Optional.ofNullable(request.getContext()).orElse(Collections.emptyMap())) - .set("availableFunctions", availableFunctions()); - - return StrUtil.format(""" - 请根据以下上下文识别用户意图,并输出符合系统提示要求的 JSON: - {} - """, JSONUtil.toJsonPrettyStr(payload)); - } - - private List> availableFunctions() { - return List.of( - Map.of( - "name", "updateUserNickname", - "description", "根据用户名更新用户昵称", - "requiredParameters", List.of("username", "nickname") - ) - ); - } - - private ParseResult parseAiResponse(String rawContent) { - if (StrUtil.isBlank(rawContent)) { - throw new IllegalStateException("AI 返回内容为空"); - } - - try { - JSONObject jsonObject = JSONUtil.parseObj(rawContent); - boolean success = jsonObject.getBool("success", false); - String explanation = jsonObject.getStr("explanation"); - Double confidence = jsonObject.containsKey("confidence") ? jsonObject.getDouble("confidence") : null; - String error = jsonObject.getStr("error"); - String provider = jsonObject.getStr("provider"); - String model = jsonObject.getStr("model"); - - List functionCalls = toFunctionCallList(jsonObject.getJSONArray("functionCalls")); - - return new ParseResult(success, explanation, confidence, error, provider, model, functionCalls); - } catch (Exception ex) { - throw new IllegalStateException("无法解析 AI 响应: " + ex.getMessage(), ex); - } - } - - private List toFunctionCallList(JSONArray array) { - if (array == null || array.isEmpty()) { - return Collections.emptyList(); - } - - List result = new ArrayList<>(); - for (Object element : array) { - JSONObject functionJson = JSONUtil.parseObj(element); - Map arguments = Optional.ofNullable(functionJson.getJSONObject("arguments")) - .map(obj -> obj.toBean(new TypeReference>() { - })) - .orElse(Collections.emptyMap()); - - result.add(AiFunctionCallDTO.builder() - .name(functionJson.getStr("name")) - .description(functionJson.getStr("description")) - .arguments(arguments) - .build()); - } - return result; - } - - private record ParseResult( - boolean success, - String explanation, - Double confidence, - String error, - String provider, - String model, - List functionCalls - ) { - } - - @Override - public Object executeCommand(AiExecuteRequestDTO request, HttpServletRequest httpRequest) throws Exception { - Long userId = SecurityUtils.getUserId(); - String username = SecurityUtils.getUsername(); - String ipAddress = JakartaServletUtil.getClientIP(httpRequest); - - AiFunctionCallDTO functionCall = request.getFunctionCall(); - - AiAssistantRecord commandRecord; - if (StrUtil.isNotBlank(request.getParseLogId())) { - commandRecord = this.getById(request.getParseLogId()); - if (commandRecord == null) { - throw new IllegalStateException("未找到对应的解析记录,ID: " + request.getParseLogId()); - } - } else { - commandRecord = new AiAssistantRecord(); - commandRecord.setUserId(userId); - commandRecord.setUsername(username); - commandRecord.setOriginalCommand(request.getOriginalCommand()); - commandRecord.setIpAddress(ipAddress); - this.save(commandRecord); - } - - commandRecord.setFunctionName(functionCall.getName()); - commandRecord.setFunctionArguments(JSONUtil.toJsonStr(functionCall.getArguments())); - commandRecord.setExecuteStatus(0); - - try { - Object result = executeFunctionCall(functionCall); - commandRecord.setExecuteStatus(1); - commandRecord.setExecuteErrorMessage(null); - this.updateById(commandRecord); - log.info("✅ 命令执行成功,审计记录ID: {}", commandRecord.getId()); - return result; - } catch (Exception e) { - commandRecord.setExecuteStatus(-1); - commandRecord.setExecuteErrorMessage(e.getMessage()); - this.updateById(commandRecord); - log.error("❌ 命令执行失败,审计记录ID: {}", commandRecord.getId(), e); - throw e; - } - } - - private Object executeFunctionCall(AiFunctionCallDTO functionCall) { - String functionName = functionCall.getName(); - Map arguments = functionCall.getArguments(); - - log.info("🎯 执行函数: {}, 参数: {}", functionName, arguments); - - switch (functionName) { - case "updateUserNickname": - return executeUpdateUserNickname(arguments); - default: - throw new UnsupportedOperationException("不支持的函数: " + functionName); - } - } - - private Object executeUpdateUserNickname(Map arguments) { - String username = (String) arguments.get("username"); - String nickname = (String) arguments.get("nickname"); - - log.info("🔧 [Tool] 更新用户昵称: username={}, nickname={}", username, nickname); - String resultMsg = userTools.updateUserNickname(username, nickname); - - boolean success = resultMsg != null && resultMsg.contains("成功"); - if (!success) { - throw new RuntimeException(resultMsg != null ? resultMsg : "更新用户昵称失败"); - } - - return Map.of("username", username, "nickname", nickname, "message", resultMsg); - } - - @Override - public IPage getRecordPage(AiAssistantQuery queryParams) { - Page page = new Page<>(queryParams.getPageNum(), queryParams.getPageSize()); - return this.baseMapper.getRecordPage(page, queryParams); - } - -} diff --git a/src/main/java/com/youlai/boot/platform/ai/tools/UserTools.java b/src/main/java/com/youlai/boot/platform/ai/tools/UserTools.java deleted file mode 100644 index 64d9e923..00000000 --- a/src/main/java/com/youlai/boot/platform/ai/tools/UserTools.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.youlai.boot.platform.ai.tools; - -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.youlai.boot.system.model.entity.User; -import com.youlai.boot.system.service.UserService; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; -import org.springframework.ai.tool.annotation.Tool; -import org.springframework.ai.tool.annotation.ToolParam; - -/** - * 基于 Spring AI Tool 的用户管理工具 - * - * 提供受控的 CRUD 能力,供 LLM 通过 Tool Calling 调用 - */ -@Component -@RequiredArgsConstructor -public class UserTools { - - private final UserService userService; - - @Tool(description = "根据关键字在用户列表中筛选用户") - public String queryUser( - @ToolParam(description = "搜索关键字,用于在列表中搜索筛选") String keywords - ) { - // 返回搜索关键字,前端会在用户列表页面进行筛选 - return "将在用户列表中搜索:" + keywords; - } - - @Tool(description = "根据用户名更新用户昵称") - public String updateUserNickname( - @ToolParam(description = "用户名") String username, - @ToolParam(description = "新的昵称") String nickname - ) { - - boolean ok = userService.update(new LambdaUpdateWrapper() - .eq(User::getUsername, username) - .set(User::getNickname, nickname) - ); - return ok ? "用户昵称更新成功" : "用户昵称更新失败"; - } -} - - - - - - - diff --git a/src/main/java/com/youlai/boot/security/model/WxMiniAppCodeAuthenticationToken.java b/src/main/java/com/youlai/boot/security/model/WxMiniAppCodeAuthenticationToken.java deleted file mode 100644 index 21b4d36f..00000000 --- a/src/main/java/com/youlai/boot/security/model/WxMiniAppCodeAuthenticationToken.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.youlai.boot.security.model; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; - -import java.io.Serial; -import java.util.Collection; - -/** - * 微信小程序Code认证Token - * - * @author 有来技术团队 - * @since 2.0.0 - */ -public class WxMiniAppCodeAuthenticationToken extends AbstractAuthenticationToken { - @Serial - private static final long serialVersionUID = 621L; - private final Object principal; - - /** - * 微信小程序Code认证Token (未认证) - * - * @param principal 微信code - */ - public WxMiniAppCodeAuthenticationToken(Object principal) { - // 没有授权信息时,设置为 null - super((Collection) null); - this.principal = principal; - // 默认未认证 - this.setAuthenticated(false); - } - - - /** - * 微信小程序Code认证Token (已认证) - * - * @param principal 微信用户信息 - * @param authorities 授权信息 - */ - public WxMiniAppCodeAuthenticationToken(Object principal, Collection authorities) { - super(authorities); - this.principal = principal; - // 认证通过 - super.setAuthenticated(true); - } - - - /** - * 认证通过 - * - * @param principal 微信用户信息 - * @param authorities 授权信息 - * @return 已认证的Token - */ - public static WxMiniAppCodeAuthenticationToken authenticated(Object principal, Collection authorities) { - return new WxMiniAppCodeAuthenticationToken(principal, authorities); - } - - @Override - public Object getCredentials() { - // 微信认证不需要密码 - return null; - } - - @Override - public Object getPrincipal() { - return this.principal; - } -} \ No newline at end of file diff --git a/src/main/java/com/youlai/boot/security/model/WxMiniAppPhoneAuthenticationToken.java b/src/main/java/com/youlai/boot/security/model/WxMiniAppPhoneAuthenticationToken.java deleted file mode 100644 index c690e8b6..00000000 --- a/src/main/java/com/youlai/boot/security/model/WxMiniAppPhoneAuthenticationToken.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.youlai.boot.security.model; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; - -import java.io.Serial; -import java.util.Collection; - -/** - * 微信小程序手机号认证Token - * - * @author 有来技术团队 - * @since 2.0.0 - */ -public class WxMiniAppPhoneAuthenticationToken extends AbstractAuthenticationToken { - @Serial - private static final long serialVersionUID = 622L; - - private final Object principal; // code - private String encryptedData; - private String iv; - - /** - * 微信小程序手机号认证Token (未认证) - * - * @param code 微信登录code - * @param encryptedData 加密数据 - * @param iv 初始向量 - */ - public WxMiniAppPhoneAuthenticationToken(String code, String encryptedData, String iv) { - super((Collection) null); - this.principal = code; - this.encryptedData = encryptedData; - this.iv = iv; - this.setAuthenticated(false); - } - - /** - * 微信小程序手机号认证Token (已认证) - * - * @param principal 用户信息 - * @param authorities 授权信息 - */ - public WxMiniAppPhoneAuthenticationToken(Object principal, Collection authorities) { - super(authorities); - this.principal = principal; - super.setAuthenticated(true); - } - - /** - * 认证通过 - * - * @param principal 用户信息 - * @param authorities 授权信息 - * @return 认证通过的Token - */ - public static WxMiniAppPhoneAuthenticationToken authenticated(Object principal, Collection authorities) { - return new WxMiniAppPhoneAuthenticationToken(principal, authorities); - } - - @Override - public Object getCredentials() { - // 微信小程序手机号认证不需要密码 - return null; - } - - @Override - public Object getPrincipal() { - return this.principal; - } - - /** - * 获取加密数据 - * - * @return 加密数据 - */ - public String getEncryptedData() { - return encryptedData; - } - - /** - * 获取初始向量 - * - * @return 初始向量 - */ - public String getIv() { - return iv; - } -} diff --git a/src/main/java/com/youlai/boot/security/provider/WxMiniAppCodeAuthenticationProvider.java b/src/main/java/com/youlai/boot/security/provider/WxMiniAppCodeAuthenticationProvider.java deleted file mode 100644 index 46fc17cf..00000000 --- a/src/main/java/com/youlai/boot/security/provider/WxMiniAppCodeAuthenticationProvider.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.youlai.boot.security.provider; - -import cn.binarywang.wx.miniapp.api.WxMaService; -import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import com.youlai.boot.security.model.SysUserDetails; -import com.youlai.boot.security.model.UserAuthInfo; -import com.youlai.boot.security.model.WxMiniAppCodeAuthenticationToken; -import com.youlai.boot.system.service.UserService; -import lombok.extern.slf4j.Slf4j; -import me.chanjar.weixin.common.error.WxErrorException; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.CredentialsExpiredException; -import org.springframework.security.authentication.DisabledException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.userdetails.UsernameNotFoundException; - -/** - * 微信小程序Code认证Provider - * - * @author 有来技术团队 - * @since 2.0.0 - */ -@Slf4j -public class WxMiniAppCodeAuthenticationProvider implements AuthenticationProvider { - - private final UserService userService; - private final WxMaService wxMaService; - - public WxMiniAppCodeAuthenticationProvider(UserService userService, WxMaService wxMaService) { - this.userService = userService; - this.wxMaService = wxMaService; - } - - /** - * 微信认证逻辑,参考 Spring Security 认证密码校验流程 - * - * @param authentication 认证对象 - * @return 认证后的 Authentication 对象 - * @throws AuthenticationException 认证异常 - * @see org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider#authenticate(Authentication) - */ - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - String code = (String) authentication.getPrincipal(); - - // 通过微信服务端验证 code 并获取用户会话信息 - WxMaJscode2SessionResult sessionInfo; - try { - sessionInfo = wxMaService.getUserService().getSessionInfo(code); - } catch (WxErrorException e) { - throw new CredentialsExpiredException("微信登录 code 无效或已失效,请重新获取"); - } - - String openId = sessionInfo.getOpenid(); - if (StrUtil.isBlank(openId)) { - throw new UsernameNotFoundException("未能获取到微信 OpenID,请稍后重试"); - } - - // 根据微信 OpenID 查询用户信息 - UserAuthInfo userAuthInfo = userService.getAuthInfoByOpenId(openId); - - if (userAuthInfo == null) { - // 用户不存在则注册 - userService.registerOrBindWechatUser(openId); - - // 再次查询用户信息,确保用户注册成功 - userAuthInfo = userService.getAuthInfoByOpenId(openId); - if (userAuthInfo == null) { - throw new UsernameNotFoundException("用户注册失败,请稍后重试"); - } - } - - // 检查用户状态是否有效 - if (ObjectUtil.notEqual(userAuthInfo.getStatus(), 1)) { - throw new DisabledException("用户已被禁用"); - } - - // 构建认证后的用户详情信息 - SysUserDetails userDetails = new SysUserDetails(userAuthInfo); - - // 创建已认证的Token - return WxMiniAppCodeAuthenticationToken.authenticated( - userDetails, - userDetails.getAuthorities() - ); - } - - @Override - public boolean supports(Class authentication) { - return WxMiniAppCodeAuthenticationToken.class.isAssignableFrom(authentication); - } -} \ No newline at end of file diff --git a/src/main/java/com/youlai/boot/security/provider/WxMiniAppPhoneAuthenticationProvider.java b/src/main/java/com/youlai/boot/security/provider/WxMiniAppPhoneAuthenticationProvider.java deleted file mode 100644 index f7077764..00000000 --- a/src/main/java/com/youlai/boot/security/provider/WxMiniAppPhoneAuthenticationProvider.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.youlai.boot.security.provider; - -import cn.binarywang.wx.miniapp.api.WxMaService; -import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; -import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import com.youlai.boot.security.model.SysUserDetails; -import com.youlai.boot.security.model.UserAuthInfo; -import com.youlai.boot.security.model.WxMiniAppPhoneAuthenticationToken; -import com.youlai.boot.system.service.UserService; -import lombok.extern.slf4j.Slf4j; -import me.chanjar.weixin.common.error.WxErrorException; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.CredentialsExpiredException; -import org.springframework.security.authentication.DisabledException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.userdetails.UsernameNotFoundException; - -/** - * 微信小程序手机号认证Provider - * - * @author 有来技术团队 - * @since 2.0.0 - */ -@Slf4j -public class WxMiniAppPhoneAuthenticationProvider implements AuthenticationProvider { - - private final UserService userService; - private final WxMaService wxMaService; - - public WxMiniAppPhoneAuthenticationProvider(UserService userService, WxMaService wxMaService) { - this.userService = userService; - this.wxMaService = wxMaService; - } - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - WxMiniAppPhoneAuthenticationToken authenticationToken = (WxMiniAppPhoneAuthenticationToken) authentication; - String code = (String) authenticationToken.getPrincipal(); - String encryptedData = authenticationToken.getEncryptedData(); - String iv = authenticationToken.getIv(); - - // 1. 通过code获取session_key - WxMaJscode2SessionResult sessionInfo; - try { - sessionInfo = wxMaService.getUserService().getSessionInfo(code); - } catch (WxErrorException e) { - log.error("获取微信session_key失败", e); - throw new CredentialsExpiredException("微信登录code无效或已过期"); - } - - String sessionKey = sessionInfo.getSessionKey(); - String openId = sessionInfo.getOpenid(); - - if (StrUtil.isBlank(sessionKey) || StrUtil.isBlank(openId)) { - throw new CredentialsExpiredException("获取微信会话信息失败"); - } - - // 2. 解密手机号信息 - WxMaPhoneNumberInfo phoneNumberInfo; - try { - if (StrUtil.isNotBlank(encryptedData) && StrUtil.isNotBlank(iv)) { - phoneNumberInfo = wxMaService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv); - } else { - throw new IllegalArgumentException("缺少手机号加密数据"); - } - } catch (Exception e) { - log.error("解密微信手机号失败", e); - throw new CredentialsExpiredException("解密手机号信息失败"); - } - - if (phoneNumberInfo == null || StrUtil.isBlank(phoneNumberInfo.getPhoneNumber())) { - throw new CredentialsExpiredException("获取手机号失败"); - } - - String phoneNumber = phoneNumberInfo.getPhoneNumber(); - - // 3. 根据手机号查询用户,不存在则创建新用户 - UserAuthInfo userAuthInfo = userService.getAuthInfoByMobile(phoneNumber); - - if (userAuthInfo == null) { - // 用户不存在,注册新用户 - boolean registered = userService.registerUserByMobileAndOpenId(phoneNumber, openId); - if (!registered) { - throw new UsernameNotFoundException("用户注册失败"); - } - // 重新获取用户信息 - userAuthInfo = userService.getAuthInfoByMobile(phoneNumber); - } else { - // 用户存在,绑定openId(如果未绑定) - userService.bindUserOpenId(userAuthInfo.getUserId(), openId); - } - - // 4. 检查用户状态 - if (ObjectUtil.notEqual(userAuthInfo.getStatus(), 1)) { - throw new DisabledException("用户已被禁用"); - } - - // 5. 构建认证后的用户详情 - SysUserDetails userDetails = new SysUserDetails(userAuthInfo); - - // 6. 创建已认证的Token - return WxMiniAppPhoneAuthenticationToken.authenticated( - userDetails, - userDetails.getAuthorities() - ); - } - - @Override - public boolean supports(Class authentication) { - return WxMiniAppPhoneAuthenticationToken.class.isAssignableFrom(authentication); - } -} \ No newline at end of file diff --git a/src/main/java/com/youlai/boot/system/mapper/UserMapper.java b/src/main/java/com/youlai/boot/system/mapper/UserMapper.java index df098363..fc13fa64 100644 --- a/src/main/java/com/youlai/boot/system/mapper/UserMapper.java +++ b/src/main/java/com/youlai/boot/system/mapper/UserMapper.java @@ -53,18 +53,6 @@ public interface UserMapper extends BaseMapper { return getAuthInfoByUsername(username); } - /** - * 根据微信openid获取用户认证信息 - * - * @param openid 微信openid - * @return 认证信息 - */ - UserAuthInfo getAuthInfoByOpenId(String openid); - - default UserAuthInfo getAuthCredentialsByOpenId(String openid) { - return getAuthInfoByOpenId(openid); - } - /** * 根据手机号获取用户认证信息 * diff --git a/src/main/java/com/youlai/boot/system/model/entity/User.java b/src/main/java/com/youlai/boot/system/model/entity/User.java index 3de90790..62535380 100644 --- a/src/main/java/com/youlai/boot/system/model/entity/User.java +++ b/src/main/java/com/youlai/boot/system/model/entity/User.java @@ -1,8 +1,6 @@ package com.youlai.boot.system.model.entity; -import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; -import com.fasterxml.jackson.annotation.JsonInclude; import com.youlai.boot.common.base.BaseEntity; import lombok.Getter; import lombok.Setter; @@ -74,9 +72,4 @@ public class User extends BaseEntity { * 是否删除(0-否 1-是) */ private Integer isDeleted; - - /** - * 微信 OpenID - */ - private String openid; } \ No newline at end of file diff --git a/src/main/java/com/youlai/boot/system/model/form/UserForm.java b/src/main/java/com/youlai/boot/system/model/form/UserForm.java index 8d4f8423..8b48259a 100644 --- a/src/main/java/com/youlai/boot/system/model/form/UserForm.java +++ b/src/main/java/com/youlai/boot/system/model/form/UserForm.java @@ -56,7 +56,4 @@ public class UserForm { @NotEmpty(message = "用户角色不能为空") private List roleIds; - @Schema(description="微信openId") - private String openId; - } diff --git a/src/main/java/com/youlai/boot/system/service/UserService.java b/src/main/java/com/youlai/boot/system/service/UserService.java index 93a2b683..0b0fac89 100644 --- a/src/main/java/com/youlai/boot/system/service/UserService.java +++ b/src/main/java/com/youlai/boot/system/service/UserService.java @@ -181,26 +181,6 @@ public interface UserService extends IService { */ List> listUserOptions(); - /** - * 根据 openid 获取用户认证信息 - * - * @param openId 用户名 - * @return {@link UserAuthInfo} - */ - - UserAuthInfo getAuthInfoByOpenId(String openId); - - default UserAuthInfo getAuthCredentialsByOpenId(String openId) { - return getAuthInfoByOpenId(openId); - } - - /** - * 根据微信 OpenID 注册或绑定用户 - * - * @param openId 微信 OpenID - */ - boolean registerOrBindWechatUser(String openId); - /** * 根据手机号获取用户认证信息 * @@ -213,22 +193,4 @@ public interface UserService extends IService { return getAuthInfoByMobile(mobile); } - /** - * 根据手机号和OpenID注册用户 - * - * @param mobile 手机号 - * @param openId 微信OpenID - * @return 是否成功 - */ - boolean registerUserByMobileAndOpenId(String mobile, String openId); - - /** - * 绑定用户微信OpenID - * - * @param userId 用户ID - * @param openId 微信OpenID - * @return 是否成功 - */ - boolean bindUserOpenId(Long userId, String openId); - } diff --git a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java index 108de0fa..275121ca 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java @@ -222,28 +222,6 @@ public class UserServiceImpl extends ServiceImpl implements Us return userAuthInfo; } - - /** - * 根据OpenID获取用户认证信息 - * - * @param openId 微信OpenID - * @return 用户认证信息 - */ - @Override - public UserAuthInfo getAuthInfoByOpenId(String openId) { - if (StrUtil.isBlank(openId)) { - return null; - } - UserAuthInfo userAuthInfo = this.baseMapper.getAuthInfoByOpenId(openId); - if (userAuthInfo != null) { - Set roles = userAuthInfo.getRoles(); - // 获取最大范围的数据权限 - Integer dataScope = roleService.getMaximumDataScope(roles); - userAuthInfo.setDataScope(dataScope); - } - return userAuthInfo; - } - /** * 根据手机号获取用户认证信息 * @@ -265,137 +243,6 @@ public class UserServiceImpl extends ServiceImpl implements Us return userAuthInfo; } - /** - * 注册或绑定微信用户 - * - * @param openId 微信OpenID - * @return 是否成功 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public boolean registerOrBindWechatUser(String openId) { - if (StrUtil.isBlank(openId)) { - return false; - } - - // 查询是否已存在该openId的用户 - User existUser = this.getOne( - new LambdaQueryWrapper() - .eq(User::getOpenid, openId) - ); - - if (existUser != null) { - // 用户已存在,不需要注册 - return true; - } - - // 创建新用户 - User newUser = new User(); - newUser.setNickname("微信用户"); // 默认昵称 - newUser.setUsername(openId); // TODO 后续替换为手机号 - newUser.setOpenid(openId); - newUser.setGender(0); // 保密 - newUser.setUpdateBy(SecurityUtils.getUserId()); - newUser.setPassword(SystemConstants.DEFAULT_PASSWORD); - newUser.setCreateTime(LocalDateTime.now()); - newUser.setUpdateTime(LocalDateTime.now()); - this.save(newUser); - // 为了默认系统管理员角色,这里按需调整,实际情况绑定已存在的系统用户,另一种情况是给默认游客角色,然后由系统管理员设置用户的角色 - UserRole userRole = new UserRole(); - userRole.setUserId(newUser.getId()); - userRole.setRoleId(1L); // TODO 系统管理员 - userRoleService.save(userRole); - return true; - } - - /** - * 根据手机号和OpenID注册用户 - * - * @param mobile 手机号 - * @param openId 微信OpenID - * @return 是否成功 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public boolean registerUserByMobileAndOpenId(String mobile, String openId) { - if (StrUtil.isBlank(mobile) || StrUtil.isBlank(openId)) { - return false; - } - - // 先查询是否已存在手机号对应的用户 - User existingUser = this.getOne( - new LambdaQueryWrapper() - .eq(User::getMobile, mobile) - ); - - if (existingUser != null) { - // 如果存在用户但没绑定openId,则绑定openId - if (StrUtil.isBlank(existingUser.getOpenid())) { - return bindUserOpenId(existingUser.getId(), openId); - } - // 如果已经绑定了其他openId,则判断是否需要更新 - else if (!openId.equals(existingUser.getOpenid())) { - return bindUserOpenId(existingUser.getId(), openId); - } - // 如果已经绑定了相同的openId,则不需要任何操作 - return true; - } - - // 不存在用户,创建新用户 - User newUser = new User(); - newUser.setMobile(mobile); - newUser.setOpenid(openId); - newUser.setUsername(mobile); // 使用手机号作为用户名 - newUser.setNickname("微信用户_" + mobile.substring(mobile.length() - 4)); // 使用手机号后4位作为昵称 - newUser.setPassword(SystemConstants.DEFAULT_PASSWORD); // 使用加密的openId作为初始密码 - newUser.setGender(0); // 保密 - newUser.setCreateTime(LocalDateTime.now()); - newUser.setUpdateTime(LocalDateTime.now()); - this.save(newUser); - // 为了默认系统管理员角色,这里按需调整,实际情况绑定已存在的系统用户,另一种情况是给默认游客角色,然后由系统管理员设置用户的角色 - UserRole userRole = new UserRole(); - userRole.setUserId(newUser.getId()); - userRole.setRoleId(1L); // TODO 系统管理员 - userRoleService.save(userRole); - return true; - } - - /** - * 绑定用户微信OpenID - * - * @param userId 用户ID - * @param openId 微信OpenID - * @return 是否成功 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public boolean bindUserOpenId(Long userId, String openId) { - if (userId == null || StrUtil.isBlank(openId)) { - return false; - } - - // 检查是否已有其他用户绑定了此openId - User existingUser = this.getOne( - new LambdaQueryWrapper() - .eq(User::getOpenid, openId) - .ne(User::getId, userId) - ); - - if (existingUser != null) { - log.warn("OpenID {} 已被用户 {} 绑定,无法为用户 {} 绑定", openId, existingUser.getId(), userId); - return false; - } - - // 更新用户openId - boolean updated = this.update( - new LambdaUpdateWrapper() - .eq(User::getId, userId) - .set(User::getOpenid, openId) - .set(User::getUpdateTime, LocalDateTime.now()) - ); - return updated ; - } - /** * 获取导出用户列表 * diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 3dadab9d..dfbcb392 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -8,19 +8,6 @@ spring: url: jdbc:mysql://www.youlai.tech:3306/youlai_admin?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true username: youlai password: 123456 - - ai: - openai: - api-key: ${QWEN_API_KEY:sk-c2941d05bf2f411ca80424fcd1exxxxx} - base-url: https://dashscope.aliyuncs.com/compatible-mode - chat: - enabled: true - options: - model: qwen-plus - temperature: 0.7 - response-format: - type: json_object - data: redis: database: 11 @@ -85,19 +72,18 @@ mybatis-plus: security: session: type: jwt # 会话方式 [jwt|redis-token] - access-token-time-to-live: 7200 # 访问令牌 有效期(单位:秒),默认 2 小时,-1 表示永不过期 - refresh-token-time-to-live: 604800 # 刷新令牌有效期(单位:秒),默认 7 天,-1 表示永不过期 + access-token-time-to-live: 7200 # 访问令牌 有效期(单位:秒),默认 2 小时,-1 表示永不过期 + refresh-token-time-to-live: 604800 # 刷新令牌有效期(单位:秒),默认 7 天,-1 表示永不过期 jwt: secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789 # JWT密钥(HS256算法至少32字符) redis-token: allow-multi-login: true # 是否允许多设备登录 # 安全白名单路径,仅跳过 AuthorizationFilter 过滤器,还是会走 Spring Security 的其他过滤器(CSRF、CORS等) ignore-urls: - - /api/v1/auth/login/** # 登录接口(账号密码登录、手机验证码登录和微信登录) - - /api/v1/auth/captcha # 验证码获取接口 - - /api/v1/auth/refresh-token # 刷新令牌接口 - - //api/v1/auth/wx/miniapp/code-login # 微信小程序code登陆 - - /ws/** # WebSocket接口 + - /api/v1/auth/login/** # 登录接口(账号密码登录、手机验证码登录和微信登录) + - /api/v1/auth/captcha # 验证码获取接口 + - /api/v1/auth/refresh-token # 刷新令牌接口 + - /ws/** # WebSocket接口 # 非安全端点路径,完全绕过 Spring Security 的过滤器 unsecured-urls: - ${springdoc.swagger-ui.path} @@ -122,7 +108,7 @@ oss: # 存储桶名称 bucket-name: public # (可选)自定义域名,如果配置了域名,生成的文件URL是域名格式,未配置则URL则是IP格式 (eg: https://www.youlai.tech/storage) - custom-domain: + custom-domain: # 阿里云OSS对象存储服务 aliyun: # 服务Endpoint @@ -157,29 +143,28 @@ sms: # springdoc 配置文档: https://springdoc.org/properties.html springdoc: swagger-ui: - path: /swagger-ui.html # Swagger UI 访问路径 - operationsSorter: alpha # 接口按方法名排序 - tags-sorter: alpha # 标签按字母排序 + path: /swagger-ui.html # Swagger UI 访问路径 + operationsSorter: alpha # 接口按方法名排序 + tags-sorter: alpha # 标签按字母排序 api-docs: - path: /v3/api-docs # OpenAPI JSON 地址 - group-configs: # 分组配置 - - group: '系统管理' # 分组名称 - paths-to-match: "/**" # 匹配的请求路径 - packages-to-scan: # 扫描的 Controller 包,限制只生成指定包的接口文档 + path: /v3/api-docs # OpenAPI JSON 地址 + group-configs: # 分组配置 + - group: "系统管理" # 分组名称 + paths-to-match: "/**" # 匹配的请求路径 + packages-to-scan: # 扫描的 Controller 包,限制只生成指定包的接口文档 - com.youlai.boot.auth.controller - com.youlai.boot.system.controller - com.youlai.boot.platform.file.controller - com.youlai.boot.platform.codegen.controller - default-flat-param-object: true # 将对象参数扁平化显示在文档中 + default-flat-param-object: true # 将对象参数扁平化显示在文档中 # knife4j 配置 knife4j: - enable: true # 是否启用 Knife4j 增强功能 - production: false # 是否启用生产环境保护(true=生产环境隐藏文档,false=开发环境可访问) + enable: true # 是否启用 Knife4j 增强功能 + production: false # 是否启用生产环境保护(true=生产环境隐藏文档,false=开发环境可访问) setting: language: zh_cn - # xxl-job 定时任务配置 xxl: job: @@ -231,4 +216,3 @@ wx: miniapp: app-id: xxxxxx app-secret: xxxxxx - diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 70e668a0..dfbcb392 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -8,19 +8,6 @@ spring: url: jdbc:mysql://www.youlai.tech:3306/youlai_admin?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true username: youlai password: 123456 - - ai: - openai: - api-key: ${QWEN_API_KEY:sk-c2941d05bf2f411ca80424fcd1exxxxx} - base-url: https://dashscope.aliyuncs.com/compatible-mode - chat: - enabled: true - options: - model: qwen-plus - temperature: 0.7 - response-format: - type: json_object - data: redis: database: 11 @@ -85,19 +72,18 @@ mybatis-plus: security: session: type: jwt # 会话方式 [jwt|redis-token] - access-token-time-to-live: 7200 # 访问令牌 有效期(单位:秒),默认 2 小时,-1 表示永不过期 - refresh-token-time-to-live: 604800 # 刷新令牌有效期(单位:秒),默认 7 天,-1 表示永不过期 + access-token-time-to-live: 7200 # 访问令牌 有效期(单位:秒),默认 2 小时,-1 表示永不过期 + refresh-token-time-to-live: 604800 # 刷新令牌有效期(单位:秒),默认 7 天,-1 表示永不过期 jwt: secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789 # JWT密钥(HS256算法至少32字符) redis-token: allow-multi-login: true # 是否允许多设备登录 # 安全白名单路径,仅跳过 AuthorizationFilter 过滤器,还是会走 Spring Security 的其他过滤器(CSRF、CORS等) ignore-urls: - - /api/v1/auth/login/** # 登录接口(账号密码登录、手机验证码登录和微信登录) - - /api/v1/auth/captcha # 验证码获取接口 - - /api/v1/auth/refresh-token # 刷新令牌接口 - - //api/v1/auth/wx/miniapp/code-login # 微信小程序code登陆 - - /ws/** # WebSocket接口 + - /api/v1/auth/login/** # 登录接口(账号密码登录、手机验证码登录和微信登录) + - /api/v1/auth/captcha # 验证码获取接口 + - /api/v1/auth/refresh-token # 刷新令牌接口 + - /ws/** # WebSocket接口 # 非安全端点路径,完全绕过 Spring Security 的过滤器 unsecured-urls: - ${springdoc.swagger-ui.path} @@ -122,7 +108,7 @@ oss: # 存储桶名称 bucket-name: public # (可选)自定义域名,如果配置了域名,生成的文件URL是域名格式,未配置则URL则是IP格式 (eg: https://www.youlai.tech/storage) - custom-domain: + custom-domain: # 阿里云OSS对象存储服务 aliyun: # 服务Endpoint @@ -157,29 +143,28 @@ sms: # springdoc 配置文档: https://springdoc.org/properties.html springdoc: swagger-ui: - path: /swagger-ui.html # Swagger UI 访问路径 - operationsSorter: alpha # 接口按方法名排序 - tags-sorter: alpha # 标签按字母排序 + path: /swagger-ui.html # Swagger UI 访问路径 + operationsSorter: alpha # 接口按方法名排序 + tags-sorter: alpha # 标签按字母排序 api-docs: - path: /v3/api-docs # OpenAPI JSON 地址 - group-configs: # 分组配置 - - group: '系统管理' # 分组名称 - paths-to-match: "/**" # 匹配的请求路径 - packages-to-scan: # 扫描的 Controller 包,限制只生成指定包的接口文档 + path: /v3/api-docs # OpenAPI JSON 地址 + group-configs: # 分组配置 + - group: "系统管理" # 分组名称 + paths-to-match: "/**" # 匹配的请求路径 + packages-to-scan: # 扫描的 Controller 包,限制只生成指定包的接口文档 - com.youlai.boot.auth.controller - com.youlai.boot.system.controller - com.youlai.boot.platform.file.controller - com.youlai.boot.platform.codegen.controller - default-flat-param-object: true # 将对象参数扁平化显示在文档中 + default-flat-param-object: true # 将对象参数扁平化显示在文档中 # knife4j 配置 knife4j: - enable: true # 是否启用 Knife4j 增强功能 - production: false # 是否启用生产环境保护(true=生产环境隐藏文档,false=开发环境可访问) + enable: true # 是否启用 Knife4j 增强功能 + production: false # 是否启用生产环境保护(true=生产环境隐藏文档,false=开发环境可访问) setting: language: zh_cn - # xxl-job 定时任务配置 xxl: job: diff --git a/src/main/resources/mapper/ai/AiAssistantRecordMapper.xml b/src/main/resources/mapper/ai/AiAssistantRecordMapper.xml deleted file mode 100644 index 77a96cd8..00000000 --- a/src/main/resources/mapper/ai/AiAssistantRecordMapper.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/mapper/system/UserMapper.xml b/src/main/resources/mapper/system/UserMapper.xml index 72d23d6f..1f018609 100644 --- a/src/main/resources/mapper/system/UserMapper.xml +++ b/src/main/resources/mapper/system/UserMapper.xml @@ -147,24 +147,6 @@ t1.username = #{username} AND t1.is_deleted = 0 - - -