From 5aff74d36f7a634130def65f497d09c411897262 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Tue, 22 Apr 2025 20:49:49 +0800 Subject: [PATCH 01/60] =?UTF-8?q?feat:=20=E5=AD=97=E5=85=B8=E5=AE=9E?= =?UTF-8?q?=E6=97=B6=E5=90=8C=E6=AD=A5=E5=92=8C=20`websocket`=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/mysql/youlai_boot.sql | 5 +- .../youlai/boot/config/WebSocketConfig.java | 243 ++++++++-------- .../mail/controller/MailController.java | 2 +- .../listener/OnlineUserListener.java | 44 --- .../websocket/service/OnlineUserService.java | 70 ----- .../system/controller/DictController.java | 41 ++- ...aConverter.java => DictItemConverter.java} | 6 +- .../system/event/UserConnectionEvent.java | 38 --- .../handler/OnlineUserJobHandler.java | 8 +- .../boot/system/model/dto/UserSessionDTO.java | 37 +++ .../boot/system/model/event/DictEvent.java | 27 ++ .../boot/system/model/form/DictForm.java | 2 + .../boot/system/service/DictService.java | 9 +- .../system/service/UserOnlineService.java | 157 ++++++++++ .../boot/system/service/WebSocketService.java | 46 +++ .../service/impl/DictItemServiceImpl.java | 14 +- .../system/service/impl/DictServiceImpl.java | 67 +++-- .../service/impl/NoticeServiceImpl.java | 8 +- .../service/impl/WebSocketServiceImpl.java | 269 ++++++++++++++++++ .../mapper/system/RoleMenuMapper.xml | 2 +- .../resources/templates/codegen/index.vue.vm | 4 +- 21 files changed, 770 insertions(+), 329 deletions(-) delete mode 100644 src/main/java/com/youlai/boot/shared/websocket/listener/OnlineUserListener.java delete mode 100644 src/main/java/com/youlai/boot/shared/websocket/service/OnlineUserService.java rename src/main/java/com/youlai/boot/system/converter/{DictDataConverter.java => DictItemConverter.java} (89%) delete mode 100644 src/main/java/com/youlai/boot/system/event/UserConnectionEvent.java rename src/main/java/com/youlai/boot/{shared/websocket => system}/handler/OnlineUserJobHandler.java (77%) create mode 100644 src/main/java/com/youlai/boot/system/model/dto/UserSessionDTO.java create mode 100644 src/main/java/com/youlai/boot/system/model/event/DictEvent.java create mode 100644 src/main/java/com/youlai/boot/system/service/UserOnlineService.java create mode 100644 src/main/java/com/youlai/boot/system/service/WebSocketService.java create mode 100644 src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java diff --git a/sql/mysql/youlai_boot.sql b/sql/mysql/youlai_boot.sql index a6402555..d8a7927b 100644 --- a/sql/mysql/youlai_boot.sql +++ b/sql/mysql/youlai_boot.sql @@ -173,7 +173,6 @@ INSERT INTO `sys_menu` VALUES (84, 6, '0,1,6', '字典删除', 4, NULL, '', NULL INSERT INTO `sys_menu` VALUES (88, 2, '0,1,2', '重置密码', 4, NULL, '', NULL, 'sys:user:reset-password', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL); INSERT INTO `sys_menu` VALUES (89, 0, '0', '功能演示', 2, NULL, '/function', 'Layout', NULL, NULL, NULL, 1, 12, 'menu', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (90, 89, '0,89', 'Websocket', 1, NULL, '/function/websocket', 'demo/websocket', NULL, NULL, 1, 1, 3, '', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (91, 89, '0,89', '敬请期待...', 2, NULL, 'other/:id', 'demo/other', NULL, NULL, NULL, 1, 4, '', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (95, 36, '0,36', '字典组件', 1, NULL, 'dict-demo', 'demo/dictionary', NULL, NULL, 1, 1, 4, '', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (97, 89, '0,89', 'Icons', 1, NULL, 'icon-demo', 'demo/icons', NULL, NULL, 1, 1, 2, 'el-icon-Notification', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (102, 26, '0,26', 'document', 3, '', 'internal-doc', 'demo/internal-doc', NULL, NULL, NULL, 1, 1, 'document', '', now(), now(), NULL); @@ -214,6 +213,7 @@ INSERT INTO `sys_menu` VALUES (144, 26, '0,26', '后端文档', 3, NULL, 'https: INSERT INTO `sys_menu` VALUES (145, 26, '0,26', '移动端文档', 3, NULL, 'https://youlai.blog.csdn.net/article/details/143222890', '', NULL, NULL, NULL, 1, 4, 'document', '', '2024-10-05 23:36:03', '2024-10-05 23:36:03', NULL); INSERT INTO `sys_menu` VALUES (146, 36, '0,36', '拖拽组件', 1, NULL, 'drag', 'demo/drag', NULL, NULL, NULL, 1, 5, '', '', '2025-03-31 14:14:45', '2025-03-31 14:14:52', NULL); INSERT INTO `sys_menu` VALUES (147, 36, '0,36', '滚动文本', 1, NULL, 'text-scroll', 'demo/text-scroll', NULL, NULL, NULL, 1, 6, '', '', '2025-03-31 14:14:49', '2025-03-31 14:14:56', NULL); +INSERT INTO `sys_menu` VALUES (148, 89, '0,89', '字典实时同步', 1, NULL, 'dict-sync', 'demo/dict-sync', NULL, NULL, NULL, 1, 3, '', '', '2025-03-31 14:14:49', '2025-03-31 14:14:56', NULL); -- ---------------------------- -- Table structure for sys_role @@ -351,6 +351,7 @@ INSERT INTO `sys_role_menu` VALUES (2, 144); INSERT INTO `sys_role_menu` VALUES (2, 145); INSERT INTO `sys_role_menu` VALUES (2, 146); INSERT INTO `sys_role_menu` VALUES (2, 147); +INSERT INTO `sys_role_menu` VALUES (2, 148); -- ---------------------------- -- Table structure for sys_user @@ -559,4 +560,4 @@ 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); -SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file +SET FOREIGN_KEY_CHECKS = 1; diff --git a/src/main/java/com/youlai/boot/config/WebSocketConfig.java b/src/main/java/com/youlai/boot/config/WebSocketConfig.java index 2d9bbf48..a344010b 100644 --- a/src/main/java/com/youlai/boot/config/WebSocketConfig.java +++ b/src/main/java/com/youlai/boot/config/WebSocketConfig.java @@ -3,11 +3,11 @@ package com.youlai.boot.config; import cn.hutool.core.util.StrUtil; import com.youlai.boot.core.security.model.SysUserDetails; import com.youlai.boot.core.security.token.TokenManager; -import com.youlai.boot.system.event.UserConnectionEvent; +import com.youlai.boot.system.service.WebSocketService; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.http.HttpHeaders; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; @@ -27,144 +27,143 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; /** - * WebSocket 配置 + * WebSocket配置 * * @author Ray.Hao - * @since 2.4.0 + * @since 3.0.0 */ -// 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递 @EnableWebSocketMessageBroker @Configuration @Slf4j public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { - private final ApplicationEventPublisher eventPublisher; + private final TokenManager tokenManager; + private final WebSocketService webSocketService; - private final TokenManager tokenManager; + public WebSocketConfig(TokenManager tokenManager, @Lazy WebSocketService webSocketService) { + this.tokenManager = tokenManager; + this.webSocketService = webSocketService; + } - public WebSocketConfig(ApplicationEventPublisher eventPublisher, TokenManager tokenManager) { - this.eventPublisher = eventPublisher; - this.tokenManager = tokenManager; - } - - /** - * 注册一个端点,客户端通过这个端点进行连接 - */ - @Override - public void registerStompEndpoints(StompEndpointRegistry registry) { - registry - // 注册 /ws 的端点 - .addEndpoint("/ws") - // 允许跨域 - .setAllowedOriginPatterns("*"); - } + /** + * 注册一个端点,客户端通过这个端点进行连接 + */ + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry + // 注册 /ws 的端点 + .addEndpoint("/ws") + // 允许跨域 + .setAllowedOriginPatterns("*"); + } - /** - * 配置消息代理 - */ - @Override - public void configureMessageBroker(MessageBrokerRegistry registry) { - // 客户端发送消息的请求前缀 - registry.setApplicationDestinationPrefixes("/app"); + /** + * 配置消息代理 + */ + @Override + public void configureMessageBroker(MessageBrokerRegistry registry) { + // 客户端发送消息的请求前缀 + registry.setApplicationDestinationPrefixes("/app"); - // 客户端订阅消息的请求前缀,topic一般用于广播推送,queue用于点对点推送 - registry.enableSimpleBroker("/topic", "/queue"); + // 客户端订阅消息的请求前缀,topic一般用于广播推送,queue用于点对点推送 + registry.enableSimpleBroker("/topic", "/queue"); - // 服务端通知客户端的前缀,可以不设置,默认为user - registry.setUserDestinationPrefix("/user"); - } + // 服务端通知客户端的前缀,可以不设置,默认为user + registry.setUserDestinationPrefix("/user"); + } - /** - * 配置客户端入站通道拦截器 - *

- * 核心功能: - * 1. 连接建立时解析令牌并绑定用户身份 - * 2. 连接关闭时触发下线通知 - * 3. 异常Token的防御性处理 - */ - @Override - public void configureClientInboundChannel(ChannelRegistration registration) { - registration.interceptors(new ChannelInterceptor() { - @Override - public Message preSend(@NotNull Message message, @NotNull MessageChannel channel) { - StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); - if (accessor == null) { - return ChannelInterceptor.super.preSend(message, channel); - } + /** + * 配置客户端入站通道拦截器 + *

+ * 核心功能: + * 1. 连接建立时解析令牌并绑定用户身份 + * 2. 连接关闭时触发下线通知 + * 3. 异常Token的防御性处理 + */ + @Override + public void configureClientInboundChannel(ChannelRegistration registration) { + registration.interceptors(new ChannelInterceptor() { + @Override + public Message preSend(@NotNull Message message, @NotNull MessageChannel channel) { + StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); + if (accessor == null) { + return ChannelInterceptor.super.preSend(message, channel); + } - try { - // 处理客户端连接请求 - if (StompCommand.CONNECT.equals(accessor.getCommand())) { - /* - * 安全校验流程: - * 1. 从HEADER中获取Authorization值 - * 2. 校验Bearer Token格式合法性 - * 3. 解析并验证JWT有效性 - * 4. 绑定用户身份到当前会话 - */ - String authorization = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION); + try { + // 处理客户端连接请求 + if (StompCommand.CONNECT.equals(accessor.getCommand())) { + /* + * 安全校验流程: + * 1. 从HEADER中获取Authorization值 + * 2. 校验Bearer Token格式合法性 + * 3. 解析并验证JWT有效性 + * 4. 绑定用户身份到当前会话 + */ + String authorization = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION); - // 防御性校验:确保Authorization头存在且格式正确 - if (StrUtil.isBlank(authorization) || !authorization.startsWith("Bearer ")) { - log.warn("非法连接请求:缺少有效的Authorization头"); - throw new AuthenticationCredentialsNotFoundException("Missing authorization header"); - } - - // 提取并处理JWT令牌(移除Bearer前缀) - String token = authorization.substring(7); - Authentication authentication = tokenManager.parseToken(token); - - // 令牌解析失败处理 - if (authentication == null) { - log.error("令牌解析失败:{}", token); - throw new BadCredentialsException("Invalid token"); - } - - // 获取用户详细信息 - SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal(); - if (userDetails == null || StrUtil.isBlank(userDetails.getUsername())) { - log.error("无效的用户凭证:{}", token); - throw new BadCredentialsException("Invalid user credentials"); - } - - String username = userDetails.getUsername(); - log.info("WebSocket连接建立:用户[{}]", username); - - // 绑定用户身份到当前会话(重要:用于@SendToUser等注解) - accessor.setUser(authentication); - - // 发布用户上线事件(示例:可用于更新在线用户列表) - eventPublisher.publishEvent(new UserConnectionEvent(this, username, true)); - - } - // 处理客户端断开请求 - else if (StompCommand.DISCONNECT.equals(accessor.getCommand())) { - /* - * 注意:只有成功建立过认证的连接才会触发下线事件 - * 防止未认证成功的连接产生脏数据 - */ - Authentication authentication = (Authentication) accessor.getUser(); - if (authentication != null && authentication.isAuthenticated()) { - String username = ((SysUserDetails) authentication.getPrincipal()).getUsername(); - log.info("WebSocket连接关闭:用户[{}]", username); - eventPublisher.publishEvent(new UserConnectionEvent(this, username, false)); - } - } - } catch (AuthenticationException ex) { - // 认证失败时强制关闭连接 - log.error("连接认证失败:{}", ex.getMessage()); - throw ex; - } catch (Exception ex) { - // 捕获其他未知异常 - log.error("WebSocket连接处理异常:", ex); - throw new MessagingException("Connection processing failed"); - } - - return ChannelInterceptor.super.preSend(message, channel); + // 防御性校验:确保Authorization头存在且格式正确 + if (StrUtil.isBlank(authorization) || !authorization.startsWith("Bearer ")) { + log.warn("非法连接请求:缺少有效的Authorization头"); + throw new AuthenticationCredentialsNotFoundException("Missing authorization header"); } - }); - } + // 提取并处理JWT令牌(移除Bearer前缀) + String token = authorization.substring(7); + Authentication authentication = tokenManager.parseToken(token); + + // 令牌解析失败处理 + if (authentication == null) { + log.error("令牌解析失败:{}", token); + throw new BadCredentialsException("Invalid token"); + } + + // 获取用户详细信息 + SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal(); + if (userDetails == null || StrUtil.isBlank(userDetails.getUsername())) { + log.error("无效的用户凭证:{}", token); + throw new BadCredentialsException("Invalid user credentials"); + } + + String username = userDetails.getUsername(); + log.info("WebSocket连接建立:用户[{}]", username); + + // 绑定用户身份到当前会话(重要:用于@SendToUser等注解) + accessor.setUser(authentication); + + // 记录用户上线状态 + webSocketService.userConnected(username, accessor.getSessionId()); + + } + // 处理客户端断开请求 + else if (StompCommand.DISCONNECT.equals(accessor.getCommand())) { + /* + * 注意:只有成功建立过认证的连接才会触发下线事件 + * 防止未认证成功的连接产生脏数据 + */ + Authentication authentication = (Authentication) accessor.getUser(); + if (authentication != null && authentication.isAuthenticated()) { + String username = ((SysUserDetails) authentication.getPrincipal()).getUsername(); + log.info("WebSocket连接关闭:用户[{}]", username); + + // 记录用户下线状态 + webSocketService.userDisconnected(username); + } + } + } catch (AuthenticationException ex) { + // 认证失败时强制关闭连接 + log.error("连接认证失败:{}", ex.getMessage()); + throw ex; + } catch (Exception ex) { + // 捕获其他未知异常 + log.error("WebSocket连接处理异常:", ex); + throw new MessagingException("Connection processing failed"); + } + + return ChannelInterceptor.super.preSend(message, channel); + } + }); + } } diff --git a/src/main/java/com/youlai/boot/shared/mail/controller/MailController.java b/src/main/java/com/youlai/boot/shared/mail/controller/MailController.java index 7eb1acbc..d9e4c20d 100644 --- a/src/main/java/com/youlai/boot/shared/mail/controller/MailController.java +++ b/src/main/java/com/youlai/boot/shared/mail/controller/MailController.java @@ -5,7 +5,7 @@ import org.springframework.web.bind.annotation.*; /** * 邮件控制层 * - * @author Ray + * @author Ray.Hao * @since 2.10.0 */ @RestController diff --git a/src/main/java/com/youlai/boot/shared/websocket/listener/OnlineUserListener.java b/src/main/java/com/youlai/boot/shared/websocket/listener/OnlineUserListener.java deleted file mode 100644 index 54fd3481..00000000 --- a/src/main/java/com/youlai/boot/shared/websocket/listener/OnlineUserListener.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.youlai.boot.shared.websocket.listener; - -import com.youlai.boot.shared.websocket.service.OnlineUserService; -import com.youlai.boot.system.event.UserConnectionEvent; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.messaging.simp.SimpMessagingTemplate; -import org.springframework.stereotype.Component; - -/** - * 在线用户监听器 - * - * @author haoxr - * @since 2024/9/25 - */ -@Component -@RequiredArgsConstructor -@Slf4j -public class OnlineUserListener { - - private final SimpMessagingTemplate messagingTemplate; - private final OnlineUserService onlineUserService; - - /** - * 用户连接事件处理 - * - * @param event 用户连接事件 - */ - @EventListener - public void handleUserConnectionEvent(UserConnectionEvent event) { - String username = event.getUsername(); - if (event.isConnected()) { - onlineUserService.addOnlineUser(username); - log.info("User connected: {}", username); - } else { - onlineUserService.removeOnlineUser(username); - log.info("User disconnected: {}", username); - } - // 推送在线用户人数 - messagingTemplate.convertAndSend("/topic/onlineUserCount", onlineUserService.getOnlineUserCount()); - } - -} diff --git a/src/main/java/com/youlai/boot/shared/websocket/service/OnlineUserService.java b/src/main/java/com/youlai/boot/shared/websocket/service/OnlineUserService.java deleted file mode 100644 index 6ef06a57..00000000 --- a/src/main/java/com/youlai/boot/shared/websocket/service/OnlineUserService.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.youlai.boot.shared.websocket.service; - -import org.springframework.stereotype.Service; - -import java.util.Collections; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -/** - * 在线用户服务 - * - * @author haoxr - * @since 2024/9/26 - */ - -@Service -public class OnlineUserService { - - private final Set onlineUsers = ConcurrentHashMap.newKeySet(); - - /** - * 添加用户到在线用户集合 - * - * @param username 用户名 - */ - public void addOnlineUser(String username) { - onlineUsers.add(username); - } - - /** - * 从在线用户集合移除用户 - * - * @param username 用户名 - */ - public void removeOnlineUser(String username) { - onlineUsers.remove(username); - } - - /** - * 获取所有在线用户 - * - * @return 在线用户集合 - */ - public Set getAllOnlineUsers() { - return Collections.unmodifiableSet(onlineUsers); - } - - /** - * 获取在线的接收者 - * 从所有接收者中过滤出在线的接收者 - * - * @param receivers 接收者 - * @return 在线的接收者集合 - */ - public Set getOnlineReceivers(Set receivers) { - return receivers.stream().filter(onlineUsers::contains).collect(Collectors.toSet()); - } - - /** - * 获取在线用户数量 - * - * @return 在线用户数量 - */ - public int getOnlineUserCount() { - return onlineUsers.size(); - } - - -} diff --git a/src/main/java/com/youlai/boot/system/controller/DictController.java b/src/main/java/com/youlai/boot/system/controller/DictController.java index 3ec31b3b..252af202 100644 --- a/src/main/java/com/youlai/boot/system/controller/DictController.java +++ b/src/main/java/com/youlai/boot/system/controller/DictController.java @@ -16,6 +16,7 @@ import com.youlai.boot.system.model.form.DictForm; import com.youlai.boot.common.annotation.Log; import com.youlai.boot.system.service.DictItemService; import com.youlai.boot.system.service.DictService; +import com.youlai.boot.system.service.WebSocketService; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; @@ -42,7 +43,7 @@ public class DictController { private final DictService dictService; private final DictItemService dictItemService; - + private final WebSocketService webSocketService; //--------------------------------------------------- // 字典相关接口 @@ -80,6 +81,10 @@ public class DictController { @RepeatSubmit public Result saveDict(@Valid @RequestBody DictForm formData) { boolean result = dictService.saveDict(formData); + // 发送字典更新通知 + if (result) { + webSocketService.broadcastDictChange(formData.getDictCode()); + } return Result.judge(result); } @@ -88,9 +93,13 @@ public class DictController { @PreAuthorize("@ss.hasPerm('sys:dict:edit')") public Result updateDict( @PathVariable Long id, - @RequestBody DictForm DictForm + @RequestBody DictForm dictForm ) { - boolean status = dictService.updateDict(id, DictForm); + boolean status = dictService.updateDict(id, dictForm); + // 发送字典更新通知 + if (status && dictForm.getDictCode() != null) { + webSocketService.broadcastDictChange(dictForm.getDictCode()); + } return Result.judge(status); } @@ -100,7 +109,16 @@ public class DictController { public Result deleteDictionaries( @Parameter(description = "字典ID,多个以英文逗号(,)拼接") @PathVariable String ids ) { + // 获取字典编码列表,用于发送删除通知 + List dictCodes = dictService.getDictCodesByIds(Arrays.stream(ids.split(",")).toList()); + dictService.deleteDictByIds(Arrays.stream(ids.split(",")).toList()); + + // 发送字典删除通知 + for (String dictCode : dictCodes) { + webSocketService.broadcastDictChange(dictCode); + } + return Result.success(); } @@ -138,6 +156,12 @@ public class DictController { ) { formData.setDictCode(dictCode); boolean result = dictItemService.saveDictItem(formData); + + // 发送字典更新通知 + if (result) { + webSocketService.broadcastDictChange(dictCode); + } + return Result.judge(result); } @@ -163,6 +187,12 @@ public class DictController { formData.setId(itemId); formData.setDictCode(dictCode); boolean status = dictItemService.updateDictItem(formData); + + // 发送字典更新通知 + if (status) { + webSocketService.broadcastDictChange(dictCode); + } + return Result.judge(status); } @@ -170,9 +200,14 @@ public class DictController { @DeleteMapping("/{dictCode}/items/{itemIds}") @PreAuthorize("@ss.hasPerm('sys:dict-item:delete')") public Result deleteDictItems( + @PathVariable String dictCode, @Parameter(description = "字典ID,多个以英文逗号(,)拼接") @PathVariable String itemIds ) { dictItemService.deleteDictItemByIds(itemIds); + + // 发送字典更新通知 + webSocketService.broadcastDictChange(dictCode); + return Result.success(); } diff --git a/src/main/java/com/youlai/boot/system/converter/DictDataConverter.java b/src/main/java/com/youlai/boot/system/converter/DictItemConverter.java similarity index 89% rename from src/main/java/com/youlai/boot/system/converter/DictDataConverter.java rename to src/main/java/com/youlai/boot/system/converter/DictItemConverter.java index c2480f78..99a354b0 100644 --- a/src/main/java/com/youlai/boot/system/converter/DictDataConverter.java +++ b/src/main/java/com/youlai/boot/system/converter/DictItemConverter.java @@ -10,13 +10,13 @@ import org.mapstruct.Mapper; import java.util.List; /** - * 字典项 对象转换器 + * 字典项对象转换器 * - * @author Ray + * @author Ray.Hao * @since 2022/6/8 */ @Mapper(componentModel = "spring") -public interface DictDataConverter { +public interface DictItemConverter { Page toPageVo(Page page); diff --git a/src/main/java/com/youlai/boot/system/event/UserConnectionEvent.java b/src/main/java/com/youlai/boot/system/event/UserConnectionEvent.java deleted file mode 100644 index dadea814..00000000 --- a/src/main/java/com/youlai/boot/system/event/UserConnectionEvent.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.youlai.boot.system.event; - -import lombok.Getter; -import org.springframework.context.ApplicationEvent; - - -/** - * 用户连接事件 - * - * @author Ray - * @since 2.3.0 - */ -@Getter -public class UserConnectionEvent extends ApplicationEvent { - - /** - * 用户名 - */ - private final String username; - - /** - * 是否连接 - */ - private final boolean connected; - - /** - * 用户连接事件 - * - * @param source 事件源 - * @param username 用户名 - * @param connected 是否连接 - */ - public UserConnectionEvent(Object source, String username, boolean connected) { - super(source); - this.username = username; - this.connected = connected; - } -} diff --git a/src/main/java/com/youlai/boot/shared/websocket/handler/OnlineUserJobHandler.java b/src/main/java/com/youlai/boot/system/handler/OnlineUserJobHandler.java similarity index 77% rename from src/main/java/com/youlai/boot/shared/websocket/handler/OnlineUserJobHandler.java rename to src/main/java/com/youlai/boot/system/handler/OnlineUserJobHandler.java index 75b148e2..1bcc405d 100644 --- a/src/main/java/com/youlai/boot/shared/websocket/handler/OnlineUserJobHandler.java +++ b/src/main/java/com/youlai/boot/system/handler/OnlineUserJobHandler.java @@ -1,7 +1,7 @@ -package com.youlai.boot.shared.websocket.handler; +package com.youlai.boot.system.handler; -import com.youlai.boot.shared.websocket.service.OnlineUserService; +import com.youlai.boot.system.service.UserOnlineService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.messaging.simp.SimpMessagingTemplate; @@ -19,7 +19,7 @@ import org.springframework.stereotype.Component; @RequiredArgsConstructor public class OnlineUserJobHandler { - private final OnlineUserService onlineUserService; + private final UserOnlineService userOnlineService; private final SimpMessagingTemplate messagingTemplate; // 每分钟统计一次在线用户数 @@ -27,7 +27,7 @@ public class OnlineUserJobHandler { public void execute() { log.info("定时任务:统计在线用户数"); // 推送在线用户人数 - messagingTemplate.convertAndSend("/topic/onlineUserCount", onlineUserService.getOnlineUserCount()); + messagingTemplate.convertAndSend("/topic/onlineUserCount", userOnlineService.getOnlineUserCount()); } } diff --git a/src/main/java/com/youlai/boot/system/model/dto/UserSessionDTO.java b/src/main/java/com/youlai/boot/system/model/dto/UserSessionDTO.java new file mode 100644 index 00000000..80cb44c1 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/dto/UserSessionDTO.java @@ -0,0 +1,37 @@ +package com.youlai.boot.system.model.dto; + +import lombok.Data; + +import java.util.HashSet; +import java.util.Set; + +/** + * 用户会话DTO + * + * @author Ray.Hao + * @since 3.0.0 + */ +@Data +public class UserSessionDTO { + + /** + * 用户名 + */ + private String username; + + /** + * 用户会话ID集合 + */ + private Set sessionIds; + + /** + * 最后活动时间 + */ + private long lastActiveTime; + + public UserSessionDTO(String username) { + this.username = username; + this.sessionIds = new HashSet<>(); + this.lastActiveTime = System.currentTimeMillis(); + } +} diff --git a/src/main/java/com/youlai/boot/system/model/event/DictEvent.java b/src/main/java/com/youlai/boot/system/model/event/DictEvent.java new file mode 100644 index 00000000..7f30ee10 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/event/DictEvent.java @@ -0,0 +1,27 @@ +package com.youlai.boot.system.model.event; + +import lombok.Data; + +/** + * 字典更新事件 + * + * @author Ray.Hao + * @since 3.0.0 + */ +@Data +public class DictEvent { + /** + * 字典编码 + */ + private String dictCode; + + /** + * 时间戳 + */ + private long timestamp; + + public DictEvent(String dictCode) { + this.dictCode = dictCode; + this.timestamp = System.currentTimeMillis(); + } +} diff --git a/src/main/java/com/youlai/boot/system/model/form/DictForm.java b/src/main/java/com/youlai/boot/system/model/form/DictForm.java index 31e5a996..b17d320a 100644 --- a/src/main/java/com/youlai/boot/system/model/form/DictForm.java +++ b/src/main/java/com/youlai/boot/system/model/form/DictForm.java @@ -2,6 +2,7 @@ package com.youlai.boot.system.model.form; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; import lombok.Data; import lombok.Getter; import lombok.Setter; @@ -26,6 +27,7 @@ public class DictForm { private String name; @Schema(description = "字典编码", example ="gender") + @NotBlank(message = "字典编码不能为空") private String dictCode; @Schema(description = "备注") diff --git a/src/main/java/com/youlai/boot/system/service/DictService.java b/src/main/java/com/youlai/boot/system/service/DictService.java index 6c9b9b68..5ab06ab7 100644 --- a/src/main/java/com/youlai/boot/system/service/DictService.java +++ b/src/main/java/com/youlai/boot/system/service/DictService.java @@ -66,6 +66,11 @@ public interface DictService extends IService { */ void deleteDictByIds(List ids); - - + /** + * 根据字典ID列表获取字典编码列表 + * + * @param ids 字典ID列表 + * @return 字典编码列表 + */ + List getDictCodesByIds(List ids); } diff --git a/src/main/java/com/youlai/boot/system/service/UserOnlineService.java b/src/main/java/com/youlai/boot/system/service/UserOnlineService.java new file mode 100644 index 00000000..0632a76c --- /dev/null +++ b/src/main/java/com/youlai/boot/system/service/UserOnlineService.java @@ -0,0 +1,157 @@ +package com.youlai.boot.system.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.youlai.boot.core.security.model.SysUserDetails; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 用户在线状态服务 + * 负责维护用户的在线状态和相关统计 + * + * @author Ray.Hao + * @since 3.0.0 + */ +@Service +@Slf4j +public class UserOnlineService { + + // 在线用户映射表,key为用户名,value为用户在线信息 + private final Map onlineUsers = new ConcurrentHashMap<>(); + + private SimpMessagingTemplate messagingTemplate; + private final ObjectMapper objectMapper; + + @Autowired + public UserOnlineService(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Autowired(required = false) + public void setMessagingTemplate(SimpMessagingTemplate messagingTemplate) { + this.messagingTemplate = messagingTemplate; + } + + /** + * 用户上线 + * + * @param username 用户名 + * @param sessionId WebSocket会话ID(可选) + */ + public void userConnected(String username, String sessionId) { + // 生成会话ID(如果未提供) + String actualSessionId = sessionId != null ? sessionId : "session-" + System.nanoTime(); + UserOnlineInfo info = new UserOnlineInfo(username, actualSessionId, System.currentTimeMillis()); + onlineUsers.put(username, info); + log.info("用户[{}]上线,当前在线用户数:{}", username, onlineUsers.size()); + + // 通知在线用户状态变更 + notifyOnlineUsersChange(); + } + + /** + * 用户下线 + * + * @param username 用户名 + */ + public void userDisconnected(String username) { + onlineUsers.remove(username); + log.info("用户[{}]下线,当前在线用户数:{}", username, onlineUsers.size()); + + // 通知在线用户状态变更 + notifyOnlineUsersChange(); + } + + /** + * 获取在线用户列表 + * + * @return 在线用户名列表 + */ + public List getOnlineUsers() { + return onlineUsers.values().stream() + .map(info -> new UserOnlineDTO(info.getUsername(), info.getLoginTime())) + .collect(Collectors.toList()); + } + + /** + * 获取在线用户数量 + * + * @return 在线用户数 + */ + public int getOnlineUserCount() { + return onlineUsers.size(); + } + + /** + * 检查用户是否在线 + * + * @param username 用户名 + * @return 是否在线 + */ + public boolean isUserOnline(String username) { + return onlineUsers.containsKey(username); + } + + /** + * 通知所有客户端在线用户变更 + */ + private void notifyOnlineUsersChange() { + if (messagingTemplate == null) { + log.warn("消息模板尚未初始化,无法发送在线用户变更通知"); + return; + } + + try { + OnlineUsersChangeEvent event = new OnlineUsersChangeEvent(); + event.setType("ONLINE_USERS_CHANGE"); + event.setCount(onlineUsers.size()); + event.setUsers(getOnlineUsers()); + event.setTimestamp(System.currentTimeMillis()); + + String message = objectMapper.writeValueAsString(event); + messagingTemplate.convertAndSend("/topic/online-users", message); + } catch (JsonProcessingException e) { + log.error("Failed to send online users change event", e); + } + } + + /** + * 用户在线信息 + */ + @Data + private static class UserOnlineInfo { + private final String username; + private final String sessionId; + private final long loginTime; + } + + /** + * 用户在线DTO(用于返回给前端) + */ + @Data + public static class UserOnlineDTO { + private final String username; + private final long loginTime; + } + + /** + * 在线用户变更事件 + */ + @Data + private static class OnlineUsersChangeEvent { + private String type; + private int count; + private List users; + private long timestamp; + } +} diff --git a/src/main/java/com/youlai/boot/system/service/WebSocketService.java b/src/main/java/com/youlai/boot/system/service/WebSocketService.java new file mode 100644 index 00000000..487412a8 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/service/WebSocketService.java @@ -0,0 +1,46 @@ +package com.youlai.boot.system.service; + +/** + * WebSocket服务接口 + *

+ * 提供与WebSocket连接管理相关的功能,包括: + * - 用户连接/断开事件处理 + * - 字典数据变更通知 + * - 系统消息推送 + *

+ * + * @author Ray.Hao + * @since 3.0.0 + */ +public interface WebSocketService { + + /** + * 处理用户连接事件 + * + * @param username 用户名 + * @param sessionId WebSocket会话ID + */ + void userConnected(String username, String sessionId); + + /** + * 处理用户断开连接事件 + * + * @param username 用户名 + */ + void userDisconnected(String username); + + /** + * 广播字典数据变更通知 + * + * @param dictCode 字典编码 + */ + void broadcastDictChange(String dictCode); + + /** + * 发送系统通知给特定用户 + * + * @param username 目标用户名 + * @param message 通知消息内容 + */ + void sendNotification(String username, Object message); +} diff --git a/src/main/java/com/youlai/boot/system/service/impl/DictItemServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/DictItemServiceImpl.java index 620a53cf..7c1d8058 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/DictItemServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/DictItemServiceImpl.java @@ -3,7 +3,7 @@ package com.youlai.boot.system.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.youlai.boot.system.converter.DictDataConverter; +import com.youlai.boot.system.converter.DictItemConverter; import com.youlai.boot.system.mapper.DictItemMapper; import com.youlai.boot.system.model.entity.DictItem; import com.youlai.boot.system.model.form.DictItemForm; @@ -27,7 +27,7 @@ import java.util.List; @RequiredArgsConstructor public class DictItemServiceImpl extends ServiceImpl implements DictItemService { - private final DictDataConverter dictDataConverter; + private final DictItemConverter dictItemConverter; /** * 获取字典项分页列表 @@ -78,7 +78,7 @@ public class DictItemServiceImpl extends ServiceImpl i @Override public DictItemForm getDictItemForm( Long itemId) { DictItem entity = this.getById(itemId); - return dictDataConverter.toForm(entity); + return dictItemConverter.toForm(entity); } /** @@ -89,7 +89,7 @@ public class DictItemServiceImpl extends ServiceImpl i */ @Override public boolean saveDictItem(DictItemForm formData) { - DictItem entity = dictDataConverter.toEntity(formData); + DictItem entity = dictItemConverter.toEntity(formData); return this.save(entity); } @@ -101,7 +101,7 @@ public class DictItemServiceImpl extends ServiceImpl i */ @Override public boolean updateDictItem(DictItemForm formData) { - DictItem entity = dictDataConverter.toEntity(formData); + DictItem entity = dictItemConverter.toEntity(formData); return this.updateById(entity); } @@ -112,7 +112,9 @@ public class DictItemServiceImpl extends ServiceImpl i */ @Override public void deleteDictItemByIds(String ids) { - List idList = Arrays.stream(ids.split(",")).map(Long::parseLong).toList(); + List idList = Arrays.stream(ids.split(",")) + .map(Long::parseLong) + .toList(); this.removeByIds(idList); } diff --git a/src/main/java/com/youlai/boot/system/service/impl/DictServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/DictServiceImpl.java index 8bd1d11f..256f76bb 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/DictServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/DictServiceImpl.java @@ -12,7 +12,6 @@ import com.youlai.boot.system.model.entity.Dict; import com.youlai.boot.system.model.entity.DictItem; import com.youlai.boot.system.model.form.DictForm; import com.youlai.boot.system.model.query.DictPageQuery; -import com.youlai.boot.system.model.vo.DictItemOptionVO; import com.youlai.boot.system.model.vo.DictPageVO; import com.youlai.boot.system.service.DictItemService; import com.youlai.boot.system.service.DictService; @@ -23,7 +22,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; /** - * 数据字典业务实现类 + * 字典业务实现类 * * @author haoxr * @since 2022/10/12 @@ -110,20 +109,23 @@ public class DictServiceImpl extends ServiceImpl implements Di */ @Override public boolean updateDict(Long id, DictForm dictForm) { - // 更新字典 - Dict entity = dictConverter.toEntity(dictForm); - - // 校验 code 是否唯一 - String dictCode = entity.getDictCode(); - long count = this.count(new LambdaQueryWrapper() - .eq(Dict::getDictCode, dictCode) - .ne(Dict::getId, id) - ); - if (count > 0) { - throw new BusinessException("字典编码已存在"); + // 获取字典 + Dict entity = this.getById(id); + if (entity == null) { + throw new BusinessException("字典不存在"); } - - return this.updateById(entity); + // 校验 code 是否唯一 + String dictCode = dictForm.getDictCode(); + if (!entity.getDictCode().equals(dictCode)) { + long count = this.count(new LambdaQueryWrapper() + .eq(Dict::getDictCode, dictCode) + ); + Assert.isTrue(count == 0, "字典编码已存在"); + } + // 更新字典 + Dict dict = dictConverter.toEntity(dictForm); + dict.setId(id); + return this.updateById(dict); } /** @@ -131,25 +133,34 @@ public class DictServiceImpl extends ServiceImpl implements Di * * @param ids 字典ID,多个以英文逗号(,)分割 */ - @Override @Transactional + @Override public void deleteDictByIds(List ids) { - for (String id : ids) { - Dict dict = this.getById(id); - if (dict != null) { - boolean removeResult = this.removeById(id); - // 删除字典下的字典项 - if (removeResult) { - dictItemService.remove( - new LambdaQueryWrapper() - .eq(DictItem::getDictCode, dict.getDictCode()) - ); - } + // 删除字典 + this.removeByIds(ids); - } + // 删除字典项 + List list = this.listByIds(ids); + if (!list.isEmpty()) { + List dictCodes = list.stream().map(Dict::getDictCode).toList(); + dictItemService.remove(new LambdaQueryWrapper() + .in(DictItem::getDictCode, dictCodes) + ); } } + /** + * 根据字典ID列表获取字典编码列表 + * + * @param ids 字典ID列表 + * @return 字典编码列表 + */ + @Override + public List getDictCodesByIds(List ids) { + List dictList = this.listByIds(ids); + return dictList.stream().map(Dict::getDictCode).toList(); + } + } diff --git a/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java index a05f3051..e78c876e 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java @@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.youlai.boot.common.exception.BusinessException; import com.youlai.boot.core.security.util.SecurityUtils; -import com.youlai.boot.shared.websocket.service.OnlineUserService; import com.youlai.boot.system.converter.NoticeConverter; import com.youlai.boot.system.enums.NoticePublishStatusEnum; import com.youlai.boot.system.enums.NoticeTargetEnum; @@ -26,6 +25,7 @@ import com.youlai.boot.system.model.vo.UserNoticePageVO; import com.youlai.boot.system.model.vo.NoticeDetailVO; import com.youlai.boot.system.service.NoticeService; import com.youlai.boot.system.service.UserNoticeService; +import com.youlai.boot.system.service.UserOnlineService; import com.youlai.boot.system.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.messaging.simp.SimpMessagingTemplate; @@ -53,7 +53,7 @@ public class NoticeServiceImpl extends ServiceImpl impleme private final UserNoticeService userNoticeService; private final UserService userService; private final SimpMessagingTemplate messagingTemplate; - private final OnlineUserService onlineUserService; + private final UserOnlineService userOnlineService; /** * 获取通知公告分页列表 @@ -213,7 +213,9 @@ public class NoticeServiceImpl extends ServiceImpl impleme Set receivers = targetUserList.stream().map(User::getUsername).collect(Collectors.toSet()); - Set allOnlineUsers = onlineUserService.getAllOnlineUsers(); + Set allOnlineUsers = userOnlineService.getOnlineUsers().stream() + .map(UserOnlineService.UserOnlineDTO::getUsername) + .collect(Collectors.toSet()); // 找出在线用户的通知接收者 Set onlineReceivers = new HashSet<>(CollectionUtil.intersection(receivers, allOnlineUsers)); diff --git a/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java new file mode 100644 index 00000000..db714dab --- /dev/null +++ b/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java @@ -0,0 +1,269 @@ +package com.youlai.boot.system.service.impl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.youlai.boot.system.model.event.DictEvent; +import com.youlai.boot.system.service.WebSocketService; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * WebSocket服务实现类 + * 统一处理WebSocket消息发送和用户在线状态管理 + * + * @author Ray.Hao + * @since 3.0.0 + */ +@Service +@Slf4j +public class WebSocketServiceImpl implements WebSocketService { + + // 在线用户映射表,key为用户名,value为用户在线信息 + private final Map onlineUsers = new ConcurrentHashMap<>(); + + private SimpMessagingTemplate messagingTemplate; + private final ObjectMapper objectMapper; + + @Autowired + public WebSocketServiceImpl(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Autowired(required = false) + public void setMessagingTemplate(SimpMessagingTemplate messagingTemplate) { + this.messagingTemplate = messagingTemplate; + log.info("WebSocket消息模板已初始化"); + } + + //================================== + // 用户在线状态管理功能 + //================================== + + /** + * 用户上线 + * + * @param username 用户名 + * @param sessionId WebSocket会话ID(可选) + */ + @Override + public void userConnected(String username, String sessionId) { + // 生成会话ID(如果未提供) + String actualSessionId = sessionId != null ? sessionId : "session-" + System.nanoTime(); + UserOnlineInfo info = new UserOnlineInfo(username, actualSessionId, System.currentTimeMillis()); + onlineUsers.put(username, info); + log.info("用户[{}]上线,当前在线用户数:{}", username, onlineUsers.size()); + + // 通知在线用户状态变更 + notifyOnlineUsersChangeInternal(); + } + + /** + * 用户下线 + * + * @param username 用户名 + */ + @Override + public void userDisconnected(String username) { + onlineUsers.remove(username); + log.info("用户[{}]下线,当前在线用户数:{}", username, onlineUsers.size()); + + // 通知在线用户状态变更 + notifyOnlineUsersChangeInternal(); + } + + /** + * 获取在线用户列表 + * + * @return 在线用户名列表 + */ + public List getOnlineUsers() { + return onlineUsers.values().stream() + .map(info -> new UserOnlineDTO(info.getUsername(), info.getLoginTime())) + .collect(Collectors.toList()); + } + + /** + * 获取在线用户数量 + * + * @return 在线用户数 + */ + public int getOnlineUserCount() { + return onlineUsers.size(); + } + + /** + * 检查用户是否在线 + * + * @param username 用户名 + * @return 是否在线 + */ + public boolean isUserOnline(String username) { + return onlineUsers.containsKey(username); + } + + /** + * 手动触发在线用户变更通知 + * 供外部手动触发通知使用 + */ + public void notifyOnlineUsersChange() { + log.info("手动触发在线用户变更通知,当前在线用户数:{}", onlineUsers.size()); + notifyOnlineUsersChangeInternal(); + } + + /** + * 内部通用通知方法 + * 通知所有客户端在线用户变更 + */ + private void notifyOnlineUsersChangeInternal() { + if (messagingTemplate == null) { + log.warn("消息模板尚未初始化,无法发送在线用户变更通知"); + return; + } + + try { + OnlineUsersChangeEvent event = new OnlineUsersChangeEvent(); + event.setType("ONLINE_USERS_CHANGE"); + event.setCount(onlineUsers.size()); + event.setUsers(getOnlineUsers()); + event.setTimestamp(System.currentTimeMillis()); + + String message = objectMapper.writeValueAsString(event); + messagingTemplate.convertAndSend("/topic/online-users", message); + log.debug("已发送在线用户变更通知"); + } catch (JsonProcessingException e) { + log.error("发送在线用户变更事件失败", e); + } + } + + /** + * 用户在线信息 + */ + @Data + private static class UserOnlineInfo { + private final String username; + private final String sessionId; + private final long loginTime; + } + + /** + * 用户在线DTO(用于返回给前端) + */ + @Data + public static class UserOnlineDTO { + private final String username; + private final long loginTime; + } + + /** + * 在线用户变更事件 + */ + @Data + private static class OnlineUsersChangeEvent { + private String type; + private int count; + private List users; + private long timestamp; + } + + //================================== + // WebSocket消息发送功能 + //================================== + + /** + * 向所有客户端发送字典更新事件 + * + * @param dictCode 字典编码 + */ + @Override + public void broadcastDictChange(String dictCode) { + DictEvent event = new DictEvent(dictCode); + sendDictEvent(event); + } + + /** + * 发送字典事件消息 + * + * @param event 字典事件 + */ + private void sendDictEvent(DictEvent event) { + if (messagingTemplate == null) { + log.warn("消息模板尚未初始化,无法发送字典更新通知"); + return; + } + + try { + String message = objectMapper.writeValueAsString(event); + messagingTemplate.convertAndSend("/topic/dict", message); + log.info("已发送字典事件通知, dictCode: {}", event.getDictCode()); + } catch (JsonProcessingException e) { + log.error("发送字典事件失败", e); + } + } + + /** + * 向特定用户发送系统消息 + * + * @param username 用户名 + * @param message 消息内容 + */ + @Override + public void sendNotification(String username, Object message) { + if (messagingTemplate == null) { + log.warn("消息模板尚未初始化,无法发送用户消息"); + return; + } + + try { + String messageJson = objectMapper.writeValueAsString(message); + messagingTemplate.convertAndSendToUser(username, "/queue/messages", messageJson); + log.info("已向用户[{}]发送消息", username); + } catch (JsonProcessingException e) { + log.error("向用户[{}]发送消息失败", username, e); + } + } + + /** + * 发送广播消息给所有用户 + * + * @param message 消息内容 + */ + public void broadcastMessage(String message) { + if (messagingTemplate == null) { + log.warn("消息模板尚未初始化,无法发送广播消息"); + return; + } + + try { + SystemMessage systemMessage = new SystemMessage("系统", message, System.currentTimeMillis()); + String messageJson = objectMapper.writeValueAsString(systemMessage); + messagingTemplate.convertAndSend("/topic/public", messageJson); + log.info("已发送广播消息: {}", message); + } catch (JsonProcessingException e) { + log.error("发送广播消息失败", e); + } + } + + /** + * 系统消息对象 + */ + @Data + public static class SystemMessage { + private String sender; + private String content; + private long timestamp; + + public SystemMessage(String sender, String content, long timestamp) { + this.sender = sender; + this.content = content; + this.timestamp = timestamp; + } + } +} diff --git a/src/main/resources/mapper/system/RoleMenuMapper.xml b/src/main/resources/mapper/system/RoleMenuMapper.xml index c6654581..11fc527d 100644 --- a/src/main/resources/mapper/system/RoleMenuMapper.xml +++ b/src/main/resources/mapper/system/RoleMenuMapper.xml @@ -33,7 +33,7 @@ INNER JOIN sys_role t2 ON t1.role_id = t2.id AND t2.is_deleted = 0 AND t2.`status` = 1 INNER JOIN sys_menu t3 ON t1.menu_id = t3.id WHERE - type = '${@com.youlai.boot.system.enums.MenuTypeEnum@BUTTON.getValue()}' + t3.type = '${@com.youlai.boot.system.enums.MenuTypeEnum@BUTTON.getValue()}' AND t2.`code` = #{roleCode} diff --git a/src/main/resources/templates/codegen/index.vue.vm b/src/main/resources/templates/codegen/index.vue.vm index b7c4021a..67ce5bd2 100644 --- a/src/main/resources/templates/codegen/index.vue.vm +++ b/src/main/resources/templates/codegen/index.vue.vm @@ -26,8 +26,8 @@ #else - 选项一 - 选项二 + 选项一 + 选项二 #end #elseif($fieldConfig.formType == "CHECK_BOX") From 4dfa6ae1d820c114d9b805bdb3a743774e87c851 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Fri, 25 Apr 2025 17:39:11 +0800 Subject: [PATCH 02/60] =?UTF-8?q?wip:=20=E4=BC=98=E5=8C=96=E4=B8=B4?= =?UTF-8?q?=E6=97=B6=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/handler/OnlineUserJobHandler.java | 9 +++-- .../system/service/UserOnlineService.java | 31 +++++++++----- .../service/impl/WebSocketServiceImpl.java | 40 +++++++++++-------- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/youlai/boot/system/handler/OnlineUserJobHandler.java b/src/main/java/com/youlai/boot/system/handler/OnlineUserJobHandler.java index 1bcc405d..39d1b00f 100644 --- a/src/main/java/com/youlai/boot/system/handler/OnlineUserJobHandler.java +++ b/src/main/java/com/youlai/boot/system/handler/OnlineUserJobHandler.java @@ -22,12 +22,13 @@ public class OnlineUserJobHandler { private final UserOnlineService userOnlineService; private final SimpMessagingTemplate messagingTemplate; - // 每分钟统计一次在线用户数 - @Scheduled(cron = "0 * * * * ?") + // 每3分钟统计一次在线用户数,减少服务器压力 + @Scheduled(cron = "0 */3 * * * ?") public void execute() { log.info("定时任务:统计在线用户数"); - // 推送在线用户人数 - messagingTemplate.convertAndSend("/topic/onlineUserCount", userOnlineService.getOnlineUserCount()); + // 推送在线用户数量到新主题 + int count = userOnlineService.getOnlineUserCount(); + messagingTemplate.convertAndSend("/topic/online-count", count); } } diff --git a/src/main/java/com/youlai/boot/system/service/UserOnlineService.java b/src/main/java/com/youlai/boot/system/service/UserOnlineService.java index 0632a76c..d3b9794a 100644 --- a/src/main/java/com/youlai/boot/system/service/UserOnlineService.java +++ b/src/main/java/com/youlai/boot/system/service/UserOnlineService.java @@ -107,21 +107,30 @@ public class UserOnlineService { */ private void notifyOnlineUsersChange() { if (messagingTemplate == null) { - log.warn("消息模板尚未初始化,无法发送在线用户变更通知"); + log.warn("消息模板尚未初始化,无法发送在线用户数量"); + return; + } + + // 发送简化版数据(仅数量) + sendOnlineUserCount(); + } + + /** + * 发送在线用户数量(简化版,不包含用户详情) + */ + private void sendOnlineUserCount() { + if (messagingTemplate == null) { + log.warn("消息模板尚未初始化,无法发送在线用户数量"); return; } try { - OnlineUsersChangeEvent event = new OnlineUsersChangeEvent(); - event.setType("ONLINE_USERS_CHANGE"); - event.setCount(onlineUsers.size()); - event.setUsers(getOnlineUsers()); - event.setTimestamp(System.currentTimeMillis()); - - String message = objectMapper.writeValueAsString(event); - messagingTemplate.convertAndSend("/topic/online-users", message); - } catch (JsonProcessingException e) { - log.error("Failed to send online users change event", e); + // 直接发送数量,更轻量 + int count = onlineUsers.size(); + messagingTemplate.convertAndSend("/topic/online-count", count); + log.debug("已发送在线用户数量: {}", count); + } catch (Exception e) { + log.error("发送在线用户数量失败", e); } } diff --git a/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java index db714dab..addbccf5 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java @@ -114,8 +114,27 @@ public class WebSocketServiceImpl implements WebSocketService { * 供外部手动触发通知使用 */ public void notifyOnlineUsersChange() { - log.info("手动触发在线用户变更通知,当前在线用户数:{}", onlineUsers.size()); - notifyOnlineUsersChangeInternal(); + log.info("手动触发在线用户数量通知,当前在线用户数:{}", onlineUsers.size()); + sendOnlineUserCount(); + } + + /** + * 发送在线用户数量(简化版,不包含用户详情) + */ + private void sendOnlineUserCount() { + if (messagingTemplate == null) { + log.warn("消息模板尚未初始化,无法发送在线用户数量"); + return; + } + + try { + // 直接发送数量,更轻量 + int count = onlineUsers.size(); + messagingTemplate.convertAndSend("/topic/online-count", count); + log.debug("已发送在线用户数量: {}", count); + } catch (Exception e) { + log.error("发送在线用户数量失败", e); + } } /** @@ -124,23 +143,12 @@ public class WebSocketServiceImpl implements WebSocketService { */ private void notifyOnlineUsersChangeInternal() { if (messagingTemplate == null) { - log.warn("消息模板尚未初始化,无法发送在线用户变更通知"); + log.warn("消息模板尚未初始化,无法发送在线用户数量通知"); return; } - try { - OnlineUsersChangeEvent event = new OnlineUsersChangeEvent(); - event.setType("ONLINE_USERS_CHANGE"); - event.setCount(onlineUsers.size()); - event.setUsers(getOnlineUsers()); - event.setTimestamp(System.currentTimeMillis()); - - String message = objectMapper.writeValueAsString(event); - messagingTemplate.convertAndSend("/topic/online-users", message); - log.debug("已发送在线用户变更通知"); - } catch (JsonProcessingException e) { - log.error("发送在线用户变更事件失败", e); - } + // 只发送简化版数据(仅数量) + sendOnlineUserCount(); } /** From 42e25a0e5832c2b3472f6ef9eda792c9e427657a Mon Sep 17 00:00:00 2001 From: wx <473856015@qq.com> Date: Fri, 16 May 2025 16:27:48 +0800 Subject: [PATCH 03/60] =?UTF-8?q?fix:=E5=9C=A8=E4=B8=AA=E4=BA=BA=E4=B8=AD?= =?UTF-8?q?=E5=BF=83=E4=BF=AE=E6=94=B9=E5=AF=86=E7=A0=81=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E5=AD=97=E6=AE=B5=E4=B8=8D=E5=AD=98=E5=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boot/system/model/form/PasswordUpdateForm.java | 2 ++ .../youlai/boot/system/service/impl/UserServiceImpl.java | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/youlai/boot/system/model/form/PasswordUpdateForm.java b/src/main/java/com/youlai/boot/system/model/form/PasswordUpdateForm.java index 453207a2..aaab86d7 100644 --- a/src/main/java/com/youlai/boot/system/model/form/PasswordUpdateForm.java +++ b/src/main/java/com/youlai/boot/system/model/form/PasswordUpdateForm.java @@ -19,4 +19,6 @@ public class PasswordUpdateForm { @Schema(description = "新密码") private String newPassword; + @Schema(description = "确认密码") + private String confirmPassword; } 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 d950ffa9..5a8a6f42 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 @@ -12,8 +12,9 @@ import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SystemConstants; import com.youlai.boot.common.exception.BusinessException; import com.youlai.boot.common.model.Option; -import com.youlai.boot.core.security.token.TokenManager; +import com.youlai.boot.core.security.model.UserAuthCredentials; import com.youlai.boot.core.security.service.PermissionService; +import com.youlai.boot.core.security.token.TokenManager; import com.youlai.boot.core.security.util.SecurityUtils; import com.youlai.boot.shared.mail.service.MailService; import com.youlai.boot.shared.sms.enums.SmsTypeEnum; @@ -22,7 +23,6 @@ import com.youlai.boot.system.converter.UserConverter; import com.youlai.boot.system.enums.DictCodeEnum; import com.youlai.boot.system.mapper.UserMapper; import com.youlai.boot.system.model.bo.UserBO; -import com.youlai.boot.core.security.model.UserAuthCredentials; import com.youlai.boot.system.model.dto.CurrentUserDTO; import com.youlai.boot.system.model.dto.UserExportDTO; import com.youlai.boot.system.model.entity.DictItem; @@ -398,6 +398,11 @@ public class UserServiceImpl extends ServiceImpl implements Us throw new BusinessException("新密码不能与原密码相同"); } + // 判断新密码和确认密码是否一致 + if (passwordEncoder.matches(data.getNewPassword(), data.getConfirmPassword())) { + throw new BusinessException("新密码和确认密码不一致"); + } + String newPassword = data.getNewPassword(); boolean result = this.update(new LambdaUpdateWrapper() .eq(User::getId, userId) From fa86addc49b61720586b8d6c6831c47537fc4d1a Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 21 May 2025 15:39:24 +0800 Subject: [PATCH 04/60] =?UTF-8?q?fix(log):=20=E4=BF=AE=E5=A4=8D=E9=80=80?= =?UTF-8?q?=E5=87=BA=E7=99=BB=E5=BD=95=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E8=8E=B7=E5=8F=96=E6=93=8D=E4=BD=9C=E4=BA=BA?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/youlai/boot/core/aspect/LogAspect.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/youlai/boot/core/aspect/LogAspect.java b/src/main/java/com/youlai/boot/core/aspect/LogAspect.java index f39a7562..40526ead 100644 --- a/src/main/java/com/youlai/boot/core/aspect/LogAspect.java +++ b/src/main/java/com/youlai/boot/core/aspect/LogAspect.java @@ -60,6 +60,9 @@ public class LogAspect { */ @Around("logPointcut() && @annotation(logAnnotation)") public Object doAround(ProceedingJoinPoint joinPoint, com.youlai.boot.common.annotation.Log logAnnotation) throws Throwable { + // 在方法执行前获取用户ID,避免在方法执行过程中清除上下文导致获取不到用户ID + Long userId = SecurityUtils.getUserId(); + TimeInterval timer = DateUtil.timer(); Object result = null; Exception exception = null; @@ -71,7 +74,7 @@ public class LogAspect { throw e; } finally { long executionTime = timer.interval(); // 执行时长 - this.saveLog(joinPoint, exception, result, logAnnotation, executionTime); + this.saveLog(joinPoint, exception, result, logAnnotation, executionTime, userId); } return result; } @@ -84,8 +87,9 @@ public class LogAspect { * @param e 异常 * @param jsonResult 响应结果 * @param logAnnotation 日志注解 + * @param userId 用户ID */ - private void saveLog(final JoinPoint joinPoint, final Exception e, Object jsonResult, com.youlai.boot.common.annotation.Log logAnnotation, long executionTime) { + private void saveLog(final JoinPoint joinPoint, final Exception e, Object jsonResult, com.youlai.boot.common.annotation.Log logAnnotation, long executionTime, Long userId) { String requestURI = request.getRequestURI(); // 创建日志记录 Log log = new Log(); @@ -108,7 +112,6 @@ public class LogAspect { } } log.setRequestUri(requestURI); - Long userId = SecurityUtils.getUserId(); log.setCreateBy(userId); String ipAddr = IPUtils.getIpAddr(request); if (StrUtil.isNotBlank(ipAddr)) { From ea7eba05e1cfabdd7f4716ad463c748fd6897caf Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 21 May 2025 15:41:35 +0800 Subject: [PATCH 05/60] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E6=97=A0?= =?UTF-8?q?=E6=9D=83=E6=93=8D=E4=BD=9C=E6=95=B0=E6=8D=AE=E5=BA=93=E7=9A=84?= =?UTF-8?q?=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../youlai/boot/common/exception/GlobalExceptionHandler.java | 2 +- src/main/java/com/youlai/boot/common/result/ResultCode.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java b/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java index cf7722a8..519c1ca9 100644 --- a/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java @@ -192,7 +192,7 @@ public class GlobalExceptionHandler { log.error(e.getMessage(), e); String errorMsg = e.getMessage(); if (StrUtil.isNotBlank(errorMsg) && errorMsg.contains("denied to user")) { - return Result.failed(ResultCode.ACCESS_UNAUTHORIZED); + return Result.failed(ResultCode.DATABASE_ACCESS_DENIED); } else { return Result.failed(e.getMessage()); } diff --git a/src/main/java/com/youlai/boot/common/result/ResultCode.java b/src/main/java/com/youlai/boot/common/result/ResultCode.java index 7491e913..e55042da 100644 --- a/src/main/java/com/youlai/boot/common/result/ResultCode.java +++ b/src/main/java/com/youlai/boot/common/result/ResultCode.java @@ -250,6 +250,8 @@ public enum ResultCode implements IResultCode, Serializable { PRIMARY_KEY_CONFLICT("C0341", "主键冲突"), + DATABASE_ACCESS_DENIED("C0351", "演示环境已禁用数据库写入功能,请本地部署修改数据库链接或开启Mock模式进行体验"), + /** 二级宏观错误码 */ THIRD_PARTY_DISASTER_RECOVERY_SYSTEM_TRIGGERED("C0400", "第三方容灾系统被触发"), THIRD_PARTY_SYSTEM_RATE_LIMITING("C0401", "第三方系统限流"), From ecc5f6f780c4d55f774ead03080d7093565d10b3 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 21 May 2025 15:44:52 +0800 Subject: [PATCH 06/60] =?UTF-8?q?docs:=20=E5=AE=8C=E5=96=84=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/youlai/boot/system/controller/DeptController.java | 2 +- .../com/youlai/boot/system/controller/DictController.java | 2 +- .../com/youlai/boot/system/controller/RoleController.java | 4 ++-- .../com/youlai/boot/system/controller/UserController.java | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/youlai/boot/system/controller/DeptController.java b/src/main/java/com/youlai/boot/system/controller/DeptController.java index 68b62ecf..39016286 100644 --- a/src/main/java/com/youlai/boot/system/controller/DeptController.java +++ b/src/main/java/com/youlai/boot/system/controller/DeptController.java @@ -22,7 +22,7 @@ import java.util.List; /** * 部门控制器 * - * @author haoxr + * @author Ray.Hao * @since 2020/11/6 */ @Tag(name = "05.部门接口") diff --git a/src/main/java/com/youlai/boot/system/controller/DictController.java b/src/main/java/com/youlai/boot/system/controller/DictController.java index 252af202..e4825301 100644 --- a/src/main/java/com/youlai/boot/system/controller/DictController.java +++ b/src/main/java/com/youlai/boot/system/controller/DictController.java @@ -66,7 +66,7 @@ public class DictController { return Result.success(list); } - @Operation(summary = "字典表单数据") + @Operation(summary = "获取字典表单数据") @GetMapping("/{id}/form") public Result getDictForm( @Parameter(description = "字典ID") @PathVariable Long id diff --git a/src/main/java/com/youlai/boot/system/controller/RoleController.java b/src/main/java/com/youlai/boot/system/controller/RoleController.java index b5b7419a..eaabbdf1 100644 --- a/src/main/java/com/youlai/boot/system/controller/RoleController.java +++ b/src/main/java/com/youlai/boot/system/controller/RoleController.java @@ -62,7 +62,7 @@ public class RoleController { return Result.judge(result); } - @Operation(summary = "角色表单数据") + @Operation(summary = "获取角色表单数据") @GetMapping("/{roleId}/form") public Result getRoleForm( @Parameter(description = "角色ID") @PathVariable Long roleId @@ -108,7 +108,7 @@ public class RoleController { return Result.success(menuIds); } - @Operation(summary = "分配菜单(包括按钮权限)给角色") + @Operation(summary = "角色分配菜单权限") @PutMapping("/{roleId}/menus") public Result assignMenusToRole( @PathVariable Long roleId, diff --git a/src/main/java/com/youlai/boot/system/controller/UserController.java b/src/main/java/com/youlai/boot/system/controller/UserController.java index c9080fd1..69a4d41b 100644 --- a/src/main/java/com/youlai/boot/system/controller/UserController.java +++ b/src/main/java/com/youlai/boot/system/controller/UserController.java @@ -78,7 +78,7 @@ public class UserController { return Result.judge(result); } - @Operation(summary = "用户表单数据") + @Operation(summary = "获取用户表单数据") @GetMapping("/{userId}/form") @Log(value = "用户表单数据", module = LogModuleEnum.USER) public Result getUserForm( @@ -248,7 +248,7 @@ public class UserController { return Result.judge(result); } - @Operation(summary = "用户下拉选项") + @Operation(summary = "获取用户下拉选项") @GetMapping("/options") public Result>> listUserOptions() { List> list = userService.listUserOptions(); From 8f9d828205660122fc11b61dbe742dfc1548ca76 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Tue, 27 May 2025 18:29:44 +0800 Subject: [PATCH 07/60] =?UTF-8?q?fix:=20=E7=94=A8=E6=88=B7=E8=A7=92?= =?UTF-8?q?=E8=89=B2=E5=8F=98=E5=8C=96=E5=BC=BA=E5=88=B6=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E9=80=80=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boot/system/service/UserRoleService.java | 6 +- .../service/impl/UserRoleServiceImpl.java | 82 +++++++++++-------- .../system/service/impl/UserServiceImpl.java | 14 ++-- 3 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/main/java/com/youlai/boot/system/service/UserRoleService.java b/src/main/java/com/youlai/boot/system/service/UserRoleService.java index 697b86ca..e1d8c033 100644 --- a/src/main/java/com/youlai/boot/system/service/UserRoleService.java +++ b/src/main/java/com/youlai/boot/system/service/UserRoleService.java @@ -11,11 +11,11 @@ public interface UserRoleService extends IService { /** * 保存用户角色 * - * @param userId - * @param roleIds + * @param userId 用户ID + * @param roleIds 角色ID集合 * @return */ - boolean saveUserRoles(Long userId, List roleIds); + void saveUserRoles(Long userId, List roleIds); /** * 判断角色是否存在绑定的用户 diff --git a/src/main/java/com/youlai/boot/system/service/impl/UserRoleServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/UserRoleServiceImpl.java index ce747648..d40c75c8 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/UserRoleServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/UserRoleServiceImpl.java @@ -3,69 +3,81 @@ package com.youlai.boot.system.service.impl; import cn.hutool.core.collection.CollectionUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.youlai.boot.core.security.token.TokenManager; +import com.youlai.boot.core.security.util.SecurityUtils; import com.youlai.boot.system.mapper.UserRoleMapper; import com.youlai.boot.system.model.entity.UserRole; import com.youlai.boot.system.service.UserRoleService; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @Service +@RequiredArgsConstructor public class UserRoleServiceImpl extends ServiceImpl implements UserRoleService { + + private final TokenManager tokenManager; + /** * 保存用户角色 * - * @param userId - * @param roleIds + * @param userId 用户ID + * @param roleIds 选择的角色ID集合 * @return */ @Override - public boolean saveUserRoles(Long userId, List roleIds) { - + public void saveUserRoles(Long userId, List roleIds) { if (userId == null || CollectionUtil.isEmpty(roleIds)) { - return false; + return ; } - // 用户原角色ID集合 + // 获取现有角色 List userRoleIds = this.list(new LambdaQueryWrapper() + .select(UserRole::getRoleId) .eq(UserRole::getUserId, userId)) - .stream() + .parallelStream() .map(UserRole::getRoleId) - .collect(Collectors.toList()); + .toList(); - // 新增用户角色 - List saveRoleIds; - if (CollectionUtil.isEmpty(userRoleIds)) { - saveRoleIds = roleIds; - } else { - saveRoleIds = roleIds.stream() - .filter(roleId -> !userRoleIds.contains(roleId)) - .collect(Collectors.toList()); + // 使用Set提升对比效率 + Set oldRoles = new HashSet<>(userRoleIds); + Set newRoles = new HashSet<>(roleIds); + + // 计算变更集 + Set addedRoles = new HashSet<>(newRoles); + addedRoles.removeAll(oldRoles); + + Set removedRoles = new HashSet<>(oldRoles); + removedRoles.removeAll(newRoles); + + boolean rolesChanged = !addedRoles.isEmpty() || !removedRoles.isEmpty(); + + // 批量保存新增角色 + if (!addedRoles.isEmpty()) { + this.saveBatch(addedRoles.stream() + .map(roleId -> new UserRole(userId, roleId)) + .collect(Collectors.toList())); } - List saveUserRoles = saveRoleIds - .stream() - .map(roleId -> new UserRole(userId, roleId)) - .collect(Collectors.toList()); - this.saveBatch(saveUserRoles); - - // 删除用户角色 - if (CollectionUtil.isNotEmpty(userRoleIds)) { - List removeRoleIds = userRoleIds.stream() - .filter(roleId -> !roleIds.contains(roleId)) - .collect(Collectors.toList()); - - if (CollectionUtil.isNotEmpty(removeRoleIds)) { - this.remove(new LambdaQueryWrapper() - .eq(UserRole::getUserId, userId) - .in(UserRole::getRoleId, removeRoleIds) - ); - } + // 删除废弃角色 + if (!removedRoles.isEmpty()) { + this.remove(new LambdaQueryWrapper() + .eq(UserRole::getUserId, userId) + .in(UserRole::getRoleId, removedRoles)); } - return true; + // 当权限变更时清除登录态 + if (rolesChanged) { + // 获取用户所有有效token(根据实际token存储实现) + String accessToken = SecurityUtils.getTokenFromRequest(); + tokenManager.invalidateToken(accessToken); + } } /** 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 d950ffa9..b67d66d0 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 @@ -34,6 +34,7 @@ import com.youlai.boot.system.model.vo.UserPageVO; import com.youlai.boot.system.model.vo.UserProfileVO; import com.youlai.boot.system.service.*; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -51,6 +52,7 @@ import java.util.stream.Collectors; */ @Service @RequiredArgsConstructor +@Slf4j public class UserServiceImpl extends ServiceImpl implements UserService { private final PasswordEncoder passwordEncoder; @@ -436,9 +438,9 @@ public class UserServiceImpl extends ServiceImpl implements Us @Override public boolean sendMobileCode(String mobile) { - // String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000)); - // TODO 为了方便测试,验证码固定为 1234,实际开发中在配置了厂商短信服务后,可以使用上面的随机验证码 - String code = "1234"; + String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000)); + + log.info("【调试模式】手机号 {} 的验证码为:{}", mobile, code); Map templateParams = new HashMap<>(); templateParams.put("code", code); @@ -500,9 +502,9 @@ public class UserServiceImpl extends ServiceImpl implements Us @Override public void sendEmailCode(String email) { - // String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000)); - // TODO 为了方便测试,验证码固定为 1234,实际开发中在配置了邮箱服务后,可以使用上面的随机验证码 - String code = "1234"; + String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000)); + + log.info("【调试模式】邮箱 {} 的验证码为:{}", email, code); mailService.sendMail(email, "邮箱验证码", "您的验证码为:" + code + ",请在5分钟内使用"); // 缓存验证码,5分钟有效,用于更换邮箱校验 From 1ff53a1c963f1e067e80d0d2b74ec6ae627031a3 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 28 May 2025 18:27:30 +0800 Subject: [PATCH 08/60] =?UTF-8?q?fix:=20search-bar=20=E2=86=92=20search-co?= =?UTF-8?q?ntainer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/templates/codegen/index.vue.vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/codegen/index.vue.vm b/src/main/resources/templates/codegen/index.vue.vm index 67ce5bd2..0c256c33 100644 --- a/src/main/resources/templates/codegen/index.vue.vm +++ b/src/main/resources/templates/codegen/index.vue.vm @@ -1,6 +1,6 @@ @@ -40,44 +63,84 @@ From 690265b17713a2b9e2c2c4101a42e799e98ffcf9 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 17 Sep 2025 15:32:34 +0800 Subject: [PATCH 36/60] =?UTF-8?q?build(pom):=20=E5=8D=87=E7=BA=A7=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=89=88=E6=9C=AC=E5=B9=B6=E6=9B=B4=E6=96=B0=20Spring?= =?UTF-8?q?=20Boot=20=E4=BE=9D=E8=B5=96-=20=E5=B0=86=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E4=BB=8E=203.1.0=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=203.2.0=20-=20=E5=B0=86=20Spring=20Boot=20=E7=9A=84?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E4=BB=8E=203.5.0=20=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E5=88=B03.5.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1639be87..214010ed 100644 --- a/pom.xml +++ b/pom.xml @@ -6,13 +6,13 @@ com.youlai youlai-boot - 3.1.0 + 3.2.0 基于 Java 17 + SpringBoot 3 + Spring Security 构建的权限管理系统。 org.springframework.boot spring-boot-starter-parent - 3.5.0 + 3.5.5 From 4b39373c6de75f1b5f1114a2ec32a9e2280d665e Mon Sep 17 00:00:00 2001 From: theo <971366405@qq.com> Date: Fri, 19 Sep 2025 14:58:27 +0800 Subject: [PATCH 37/60] =?UTF-8?q?feat(exception):=20=E5=A2=9E=E5=8A=A0=20S?= =?UTF-8?q?QL=20=E5=AE=8C=E6=95=B4=E6=80=A7=E7=BA=A6=E6=9D=9F=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E5=A4=84=E7=90=86-=20=E6=96=B0=E5=A2=9E=20SQLIntegrit?= =?UTF-8?q?yConstraintViolationException=20=E5=BC=82=E5=B8=B8=E5=A4=84?= =?UTF-8?q?=E7=90=86=E6=96=B9=E6=B3=95=20-=20=E6=B7=BB=E5=8A=A0=E7=9B=B8?= =?UTF-8?q?=E5=BA=94=E7=9A=84=E9=94=99=E8=AF=AF=E7=A0=81=E5=92=8C=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E4=BF=A1=E6=81=AF-=20=E4=BC=98=E5=8C=96=20SQLSyntaxEr?= =?UTF-8?q?rorException=20=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86=EF=BC=8C?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E9=94=99=E8=AF=AF=E7=A0=81=E4=BB=A3=E6=9B=BF?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E8=BF=94=E5=9B=9E=E9=94=99=E8=AF=AF=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 23 +++++++++++++++---- .../youlai/boot/common/result/ResultCode.java | 2 ++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java b/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java index 519c1ca9..434921a2 100644 --- a/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java @@ -4,6 +4,9 @@ import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.youlai.boot.common.result.Result; import com.youlai.boot.common.result.ResultCode; +import jakarta.servlet.ServletException; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.TypeMismatchException; import org.springframework.context.support.DefaultMessageSourceResolvable; @@ -21,10 +24,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.NoHandlerFoundException; -import jakarta.servlet.ServletException; -import jakarta.validation.ConstraintViolation; -import jakarta.validation.ConstraintViolationException; - +import java.sql.SQLIntegrityConstraintViolationException; import java.sql.SQLSyntaxErrorException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -207,7 +207,20 @@ public class GlobalExceptionHandler { @ResponseStatus(HttpStatus.FORBIDDEN) public Result processSQLSyntaxErrorException(SQLSyntaxErrorException e) { log.error(e.getMessage(), e); - return Result.failed(e.getMessage()); + return Result.failed(ResultCode.DATABASE_EXECUTION_SYNTAX_ERROR); + } + + + /** + * 处理 SQL 违反了完整性约束 + *

+ * 当 SQL 违反了完整性约束时,会抛出 SQLIntegrityConstraintViolationException 异常。 + */ + @ExceptionHandler(SQLIntegrityConstraintViolationException.class) + @ResponseStatus(HttpStatus.FORBIDDEN) + public Result handleSQLIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException e) { + log.error(e.getMessage(), e); + return Result.failed(ResultCode.INTEGRITY_CONSTRAINT_VIOLATION); } /** diff --git a/src/main/java/com/youlai/boot/common/result/ResultCode.java b/src/main/java/com/youlai/boot/common/result/ResultCode.java index e55042da..2876e6c1 100644 --- a/src/main/java/com/youlai/boot/common/result/ResultCode.java +++ b/src/main/java/com/youlai/boot/common/result/ResultCode.java @@ -243,12 +243,14 @@ public enum ResultCode implements IResultCode, Serializable { TABLE_NOT_EXIST("C0311", "表不存在"), COLUMN_NOT_EXIST("C0312", "列不存在"), + DATABASE_EXECUTION_SYNTAX_ERROR("C0313", "数据库执行语法错误"), MULTIPLE_SAME_NAME_COLUMNS_IN_MULTI_TABLE_ASSOCIATION("C0321", "多表关联中存在多个相同名称的列"), DATABASE_DEADLOCK("C0331", "数据库死锁"), PRIMARY_KEY_CONFLICT("C0341", "主键冲突"), + INTEGRITY_CONSTRAINT_VIOLATION("C0342", "违反了完整性约束"), DATABASE_ACCESS_DENIED("C0351", "演示环境已禁用数据库写入功能,请本地部署修改数据库链接或开启Mock模式进行体验"), From 1f92b7a8eee89440dded34aa806c84e78ff009bd Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Tue, 23 Sep 2025 17:21:15 +0800 Subject: [PATCH 38/60] =?UTF-8?q?fix(sql):=20=E8=8F=9C=E5=8D=95=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E7=BC=BA=E5=B0=91=E8=B7=AF=E7=94=B1=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/mysql/youlai_boot.sql | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/sql/mysql/youlai_boot.sql b/sql/mysql/youlai_boot.sql index d3a5dd23..c24a3590 100644 --- a/sql/mysql/youlai_boot.sql +++ b/sql/mysql/youlai_boot.sql @@ -153,11 +153,11 @@ INSERT INTO `sys_menu` VALUES (31, 2, '0,1,2', '用户新增', 4, NULL, '', NULL INSERT INTO `sys_menu` VALUES (32, 2, '0,1,2', '用户编辑', 4, NULL, '', NULL, 'sys:user:edit', NULL, NULL, 1, 2, '', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (33, 2, '0,1,2', '用户删除', 4, NULL, '', NULL, 'sys:user:delete', NULL, NULL, 1, 3, '', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (36, 0, '0', '组件封装', 2, NULL, '/component', 'Layout', NULL, NULL, NULL, 1, 10, 'menu', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (37, 36, '0,36', '富文本编辑器', 1, NULL, 'wang-editor', 'demo/wang-editor', NULL, NULL, 1, 1, 2, '', '', NULL, NULL, NULL); -INSERT INTO `sys_menu` VALUES (38, 36, '0,36', '图片上传', 1, NULL, 'upload', 'demo/upload', NULL, NULL, 1, 1, 3, '', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (39, 36, '0,36', '图标选择器', 1, NULL, 'icon-selector', 'demo/icon-selector', NULL, NULL, 1, 1, 4, '', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (37, 36, '0,36', '富文本编辑器', 1, 'WangEditor', 'wang-editor', 'demo/wang-editor', NULL, NULL, 1, 1, 2, '', '', NULL, NULL, NULL); +INSERT INTO `sys_menu` VALUES (38, 36, '0,36', '图片上传', 1, 'Upload', 'upload', 'demo/upload', NULL, NULL, 1, 1, 3, '', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (39, 36, '0,36', '图标选择器', 1, 'IconSelect', 'icon-select', 'demo/icon-select', NULL, NULL, 1, 1, 4, '', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (40, 0, '0', '接口文档', 2, NULL, '/api', 'Layout', NULL, 1, NULL, 1, 7, 'api', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (41, 40, '0,40', 'Apifox', 1, NULL, 'apifox', 'demo/api/apifox', NULL, NULL, 1, 1, 1, 'api', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (41, 40, '0,40', 'Apifox', 1, 'Apifox', 'apifox', 'demo/api/apifox', NULL, NULL, 1, 1, 1, 'api', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (70, 3, '0,1,3', '角色新增', 4, NULL, '', NULL, 'sys:role:add', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL); INSERT INTO `sys_menu` VALUES (71, 3, '0,1,3', '角色编辑', 4, NULL, '', NULL, 'sys:role:edit', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL); INSERT INTO `sys_menu` VALUES (72, 3, '0,1,3', '角色删除', 4, NULL, '', NULL, 'sys:role:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL); @@ -172,18 +172,18 @@ INSERT INTO `sys_menu` VALUES (81, 6, '0,1,6', '字典编辑', 4, NULL, '', NULL INSERT INTO `sys_menu` VALUES (84, 6, '0,1,6', '字典删除', 4, NULL, '', NULL, 'sys:dict:delete', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL); INSERT INTO `sys_menu` VALUES (88, 2, '0,1,2', '重置密码', 4, NULL, '', NULL, 'sys:user:reset-password', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL); INSERT INTO `sys_menu` VALUES (89, 0, '0', '功能演示', 2, NULL, '/function', 'Layout', NULL, NULL, NULL, 1, 12, 'menu', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (90, 89, '0,89', 'Websocket', 1, NULL, '/function/websocket', 'demo/websocket', NULL, NULL, 1, 1, 3, '', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (95, 36, '0,36', '字典组件', 1, NULL, 'dict-demo', 'demo/dictionary', NULL, NULL, 1, 1, 4, '', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (97, 89, '0,89', 'Icons', 1, NULL, 'icon-demo', 'demo/icons', NULL, NULL, 1, 1, 2, 'el-icon-Notification', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (102, 26, '0,26', 'document', 3, '', 'internal-doc', 'demo/internal-doc', NULL, NULL, NULL, 1, 1, 'document', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (90, 89, '0,89', 'Websocket', 1, 'WebSocket', '/function/websocket', 'demo/websocket', NULL, NULL, 1, 1, 3, '', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (95, 36, '0,36', '字典组件', 1, 'DictDemo', 'dict-demo', 'demo/dictionary', NULL, NULL, 1, 1, 4, '', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (97, 89, '0,89', 'Icons', 1, 'IconDemo', 'icon-demo', 'demo/icons', NULL, NULL, 1, 1, 2, 'el-icon-Notification', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (102, 26, '0,26', 'document', 3, NULL, 'internal-doc', 'demo/internal-doc', NULL, NULL, NULL, 1, 1, 'document', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (105, 2, '0,1,2', '用户查询', 4, NULL, '', NULL, 'sys:user:query', 0, 0, 1, 0, '', NULL, now(), now(), NULL); INSERT INTO `sys_menu` VALUES (106, 2, '0,1,2', '用户导入', 4, NULL, '', NULL, 'sys:user:import', NULL, NULL, 1, 5, '', NULL, now(), now(), NULL); INSERT INTO `sys_menu` VALUES (107, 2, '0,1,2', '用户导出', 4, NULL, '', NULL, 'sys:user:export', NULL, NULL, 1, 6, '', NULL, now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (108, 36, '0,36', '增删改查', 1, NULL, 'curd', 'demo/curd/index', NULL, NULL, 1, 1, 0, '', '', NULL, NULL, NULL); -INSERT INTO `sys_menu` VALUES (109, 36, '0,36', '列表选择器', 1, NULL, 'table-select', 'demo/table-select/index', NULL, NULL, 1, 1, 1, '', '', NULL, NULL, NULL); +INSERT INTO `sys_menu` VALUES (108, 36, '0,36', '增删改查', 1, 'Curd', 'curd', 'demo/curd/index', NULL, NULL, 1, 1, 0, '', '', NULL, NULL, NULL); +INSERT INTO `sys_menu` VALUES (109, 36, '0,36', '列表选择器', 1, 'TableSelect', 'table-select', 'demo/table-select/index', NULL, NULL, 1, 1, 1, '', '', NULL, NULL, NULL); INSERT INTO `sys_menu` VALUES (110, 0, '0', '路由参数', 2, NULL, '/route-param', 'Layout', NULL, 1, 1, 1, 11, 'el-icon-ElementPlus', NULL, now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (111, 110, '0,110', '参数(type=1)', 1, NULL, 'route-param-type1', 'demo/route-param', NULL, 0, 1, 1, 1, 'el-icon-Star', NULL, now(), now(), '{\"type\": \"1\"}'); -INSERT INTO `sys_menu` VALUES (112, 110, '0,110', '参数(type=2)', 1, NULL, 'route-param-type2', 'demo/route-param', NULL, 0, 1, 1, 2, 'el-icon-StarFilled', NULL, now(), now(), '{\"type\": \"2\"}'); +INSERT INTO `sys_menu` VALUES (111, 110, '0,110', '参数(type=1)', 1, 'RouteParamType1', 'route-param-type1', 'demo/route-param', NULL, 0, 1, 1, 1, 'el-icon-Star', NULL, now(), now(), '{\"type\": \"1\"}'); +INSERT INTO `sys_menu` VALUES (112, 110, '0,110', '参数(type=2)', 1, 'RouteParamType2', 'route-param-type2', 'demo/route-param', NULL, 0, 1, 1, 2, 'el-icon-StarFilled', NULL, now(), now(), '{\"type\": \"2\"}'); INSERT INTO `sys_menu` VALUES (117, 1, '0,1', '系统日志', 1, 'Log', 'log', 'system/log/index', NULL, 0, 1, 1, 6, 'document', NULL, now(), now(), NULL); INSERT INTO `sys_menu` VALUES (118, 0, '0', '系统工具', 2, NULL, '/codegen', 'Layout', NULL, 0, 1, 1, 2, 'menu', NULL, now(), now(), NULL); INSERT INTO `sys_menu` VALUES (119, 118, '0,118', '代码生成', 1, 'Codegen', 'codegen', 'codegen/index', NULL, 0, 1, 1, 1, 'code', NULL, now(), now(), NULL); @@ -211,13 +211,12 @@ INSERT INTO `sys_menu` VALUES (142, 6, '0,1,6', '字典查询', 4, NULL, '', NUL INSERT INTO `sys_menu` VALUES (143, 135, '0,1,135', '字典项查询', 4, NULL, '', NULL, 'sys:dict-item:query', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL); INSERT INTO `sys_menu` VALUES (144, 26, '0,26', '后端文档', 3, NULL, 'https://youlai.blog.csdn.net/article/details/145178880', '', NULL, NULL, NULL, 1, 3, 'document', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (145, 26, '0,26', '移动端文档', 3, NULL, 'https://youlai.blog.csdn.net/article/details/143222890', '', NULL, NULL, NULL, 1, 4, 'document', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (146, 36, '0,36', '拖拽组件', 1, NULL, 'drag', 'demo/drag', NULL, NULL, NULL, 1, 5, '', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (147, 36, '0,36', '滚动文本', 1, NULL, 'text-scroll', 'demo/text-scroll', NULL, NULL, NULL, 1, 6, '', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (148, 89, '0,89', '字典实时同步', 1, NULL, 'dict-sync', 'demo/dict-sync', NULL, NULL, NULL, 1, 3, '', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (149, 89, '0,89', 'VxeTable', 1, NULL, 'vxe-table', 'demo/vxe-table/index', NULL, NULL, 1, 1, 0, 'el-icon-MagicStick', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (150, 36, '0,36', '自适应表格操作列', 1, 'AutoOpreationColumn', 'opreation-column', 'demo/auto-opreation-column', NULL, NULL, 1, 1, 1, '', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (151, 89, '0,89', 'PDF预览', 1, NULL, 'pdf-preview', 'demo/pdf-preview', NULL, NULL, 1, 1, 7, 'el-icon-Reading', '', now(), now(), NULL); - +INSERT INTO `sys_menu` VALUES (146, 36, '0,36', '拖拽组件', 1, 'Drag', 'drag', 'demo/drag', NULL, NULL, NULL, 1, 5, '', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (147, 36, '0,36', '滚动文本', 1, 'TextScroll', 'text-scroll', 'demo/text-scroll', NULL, NULL, NULL, 1, 6, '', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (148, 89, '0,89', '字典实时同步', 1, 'DictSync', 'dict-sync', 'demo/dict-sync', NULL, NULL, NULL, 1, 3, '', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (149, 89, '0,89', 'VxeTable', 1, 'VxeTable', 'vxe-table', 'demo/vxe-table/index', NULL, NULL, 1, 1, 0, 'el-icon-MagicStick', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (150, 36, '0,36', '自适应表格操作列', 1, 'AutoOperationColumn', 'operation-column', 'demo/auto-operation-column', NULL, NULL, 1, 1, 1, '', '', now(), now(), NULL); +INSERT INTO `sys_menu` VALUES (151, 89, '0,89', 'CURD单文件', 1, 'CurdDemo', 'curd-demo', 'demo/curd-demo', NULL, NULL, 1, 1, 7, 'el-icon-Reading', '', now(),now(), NULL); -- ---------------------------- From 1bb80c082a3b6c0a4349cf5ea473077a8e28424c Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 24 Sep 2025 14:40:25 +0800 Subject: [PATCH 39/60] =?UTF-8?q?chore(pom.xml):=20=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E7=89=88=E6=9C=AC=E5=B9=B6=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E6=95=B0=E6=8D=AE=E6=BA=90=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 214010ed..497654ca 100644 --- a/pom.xml +++ b/pom.xml @@ -25,15 +25,16 @@ 9.1.0 1.2.24 3.5.5 + 4.3.1 4.5.0 1.6.3 0.2.0 - 2.4.2 + 3.2.0 - 1.1.0 + 1.3.0 8.5.10 @@ -42,7 +43,7 @@ 3.16.3 - 3.40.2 + 3.51.0 3.5.6 @@ -52,11 +53,11 @@ 2.7.0 - 4.6.4 + 4.7.6 2.2.1 - 4.5.5.B + 4.7.7.B 2.9.3 @@ -259,6 +260,13 @@ ${caffeine.version} + + + com.baomidou + dynamic-datasource-spring-boot3-starter + ${dynamic-datasource.version} + + From 7174f0fbe62ad69b67363e0386de590dbcfd141d Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 24 Sep 2025 14:40:42 +0800 Subject: [PATCH 40/60] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0.gitignore?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加application-youlai.yml到忽略列表 --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 125a2c39..ac7c8134 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ logs docker/*/data/ docker/minio/config -docker/xxljob/logs \ No newline at end of file +docker/xxljob/logs +application-youlai.yml \ No newline at end of file From a27f68e5b21314e11d9476277fac8e2e6daf3c85 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 24 Sep 2025 14:41:03 +0800 Subject: [PATCH 41/60] =?UTF-8?q?feat(menu):=E9=87=8D=E6=9E=84=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E8=B7=AF=E7=94=B1=E6=8E=A5=E5=8F=A3=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=A4=9A=E6=95=B0=E6=8D=AE=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/controller/MenuController.java | 24 +++++++++++++------ .../boot/system/service/MenuService.java | 11 +++++++-- .../system/service/impl/MenuServiceImpl.java | 22 ++++++++++++++--- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/youlai/boot/system/controller/MenuController.java b/src/main/java/com/youlai/boot/system/controller/MenuController.java index f8064bc9..a7975066 100644 --- a/src/main/java/com/youlai/boot/system/controller/MenuController.java +++ b/src/main/java/com/youlai/boot/system/controller/MenuController.java @@ -37,26 +37,36 @@ public class MenuController { @Operation(summary = "菜单列表") @GetMapping - @Log( value = "菜单列表",module = LogModuleEnum.MENU) - public Result> listMenus(MenuQuery queryParams) { + @Log(value = "菜单列表", module = LogModuleEnum.MENU) + public Result> getMenus(MenuQuery queryParams) { List menuList = menuService.listMenus(queryParams); return Result.success(menuList); } @Operation(summary = "菜单下拉列表") @GetMapping("/options") - public Result>> listMenuOptions( - @Parameter(description = "是否只查询父级菜单") - @RequestParam(required = false, defaultValue = "false") boolean onlyParent + public Result>> getMenuOptions( + @Parameter(description = "是否只查询父级菜单") + @RequestParam(required = false, defaultValue = "false") boolean onlyParent ) { List> menus = menuService.listMenuOptions(onlyParent); return Result.success(menus); } - @Operation(summary = "菜单路由列表") + @Operation(summary = "当前用户菜单路由列表") @GetMapping("/routes") public Result> getCurrentUserRoutes() { - List routeList = menuService.getCurrentUserRoutes(); + List routeList = menuService.listCurrentUserRoutes(); + return Result.success(routeList); + } + + @Operation(summary = "获取指定数据源的菜单路由列表") + @GetMapping("/routes/{datasource}") + public Result> getCurrentUserRoutesByDatasource( + @Parameter(description = "数据源名称,如:master(主库)、naiveui(NaiveUI数据库)、template(模板数据库)") + @PathVariable String datasource + ) { + List routeList = menuService.listCurrentUserRoutes(datasource); return Result.success(routeList); } diff --git a/src/main/java/com/youlai/boot/system/service/MenuService.java b/src/main/java/com/youlai/boot/system/service/MenuService.java index 41ba6f15..eb0ba54b 100644 --- a/src/main/java/com/youlai/boot/system/service/MenuService.java +++ b/src/main/java/com/youlai/boot/system/service/MenuService.java @@ -40,9 +40,16 @@ public interface MenuService extends IService

{ boolean saveMenu(MenuForm menuForm); /** - * 获取路由列表 + * 获取当前用户的菜单路由列表 */ - List getCurrentUserRoutes(); + List listCurrentUserRoutes(); + + /** + * 获取当前用户的菜单路由列表(指定数据源) + * + * @param datasource 数据源名称,如:master(主库)、naiveui(NaiveUI数据库)、template(模板数据库) + */ + List listCurrentUserRoutes(String datasource); /** * 修改菜单显示状态 diff --git a/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java index 7acfc8f9..e6cda9c3 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java @@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; +import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -139,16 +140,16 @@ public class MenuServiceImpl extends ServiceImpl implements Me } /** - * 获取菜单路由列表 + * 获取当前用户的菜单路由列表 */ @Override - public List getCurrentUserRoutes() { - + public List listCurrentUserRoutes() { Set roleCodes = SecurityUtils.getRoles(); if (CollectionUtil.isEmpty(roleCodes)) { return Collections.emptyList(); } + List menuList; if (SecurityUtils.isRoot()) { // 超级管理员获取所有菜单 @@ -162,6 +163,21 @@ public class MenuServiceImpl extends ServiceImpl implements Me return buildRoutes(SystemConstants.ROOT_NODE_ID, menuList); } + /** + * 获取当前用户的菜单路由列表(指定数据源) + * + * @param datasource 数据源名称 + * - master: 主库菜单数据 + * - naiveui: NaiveUI项目菜单数据 + * - template: 模板项目菜单数据 + */ + @Override + @DS("#datasource") + public List listCurrentUserRoutes(String datasource) { + return listCurrentUserRoutes(); + } + + /** * 递归生成菜单路由层级列表 * From df871bc967fa4757b6e1d220376868150dc4e545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=AA=E7=A9=BA=E7=9C=BC=E7=9D=9B?= Date: Thu, 25 Sep 2025 16:09:17 +0800 Subject: [PATCH 42/60] =?UTF-8?q?=E8=8F=9C=E5=8D=95ID=E4=B8=BA151=E7=9A=84?= =?UTF-8?q?SQL=EF=BC=8C=E8=B7=AF=E5=BE=84=E5=BA=94=E8=AF=A5=E6=98=AFdemo/c?= =?UTF-8?q?urd-single?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/mysql/youlai_boot.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysql/youlai_boot.sql b/sql/mysql/youlai_boot.sql index c24a3590..71191347 100644 --- a/sql/mysql/youlai_boot.sql +++ b/sql/mysql/youlai_boot.sql @@ -216,7 +216,7 @@ INSERT INTO `sys_menu` VALUES (147, 36, '0,36', '滚动文本', 1, 'TextScroll', INSERT INTO `sys_menu` VALUES (148, 89, '0,89', '字典实时同步', 1, 'DictSync', 'dict-sync', 'demo/dict-sync', NULL, NULL, NULL, 1, 3, '', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (149, 89, '0,89', 'VxeTable', 1, 'VxeTable', 'vxe-table', 'demo/vxe-table/index', NULL, NULL, 1, 1, 0, 'el-icon-MagicStick', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (150, 36, '0,36', '自适应表格操作列', 1, 'AutoOperationColumn', 'operation-column', 'demo/auto-operation-column', NULL, NULL, 1, 1, 1, '', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (151, 89, '0,89', 'CURD单文件', 1, 'CurdDemo', 'curd-demo', 'demo/curd-demo', NULL, NULL, 1, 1, 7, 'el-icon-Reading', '', now(),now(), NULL); +INSERT INTO `sys_menu` VALUES (151, 89, '0,89', 'CURD单文件', 1, 'CurdDemo', 'curd-demo', 'demo/curd-single', NULL, NULL, 1, 1, 7, 'el-icon-Reading', '', now(),now(), NULL); -- ---------------------------- From 6be5f82fe9d83d3cb7762effece354a44d14d003 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Fri, 26 Sep 2025 10:23:00 +0800 Subject: [PATCH 43/60] =?UTF-8?q?chore(menu):=20=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/mysql/youlai_boot.sql | 2 +- .../youlai/boot/system/controller/MenuController.java | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/sql/mysql/youlai_boot.sql b/sql/mysql/youlai_boot.sql index 71191347..aedf2e54 100644 --- a/sql/mysql/youlai_boot.sql +++ b/sql/mysql/youlai_boot.sql @@ -216,7 +216,7 @@ INSERT INTO `sys_menu` VALUES (147, 36, '0,36', '滚动文本', 1, 'TextScroll', INSERT INTO `sys_menu` VALUES (148, 89, '0,89', '字典实时同步', 1, 'DictSync', 'dict-sync', 'demo/dict-sync', NULL, NULL, NULL, 1, 3, '', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (149, 89, '0,89', 'VxeTable', 1, 'VxeTable', 'vxe-table', 'demo/vxe-table/index', NULL, NULL, 1, 1, 0, 'el-icon-MagicStick', '', now(), now(), NULL); INSERT INTO `sys_menu` VALUES (150, 36, '0,36', '自适应表格操作列', 1, 'AutoOperationColumn', 'operation-column', 'demo/auto-operation-column', NULL, NULL, 1, 1, 1, '', '', now(), now(), NULL); -INSERT INTO `sys_menu` VALUES (151, 89, '0,89', 'CURD单文件', 1, 'CurdDemo', 'curd-demo', 'demo/curd-single', NULL, NULL, 1, 1, 7, 'el-icon-Reading', '', now(),now(), NULL); +INSERT INTO `sys_menu` VALUES (151, 89, '0,89', 'CURD单文件', 1, 'CurdSingle', 'curd-single', 'demo/curd-single', NULL, NULL, 1, 1, 7, 'el-icon-Reading', '', now(),now(), NULL); -- ---------------------------- diff --git a/src/main/java/com/youlai/boot/system/controller/MenuController.java b/src/main/java/com/youlai/boot/system/controller/MenuController.java index a7975066..fd897650 100644 --- a/src/main/java/com/youlai/boot/system/controller/MenuController.java +++ b/src/main/java/com/youlai/boot/system/controller/MenuController.java @@ -60,16 +60,6 @@ public class MenuController { return Result.success(routeList); } - @Operation(summary = "获取指定数据源的菜单路由列表") - @GetMapping("/routes/{datasource}") - public Result> getCurrentUserRoutesByDatasource( - @Parameter(description = "数据源名称,如:master(主库)、naiveui(NaiveUI数据库)、template(模板数据库)") - @PathVariable String datasource - ) { - List routeList = menuService.listCurrentUserRoutes(datasource); - return Result.success(routeList); - } - @Operation(summary = "菜单表单数据") @GetMapping("/{id}/form") @PreAuthorize("@ss.hasPerm('sys:menu:edit')") From 504992fad49a012147ffed62b544db3c0c22f533 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Fri, 26 Sep 2025 10:45:25 +0800 Subject: [PATCH 44/60] =?UTF-8?q?chore:=20=E6=B3=A8=E9=87=8A=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E6=95=B0=E6=8D=AE=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 497654ca..942363b0 100644 --- a/pom.xml +++ b/pom.xml @@ -261,11 +261,11 @@ - + From 7929b91592f4acd24d44d6ed71d1d960a135bd56 Mon Sep 17 00:00:00 2001 From: theo <971366405@qq.com> Date: Fri, 26 Sep 2025 10:55:00 +0800 Subject: [PATCH 45/60] =?UTF-8?q?feat(config):=20=E5=90=AF=E7=94=A8?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E5=A4=9A=E6=95=B0=E6=8D=AE=E6=BA=90=E9=85=8D?= =?UTF-8?q?=E7=BD=AE-=20=E5=9C=A8=E5=BC=80=E5=8F=91=E7=8E=AF=E5=A2=83?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=B8=AD=E5=90=AF=E7=94=A8=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=BA=90=E6=94=AF=E6=8C=81=20-=20=E5=9C=A8?= =?UTF-8?q?=E7=94=9F=E4=BA=A7=E7=8E=AF=E5=A2=83=E9=85=8D=E7=BD=AE=E4=B8=AD?= =?UTF-8?q?=E5=90=AF=E7=94=A8=E5=8A=A8=E6=80=81=E6=95=B0=E6=8D=AE=E6=BA=90?= =?UTF-8?q?=E6=94=AF=E6=8C=81-=20=E6=B7=BB=E5=8A=A0=20dynamic-datasource-s?= =?UTF-8?q?pring-boot3-starter=E4=BE=9D=E8=B5=96=20-=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E4=B8=BB=E6=95=B0=E6=8D=AE=E6=BA=90=E4=B8=BA=20master-=20?= =?UTF-8?q?=E4=BF=9D=E6=8C=81=E5=8E=9F=E6=9C=89=E7=9A=84=20Druid=20?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=BA=90=E9=85=8D=E7=BD=AE=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 ++-- src/main/resources/application-dev.yml | 14 +++++++++----- src/main/resources/application-prod.yml | 14 +++++++++----- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index 942363b0..497654ca 100644 --- a/pom.xml +++ b/pom.xml @@ -261,11 +261,11 @@ - + diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 207f1fc1..71faeb83 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -3,11 +3,15 @@ server: spring: datasource: - type: com.alibaba.druid.pool.DruidDataSource - driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://www.youlai.tech:3306/youlai_boot?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true - username: youlai - password: 123456 + dynamic: + primary: master + datasource: + master: + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://www.youlai.tech:3306/youlai_boot?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true + username: youlai + password: 123456 data: redis: database: 0 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index b1309efa..e3e4dfcc 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -3,11 +3,15 @@ server: spring: datasource: - type: com.alibaba.druid.pool.DruidDataSource - driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://www.youlai.tech:3306/youlai_boot?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true - username: youlai - password: 123456 + dynamic: + primary: master + datasource: + master: + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://www.youlai.tech:3306/youlai_boot?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true + username: youlai + password: 123456 data: redis: database: 11 From d88b5dc9ded765c369a50305fa5eea3efd9a8672 Mon Sep 17 00:00:00 2001 From: theo <971366405@qq.com> Date: Fri, 26 Sep 2025 11:01:45 +0800 Subject: [PATCH 46/60] =?UTF-8?q?refactor(menu):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E6=95=B0=E6=8D=AE=E6=BA=90=E7=9A=84=E9=83=A8?= =?UTF-8?q?=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 ++-- .../boot/system/service/impl/MenuServiceImpl.java | 2 -- src/main/resources/application-dev.yml | 14 +++++--------- src/main/resources/application-prod.yml | 14 +++++--------- 4 files changed, 12 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 497654ca..942363b0 100644 --- a/pom.xml +++ b/pom.xml @@ -261,11 +261,11 @@ - + diff --git a/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java index e6cda9c3..313c564f 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java @@ -5,7 +5,6 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; -import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -172,7 +171,6 @@ public class MenuServiceImpl extends ServiceImpl implements Me * - template: 模板项目菜单数据 */ @Override - @DS("#datasource") public List listCurrentUserRoutes(String datasource) { return listCurrentUserRoutes(); } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 71faeb83..207f1fc1 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -3,15 +3,11 @@ server: spring: datasource: - dynamic: - primary: master - datasource: - master: - type: com.alibaba.druid.pool.DruidDataSource - driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://www.youlai.tech:3306/youlai_boot?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true - username: youlai - password: 123456 + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://www.youlai.tech:3306/youlai_boot?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true + username: youlai + password: 123456 data: redis: database: 0 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index e3e4dfcc..b1309efa 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -3,15 +3,11 @@ server: spring: datasource: - dynamic: - primary: master - datasource: - master: - type: com.alibaba.druid.pool.DruidDataSource - driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://www.youlai.tech:3306/youlai_boot?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true - username: youlai - password: 123456 + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://www.youlai.tech:3306/youlai_boot?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true + username: youlai + password: 123456 data: redis: database: 11 From 4d3e4832e8a673bdd9881026374d1111c1d45dd0 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Fri, 26 Sep 2025 11:27:00 +0800 Subject: [PATCH 47/60] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=20GitCode=20?= =?UTF-8?q?=E4=BB=93=E5=BA=93=E6=8C=82=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6ee30e03..f806c527 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,10 @@ logo

youlai-boot

有来技术 - 有来技术 + 有来技术 + + 有来技术 + 有来技术 @@ -37,8 +40,8 @@ ## 🌈 项目源码 -| 项目类型 | Gitee | Github | GitCode | -| --------------| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 项目类型 | Gitee | Github | GitCode | +| --------------| ------------------------- | ------------------------------- | ------------------------------------- | | ✅ Java 后端 | [youlai-boot](https://gitee.com/youlaiorg/youlai-boot) | [youlai-boot](https://github.com/haoxianrui/youlai-boot) | [youlai-boot](https://gitcode.com/youlai/youlai-boot) | | vue3 前端 | [vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) | [vue3-element-admin](https://github.com/youlaitech/vue3-element-admin) | [vue3-element-admin](https://gitcode.com/youlai/vue3-element-admin) | | uni-app 移动端 | [vue-uniapp-template](https://gitee.com/youlaiorg/vue-uniapp-template) | [vue-uniapp-template](https://github.com/youlaitech/vue-uniapp-template) | [vue-uniapp-template](https://gitcode.com/youlai/vue-uniapp-template) | From adfd4bc1ea1f0e816b8228c1fdde977411d6a345 Mon Sep 17 00:00:00 2001 From: theo <971366405@qq.com> Date: Mon, 29 Sep 2025 10:13:32 +0800 Subject: [PATCH 48/60] =?UTF-8?q?feat(system):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=85=AC=E5=91=8A=E9=98=85=E8=AF=BB=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=9F=A5=E8=AF=A2=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E9=82=AE=E7=AE=B1=E9=AA=8C=E8=AF=81=E7=A0=81=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E9=94=AE=E7=94=9F=E6=88=90-=20=E5=9C=A8UserNoticeMapp?= =?UTF-8?q?er.xml=E4=B8=AD=E6=96=B0=E5=A2=9EisRead=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6=EF=BC=8C=E6=94=AF=E6=8C=81=E6=8C=89=E9=98=85?= =?UTF-8?q?=E8=AF=BB=E7=8A=B6=E6=80=81=E7=AD=9B=E9=80=89=E5=85=AC=E5=91=8A?= =?UTF-8?q?=20-=20=E4=BF=AE=E6=94=B9UserServiceImpl.java=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E9=82=AE=E7=AE=B1=E9=AA=8C=E8=AF=81=E7=A0=81=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E9=94=AE=E7=94=9F=E6=88=90=E6=96=B9=E5=BC=8F=EF=BC=8C=E4=BD=BF?= =?UTF-8?q?=E7=94=A8StrUtil.format=E6=9B=BF=E4=BB=A3=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E6=8B=BC=E6=8E=A5=EF=BC=8C=E6=8F=90=E9=AB=98=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=8F=AF=E8=AF=BB=E6=80=A7=E5=92=8C=E7=BB=B4=E6=8A=A4?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/youlai/boot/system/service/impl/UserServiceImpl.java | 2 +- src/main/resources/mapper/system/UserNoticeMapper.xml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) 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 e3091cbb..9d8fafba 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 @@ -648,7 +648,7 @@ public class UserServiceImpl extends ServiceImpl implements Us // 获取缓存的验证码 String email = form.getEmail(); - String redisCacheKey = RedisConstants.Captcha.EMAIL_CODE + email; + String redisCacheKey = StrUtil.format(RedisConstants.Captcha.EMAIL_CODE, email); String cachedVerifyCode = redisTemplate.opsForValue().get(redisCacheKey); if (StrUtil.isBlank(cachedVerifyCode)) { diff --git a/src/main/resources/mapper/system/UserNoticeMapper.xml b/src/main/resources/mapper/system/UserNoticeMapper.xml index 46332903..c3364cb0 100644 --- a/src/main/resources/mapper/system/UserNoticeMapper.xml +++ b/src/main/resources/mapper/system/UserNoticeMapper.xml @@ -21,6 +21,9 @@ AND t2.title LIKE CONCAT('%',#{queryParams.title},'%') + + AND t1.is_read = #{queryParams.isRead} + ORDER BY t2.publish_time DESC, t2.create_time DESC From 2ddd0e161a6c42151081290224248da11e2ef10a Mon Sep 17 00:00:00 2001 From: "Jack.Zhang" Date: Tue, 30 Sep 2025 10:15:09 +0800 Subject: [PATCH 49/60] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E4=B8=BAtimestamp=E6=97=B6=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E7=94=9F=E6=88=90LocalDateTime=E7=B1=BB=E5=9E=8B=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java b/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java index a5a4bf73..a263d48e 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java +++ b/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java @@ -28,7 +28,8 @@ public enum JavaTypeEnum { DOUBLE("double", "Double", "number"), DECIMAL("decimal", "BigDecimal", "number"), DATE("date", "LocalDate", "Date"), - DATETIME("datetime", "LocalDateTime", "Date"); + DATETIME("datetime", "LocalDateTime", "Date"), + TIMESTAMP("timestamp", "LocalDateTime", "Date"); // 数据库类型 private final String dbType; From 1b140047a5219dd9d5d340377a75050ae7daf2bb Mon Sep 17 00:00:00 2001 From: "Jack.Zhang" Date: Tue, 30 Sep 2025 10:21:13 +0800 Subject: [PATCH 50/60] =?UTF-8?q?=E5=9B=9E=E6=BB=9A=E8=87=B3=E5=8E=9F?= =?UTF-8?q?=E5=A7=8B=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java b/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java index a263d48e..a5a4bf73 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java +++ b/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java @@ -28,8 +28,7 @@ public enum JavaTypeEnum { DOUBLE("double", "Double", "number"), DECIMAL("decimal", "BigDecimal", "number"), DATE("date", "LocalDate", "Date"), - DATETIME("datetime", "LocalDateTime", "Date"), - TIMESTAMP("timestamp", "LocalDateTime", "Date"); + DATETIME("datetime", "LocalDateTime", "Date"); // 数据库类型 private final String dbType; From faf1ca27d12b2f2842d3bef0e6ea9ecf53309444 Mon Sep 17 00:00:00 2001 From: "Jack.Zhang" Date: Tue, 30 Sep 2025 10:26:19 +0800 Subject: [PATCH 51/60] =?UTF-8?q?=E6=A8=A1=E5=9D=97=EF=BC=9A=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=94=9F=E6=88=90=E5=99=A8=EF=BC=9B=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=9A=E4=BF=AE=E5=A4=8Dtimestamp=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E7=9A=84=E6=97=A0=E6=B3=95=E6=AD=A3=E7=A1=AE=E7=94=9F?= =?UTF-8?q?=E6=88=90=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java b/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java index a5a4bf73..a263d48e 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java +++ b/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java @@ -28,7 +28,8 @@ public enum JavaTypeEnum { DOUBLE("double", "Double", "number"), DECIMAL("decimal", "BigDecimal", "number"), DATE("date", "LocalDate", "Date"), - DATETIME("datetime", "LocalDateTime", "Date"); + DATETIME("datetime", "LocalDateTime", "Date"), + TIMESTAMP("timestamp", "LocalDateTime", "Date"); // 数据库类型 private final String dbType; From c43e6dfb5471d46c36bed71294424bba0d578ca2 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Tue, 14 Oct 2025 16:09:46 +0800 Subject: [PATCH 52/60] =?UTF-8?q?refactor(platform):=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E6=A8=A1=E5=9D=97=E5=8C=85=E7=BB=93=E6=9E=84?= =?UTF-8?q?-=20=E5=B0=86=20shared=20=E5=8C=85=E4=B8=8B=E7=9A=84=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=A7=BB=E5=8A=A8=E5=88=B0=20platform=20=E5=8C=85?= =?UTF-8?q?=E4=B8=8B=20-=20=E6=9B=B4=E6=96=B0=E7=9B=B8=E5=85=B3=E7=B1=BB?= =?UTF-8?q?=E7=9A=84=E5=8C=85=E5=BC=95=E7=94=A8=E8=B7=AF=E5=BE=84=20-=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=20application.yml=20=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E5=8C=85=E6=89=AB=E6=8F=8F=E8=B7=AF=E5=BE=84=20-=E9=87=8D?= =?UTF-8?q?=E5=91=BD=E5=90=8D=20CaptchaInfo=20=E7=B1=BB=E4=B8=BA=20Captcha?= =?UTF-8?q?VO=20=E5=B9=B6=E8=B0=83=E6=95=B4=E5=8C=85=E8=B7=AF=E5=BE=84=20-?= =?UTF-8?q?=20=E7=A7=BB=E5=8A=A8=20BusinessException=20=E5=92=8C=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=AE=89=E5=85=A8=E7=B1=BB=E5=88=B0=20core=20?= =?UTF-8?q?=E5=8C=85-=20=E6=9B=B4=E6=96=B0=20Codegen=20=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=B1=BB=E5=8C=85=E8=B7=AF=E5=BE=84=20-=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=97=A0=E7=94=A8=E7=9A=84=E6=9D=A1=E4=BB=B6=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/redis/config/redis.conf | 12 +-- pom.xml | 2 +- .../boot/auth/controller/AuthController.java | 10 +-- .../{CaptchaInfo.java => vo/CaptchaVO.java} | 4 +- .../youlai/boot/auth/service/AuthService.java | 6 +- .../auth/service/impl/AuthServiceImpl.java | 22 ++--- .../enums/CaptchaTypeEnum.java | 0 .../boot/common/util/ResponseUtils.java | 83 ------------------- .../youlai/boot/config/SecurityConfig.java | 21 +++-- .../youlai/boot/config/WebSocketConfig.java | 4 +- .../youlai/boot/core/aspect/LogAspect.java | 2 +- .../boot/core/aspect/RepeatSubmitAspect.java | 2 +- .../exception/BusinessException.java | 2 +- .../exception/GlobalExceptionHandler.java | 4 +- .../boot/core/filter/RateLimiterFilter.java | 6 +- .../result => core/web}/ExcelResult.java | 2 +- .../result => core/web}/IResultCode.java | 2 +- .../result => core/web}/PageResult.java | 2 +- .../{common/result => core/web}/Result.java | 2 +- .../result => core/web}/ResultCode.java | 4 +- .../boot/core/web/WebResponseHelper.java | 75 +++++++++++++++++ .../member/controller/MemberController.java | 10 --- .../module/member/mapper/MemberMapper.java | 10 --- .../boot/module/member/model/Member.java | 10 --- .../module/member/service/MemberService.java | 10 --- .../order/controller/OrderController.java | 10 --- .../boot/module/order/mapper/OrderMapper.java | 10 --- .../youlai/boot/module/order/model/Order.java | 10 --- .../module/order/service/OrderService.java | 10 --- .../product/controller/ProductController.java | 10 --- .../module/product/mapper/ProductMapper.java | 10 --- .../boot/module/product/model/Product.java | 10 --- .../product/service/ProductService.java | 10 --- .../codegen/controller/CodegenController.java | 18 ++-- .../codegen/converter/CodegenConverter.java | 8 +- .../codegen/enums/FormTypeEnum.java | 2 +- .../codegen/enums/JavaTypeEnum.java | 2 +- .../codegen/enums/QueryTypeEnum.java | 2 +- .../codegen/mapper/DatabaseMapper.java | 10 +-- .../codegen/mapper/GenConfigMapper.java | 4 +- .../codegen/mapper/GenFieldConfigMapper.java | 4 +- .../codegen/model/bo/ColumnMetaData.java | 2 +- .../codegen/model/bo/TableMetaData.java | 2 +- .../codegen/model/entity/GenConfig.java | 2 +- .../codegen/model/entity/GenFieldConfig.java | 6 +- .../codegen/model/form/GenConfigForm.java | 6 +- .../codegen/model/query/TablePageQuery.java | 2 +- .../codegen/model/vo/CodegenPreviewVO.java | 2 +- .../codegen/model/vo/TablePageVO.java | 2 +- .../codegen/service/CodegenService.java | 8 +- .../codegen/service/GenConfigService.java | 6 +- .../service/GenFieldConfigService.java | 4 +- .../service/impl/CodegenServiceImpl.java | 25 +++--- .../service/impl/GenConfigServiceImpl.java | 28 +++---- .../impl/GenFieldConfigServiceImpl.java | 8 +- .../file/controller/FileController.java | 8 +- .../file/model/FileInfo.java | 2 +- .../file/service/FileService.java | 4 +- .../file/service/impl/AliyunFileService.java | 6 +- .../file/service/impl/LocalFileService.java | 6 +- .../file/service/impl/MinioFileService.java | 8 +- .../mail/controller/MailController.java | 2 +- .../mail/service/MailService.java | 2 +- .../mail/service/impl/MailServiceImpl.java | 4 +- .../sms/controller/SmsController.java | 2 +- .../sms/enums/SmsTypeEnum.java | 2 +- .../sms/service/SmsService.java | 4 +- .../sms/service/impl/AliyunSmsService.java | 6 +- .../controller/WebsocketController.java | 4 +- .../websocket/model/ChatMessage.java | 2 +- .../knife4j/Knife4jOpenApiCustomizer.java | 0 .../mybatis/MyDataPermissionHandler.java | 2 +- .../mybatis/MyMetaObjectHandler.java | 0 .../exception/CaptchaValidationException.java | 2 +- .../filter/CaptchaValidationFilter.java | 10 +-- .../filter/TokenAuthenticationFilter.java | 12 +-- .../handler}/MyAccessDeniedHandler.java | 8 +- .../handler}/MyAuthenticationEntryPoint.java | 12 +-- .../security/model/AuthenticationToken.java | 2 +- .../{core => }/security/model/OnlineUser.java | 2 +- .../model}/SmsAuthenticationToken.java | 2 +- .../security/model/SysUserDetails.java | 2 +- .../security/model/UserAuthCredentials.java | 2 +- .../WxMiniAppCodeAuthenticationToken.java | 2 +- .../WxMiniAppPhoneAuthenticationToken.java | 2 +- .../provider}/SmsAuthenticationProvider.java | 9 +- .../WxMiniAppCodeAuthenticationProvider.java | 7 +- .../WxMiniAppPhoneAuthenticationProvider.java | 7 +- .../security/service/PermissionService.java | 4 +- .../service/SysUserDetailsService.java | 6 +- .../security/token/JwtTokenManager.java | 8 +- .../security/token/RedisTokenManager.java | 10 +-- .../security/token/TokenManager.java | 4 +- .../security/util/SecurityUtils.java | 4 +- .../system/controller/ConfigController.java | 4 +- .../system/controller/DeptController.java | 2 +- .../system/controller/DictController.java | 4 +- .../boot/system/controller/LogController.java | 4 +- .../system/controller/MenuController.java | 2 +- .../system/controller/NoticeController.java | 4 +- .../system/controller/RoleController.java | 4 +- .../system/controller/UserController.java | 8 +- .../system/listener/UserImportListener.java | 2 +- .../youlai/boot/system/mapper/UserMapper.java | 2 +- .../boot/system/service/MenuService.java | 2 +- .../system/service/UserOnlineService.java | 2 +- .../boot/system/service/UserService.java | 2 +- .../service/impl/ConfigServiceImpl.java | 2 +- .../system/service/impl/DeptServiceImpl.java | 2 +- .../system/service/impl/MenuServiceImpl.java | 4 +- .../service/impl/NoticeServiceImpl.java | 2 +- .../system/service/impl/RoleServiceImpl.java | 2 +- .../service/impl/UserNoticeServiceImpl.java | 2 +- .../service/impl/UserRoleServiceImpl.java | 4 +- .../system/service/impl/UserServiceImpl.java | 14 ++-- src/main/resources/application-dev.yml | 4 +- src/main/resources/application-prod.yml | 4 +- .../mapper/codegen/DatabaseMapper.xml | 8 +- .../mapper/codegen/GenConfigMapper.xml | 2 +- .../mapper/codegen/GenFieldConfigMapper.xml | 2 +- .../resources/mapper/system/UserMapper.xml | 2 +- .../boot/generator/SystemCodeGenerator.java | 4 +- 122 files changed, 346 insertions(+), 475 deletions(-) rename src/main/java/com/youlai/boot/auth/model/{CaptchaInfo.java => vo/CaptchaVO.java} (86%) rename src/main/java/com/youlai/boot/{auth => common}/enums/CaptchaTypeEnum.java (100%) delete mode 100644 src/main/java/com/youlai/boot/common/util/ResponseUtils.java rename src/main/java/com/youlai/boot/{common => core}/exception/BusinessException.java (95%) rename src/main/java/com/youlai/boot/{common => core}/exception/GlobalExceptionHandler.java (99%) rename src/main/java/com/youlai/boot/{common/result => core/web}/ExcelResult.java (94%) rename src/main/java/com/youlai/boot/{common/result => core/web}/IResultCode.java (78%) rename src/main/java/com/youlai/boot/{common/result => core/web}/PageResult.java (95%) rename src/main/java/com/youlai/boot/{common/result => core/web}/Result.java (98%) rename src/main/java/com/youlai/boot/{common/result => core/web}/ResultCode.java (99%) create mode 100644 src/main/java/com/youlai/boot/core/web/WebResponseHelper.java delete mode 100644 src/main/java/com/youlai/boot/module/member/controller/MemberController.java delete mode 100644 src/main/java/com/youlai/boot/module/member/mapper/MemberMapper.java delete mode 100644 src/main/java/com/youlai/boot/module/member/model/Member.java delete mode 100644 src/main/java/com/youlai/boot/module/member/service/MemberService.java delete mode 100644 src/main/java/com/youlai/boot/module/order/controller/OrderController.java delete mode 100644 src/main/java/com/youlai/boot/module/order/mapper/OrderMapper.java delete mode 100644 src/main/java/com/youlai/boot/module/order/model/Order.java delete mode 100644 src/main/java/com/youlai/boot/module/order/service/OrderService.java delete mode 100644 src/main/java/com/youlai/boot/module/product/controller/ProductController.java delete mode 100644 src/main/java/com/youlai/boot/module/product/mapper/ProductMapper.java delete mode 100644 src/main/java/com/youlai/boot/module/product/model/Product.java delete mode 100644 src/main/java/com/youlai/boot/module/product/service/ProductService.java rename src/main/java/com/youlai/boot/{shared => platform}/codegen/controller/CodegenController.java (88%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/converter/CodegenConverter.java (84%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/enums/FormTypeEnum.java (97%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/enums/JavaTypeEnum.java (98%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/enums/QueryTypeEnum.java (96%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/mapper/DatabaseMapper.java (72%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/mapper/GenConfigMapper.java (69%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/mapper/GenFieldConfigMapper.java (69%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/model/bo/ColumnMetaData.java (94%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/model/bo/TableMetaData.java (91%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/model/entity/GenConfig.java (94%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/model/entity/GenFieldConfig.java (90%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/model/form/GenConfigForm.java (94%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/model/query/TablePageQuery.java (91%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/model/vo/CodegenPreviewVO.java (89%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/model/vo/TablePageVO.java (94%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/service/CodegenService.java (74%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/service/GenConfigService.java (78%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/service/GenFieldConfigService.java (63%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/service/impl/CodegenServiceImpl.java (93%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/service/impl/GenConfigServiceImpl.java (90%) rename src/main/java/com/youlai/boot/{shared => platform}/codegen/service/impl/GenFieldConfigServiceImpl.java (60%) rename src/main/java/com/youlai/boot/{shared => platform}/file/controller/FileController.java (88%) rename src/main/java/com/youlai/boot/{shared => platform}/file/model/FileInfo.java (88%) rename src/main/java/com/youlai/boot/{shared => platform}/file/service/FileService.java (82%) rename src/main/java/com/youlai/boot/{shared => platform}/file/service/impl/AliyunFileService.java (95%) rename src/main/java/com/youlai/boot/{shared => platform}/file/service/impl/LocalFileService.java (94%) rename src/main/java/com/youlai/boot/{shared => platform}/file/service/impl/MinioFileService.java (97%) rename src/main/java/com/youlai/boot/{shared => platform}/mail/controller/MailController.java (76%) rename src/main/java/com/youlai/boot/{shared => platform}/mail/service/MailService.java (92%) rename src/main/java/com/youlai/boot/{shared => platform}/mail/service/impl/MailServiceImpl.java (95%) rename src/main/java/com/youlai/boot/{shared => platform}/sms/controller/SmsController.java (66%) rename src/main/java/com/youlai/boot/{shared => platform}/sms/enums/SmsTypeEnum.java (94%) rename src/main/java/com/youlai/boot/{shared => platform}/sms/service/SmsService.java (85%) rename src/main/java/com/youlai/boot/{shared => platform}/sms/service/impl/AliyunSmsService.java (94%) rename src/main/java/com/youlai/boot/{shared => platform}/websocket/controller/WebsocketController.java (94%) rename src/main/java/com/youlai/boot/{shared => platform}/websocket/model/ChatMessage.java (86%) rename src/main/java/com/youlai/boot/{pulgin => plugin}/knife4j/Knife4jOpenApiCustomizer.java (100%) rename src/main/java/com/youlai/boot/{pulgin => plugin}/mybatis/MyDataPermissionHandler.java (98%) rename src/main/java/com/youlai/boot/{pulgin => plugin}/mybatis/MyMetaObjectHandler.java (100%) rename src/main/java/com/youlai/boot/{core => }/security/exception/CaptchaValidationException.java (85%) rename src/main/java/com/youlai/boot/{core => }/security/filter/CaptchaValidationFilter.java (88%) rename src/main/java/com/youlai/boot/{core => }/security/filter/TokenAuthenticationFilter.java (86%) rename src/main/java/com/youlai/boot/{core/security/exception => security/handler}/MyAccessDeniedHandler.java (72%) rename src/main/java/com/youlai/boot/{core/security/exception => security/handler}/MyAuthenticationEntryPoint.java (78%) rename src/main/java/com/youlai/boot/{core => }/security/model/AuthenticationToken.java (93%) rename src/main/java/com/youlai/boot/{core => }/security/model/OnlineUser.java (93%) rename src/main/java/com/youlai/boot/{core/security/extension/sms => security/model}/SmsAuthenticationToken.java (97%) rename src/main/java/com/youlai/boot/{core => }/security/model/SysUserDetails.java (98%) rename src/main/java/com/youlai/boot/{core => }/security/model/UserAuthCredentials.java (94%) rename src/main/java/com/youlai/boot/{core/security/extension/wx => security/model}/WxMiniAppCodeAuthenticationToken.java (97%) rename src/main/java/com/youlai/boot/{core/security/extension/wx => security/model}/WxMiniAppPhoneAuthenticationToken.java (97%) rename src/main/java/com/youlai/boot/{core/security/extension/sms => security/provider}/SmsAuthenticationProvider.java (91%) rename src/main/java/com/youlai/boot/{core/security/extension/wx => security/provider}/WxMiniAppCodeAuthenticationProvider.java (94%) rename src/main/java/com/youlai/boot/{core/security/extension/wx => security/provider}/WxMiniAppPhoneAuthenticationProvider.java (95%) rename src/main/java/com/youlai/boot/{core => }/security/service/PermissionService.java (96%) rename src/main/java/com/youlai/boot/{core => }/security/service/SysUserDetailsService.java (89%) rename src/main/java/com/youlai/boot/{core => }/security/token/JwtTokenManager.java (97%) rename src/main/java/com/youlai/boot/{core => }/security/token/RedisTokenManager.java (97%) rename src/main/java/com/youlai/boot/{core => }/security/token/TokenManager.java (92%) rename src/main/java/com/youlai/boot/{core => }/security/util/SecurityUtils.java (97%) diff --git a/docker/redis/config/redis.conf b/docker/redis/config/redis.conf index 36aaf08e..272c8486 100644 --- a/docker/redis/config/redis.conf +++ b/docker/redis/config/redis.conf @@ -133,7 +133,7 @@ protected-mode no # # enable-protected-configs no # enable-debug-command no -# enable-module-command no +# enable-business-command no # Accept connections on the specified port, default is 6379 (IANA #815344). # If port 0 is specified Redis will not listen on a TCP socket. @@ -867,7 +867,7 @@ replica-priority 100 # Warning: since Redis is pretty fast, an outside user can try up to # 1 million passwords per second against a modern box. This means that you # should use very strong passwords, otherwise they will be very easy to break. -# Note that because the password is really a shared secret between the client +# Note that because the password is really a platform secret between the client # and the server, and should not be memorized by any human, the password # can be easily a long string from /dev/urandom or whatever, so by using a # long and unguessable password no brute force attack will be possible. @@ -964,7 +964,7 @@ replica-priority 100 # # user alice on +@all -DEBUG ~* >somepassword # -# This will allow "alice" to use all the commands with the exception of the +# This will allow "alice" to use all the commands with the handler of the # DEBUG command, since +@all added all the commands to the set of the commands # alice can use, and later DEBUG was removed. However if we invert the order # of two ACL rules the result will be different: @@ -1066,7 +1066,7 @@ acllog-max-len 128 # create for administrative purposes. # ------------------------------------------------------------------------ # -# It is possible to change the name of dangerous commands in a shared +# It is possible to change the name of dangerous commands in a platform # environment. For instance the CONFIG command may be renamed into something # hard to guess so that it will still be available for internal-use tools # but not available for general clients. @@ -1095,7 +1095,7 @@ acllog-max-len 128 # an error 'max number of clients reached'. # # IMPORTANT: When Redis Cluster is used, the max number of connections is also -# shared with the cluster bus: every node in the cluster will use two +# platform with the cluster bus: every node in the cluster will use two # connections, one incoming and another outgoing. It is important to size the # limit accordingly in case of very large clusters. # @@ -1563,7 +1563,7 @@ aof-timestamp-enabled no # # In this state Redis will only allow a handful of commands to be executed. # For instance, SCRIPT KILL, FUNCTION KILL, SHUTDOWN NOSAVE and possibly some -# module specific 'allow-busy' commands. +# business specific 'allow-busy' commands. # # SCRIPT KILL and FUNCTION KILL will only be able to stop a script that did not # yet call any write commands, so SHUTDOWN NOSAVE may be the only way to stop diff --git a/pom.xml b/pom.xml index 942363b0..ccb0ab4d 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ org.springframework.boot spring-boot-starter-parent - 3.5.5 + 3.5.6 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 c1f22dad..e4d169fc 100644 --- a/src/main/java/com/youlai/boot/auth/controller/AuthController.java +++ b/src/main/java/com/youlai/boot/auth/controller/AuthController.java @@ -1,13 +1,13 @@ package com.youlai.boot.auth.controller; -import com.youlai.boot.auth.model.CaptchaInfo; +import com.youlai.boot.auth.model.vo.CaptchaVO; import com.youlai.boot.auth.model.dto.WxMiniAppPhoneLoginDTO; import com.youlai.boot.common.enums.LogModuleEnum; -import com.youlai.boot.common.result.Result; +import com.youlai.boot.core.web.Result; import com.youlai.boot.auth.service.AuthService; import com.youlai.boot.auth.model.dto.WxMiniAppCodeLoginDTO; -import com.youlai.boot.core.security.model.AuthenticationToken; import com.youlai.boot.common.annotation.Log; +import com.youlai.boot.security.model.AuthenticationToken; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -34,8 +34,8 @@ public class AuthController { @Operation(summary = "获取验证码") @GetMapping("/captcha") - public Result getCaptcha() { - CaptchaInfo captcha = authService.getCaptcha(); + public Result getCaptcha() { + CaptchaVO captcha = authService.getCaptcha(); return Result.success(captcha); } diff --git a/src/main/java/com/youlai/boot/auth/model/CaptchaInfo.java b/src/main/java/com/youlai/boot/auth/model/vo/CaptchaVO.java similarity index 86% rename from src/main/java/com/youlai/boot/auth/model/CaptchaInfo.java rename to src/main/java/com/youlai/boot/auth/model/vo/CaptchaVO.java index e4095850..3f42c300 100644 --- a/src/main/java/com/youlai/boot/auth/model/CaptchaInfo.java +++ b/src/main/java/com/youlai/boot/auth/model/vo/CaptchaVO.java @@ -1,4 +1,4 @@ -package com.youlai.boot.auth.model; +package com.youlai.boot.auth.model.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; @@ -13,7 +13,7 @@ import lombok.Data; @Schema(description = "验证码信息") @Data @Builder -public class CaptchaInfo { +public class CaptchaVO { @Schema(description = "验证码缓存 Key") private String captchaKey; 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 4bcb411c..5fe2eadf 100644 --- a/src/main/java/com/youlai/boot/auth/service/AuthService.java +++ b/src/main/java/com/youlai/boot/auth/service/AuthService.java @@ -1,8 +1,8 @@ package com.youlai.boot.auth.service; -import com.youlai.boot.auth.model.CaptchaInfo; +import com.youlai.boot.auth.model.vo.CaptchaVO; import com.youlai.boot.auth.model.dto.WxMiniAppPhoneLoginDTO; -import com.youlai.boot.core.security.model.AuthenticationToken; +import com.youlai.boot.security.model.AuthenticationToken; import com.youlai.boot.auth.model.dto.WxMiniAppCodeLoginDTO; /** @@ -32,7 +32,7 @@ public interface AuthService { * * @return 验证码 */ - CaptchaInfo getCaptcha(); + CaptchaVO getCaptcha(); /** * 刷新令牌 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 2e844a6d..61514c7c 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 @@ -6,21 +6,21 @@ import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import com.youlai.boot.auth.enums.CaptchaTypeEnum; -import com.youlai.boot.auth.model.CaptchaInfo; +import com.youlai.boot.auth.model.vo.CaptchaVO; import com.youlai.boot.auth.model.dto.WxMiniAppCodeLoginDTO; import com.youlai.boot.auth.model.dto.WxMiniAppPhoneLoginDTO; import com.youlai.boot.auth.service.AuthService; import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SecurityConstants; import com.youlai.boot.config.property.CaptchaProperties; -import com.youlai.boot.core.security.extension.sms.SmsAuthenticationToken; -import com.youlai.boot.core.security.extension.wx.WxMiniAppCodeAuthenticationToken; -import com.youlai.boot.core.security.extension.wx.WxMiniAppPhoneAuthenticationToken; -import com.youlai.boot.core.security.model.AuthenticationToken; -import com.youlai.boot.core.security.token.TokenManager; -import com.youlai.boot.core.security.util.SecurityUtils; -import com.youlai.boot.shared.sms.enums.SmsTypeEnum; -import com.youlai.boot.shared.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 com.youlai.boot.platform.sms.enums.SmsTypeEnum; +import com.youlai.boot.platform.sms.service.SmsService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; @@ -168,7 +168,7 @@ public class AuthServiceImpl implements AuthService { * @return 验证码 */ @Override - public CaptchaInfo getCaptcha() { + public CaptchaVO getCaptcha() { String captchaType = captchaProperties.getType(); int width = captchaProperties.getWidth(); @@ -204,7 +204,7 @@ public class AuthServiceImpl implements AuthService { TimeUnit.SECONDS ); - return CaptchaInfo.builder() + return CaptchaVO.builder() .captchaKey(captchaKey) .captchaBase64(imageBase64Data) .build(); diff --git a/src/main/java/com/youlai/boot/auth/enums/CaptchaTypeEnum.java b/src/main/java/com/youlai/boot/common/enums/CaptchaTypeEnum.java similarity index 100% rename from src/main/java/com/youlai/boot/auth/enums/CaptchaTypeEnum.java rename to src/main/java/com/youlai/boot/common/enums/CaptchaTypeEnum.java diff --git a/src/main/java/com/youlai/boot/common/util/ResponseUtils.java b/src/main/java/com/youlai/boot/common/util/ResponseUtils.java deleted file mode 100644 index d90804dc..00000000 --- a/src/main/java/com/youlai/boot/common/util/ResponseUtils.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.youlai.boot.common.util; - -import cn.hutool.json.JSONUtil; -import com.youlai.boot.common.result.Result; -import com.youlai.boot.common.result.ResultCode; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; - -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; - -/** - * 响应工具类 - * - * @author Ray.Hao - * @since 2.0.0 - */ -@Slf4j -public class ResponseUtils { - - - /** - * 异常消息返回(适用过滤器中处理异常响应) - * - * @param response HttpServletResponse - * @param resultCode 响应结果码 - */ - public static void writeErrMsg(HttpServletResponse response, ResultCode resultCode) { - int status = getHttpStatus(resultCode); - - response.setStatus(status); - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - response.setCharacterEncoding(StandardCharsets.UTF_8.name()); - - try (PrintWriter writer = response.getWriter()) { - String jsonResponse = JSONUtil.toJsonStr(Result.failed(resultCode)); - writer.print(jsonResponse); - writer.flush(); // 确保将响应内容写入到输出流 - } catch (IOException e) { - log.error("响应异常处理失败", e); - } - } - - /** - * 异常消息返回(适用过滤器中处理异常响应) - * - * @param response HttpServletResponse - * @param resultCode 响应结果码 - */ - public static void writeErrMsg(HttpServletResponse response, ResultCode resultCode, String message) { - int status = getHttpStatus(resultCode); - - response.setStatus(status); - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - response.setCharacterEncoding(StandardCharsets.UTF_8.name()); - - try (PrintWriter writer = response.getWriter()) { - String jsonResponse = JSONUtil.toJsonStr(Result.failed(resultCode, message)); - writer.print(jsonResponse); - writer.flush(); // 确保将响应内容写入到输出流 - } catch (IOException e) { - log.error("响应异常处理失败", e); - } - } - - - /** - * 根据结果码获取HTTP状态码 - * - * @param resultCode 结果码 - * @return HTTP状态码 - */ - private static int getHttpStatus(ResultCode resultCode) { - return switch (resultCode) { - case ACCESS_UNAUTHORIZED, ACCESS_TOKEN_INVALID, REFRESH_TOKEN_INVALID -> HttpStatus.UNAUTHORIZED.value(); - default -> HttpStatus.BAD_REQUEST.value(); - }; - } - -} diff --git a/src/main/java/com/youlai/boot/config/SecurityConfig.java b/src/main/java/com/youlai/boot/config/SecurityConfig.java index 6790d4d5..bab3ccaa 100644 --- a/src/main/java/com/youlai/boot/config/SecurityConfig.java +++ b/src/main/java/com/youlai/boot/config/SecurityConfig.java @@ -5,15 +5,15 @@ import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.util.ArrayUtil; import com.youlai.boot.config.property.SecurityProperties; import com.youlai.boot.core.filter.RateLimiterFilter; -import com.youlai.boot.core.security.exception.MyAccessDeniedHandler; -import com.youlai.boot.core.security.exception.MyAuthenticationEntryPoint; -import com.youlai.boot.core.security.extension.sms.SmsAuthenticationProvider; -import com.youlai.boot.core.security.extension.wx.WxMiniAppCodeAuthenticationProvider; -import com.youlai.boot.core.security.extension.wx.WxMiniAppPhoneAuthenticationProvider; -import com.youlai.boot.core.security.filter.CaptchaValidationFilter; -import com.youlai.boot.core.security.filter.TokenAuthenticationFilter; -import com.youlai.boot.core.security.token.TokenManager; -import com.youlai.boot.core.security.service.SysUserDetailsService; +import com.youlai.boot.security.filter.CaptchaValidationFilter; +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; import com.youlai.boot.system.service.UserService; import lombok.RequiredArgsConstructor; @@ -119,9 +119,8 @@ public class SecurityConfig { */ @Bean public DaoAuthenticationProvider daoAuthenticationProvider() { - DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); + DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(userDetailsService); daoAuthenticationProvider.setPasswordEncoder(passwordEncoder); - daoAuthenticationProvider.setUserDetailsService(userDetailsService); return daoAuthenticationProvider; } diff --git a/src/main/java/com/youlai/boot/config/WebSocketConfig.java b/src/main/java/com/youlai/boot/config/WebSocketConfig.java index a344010b..72e2405f 100644 --- a/src/main/java/com/youlai/boot/config/WebSocketConfig.java +++ b/src/main/java/com/youlai/boot/config/WebSocketConfig.java @@ -1,8 +1,8 @@ package com.youlai.boot.config; import cn.hutool.core.util.StrUtil; -import com.youlai.boot.core.security.model.SysUserDetails; -import com.youlai.boot.core.security.token.TokenManager; +import com.youlai.boot.security.model.SysUserDetails; +import com.youlai.boot.security.token.TokenManager; import com.youlai.boot.system.service.WebSocketService; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/youlai/boot/core/aspect/LogAspect.java b/src/main/java/com/youlai/boot/core/aspect/LogAspect.java index 40526ead..4b7ed91b 100644 --- a/src/main/java/com/youlai/boot/core/aspect/LogAspect.java +++ b/src/main/java/com/youlai/boot/core/aspect/LogAspect.java @@ -10,7 +10,7 @@ import cn.hutool.json.JSONUtil; import com.aliyun.oss.HttpMethod; import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.common.util.IPUtils; -import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.security.util.SecurityUtils; import com.youlai.boot.system.model.entity.Log; import com.youlai.boot.system.service.LogService; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/com/youlai/boot/core/aspect/RepeatSubmitAspect.java b/src/main/java/com/youlai/boot/core/aspect/RepeatSubmitAspect.java index 7ac704a9..c45766a4 100644 --- a/src/main/java/com/youlai/boot/core/aspect/RepeatSubmitAspect.java +++ b/src/main/java/com/youlai/boot/core/aspect/RepeatSubmitAspect.java @@ -4,7 +4,7 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.digest.DigestUtil; import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SecurityConstants; -import com.youlai.boot.common.result.ResultCode; +import com.youlai.boot.core.web.ResultCode; import com.youlai.boot.common.exception.BusinessException; import com.youlai.boot.common.annotation.RepeatSubmit; import com.youlai.boot.common.util.IPUtils; diff --git a/src/main/java/com/youlai/boot/common/exception/BusinessException.java b/src/main/java/com/youlai/boot/core/exception/BusinessException.java similarity index 95% rename from src/main/java/com/youlai/boot/common/exception/BusinessException.java rename to src/main/java/com/youlai/boot/core/exception/BusinessException.java index 06467091..d9b2b4cf 100644 --- a/src/main/java/com/youlai/boot/common/exception/BusinessException.java +++ b/src/main/java/com/youlai/boot/core/exception/BusinessException.java @@ -1,6 +1,6 @@ package com.youlai.boot.common.exception; -import com.youlai.boot.common.result.IResultCode; +import com.youlai.boot.core.web.IResultCode; import lombok.Getter; import org.slf4j.helpers.MessageFormatter; diff --git a/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java b/src/main/java/com/youlai/boot/core/exception/GlobalExceptionHandler.java similarity index 99% rename from src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java rename to src/main/java/com/youlai/boot/core/exception/GlobalExceptionHandler.java index 434921a2..9c97f5ad 100644 --- a/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/youlai/boot/core/exception/GlobalExceptionHandler.java @@ -2,8 +2,8 @@ package com.youlai.boot.common.exception; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.core.JsonProcessingException; -import com.youlai.boot.common.result.Result; -import com.youlai.boot.common.result.ResultCode; +import com.youlai.boot.core.web.Result; +import com.youlai.boot.core.web.ResultCode; import jakarta.servlet.ServletException; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; diff --git a/src/main/java/com/youlai/boot/core/filter/RateLimiterFilter.java b/src/main/java/com/youlai/boot/core/filter/RateLimiterFilter.java index 166455b1..186175e1 100644 --- a/src/main/java/com/youlai/boot/core/filter/RateLimiterFilter.java +++ b/src/main/java/com/youlai/boot/core/filter/RateLimiterFilter.java @@ -4,9 +4,9 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SystemConstants; -import com.youlai.boot.common.result.ResultCode; +import com.youlai.boot.core.web.ResultCode; import com.youlai.boot.common.util.IPUtils; -import com.youlai.boot.common.util.ResponseUtils; +import com.youlai.boot.core.web.WebResponseHelper; import com.youlai.boot.system.service.ConfigService; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -88,7 +88,7 @@ public class RateLimiterFilter extends OncePerRequestFilter { // 判断是否限流 if (rateLimit(ip)) { // 返回限流错误信息 - ResponseUtils.writeErrMsg(response, ResultCode.REQUEST_CONCURRENCY_LIMIT_EXCEEDED); + WebResponseHelper.writeError(response, ResultCode.REQUEST_CONCURRENCY_LIMIT_EXCEEDED); return; } diff --git a/src/main/java/com/youlai/boot/common/result/ExcelResult.java b/src/main/java/com/youlai/boot/core/web/ExcelResult.java similarity index 94% rename from src/main/java/com/youlai/boot/common/result/ExcelResult.java rename to src/main/java/com/youlai/boot/core/web/ExcelResult.java index d9d6590a..c73ef6e5 100644 --- a/src/main/java/com/youlai/boot/common/result/ExcelResult.java +++ b/src/main/java/com/youlai/boot/core/web/ExcelResult.java @@ -1,4 +1,4 @@ -package com.youlai.boot.common.result; +package com.youlai.boot.core.web; import lombok.Data; diff --git a/src/main/java/com/youlai/boot/common/result/IResultCode.java b/src/main/java/com/youlai/boot/core/web/IResultCode.java similarity index 78% rename from src/main/java/com/youlai/boot/common/result/IResultCode.java rename to src/main/java/com/youlai/boot/core/web/IResultCode.java index 741d604a..0f5930ae 100644 --- a/src/main/java/com/youlai/boot/common/result/IResultCode.java +++ b/src/main/java/com/youlai/boot/core/web/IResultCode.java @@ -1,4 +1,4 @@ -package com.youlai.boot.common.result; +package com.youlai.boot.core.web; /** * 响应码接口 diff --git a/src/main/java/com/youlai/boot/common/result/PageResult.java b/src/main/java/com/youlai/boot/core/web/PageResult.java similarity index 95% rename from src/main/java/com/youlai/boot/common/result/PageResult.java rename to src/main/java/com/youlai/boot/core/web/PageResult.java index 693fcb64..16efcaba 100644 --- a/src/main/java/com/youlai/boot/common/result/PageResult.java +++ b/src/main/java/com/youlai/boot/core/web/PageResult.java @@ -1,4 +1,4 @@ -package com.youlai.boot.common.result; +package com.youlai.boot.core.web; import com.baomidou.mybatisplus.core.metadata.IPage; import lombok.Data; diff --git a/src/main/java/com/youlai/boot/common/result/Result.java b/src/main/java/com/youlai/boot/core/web/Result.java similarity index 98% rename from src/main/java/com/youlai/boot/common/result/Result.java rename to src/main/java/com/youlai/boot/core/web/Result.java index 55fee091..783be757 100644 --- a/src/main/java/com/youlai/boot/common/result/Result.java +++ b/src/main/java/com/youlai/boot/core/web/Result.java @@ -1,4 +1,4 @@ -package com.youlai.boot.common.result; +package com.youlai.boot.core.web; import cn.hutool.core.util.StrUtil; import lombok.Data; diff --git a/src/main/java/com/youlai/boot/common/result/ResultCode.java b/src/main/java/com/youlai/boot/core/web/ResultCode.java similarity index 99% rename from src/main/java/com/youlai/boot/common/result/ResultCode.java rename to src/main/java/com/youlai/boot/core/web/ResultCode.java index 2876e6c1..d7ea897b 100644 --- a/src/main/java/com/youlai/boot/common/result/ResultCode.java +++ b/src/main/java/com/youlai/boot/core/web/ResultCode.java @@ -1,4 +1,4 @@ -package com.youlai.boot.common.result; +package com.youlai.boot.core.web; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; @@ -297,4 +297,4 @@ public enum ResultCode implements IResultCode, Serializable { } return SYSTEM_ERROR; // 默认系统执行错误 } -} \ No newline at end of file +} diff --git a/src/main/java/com/youlai/boot/core/web/WebResponseHelper.java b/src/main/java/com/youlai/boot/core/web/WebResponseHelper.java new file mode 100644 index 00000000..00ad93f2 --- /dev/null +++ b/src/main/java/com/youlai/boot/core/web/WebResponseHelper.java @@ -0,0 +1,75 @@ +package com.youlai.boot.core.web; + +import cn.hutool.extra.servlet.JakartaServletUtil; +import cn.hutool.json.JSONUtil; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +/** + * Web响应辅助类 + *

+ * 用于在过滤器、处理器等无法使用 @RestControllerAdvice 的场景中统一处理响应 + * + * @author Ray.Hao + * @since 2.0.0 + */ +@Slf4j +public class WebResponseHelper { + + /** + * 写入错误响应 + * + * @param response HttpServletResponse + * @param resultCode 响应结果码 + */ + public static void writeError(HttpServletResponse response, ResultCode resultCode) { + writeError(response, resultCode, null); + } + + /** + * 写入错误响应(带自定义消息) + * + * @param response HttpServletResponse + * @param resultCode 响应结果码 + * @param message 自定义消息 + */ + public static void writeError(HttpServletResponse response, ResultCode resultCode, String message) { + try { + // 设置HTTP状态码 + int httpStatus = mapHttpStatus(resultCode); + response.setStatus(httpStatus); + + // 构建响应对象 + Result result = message == null + ? Result.failed(resultCode) + : Result.failed(resultCode, message); + + // 写入响应 + JakartaServletUtil.write(response, + JSONUtil.toJsonStr(result), + MediaType.APPLICATION_JSON_VALUE + ); + + } catch (Exception e) { + log.error("写入错误响应失败: resultCode={}, message={}", resultCode, message, e); + } + } + + /** + * 根据业务结果码映射HTTP状态码 + * + * @param resultCode 业务结果码 + * @return HTTP状态码 + */ + private static int mapHttpStatus(ResultCode resultCode) { + return switch (resultCode) { + case ACCESS_UNAUTHORIZED, + ACCESS_TOKEN_INVALID, + REFRESH_TOKEN_INVALID -> HttpStatus.UNAUTHORIZED.value(); + default -> HttpStatus.BAD_REQUEST.value(); + }; + } +} + diff --git a/src/main/java/com/youlai/boot/module/member/controller/MemberController.java b/src/main/java/com/youlai/boot/module/member/controller/MemberController.java deleted file mode 100644 index 63644a5c..00000000 --- a/src/main/java/com/youlai/boot/module/member/controller/MemberController.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.member.controller; - -/** - * 会员控制层-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class MemberController { -} diff --git a/src/main/java/com/youlai/boot/module/member/mapper/MemberMapper.java b/src/main/java/com/youlai/boot/module/member/mapper/MemberMapper.java deleted file mode 100644 index f1b4f548..00000000 --- a/src/main/java/com/youlai/boot/module/member/mapper/MemberMapper.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.member.mapper; - -/** - * 会员数据访问层-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class MemberMapper { -} diff --git a/src/main/java/com/youlai/boot/module/member/model/Member.java b/src/main/java/com/youlai/boot/module/member/model/Member.java deleted file mode 100644 index da5ac12c..00000000 --- a/src/main/java/com/youlai/boot/module/member/model/Member.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.member.model; - -/** - * 会员实体-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class Member { -} diff --git a/src/main/java/com/youlai/boot/module/member/service/MemberService.java b/src/main/java/com/youlai/boot/module/member/service/MemberService.java deleted file mode 100644 index 304602cb..00000000 --- a/src/main/java/com/youlai/boot/module/member/service/MemberService.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.member.service; - -/** - * 会员管理服务类-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class MemberService { -} diff --git a/src/main/java/com/youlai/boot/module/order/controller/OrderController.java b/src/main/java/com/youlai/boot/module/order/controller/OrderController.java deleted file mode 100644 index fee9c91c..00000000 --- a/src/main/java/com/youlai/boot/module/order/controller/OrderController.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.order.controller; - -/** - * 订单控制层-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class OrderController { -} diff --git a/src/main/java/com/youlai/boot/module/order/mapper/OrderMapper.java b/src/main/java/com/youlai/boot/module/order/mapper/OrderMapper.java deleted file mode 100644 index c23001f4..00000000 --- a/src/main/java/com/youlai/boot/module/order/mapper/OrderMapper.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.order.mapper; - -/** - * 订单数据访问层-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class OrderMapper { -} diff --git a/src/main/java/com/youlai/boot/module/order/model/Order.java b/src/main/java/com/youlai/boot/module/order/model/Order.java deleted file mode 100644 index 603d2cf8..00000000 --- a/src/main/java/com/youlai/boot/module/order/model/Order.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.order.model; - -/** - * 订单实体-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class Order { -} diff --git a/src/main/java/com/youlai/boot/module/order/service/OrderService.java b/src/main/java/com/youlai/boot/module/order/service/OrderService.java deleted file mode 100644 index 18bc2987..00000000 --- a/src/main/java/com/youlai/boot/module/order/service/OrderService.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.order.service; - -/** - * 订单管理服务类-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class OrderService { -} diff --git a/src/main/java/com/youlai/boot/module/product/controller/ProductController.java b/src/main/java/com/youlai/boot/module/product/controller/ProductController.java deleted file mode 100644 index b94b35dc..00000000 --- a/src/main/java/com/youlai/boot/module/product/controller/ProductController.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.product.controller; - -/** - * 商品控制层-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class ProductController { -} diff --git a/src/main/java/com/youlai/boot/module/product/mapper/ProductMapper.java b/src/main/java/com/youlai/boot/module/product/mapper/ProductMapper.java deleted file mode 100644 index db214a5b..00000000 --- a/src/main/java/com/youlai/boot/module/product/mapper/ProductMapper.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.product.mapper; - -/** - * 商品数据访问层-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class ProductMapper { -} diff --git a/src/main/java/com/youlai/boot/module/product/model/Product.java b/src/main/java/com/youlai/boot/module/product/model/Product.java deleted file mode 100644 index a40cb566..00000000 --- a/src/main/java/com/youlai/boot/module/product/model/Product.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.product.model; - -/** - * 商品实体-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class Product { -} diff --git a/src/main/java/com/youlai/boot/module/product/service/ProductService.java b/src/main/java/com/youlai/boot/module/product/service/ProductService.java deleted file mode 100644 index 032225c2..00000000 --- a/src/main/java/com/youlai/boot/module/product/service/ProductService.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.youlai.boot.module.product.service; - -/** - * 会员管理服务类-业务模块演示 - * - * @author haoxr - * @since 2024/10/10 - */ -public class ProductService { -} diff --git a/src/main/java/com/youlai/boot/shared/codegen/controller/CodegenController.java b/src/main/java/com/youlai/boot/platform/codegen/controller/CodegenController.java similarity index 88% rename from src/main/java/com/youlai/boot/shared/codegen/controller/CodegenController.java rename to src/main/java/com/youlai/boot/platform/codegen/controller/CodegenController.java index 76c57116..497682f4 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/controller/CodegenController.java +++ b/src/main/java/com/youlai/boot/platform/codegen/controller/CodegenController.java @@ -1,17 +1,17 @@ -package com.youlai.boot.shared.codegen.controller; +package com.youlai.boot.platform.codegen.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.youlai.boot.common.result.PageResult; -import com.youlai.boot.common.result.Result; +import com.youlai.boot.core.web.PageResult; +import com.youlai.boot.core.web.Result; import com.youlai.boot.config.property.CodegenProperties; import com.youlai.boot.common.enums.LogModuleEnum; -import com.youlai.boot.shared.codegen.service.CodegenService; -import com.youlai.boot.shared.codegen.model.form.GenConfigForm; -import com.youlai.boot.shared.codegen.model.query.TablePageQuery; -import com.youlai.boot.shared.codegen.model.vo.CodegenPreviewVO; -import com.youlai.boot.shared.codegen.model.vo.TablePageVO; +import com.youlai.boot.platform.codegen.service.CodegenService; +import com.youlai.boot.platform.codegen.model.form.GenConfigForm; +import com.youlai.boot.platform.codegen.model.query.TablePageQuery; +import com.youlai.boot.platform.codegen.model.vo.CodegenPreviewVO; +import com.youlai.boot.platform.codegen.model.vo.TablePageVO; import com.youlai.boot.common.annotation.Log; -import com.youlai.boot.shared.codegen.service.GenConfigService; +import com.youlai.boot.platform.codegen.service.GenConfigService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; diff --git a/src/main/java/com/youlai/boot/shared/codegen/converter/CodegenConverter.java b/src/main/java/com/youlai/boot/platform/codegen/converter/CodegenConverter.java similarity index 84% rename from src/main/java/com/youlai/boot/shared/codegen/converter/CodegenConverter.java rename to src/main/java/com/youlai/boot/platform/codegen/converter/CodegenConverter.java index 7e33247b..999d5fd1 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/converter/CodegenConverter.java +++ b/src/main/java/com/youlai/boot/platform/codegen/converter/CodegenConverter.java @@ -1,8 +1,8 @@ -package com.youlai.boot.shared.codegen.converter; +package com.youlai.boot.platform.codegen.converter; -import com.youlai.boot.shared.codegen.model.entity.GenConfig; -import com.youlai.boot.shared.codegen.model.entity.GenFieldConfig; -import com.youlai.boot.shared.codegen.model.form.GenConfigForm; +import com.youlai.boot.platform.codegen.model.entity.GenConfig; +import com.youlai.boot.platform.codegen.model.entity.GenFieldConfig; +import com.youlai.boot.platform.codegen.model.form.GenConfigForm; import org.mapstruct.Mapper; import org.mapstruct.Mapping; diff --git a/src/main/java/com/youlai/boot/shared/codegen/enums/FormTypeEnum.java b/src/main/java/com/youlai/boot/platform/codegen/enums/FormTypeEnum.java similarity index 97% rename from src/main/java/com/youlai/boot/shared/codegen/enums/FormTypeEnum.java rename to src/main/java/com/youlai/boot/platform/codegen/enums/FormTypeEnum.java index 255d83f5..34bcbd6e 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/enums/FormTypeEnum.java +++ b/src/main/java/com/youlai/boot/platform/codegen/enums/FormTypeEnum.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.codegen.enums; +package com.youlai.boot.platform.codegen.enums; import com.baomidou.mybatisplus.annotation.EnumValue; import com.fasterxml.jackson.annotation.JsonCreator; diff --git a/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java b/src/main/java/com/youlai/boot/platform/codegen/enums/JavaTypeEnum.java similarity index 98% rename from src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java rename to src/main/java/com/youlai/boot/platform/codegen/enums/JavaTypeEnum.java index a263d48e..44ea85c6 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/enums/JavaTypeEnum.java +++ b/src/main/java/com/youlai/boot/platform/codegen/enums/JavaTypeEnum.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.codegen.enums; +package com.youlai.boot.platform.codegen.enums; import lombok.Getter; diff --git a/src/main/java/com/youlai/boot/shared/codegen/enums/QueryTypeEnum.java b/src/main/java/com/youlai/boot/platform/codegen/enums/QueryTypeEnum.java similarity index 96% rename from src/main/java/com/youlai/boot/shared/codegen/enums/QueryTypeEnum.java rename to src/main/java/com/youlai/boot/platform/codegen/enums/QueryTypeEnum.java index cd2d52b4..7c0469d0 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/enums/QueryTypeEnum.java +++ b/src/main/java/com/youlai/boot/platform/codegen/enums/QueryTypeEnum.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.codegen.enums; +package com.youlai.boot.platform.codegen.enums; import com.baomidou.mybatisplus.annotation.EnumValue; import com.fasterxml.jackson.annotation.JsonCreator; diff --git a/src/main/java/com/youlai/boot/shared/codegen/mapper/DatabaseMapper.java b/src/main/java/com/youlai/boot/platform/codegen/mapper/DatabaseMapper.java similarity index 72% rename from src/main/java/com/youlai/boot/shared/codegen/mapper/DatabaseMapper.java rename to src/main/java/com/youlai/boot/platform/codegen/mapper/DatabaseMapper.java index 1a54cdb2..3d742c63 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/mapper/DatabaseMapper.java +++ b/src/main/java/com/youlai/boot/platform/codegen/mapper/DatabaseMapper.java @@ -1,11 +1,11 @@ -package com.youlai.boot.shared.codegen.mapper; +package com.youlai.boot.platform.codegen.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.youlai.boot.shared.codegen.model.bo.ColumnMetaData; -import com.youlai.boot.shared.codegen.model.bo.TableMetaData; -import com.youlai.boot.shared.codegen.model.query.TablePageQuery; -import com.youlai.boot.shared.codegen.model.vo.TablePageVO; +import com.youlai.boot.platform.codegen.model.bo.ColumnMetaData; +import com.youlai.boot.platform.codegen.model.bo.TableMetaData; +import com.youlai.boot.platform.codegen.model.query.TablePageQuery; +import com.youlai.boot.platform.codegen.model.vo.TablePageVO; import org.apache.ibatis.annotations.Mapper; import java.util.List; diff --git a/src/main/java/com/youlai/boot/shared/codegen/mapper/GenConfigMapper.java b/src/main/java/com/youlai/boot/platform/codegen/mapper/GenConfigMapper.java similarity index 69% rename from src/main/java/com/youlai/boot/shared/codegen/mapper/GenConfigMapper.java rename to src/main/java/com/youlai/boot/platform/codegen/mapper/GenConfigMapper.java index a33aa22a..ac3c79af 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/mapper/GenConfigMapper.java +++ b/src/main/java/com/youlai/boot/platform/codegen/mapper/GenConfigMapper.java @@ -1,7 +1,7 @@ -package com.youlai.boot.shared.codegen.mapper; +package com.youlai.boot.platform.codegen.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.youlai.boot.shared.codegen.model.entity.GenConfig; +import com.youlai.boot.platform.codegen.model.entity.GenConfig; import org.apache.ibatis.annotations.Mapper; /** diff --git a/src/main/java/com/youlai/boot/shared/codegen/mapper/GenFieldConfigMapper.java b/src/main/java/com/youlai/boot/platform/codegen/mapper/GenFieldConfigMapper.java similarity index 69% rename from src/main/java/com/youlai/boot/shared/codegen/mapper/GenFieldConfigMapper.java rename to src/main/java/com/youlai/boot/platform/codegen/mapper/GenFieldConfigMapper.java index de54ec3e..14aaf2b1 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/mapper/GenFieldConfigMapper.java +++ b/src/main/java/com/youlai/boot/platform/codegen/mapper/GenFieldConfigMapper.java @@ -1,7 +1,7 @@ -package com.youlai.boot.shared.codegen.mapper; +package com.youlai.boot.platform.codegen.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.youlai.boot.shared.codegen.model.entity.GenFieldConfig; +import com.youlai.boot.platform.codegen.model.entity.GenFieldConfig; import org.apache.ibatis.annotations.Mapper; /** diff --git a/src/main/java/com/youlai/boot/shared/codegen/model/bo/ColumnMetaData.java b/src/main/java/com/youlai/boot/platform/codegen/model/bo/ColumnMetaData.java similarity index 94% rename from src/main/java/com/youlai/boot/shared/codegen/model/bo/ColumnMetaData.java rename to src/main/java/com/youlai/boot/platform/codegen/model/bo/ColumnMetaData.java index 0bb0e6c0..154947bc 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/model/bo/ColumnMetaData.java +++ b/src/main/java/com/youlai/boot/platform/codegen/model/bo/ColumnMetaData.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.codegen.model.bo; +package com.youlai.boot.platform.codegen.model.bo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/src/main/java/com/youlai/boot/shared/codegen/model/bo/TableMetaData.java b/src/main/java/com/youlai/boot/platform/codegen/model/bo/TableMetaData.java similarity index 91% rename from src/main/java/com/youlai/boot/shared/codegen/model/bo/TableMetaData.java rename to src/main/java/com/youlai/boot/platform/codegen/model/bo/TableMetaData.java index 0f8a18bc..a741ba49 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/model/bo/TableMetaData.java +++ b/src/main/java/com/youlai/boot/platform/codegen/model/bo/TableMetaData.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.codegen.model.bo; +package com.youlai.boot.platform.codegen.model.bo; import lombok.Data; diff --git a/src/main/java/com/youlai/boot/shared/codegen/model/entity/GenConfig.java b/src/main/java/com/youlai/boot/platform/codegen/model/entity/GenConfig.java similarity index 94% rename from src/main/java/com/youlai/boot/shared/codegen/model/entity/GenConfig.java rename to src/main/java/com/youlai/boot/platform/codegen/model/entity/GenConfig.java index 45df1203..13ad52f7 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/model/entity/GenConfig.java +++ b/src/main/java/com/youlai/boot/platform/codegen/model/entity/GenConfig.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.codegen.model.entity; +package com.youlai.boot.platform.codegen.model.entity; import com.baomidou.mybatisplus.annotation.*; diff --git a/src/main/java/com/youlai/boot/shared/codegen/model/entity/GenFieldConfig.java b/src/main/java/com/youlai/boot/platform/codegen/model/entity/GenFieldConfig.java similarity index 90% rename from src/main/java/com/youlai/boot/shared/codegen/model/entity/GenFieldConfig.java rename to src/main/java/com/youlai/boot/platform/codegen/model/entity/GenFieldConfig.java index 7b8e62e7..7c9bb91f 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/model/entity/GenFieldConfig.java +++ b/src/main/java/com/youlai/boot/platform/codegen/model/entity/GenFieldConfig.java @@ -1,12 +1,12 @@ -package com.youlai.boot.shared.codegen.model.entity; +package com.youlai.boot.platform.codegen.model.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonIgnore; import com.youlai.boot.common.base.BaseEntity; -import com.youlai.boot.shared.codegen.enums.FormTypeEnum; -import com.youlai.boot.shared.codegen.enums.QueryTypeEnum; +import com.youlai.boot.platform.codegen.enums.FormTypeEnum; +import com.youlai.boot.platform.codegen.enums.QueryTypeEnum; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/youlai/boot/shared/codegen/model/form/GenConfigForm.java b/src/main/java/com/youlai/boot/platform/codegen/model/form/GenConfigForm.java similarity index 94% rename from src/main/java/com/youlai/boot/shared/codegen/model/form/GenConfigForm.java rename to src/main/java/com/youlai/boot/platform/codegen/model/form/GenConfigForm.java index 60a6dc03..cf5d22ec 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/model/form/GenConfigForm.java +++ b/src/main/java/com/youlai/boot/platform/codegen/model/form/GenConfigForm.java @@ -1,7 +1,7 @@ -package com.youlai.boot.shared.codegen.model.form; +package com.youlai.boot.platform.codegen.model.form; -import com.youlai.boot.shared.codegen.enums.FormTypeEnum; -import com.youlai.boot.shared.codegen.enums.QueryTypeEnum; +import com.youlai.boot.platform.codegen.enums.FormTypeEnum; +import com.youlai.boot.platform.codegen.enums.QueryTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/src/main/java/com/youlai/boot/shared/codegen/model/query/TablePageQuery.java b/src/main/java/com/youlai/boot/platform/codegen/model/query/TablePageQuery.java similarity index 91% rename from src/main/java/com/youlai/boot/shared/codegen/model/query/TablePageQuery.java rename to src/main/java/com/youlai/boot/platform/codegen/model/query/TablePageQuery.java index f44cd8a3..4213560d 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/model/query/TablePageQuery.java +++ b/src/main/java/com/youlai/boot/platform/codegen/model/query/TablePageQuery.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.codegen.model.query; +package com.youlai.boot.platform.codegen.model.query; import com.fasterxml.jackson.annotation.JsonIgnore; import com.youlai.boot.common.base.BasePageQuery; diff --git a/src/main/java/com/youlai/boot/shared/codegen/model/vo/CodegenPreviewVO.java b/src/main/java/com/youlai/boot/platform/codegen/model/vo/CodegenPreviewVO.java similarity index 89% rename from src/main/java/com/youlai/boot/shared/codegen/model/vo/CodegenPreviewVO.java rename to src/main/java/com/youlai/boot/platform/codegen/model/vo/CodegenPreviewVO.java index 2b8e1a71..749d21bf 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/model/vo/CodegenPreviewVO.java +++ b/src/main/java/com/youlai/boot/platform/codegen/model/vo/CodegenPreviewVO.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.codegen.model.vo; +package com.youlai.boot.platform.codegen.model.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/src/main/java/com/youlai/boot/shared/codegen/model/vo/TablePageVO.java b/src/main/java/com/youlai/boot/platform/codegen/model/vo/TablePageVO.java similarity index 94% rename from src/main/java/com/youlai/boot/shared/codegen/model/vo/TablePageVO.java rename to src/main/java/com/youlai/boot/platform/codegen/model/vo/TablePageVO.java index 30ff28b8..0b011181 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/model/vo/TablePageVO.java +++ b/src/main/java/com/youlai/boot/platform/codegen/model/vo/TablePageVO.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.codegen.model.vo; +package com.youlai.boot.platform.codegen.model.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/src/main/java/com/youlai/boot/shared/codegen/service/CodegenService.java b/src/main/java/com/youlai/boot/platform/codegen/service/CodegenService.java similarity index 74% rename from src/main/java/com/youlai/boot/shared/codegen/service/CodegenService.java rename to src/main/java/com/youlai/boot/platform/codegen/service/CodegenService.java index f42bc765..32c020d1 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/service/CodegenService.java +++ b/src/main/java/com/youlai/boot/platform/codegen/service/CodegenService.java @@ -1,9 +1,9 @@ -package com.youlai.boot.shared.codegen.service; +package com.youlai.boot.platform.codegen.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.youlai.boot.shared.codegen.model.query.TablePageQuery; -import com.youlai.boot.shared.codegen.model.vo.CodegenPreviewVO; -import com.youlai.boot.shared.codegen.model.vo.TablePageVO; +import com.youlai.boot.platform.codegen.model.query.TablePageQuery; +import com.youlai.boot.platform.codegen.model.vo.CodegenPreviewVO; +import com.youlai.boot.platform.codegen.model.vo.TablePageVO; import java.util.List; diff --git a/src/main/java/com/youlai/boot/shared/codegen/service/GenConfigService.java b/src/main/java/com/youlai/boot/platform/codegen/service/GenConfigService.java similarity index 78% rename from src/main/java/com/youlai/boot/shared/codegen/service/GenConfigService.java rename to src/main/java/com/youlai/boot/platform/codegen/service/GenConfigService.java index 9b039b2c..2731260e 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/service/GenConfigService.java +++ b/src/main/java/com/youlai/boot/platform/codegen/service/GenConfigService.java @@ -1,8 +1,8 @@ -package com.youlai.boot.shared.codegen.service; +package com.youlai.boot.platform.codegen.service; import com.baomidou.mybatisplus.extension.service.IService; -import com.youlai.boot.shared.codegen.model.entity.GenConfig; -import com.youlai.boot.shared.codegen.model.form.GenConfigForm; +import com.youlai.boot.platform.codegen.model.entity.GenConfig; +import com.youlai.boot.platform.codegen.model.form.GenConfigForm; /** * 代码生成配置接口 diff --git a/src/main/java/com/youlai/boot/shared/codegen/service/GenFieldConfigService.java b/src/main/java/com/youlai/boot/platform/codegen/service/GenFieldConfigService.java similarity index 63% rename from src/main/java/com/youlai/boot/shared/codegen/service/GenFieldConfigService.java rename to src/main/java/com/youlai/boot/platform/codegen/service/GenFieldConfigService.java index 5433da03..fbfda4d4 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/service/GenFieldConfigService.java +++ b/src/main/java/com/youlai/boot/platform/codegen/service/GenFieldConfigService.java @@ -1,7 +1,7 @@ -package com.youlai.boot.shared.codegen.service; +package com.youlai.boot.platform.codegen.service; import com.baomidou.mybatisplus.extension.service.IService; -import com.youlai.boot.shared.codegen.model.entity.GenFieldConfig; +import com.youlai.boot.platform.codegen.model.entity.GenFieldConfig; /** * 代码生成配置接口 diff --git a/src/main/java/com/youlai/boot/shared/codegen/service/impl/CodegenServiceImpl.java b/src/main/java/com/youlai/boot/platform/codegen/service/impl/CodegenServiceImpl.java similarity index 93% rename from src/main/java/com/youlai/boot/shared/codegen/service/impl/CodegenServiceImpl.java rename to src/main/java/com/youlai/boot/platform/codegen/service/impl/CodegenServiceImpl.java index 8e568714..6d8c91ec 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/service/impl/CodegenServiceImpl.java +++ b/src/main/java/com/youlai/boot/platform/codegen/service/impl/CodegenServiceImpl.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.codegen.service.impl; +package com.youlai.boot.platform.codegen.service.impl; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateUtil; @@ -11,18 +11,18 @@ import cn.hutool.extra.template.TemplateEngine; import cn.hutool.extra.template.TemplateUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.youlai.boot.shared.codegen.enums.JavaTypeEnum; +import com.youlai.boot.platform.codegen.enums.JavaTypeEnum; import com.youlai.boot.config.property.CodegenProperties; -import com.youlai.boot.shared.codegen.service.GenConfigService; -import com.youlai.boot.shared.codegen.service.GenFieldConfigService; -import com.youlai.boot.shared.codegen.service.CodegenService; +import com.youlai.boot.platform.codegen.service.GenConfigService; +import com.youlai.boot.platform.codegen.service.GenFieldConfigService; +import com.youlai.boot.platform.codegen.service.CodegenService; import com.youlai.boot.common.exception.BusinessException; -import com.youlai.boot.shared.codegen.mapper.DatabaseMapper; -import com.youlai.boot.shared.codegen.model.entity.GenConfig; -import com.youlai.boot.shared.codegen.model.entity.GenFieldConfig; -import com.youlai.boot.shared.codegen.model.query.TablePageQuery; -import com.youlai.boot.shared.codegen.model.vo.CodegenPreviewVO; -import com.youlai.boot.shared.codegen.model.vo.TablePageVO; +import com.youlai.boot.platform.codegen.mapper.DatabaseMapper; +import com.youlai.boot.platform.codegen.model.entity.GenConfig; +import com.youlai.boot.platform.codegen.model.entity.GenFieldConfig; +import com.youlai.boot.platform.codegen.model.query.TablePageQuery; +import com.youlai.boot.platform.codegen.model.vo.CodegenPreviewVO; +import com.youlai.boot.platform.codegen.model.vo.TablePageVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -258,9 +258,6 @@ public class CodegenServiceImpl implements CodegenService { TemplateEngine templateEngine = TemplateUtil.createEngine(new TemplateConfig("templates", TemplateConfig.ResourceMode.CLASSPATH)); // 根据 ui 选择不同的前端页面模板:默认 index.vue.vm;封装版使用 index.curd.vue.vm String path = templateConfig.getTemplatePath(); - if ("VIEW".equals(FileNameUtil.mainName(path))) { - // 无法通过文件名区分时,依据子包名与扩展名判断 - } if ("curd".equalsIgnoreCase(pageType) && path.endsWith("index.vue.vm")) { path = path.replace("index.vue.vm", "index.curd.vue.vm"); } diff --git a/src/main/java/com/youlai/boot/shared/codegen/service/impl/GenConfigServiceImpl.java b/src/main/java/com/youlai/boot/platform/codegen/service/impl/GenConfigServiceImpl.java similarity index 90% rename from src/main/java/com/youlai/boot/shared/codegen/service/impl/GenConfigServiceImpl.java rename to src/main/java/com/youlai/boot/platform/codegen/service/impl/GenConfigServiceImpl.java index 143f6372..fe4cb49d 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/service/impl/GenConfigServiceImpl.java +++ b/src/main/java/com/youlai/boot/platform/codegen/service/impl/GenConfigServiceImpl.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.codegen.service.impl; +package com.youlai.boot.platform.codegen.service.impl; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.lang.Assert; @@ -7,21 +7,21 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.youlai.boot.YouLaiBootApplication; import com.youlai.boot.common.enums.EnvEnum; -import com.youlai.boot.shared.codegen.enums.FormTypeEnum; -import com.youlai.boot.shared.codegen.enums.JavaTypeEnum; -import com.youlai.boot.shared.codegen.enums.QueryTypeEnum; +import com.youlai.boot.platform.codegen.enums.FormTypeEnum; +import com.youlai.boot.platform.codegen.enums.JavaTypeEnum; +import com.youlai.boot.platform.codegen.enums.QueryTypeEnum; import com.youlai.boot.common.exception.BusinessException; import com.youlai.boot.config.property.CodegenProperties; -import com.youlai.boot.shared.codegen.converter.CodegenConverter; -import com.youlai.boot.shared.codegen.mapper.DatabaseMapper; -import com.youlai.boot.shared.codegen.mapper.GenConfigMapper; -import com.youlai.boot.shared.codegen.model.bo.ColumnMetaData; -import com.youlai.boot.shared.codegen.model.bo.TableMetaData; -import com.youlai.boot.shared.codegen.model.entity.GenConfig; -import com.youlai.boot.shared.codegen.model.entity.GenFieldConfig; -import com.youlai.boot.shared.codegen.model.form.GenConfigForm; -import com.youlai.boot.shared.codegen.service.GenConfigService; -import com.youlai.boot.shared.codegen.service.GenFieldConfigService; +import com.youlai.boot.platform.codegen.converter.CodegenConverter; +import com.youlai.boot.platform.codegen.mapper.DatabaseMapper; +import com.youlai.boot.platform.codegen.mapper.GenConfigMapper; +import com.youlai.boot.platform.codegen.model.bo.ColumnMetaData; +import com.youlai.boot.platform.codegen.model.bo.TableMetaData; +import com.youlai.boot.platform.codegen.model.entity.GenConfig; +import com.youlai.boot.platform.codegen.model.entity.GenFieldConfig; +import com.youlai.boot.platform.codegen.model.form.GenConfigForm; +import com.youlai.boot.platform.codegen.service.GenConfigService; +import com.youlai.boot.platform.codegen.service.GenFieldConfigService; import com.youlai.boot.system.service.MenuService; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/com/youlai/boot/shared/codegen/service/impl/GenFieldConfigServiceImpl.java b/src/main/java/com/youlai/boot/platform/codegen/service/impl/GenFieldConfigServiceImpl.java similarity index 60% rename from src/main/java/com/youlai/boot/shared/codegen/service/impl/GenFieldConfigServiceImpl.java rename to src/main/java/com/youlai/boot/platform/codegen/service/impl/GenFieldConfigServiceImpl.java index 948711e5..e6dd938b 100644 --- a/src/main/java/com/youlai/boot/shared/codegen/service/impl/GenFieldConfigServiceImpl.java +++ b/src/main/java/com/youlai/boot/platform/codegen/service/impl/GenFieldConfigServiceImpl.java @@ -1,9 +1,9 @@ -package com.youlai.boot.shared.codegen.service.impl; +package com.youlai.boot.platform.codegen.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.youlai.boot.shared.codegen.mapper.GenFieldConfigMapper; -import com.youlai.boot.shared.codegen.model.entity.GenFieldConfig; -import com.youlai.boot.shared.codegen.service.GenFieldConfigService; +import com.youlai.boot.platform.codegen.mapper.GenFieldConfigMapper; +import com.youlai.boot.platform.codegen.model.entity.GenFieldConfig; +import com.youlai.boot.platform.codegen.service.GenFieldConfigService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/youlai/boot/shared/file/controller/FileController.java b/src/main/java/com/youlai/boot/platform/file/controller/FileController.java similarity index 88% rename from src/main/java/com/youlai/boot/shared/file/controller/FileController.java rename to src/main/java/com/youlai/boot/platform/file/controller/FileController.java index b07a9c21..a6b63feb 100644 --- a/src/main/java/com/youlai/boot/shared/file/controller/FileController.java +++ b/src/main/java/com/youlai/boot/platform/file/controller/FileController.java @@ -1,8 +1,8 @@ -package com.youlai.boot.shared.file.controller; +package com.youlai.boot.platform.file.controller; -import com.youlai.boot.common.result.Result; -import com.youlai.boot.shared.file.service.FileService; -import com.youlai.boot.shared.file.model.FileInfo; +import com.youlai.boot.core.web.Result; +import com.youlai.boot.platform.file.service.FileService; +import com.youlai.boot.platform.file.model.FileInfo; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/src/main/java/com/youlai/boot/shared/file/model/FileInfo.java b/src/main/java/com/youlai/boot/platform/file/model/FileInfo.java similarity index 88% rename from src/main/java/com/youlai/boot/shared/file/model/FileInfo.java rename to src/main/java/com/youlai/boot/platform/file/model/FileInfo.java index ec550a18..b50acc51 100644 --- a/src/main/java/com/youlai/boot/shared/file/model/FileInfo.java +++ b/src/main/java/com/youlai/boot/platform/file/model/FileInfo.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.file.model; +package com.youlai.boot.platform.file.model; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/src/main/java/com/youlai/boot/shared/file/service/FileService.java b/src/main/java/com/youlai/boot/platform/file/service/FileService.java similarity index 82% rename from src/main/java/com/youlai/boot/shared/file/service/FileService.java rename to src/main/java/com/youlai/boot/platform/file/service/FileService.java index 42bce9b4..501abfdc 100644 --- a/src/main/java/com/youlai/boot/shared/file/service/FileService.java +++ b/src/main/java/com/youlai/boot/platform/file/service/FileService.java @@ -1,6 +1,6 @@ -package com.youlai.boot.shared.file.service; +package com.youlai.boot.platform.file.service; -import com.youlai.boot.shared.file.model.FileInfo; +import com.youlai.boot.platform.file.model.FileInfo; import org.springframework.web.multipart.MultipartFile; /** diff --git a/src/main/java/com/youlai/boot/shared/file/service/impl/AliyunFileService.java b/src/main/java/com/youlai/boot/platform/file/service/impl/AliyunFileService.java similarity index 95% rename from src/main/java/com/youlai/boot/shared/file/service/impl/AliyunFileService.java rename to src/main/java/com/youlai/boot/platform/file/service/impl/AliyunFileService.java index 004475fd..4831cbc1 100644 --- a/src/main/java/com/youlai/boot/shared/file/service/impl/AliyunFileService.java +++ b/src/main/java/com/youlai/boot/platform/file/service/impl/AliyunFileService.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.file.service.impl; +package com.youlai.boot.platform.file.service.impl; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; @@ -8,8 +8,8 @@ import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.model.ObjectMetadata; import com.aliyun.oss.model.PutObjectRequest; -import com.youlai.boot.shared.file.service.FileService; -import com.youlai.boot.shared.file.model.FileInfo; +import com.youlai.boot.platform.file.service.FileService; +import com.youlai.boot.platform.file.model.FileInfo; import jakarta.annotation.PostConstruct; import lombok.Data; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/youlai/boot/shared/file/service/impl/LocalFileService.java b/src/main/java/com/youlai/boot/platform/file/service/impl/LocalFileService.java similarity index 94% rename from src/main/java/com/youlai/boot/shared/file/service/impl/LocalFileService.java rename to src/main/java/com/youlai/boot/platform/file/service/impl/LocalFileService.java index 5cd0b97f..15fb9761 100644 --- a/src/main/java/com/youlai/boot/shared/file/service/impl/LocalFileService.java +++ b/src/main/java/com/youlai/boot/platform/file/service/impl/LocalFileService.java @@ -1,11 +1,11 @@ -package com.youlai.boot.shared.file.service.impl; +package com.youlai.boot.platform.file.service.impl; import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.IdUtil; -import com.youlai.boot.shared.file.model.FileInfo; -import com.youlai.boot.shared.file.service.FileService; +import com.youlai.boot.platform.file.model.FileInfo; +import com.youlai.boot.platform.file.service.FileService; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/youlai/boot/shared/file/service/impl/MinioFileService.java b/src/main/java/com/youlai/boot/platform/file/service/impl/MinioFileService.java similarity index 97% rename from src/main/java/com/youlai/boot/shared/file/service/impl/MinioFileService.java rename to src/main/java/com/youlai/boot/platform/file/service/impl/MinioFileService.java index 030ff1aa..367a6c8f 100644 --- a/src/main/java/com/youlai/boot/shared/file/service/impl/MinioFileService.java +++ b/src/main/java/com/youlai/boot/platform/file/service/impl/MinioFileService.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.file.service.impl; +package com.youlai.boot.platform.file.service.impl; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; @@ -6,9 +6,9 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import com.youlai.boot.common.exception.BusinessException; -import com.youlai.boot.common.result.ResultCode; -import com.youlai.boot.shared.file.model.FileInfo; -import com.youlai.boot.shared.file.service.FileService; +import com.youlai.boot.core.web.ResultCode; +import com.youlai.boot.platform.file.model.FileInfo; +import com.youlai.boot.platform.file.service.FileService; import io.minio.*; import io.minio.http.Method; import jakarta.annotation.PostConstruct; diff --git a/src/main/java/com/youlai/boot/shared/mail/controller/MailController.java b/src/main/java/com/youlai/boot/platform/mail/controller/MailController.java similarity index 76% rename from src/main/java/com/youlai/boot/shared/mail/controller/MailController.java rename to src/main/java/com/youlai/boot/platform/mail/controller/MailController.java index d9e4c20d..f31c127b 100644 --- a/src/main/java/com/youlai/boot/shared/mail/controller/MailController.java +++ b/src/main/java/com/youlai/boot/platform/mail/controller/MailController.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.mail.controller; +package com.youlai.boot.platform.mail.controller; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/youlai/boot/shared/mail/service/MailService.java b/src/main/java/com/youlai/boot/platform/mail/service/MailService.java similarity index 92% rename from src/main/java/com/youlai/boot/shared/mail/service/MailService.java rename to src/main/java/com/youlai/boot/platform/mail/service/MailService.java index 60f4d827..f1304cfd 100644 --- a/src/main/java/com/youlai/boot/shared/mail/service/MailService.java +++ b/src/main/java/com/youlai/boot/platform/mail/service/MailService.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.mail.service; +package com.youlai.boot.platform.mail.service; /** * 邮件服务接口层 diff --git a/src/main/java/com/youlai/boot/shared/mail/service/impl/MailServiceImpl.java b/src/main/java/com/youlai/boot/platform/mail/service/impl/MailServiceImpl.java similarity index 95% rename from src/main/java/com/youlai/boot/shared/mail/service/impl/MailServiceImpl.java rename to src/main/java/com/youlai/boot/platform/mail/service/impl/MailServiceImpl.java index 2373401c..9ffc74b7 100644 --- a/src/main/java/com/youlai/boot/shared/mail/service/impl/MailServiceImpl.java +++ b/src/main/java/com/youlai/boot/platform/mail/service/impl/MailServiceImpl.java @@ -1,7 +1,7 @@ -package com.youlai.boot.shared.mail.service.impl; +package com.youlai.boot.platform.mail.service.impl; import com.youlai.boot.config.property.MailProperties; -import com.youlai.boot.shared.mail.service.MailService; +import com.youlai.boot.platform.mail.service.MailService; import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/youlai/boot/shared/sms/controller/SmsController.java b/src/main/java/com/youlai/boot/platform/sms/controller/SmsController.java similarity index 66% rename from src/main/java/com/youlai/boot/shared/sms/controller/SmsController.java rename to src/main/java/com/youlai/boot/platform/sms/controller/SmsController.java index 710601bc..c88b3f29 100644 --- a/src/main/java/com/youlai/boot/shared/sms/controller/SmsController.java +++ b/src/main/java/com/youlai/boot/platform/sms/controller/SmsController.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.sms.controller; +package com.youlai.boot.platform.sms.controller; /** * 短信控制层 diff --git a/src/main/java/com/youlai/boot/shared/sms/enums/SmsTypeEnum.java b/src/main/java/com/youlai/boot/platform/sms/enums/SmsTypeEnum.java similarity index 94% rename from src/main/java/com/youlai/boot/shared/sms/enums/SmsTypeEnum.java rename to src/main/java/com/youlai/boot/platform/sms/enums/SmsTypeEnum.java index b852a797..ceede8a8 100644 --- a/src/main/java/com/youlai/boot/shared/sms/enums/SmsTypeEnum.java +++ b/src/main/java/com/youlai/boot/platform/sms/enums/SmsTypeEnum.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.sms.enums; +package com.youlai.boot.platform.sms.enums; import com.youlai.boot.common.base.IBaseEnum; import lombok.Getter; diff --git a/src/main/java/com/youlai/boot/shared/sms/service/SmsService.java b/src/main/java/com/youlai/boot/platform/sms/service/SmsService.java similarity index 85% rename from src/main/java/com/youlai/boot/shared/sms/service/SmsService.java rename to src/main/java/com/youlai/boot/platform/sms/service/SmsService.java index 3deb1157..d4097353 100644 --- a/src/main/java/com/youlai/boot/shared/sms/service/SmsService.java +++ b/src/main/java/com/youlai/boot/platform/sms/service/SmsService.java @@ -1,6 +1,6 @@ -package com.youlai.boot.shared.sms.service; +package com.youlai.boot.platform.sms.service; -import com.youlai.boot.shared.sms.enums.SmsTypeEnum; +import com.youlai.boot.platform.sms.enums.SmsTypeEnum; import java.util.Map; diff --git a/src/main/java/com/youlai/boot/shared/sms/service/impl/AliyunSmsService.java b/src/main/java/com/youlai/boot/platform/sms/service/impl/AliyunSmsService.java similarity index 94% rename from src/main/java/com/youlai/boot/shared/sms/service/impl/AliyunSmsService.java rename to src/main/java/com/youlai/boot/platform/sms/service/impl/AliyunSmsService.java index b37ed844..a9adac40 100644 --- a/src/main/java/com/youlai/boot/shared/sms/service/impl/AliyunSmsService.java +++ b/src/main/java/com/youlai/boot/platform/sms/service/impl/AliyunSmsService.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.sms.service.impl; +package com.youlai.boot.platform.sms.service.impl; import cn.hutool.json.JSONUtil; import com.aliyuncs.CommonRequest; @@ -9,8 +9,8 @@ import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.http.MethodType; import com.aliyuncs.profile.DefaultProfile; import com.youlai.boot.config.property.AliyunSmsProperties; -import com.youlai.boot.shared.sms.enums.SmsTypeEnum; -import com.youlai.boot.shared.sms.service.SmsService; +import com.youlai.boot.platform.sms.enums.SmsTypeEnum; +import com.youlai.boot.platform.sms.service.SmsService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/youlai/boot/shared/websocket/controller/WebsocketController.java b/src/main/java/com/youlai/boot/platform/websocket/controller/WebsocketController.java similarity index 94% rename from src/main/java/com/youlai/boot/shared/websocket/controller/WebsocketController.java rename to src/main/java/com/youlai/boot/platform/websocket/controller/WebsocketController.java index 610a2455..0a9f8966 100644 --- a/src/main/java/com/youlai/boot/shared/websocket/controller/WebsocketController.java +++ b/src/main/java/com/youlai/boot/platform/websocket/controller/WebsocketController.java @@ -1,6 +1,6 @@ -package com.youlai.boot.shared.websocket.controller; +package com.youlai.boot.platform.websocket.controller; -import com.youlai.boot.shared.websocket.model.ChatMessage; +import com.youlai.boot.platform.websocket.model.ChatMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.messaging.handler.annotation.DestinationVariable; diff --git a/src/main/java/com/youlai/boot/shared/websocket/model/ChatMessage.java b/src/main/java/com/youlai/boot/platform/websocket/model/ChatMessage.java similarity index 86% rename from src/main/java/com/youlai/boot/shared/websocket/model/ChatMessage.java rename to src/main/java/com/youlai/boot/platform/websocket/model/ChatMessage.java index dff4ffac..a94b0b11 100644 --- a/src/main/java/com/youlai/boot/shared/websocket/model/ChatMessage.java +++ b/src/main/java/com/youlai/boot/platform/websocket/model/ChatMessage.java @@ -1,4 +1,4 @@ -package com.youlai.boot.shared.websocket.model; +package com.youlai.boot.platform.websocket.model; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/src/main/java/com/youlai/boot/pulgin/knife4j/Knife4jOpenApiCustomizer.java b/src/main/java/com/youlai/boot/plugin/knife4j/Knife4jOpenApiCustomizer.java similarity index 100% rename from src/main/java/com/youlai/boot/pulgin/knife4j/Knife4jOpenApiCustomizer.java rename to src/main/java/com/youlai/boot/plugin/knife4j/Knife4jOpenApiCustomizer.java diff --git a/src/main/java/com/youlai/boot/pulgin/mybatis/MyDataPermissionHandler.java b/src/main/java/com/youlai/boot/plugin/mybatis/MyDataPermissionHandler.java similarity index 98% rename from src/main/java/com/youlai/boot/pulgin/mybatis/MyDataPermissionHandler.java rename to src/main/java/com/youlai/boot/plugin/mybatis/MyDataPermissionHandler.java index aa98f34e..e7ff2ff8 100644 --- a/src/main/java/com/youlai/boot/pulgin/mybatis/MyDataPermissionHandler.java +++ b/src/main/java/com/youlai/boot/plugin/mybatis/MyDataPermissionHandler.java @@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler; import com.youlai.boot.common.annotation.DataPermission; import com.youlai.boot.common.base.IBaseEnum; import com.youlai.boot.common.enums.DataScopeEnum; -import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.security.util.SecurityUtils; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.expression.Expression; diff --git a/src/main/java/com/youlai/boot/pulgin/mybatis/MyMetaObjectHandler.java b/src/main/java/com/youlai/boot/plugin/mybatis/MyMetaObjectHandler.java similarity index 100% rename from src/main/java/com/youlai/boot/pulgin/mybatis/MyMetaObjectHandler.java rename to src/main/java/com/youlai/boot/plugin/mybatis/MyMetaObjectHandler.java diff --git a/src/main/java/com/youlai/boot/core/security/exception/CaptchaValidationException.java b/src/main/java/com/youlai/boot/security/exception/CaptchaValidationException.java similarity index 85% rename from src/main/java/com/youlai/boot/core/security/exception/CaptchaValidationException.java rename to src/main/java/com/youlai/boot/security/exception/CaptchaValidationException.java index 7b498a1a..5232f01c 100644 --- a/src/main/java/com/youlai/boot/core/security/exception/CaptchaValidationException.java +++ b/src/main/java/com/youlai/boot/security/exception/CaptchaValidationException.java @@ -1,4 +1,4 @@ -package com.youlai.boot.core.security.exception; +package com.youlai.boot.core.exception; import org.springframework.security.core.AuthenticationException; diff --git a/src/main/java/com/youlai/boot/core/security/filter/CaptchaValidationFilter.java b/src/main/java/com/youlai/boot/security/filter/CaptchaValidationFilter.java similarity index 88% rename from src/main/java/com/youlai/boot/core/security/filter/CaptchaValidationFilter.java rename to src/main/java/com/youlai/boot/security/filter/CaptchaValidationFilter.java index 8589a6e0..771a7555 100644 --- a/src/main/java/com/youlai/boot/core/security/filter/CaptchaValidationFilter.java +++ b/src/main/java/com/youlai/boot/security/filter/CaptchaValidationFilter.java @@ -1,11 +1,11 @@ -package com.youlai.boot.core.security.filter; +package com.youlai.boot.security.filter; import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.util.StrUtil; import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SecurityConstants; -import com.youlai.boot.common.result.ResultCode; -import com.youlai.boot.common.util.ResponseUtils; +import com.youlai.boot.core.web.ResultCode; +import com.youlai.boot.core.web.WebResponseHelper; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -58,13 +58,13 @@ public class CaptchaValidationFilter extends OncePerRequestFilter { StrUtil.format(RedisConstants.Captcha.IMAGE_CODE, verifyCodeKey) ); if (cacheVerifyCode == null) { - ResponseUtils.writeErrMsg(response, ResultCode.USER_VERIFICATION_CODE_EXPIRED); + WebResponseHelper.writeError(response, ResultCode.USER_VERIFICATION_CODE_EXPIRED); } else { // 验证码比对 if (codeGenerator.verify(cacheVerifyCode, captchaCode)) { chain.doFilter(request, response); } else { - ResponseUtils.writeErrMsg(response, ResultCode.USER_VERIFICATION_CODE_ERROR); + WebResponseHelper.writeError(response, ResultCode.USER_VERIFICATION_CODE_ERROR); } } } else { diff --git a/src/main/java/com/youlai/boot/core/security/filter/TokenAuthenticationFilter.java b/src/main/java/com/youlai/boot/security/filter/TokenAuthenticationFilter.java similarity index 86% rename from src/main/java/com/youlai/boot/core/security/filter/TokenAuthenticationFilter.java rename to src/main/java/com/youlai/boot/security/filter/TokenAuthenticationFilter.java index b71e86ce..91712520 100644 --- a/src/main/java/com/youlai/boot/core/security/filter/TokenAuthenticationFilter.java +++ b/src/main/java/com/youlai/boot/security/filter/TokenAuthenticationFilter.java @@ -1,10 +1,10 @@ -package com.youlai.boot.core.security.filter; +package com.youlai.boot.security.filter; import cn.hutool.core.util.StrUtil; import com.youlai.boot.common.constant.SecurityConstants; -import com.youlai.boot.common.result.ResultCode; -import com.youlai.boot.common.util.ResponseUtils; -import com.youlai.boot.core.security.token.TokenManager; +import com.youlai.boot.core.web.ResultCode; +import com.youlai.boot.core.web.WebResponseHelper; +import com.youlai.boot.security.token.TokenManager; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -52,7 +52,7 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { // 执行令牌有效性检查(包含密码学验签和过期时间验证) boolean isValidToken = tokenManager.validateToken(rawToken); if (!isValidToken) { - ResponseUtils.writeErrMsg(response, ResultCode.ACCESS_TOKEN_INVALID); + WebResponseHelper.writeError(response, ResultCode.ACCESS_TOKEN_INVALID); return; } @@ -63,7 +63,7 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { } catch (Exception ex) { // 安全上下文清除保障(防止上下文残留) SecurityContextHolder.clearContext(); - ResponseUtils.writeErrMsg(response, ResultCode.ACCESS_TOKEN_INVALID); + WebResponseHelper.writeError(response, ResultCode.ACCESS_TOKEN_INVALID); return; } diff --git a/src/main/java/com/youlai/boot/core/security/exception/MyAccessDeniedHandler.java b/src/main/java/com/youlai/boot/security/handler/MyAccessDeniedHandler.java similarity index 72% rename from src/main/java/com/youlai/boot/core/security/exception/MyAccessDeniedHandler.java rename to src/main/java/com/youlai/boot/security/handler/MyAccessDeniedHandler.java index 5efbfbff..e1e2a861 100644 --- a/src/main/java/com/youlai/boot/core/security/exception/MyAccessDeniedHandler.java +++ b/src/main/java/com/youlai/boot/security/handler/MyAccessDeniedHandler.java @@ -1,7 +1,7 @@ -package com.youlai.boot.core.security.exception; +package com.youlai.boot.security.handler; -import com.youlai.boot.common.result.ResultCode; -import com.youlai.boot.common.util.ResponseUtils; +import com.youlai.boot.core.web.ResultCode; +import com.youlai.boot.core.web.WebResponseHelper; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; @@ -20,7 +20,7 @@ public class MyAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) { - ResponseUtils.writeErrMsg(response, ResultCode.ACCESS_UNAUTHORIZED); + WebResponseHelper.writeError(response, ResultCode.ACCESS_UNAUTHORIZED); } } diff --git a/src/main/java/com/youlai/boot/core/security/exception/MyAuthenticationEntryPoint.java b/src/main/java/com/youlai/boot/security/handler/MyAuthenticationEntryPoint.java similarity index 78% rename from src/main/java/com/youlai/boot/core/security/exception/MyAuthenticationEntryPoint.java rename to src/main/java/com/youlai/boot/security/handler/MyAuthenticationEntryPoint.java index ff5f041e..09d0780c 100644 --- a/src/main/java/com/youlai/boot/core/security/exception/MyAuthenticationEntryPoint.java +++ b/src/main/java/com/youlai/boot/security/handler/MyAuthenticationEntryPoint.java @@ -1,7 +1,7 @@ -package com.youlai.boot.core.security.exception; +package com.youlai.boot.security.handler; -import com.youlai.boot.common.result.ResultCode; -import com.youlai.boot.common.util.ResponseUtils; +import com.youlai.boot.core.web.ResultCode; +import com.youlai.boot.core.web.WebResponseHelper; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.AuthenticationException; @@ -32,13 +32,13 @@ public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint { public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { if (authException instanceof BadCredentialsException) { // 用户名或密码错误 - ResponseUtils.writeErrMsg(response, ResultCode.USER_PASSWORD_ERROR); + WebResponseHelper.writeError(response, ResultCode.USER_PASSWORD_ERROR); } else if(authException instanceof InsufficientAuthenticationException){ // 请求头缺失Authorization、Token格式错误、Token过期、签名验证失败 - ResponseUtils.writeErrMsg(response, ResultCode.ACCESS_TOKEN_INVALID); + WebResponseHelper.writeError(response, ResultCode.ACCESS_TOKEN_INVALID); } else { // 其他未明确处理的认证异常(如账户被锁定、账户禁用等) - ResponseUtils.writeErrMsg(response, ResultCode.USER_LOGIN_EXCEPTION, authException.getMessage()); + WebResponseHelper.writeError(response, ResultCode.USER_LOGIN_EXCEPTION, authException.getMessage()); } } } diff --git a/src/main/java/com/youlai/boot/core/security/model/AuthenticationToken.java b/src/main/java/com/youlai/boot/security/model/AuthenticationToken.java similarity index 93% rename from src/main/java/com/youlai/boot/core/security/model/AuthenticationToken.java rename to src/main/java/com/youlai/boot/security/model/AuthenticationToken.java index aaa352dd..43ca5cb2 100644 --- a/src/main/java/com/youlai/boot/core/security/model/AuthenticationToken.java +++ b/src/main/java/com/youlai/boot/security/model/AuthenticationToken.java @@ -1,4 +1,4 @@ -package com.youlai.boot.core.security.model; +package com.youlai.boot.security.model; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; diff --git a/src/main/java/com/youlai/boot/core/security/model/OnlineUser.java b/src/main/java/com/youlai/boot/security/model/OnlineUser.java similarity index 93% rename from src/main/java/com/youlai/boot/core/security/model/OnlineUser.java rename to src/main/java/com/youlai/boot/security/model/OnlineUser.java index 095a2912..6dd72608 100644 --- a/src/main/java/com/youlai/boot/core/security/model/OnlineUser.java +++ b/src/main/java/com/youlai/boot/security/model/OnlineUser.java @@ -1,4 +1,4 @@ -package com.youlai.boot.core.security.model; +package com.youlai.boot.security.model; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/src/main/java/com/youlai/boot/core/security/extension/sms/SmsAuthenticationToken.java b/src/main/java/com/youlai/boot/security/model/SmsAuthenticationToken.java similarity index 97% rename from src/main/java/com/youlai/boot/core/security/extension/sms/SmsAuthenticationToken.java rename to src/main/java/com/youlai/boot/security/model/SmsAuthenticationToken.java index 54cb026c..d2aec807 100644 --- a/src/main/java/com/youlai/boot/core/security/extension/sms/SmsAuthenticationToken.java +++ b/src/main/java/com/youlai/boot/security/model/SmsAuthenticationToken.java @@ -1,4 +1,4 @@ -package com.youlai.boot.core.security.extension.sms; +package com.youlai.boot.security.model; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; diff --git a/src/main/java/com/youlai/boot/core/security/model/SysUserDetails.java b/src/main/java/com/youlai/boot/security/model/SysUserDetails.java similarity index 98% rename from src/main/java/com/youlai/boot/core/security/model/SysUserDetails.java rename to src/main/java/com/youlai/boot/security/model/SysUserDetails.java index 4037bc8c..8a79831c 100644 --- a/src/main/java/com/youlai/boot/core/security/model/SysUserDetails.java +++ b/src/main/java/com/youlai/boot/security/model/SysUserDetails.java @@ -1,4 +1,4 @@ -package com.youlai.boot.core.security.model; +package com.youlai.boot.security.model; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; diff --git a/src/main/java/com/youlai/boot/core/security/model/UserAuthCredentials.java b/src/main/java/com/youlai/boot/security/model/UserAuthCredentials.java similarity index 94% rename from src/main/java/com/youlai/boot/core/security/model/UserAuthCredentials.java rename to src/main/java/com/youlai/boot/security/model/UserAuthCredentials.java index aa064ee2..e68d119d 100644 --- a/src/main/java/com/youlai/boot/core/security/model/UserAuthCredentials.java +++ b/src/main/java/com/youlai/boot/security/model/UserAuthCredentials.java @@ -1,4 +1,4 @@ -package com.youlai.boot.core.security.model; +package com.youlai.boot.security.model; import lombok.Data; import java.util.Set; diff --git a/src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppCodeAuthenticationToken.java b/src/main/java/com/youlai/boot/security/model/WxMiniAppCodeAuthenticationToken.java similarity index 97% rename from src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppCodeAuthenticationToken.java rename to src/main/java/com/youlai/boot/security/model/WxMiniAppCodeAuthenticationToken.java index 6ae8dce1..675e9db6 100644 --- a/src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppCodeAuthenticationToken.java +++ b/src/main/java/com/youlai/boot/security/model/WxMiniAppCodeAuthenticationToken.java @@ -1,4 +1,4 @@ -package com.youlai.boot.core.security.extension.wx; +package com.youlai.boot.security.model; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; diff --git a/src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppPhoneAuthenticationToken.java b/src/main/java/com/youlai/boot/security/model/WxMiniAppPhoneAuthenticationToken.java similarity index 97% rename from src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppPhoneAuthenticationToken.java rename to src/main/java/com/youlai/boot/security/model/WxMiniAppPhoneAuthenticationToken.java index 5df01228..697e8941 100644 --- a/src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppPhoneAuthenticationToken.java +++ b/src/main/java/com/youlai/boot/security/model/WxMiniAppPhoneAuthenticationToken.java @@ -1,4 +1,4 @@ -package com.youlai.boot.core.security.extension.wx; +package com.youlai.boot.security.model; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; diff --git a/src/main/java/com/youlai/boot/core/security/extension/sms/SmsAuthenticationProvider.java b/src/main/java/com/youlai/boot/security/provider/SmsAuthenticationProvider.java similarity index 91% rename from src/main/java/com/youlai/boot/core/security/extension/sms/SmsAuthenticationProvider.java rename to src/main/java/com/youlai/boot/security/provider/SmsAuthenticationProvider.java index 74c98cda..9c6b2a85 100644 --- a/src/main/java/com/youlai/boot/core/security/extension/sms/SmsAuthenticationProvider.java +++ b/src/main/java/com/youlai/boot/security/provider/SmsAuthenticationProvider.java @@ -1,11 +1,12 @@ -package com.youlai.boot.core.security.extension.sms; +package com.youlai.boot.security.provider; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.youlai.boot.common.constant.RedisConstants; -import com.youlai.boot.core.security.exception.CaptchaValidationException; -import com.youlai.boot.core.security.model.SysUserDetails; -import com.youlai.boot.core.security.model.UserAuthCredentials; +import com.youlai.boot.core.exception.CaptchaValidationException; +import com.youlai.boot.security.model.SmsAuthenticationToken; +import com.youlai.boot.security.model.SysUserDetails; +import com.youlai.boot.security.model.UserAuthCredentials; import com.youlai.boot.system.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; diff --git a/src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppCodeAuthenticationProvider.java b/src/main/java/com/youlai/boot/security/provider/WxMiniAppCodeAuthenticationProvider.java similarity index 94% rename from src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppCodeAuthenticationProvider.java rename to src/main/java/com/youlai/boot/security/provider/WxMiniAppCodeAuthenticationProvider.java index 94a53874..3214df81 100644 --- a/src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppCodeAuthenticationProvider.java +++ b/src/main/java/com/youlai/boot/security/provider/WxMiniAppCodeAuthenticationProvider.java @@ -1,11 +1,12 @@ -package com.youlai.boot.core.security.extension.wx; +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.core.security.model.SysUserDetails; -import com.youlai.boot.core.security.model.UserAuthCredentials; +import com.youlai.boot.security.model.SysUserDetails; +import com.youlai.boot.security.model.UserAuthCredentials; +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; diff --git a/src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppPhoneAuthenticationProvider.java b/src/main/java/com/youlai/boot/security/provider/WxMiniAppPhoneAuthenticationProvider.java similarity index 95% rename from src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppPhoneAuthenticationProvider.java rename to src/main/java/com/youlai/boot/security/provider/WxMiniAppPhoneAuthenticationProvider.java index c38f1e3f..2bcc933a 100644 --- a/src/main/java/com/youlai/boot/core/security/extension/wx/WxMiniAppPhoneAuthenticationProvider.java +++ b/src/main/java/com/youlai/boot/security/provider/WxMiniAppPhoneAuthenticationProvider.java @@ -1,12 +1,13 @@ -package com.youlai.boot.core.security.extension.wx; +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.core.security.model.SysUserDetails; -import com.youlai.boot.core.security.model.UserAuthCredentials; +import com.youlai.boot.security.model.SysUserDetails; +import com.youlai.boot.security.model.UserAuthCredentials; +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; diff --git a/src/main/java/com/youlai/boot/core/security/service/PermissionService.java b/src/main/java/com/youlai/boot/security/service/PermissionService.java similarity index 96% rename from src/main/java/com/youlai/boot/core/security/service/PermissionService.java rename to src/main/java/com/youlai/boot/security/service/PermissionService.java index 0f7448c9..8b11ad4e 100644 --- a/src/main/java/com/youlai/boot/core/security/service/PermissionService.java +++ b/src/main/java/com/youlai/boot/security/service/PermissionService.java @@ -1,9 +1,9 @@ -package com.youlai.boot.core.security.service; +package com.youlai.boot.security.service; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.youlai.boot.common.constant.RedisConstants; -import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.security.util.SecurityUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; diff --git a/src/main/java/com/youlai/boot/core/security/service/SysUserDetailsService.java b/src/main/java/com/youlai/boot/security/service/SysUserDetailsService.java similarity index 89% rename from src/main/java/com/youlai/boot/core/security/service/SysUserDetailsService.java rename to src/main/java/com/youlai/boot/security/service/SysUserDetailsService.java index 86b70db8..213b698f 100644 --- a/src/main/java/com/youlai/boot/core/security/service/SysUserDetailsService.java +++ b/src/main/java/com/youlai/boot/security/service/SysUserDetailsService.java @@ -1,7 +1,7 @@ -package com.youlai.boot.core.security.service; +package com.youlai.boot.security.service; -import com.youlai.boot.core.security.model.SysUserDetails; -import com.youlai.boot.core.security.model.UserAuthCredentials; +import com.youlai.boot.security.model.SysUserDetails; +import com.youlai.boot.security.model.UserAuthCredentials; import com.youlai.boot.system.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/youlai/boot/core/security/token/JwtTokenManager.java b/src/main/java/com/youlai/boot/security/token/JwtTokenManager.java similarity index 97% rename from src/main/java/com/youlai/boot/core/security/token/JwtTokenManager.java rename to src/main/java/com/youlai/boot/security/token/JwtTokenManager.java index 805000a0..1ee154e4 100644 --- a/src/main/java/com/youlai/boot/core/security/token/JwtTokenManager.java +++ b/src/main/java/com/youlai/boot/security/token/JwtTokenManager.java @@ -1,4 +1,4 @@ -package com.youlai.boot.core.security.token; +package com.youlai.boot.security.token; import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateUtil; @@ -12,11 +12,11 @@ import com.youlai.boot.common.constant.JwtClaimConstants; import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SecurityConstants; import com.youlai.boot.common.exception.BusinessException; -import com.youlai.boot.common.result.ResultCode; +import com.youlai.boot.core.web.ResultCode; import com.youlai.boot.config.property.SecurityProperties; -import com.youlai.boot.core.security.model.AuthenticationToken; +import com.youlai.boot.security.model.AuthenticationToken; import org.apache.commons.lang3.StringUtils; -import com.youlai.boot.core.security.model.SysUserDetails; +import com.youlai.boot.security.model.SysUserDetails; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; diff --git a/src/main/java/com/youlai/boot/core/security/token/RedisTokenManager.java b/src/main/java/com/youlai/boot/security/token/RedisTokenManager.java similarity index 97% rename from src/main/java/com/youlai/boot/core/security/token/RedisTokenManager.java rename to src/main/java/com/youlai/boot/security/token/RedisTokenManager.java index 604b0ae2..51113ede 100644 --- a/src/main/java/com/youlai/boot/core/security/token/RedisTokenManager.java +++ b/src/main/java/com/youlai/boot/security/token/RedisTokenManager.java @@ -1,15 +1,15 @@ -package com.youlai.boot.core.security.token; +package com.youlai.boot.security.token; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.exception.BusinessException; -import com.youlai.boot.common.result.ResultCode; +import com.youlai.boot.core.web.ResultCode; import com.youlai.boot.config.property.SecurityProperties; -import com.youlai.boot.core.security.model.AuthenticationToken; -import com.youlai.boot.core.security.model.OnlineUser; -import com.youlai.boot.core.security.model.SysUserDetails; +import com.youlai.boot.security.model.AuthenticationToken; +import com.youlai.boot.security.model.OnlineUser; +import com.youlai.boot.security.model.SysUserDetails; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; diff --git a/src/main/java/com/youlai/boot/core/security/token/TokenManager.java b/src/main/java/com/youlai/boot/security/token/TokenManager.java similarity index 92% rename from src/main/java/com/youlai/boot/core/security/token/TokenManager.java rename to src/main/java/com/youlai/boot/security/token/TokenManager.java index 4d516879..3c051dc4 100644 --- a/src/main/java/com/youlai/boot/core/security/token/TokenManager.java +++ b/src/main/java/com/youlai/boot/security/token/TokenManager.java @@ -1,7 +1,7 @@ -package com.youlai.boot.core.security.token; +package com.youlai.boot.security.token; -import com.youlai.boot.core.security.model.AuthenticationToken; +import com.youlai.boot.security.model.AuthenticationToken; import org.springframework.security.core.Authentication; /** diff --git a/src/main/java/com/youlai/boot/core/security/util/SecurityUtils.java b/src/main/java/com/youlai/boot/security/util/SecurityUtils.java similarity index 97% rename from src/main/java/com/youlai/boot/core/security/util/SecurityUtils.java rename to src/main/java/com/youlai/boot/security/util/SecurityUtils.java index 01c2d921..48372e53 100644 --- a/src/main/java/com/youlai/boot/core/security/util/SecurityUtils.java +++ b/src/main/java/com/youlai/boot/security/util/SecurityUtils.java @@ -1,10 +1,10 @@ -package com.youlai.boot.core.security.util; +package com.youlai.boot.security.util; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.youlai.boot.common.constant.SecurityConstants; import com.youlai.boot.common.constant.SystemConstants; -import com.youlai.boot.core.security.model.SysUserDetails; +import com.youlai.boot.security.model.SysUserDetails; import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.HttpHeaders; import org.springframework.security.core.Authentication; diff --git a/src/main/java/com/youlai/boot/system/controller/ConfigController.java b/src/main/java/com/youlai/boot/system/controller/ConfigController.java index dd384d9c..1e8d446b 100644 --- a/src/main/java/com/youlai/boot/system/controller/ConfigController.java +++ b/src/main/java/com/youlai/boot/system/controller/ConfigController.java @@ -2,8 +2,8 @@ package com.youlai.boot.system.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.youlai.boot.common.enums.LogModuleEnum; -import com.youlai.boot.common.result.PageResult; -import com.youlai.boot.common.result.Result; +import com.youlai.boot.core.web.PageResult; +import com.youlai.boot.core.web.Result; import com.youlai.boot.common.annotation.Log; import com.youlai.boot.system.model.form.ConfigForm; import com.youlai.boot.system.model.query.ConfigPageQuery; diff --git a/src/main/java/com/youlai/boot/system/controller/DeptController.java b/src/main/java/com/youlai/boot/system/controller/DeptController.java index 39016286..f1064353 100644 --- a/src/main/java/com/youlai/boot/system/controller/DeptController.java +++ b/src/main/java/com/youlai/boot/system/controller/DeptController.java @@ -3,7 +3,7 @@ package com.youlai.boot.system.controller; import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.common.annotation.RepeatSubmit; import com.youlai.boot.common.model.Option; -import com.youlai.boot.common.result.Result; +import com.youlai.boot.core.web.Result; import com.youlai.boot.system.model.form.DeptForm; import com.youlai.boot.system.model.query.DeptQuery; import com.youlai.boot.system.model.vo.DeptVO; diff --git a/src/main/java/com/youlai/boot/system/controller/DictController.java b/src/main/java/com/youlai/boot/system/controller/DictController.java index e4825301..537cd87b 100644 --- a/src/main/java/com/youlai/boot/system/controller/DictController.java +++ b/src/main/java/com/youlai/boot/system/controller/DictController.java @@ -2,8 +2,8 @@ package com.youlai.boot.system.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.youlai.boot.common.model.Option; -import com.youlai.boot.common.result.PageResult; -import com.youlai.boot.common.result.Result; +import com.youlai.boot.core.web.PageResult; +import com.youlai.boot.core.web.Result; import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.system.model.form.DictItemForm; import com.youlai.boot.system.model.query.DictItemPageQuery; diff --git a/src/main/java/com/youlai/boot/system/controller/LogController.java b/src/main/java/com/youlai/boot/system/controller/LogController.java index 1db2613c..46e312c1 100644 --- a/src/main/java/com/youlai/boot/system/controller/LogController.java +++ b/src/main/java/com/youlai/boot/system/controller/LogController.java @@ -1,8 +1,8 @@ package com.youlai.boot.system.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.youlai.boot.common.result.PageResult; -import com.youlai.boot.common.result.Result; +import com.youlai.boot.core.web.PageResult; +import com.youlai.boot.core.web.Result; import com.youlai.boot.system.model.query.LogPageQuery; import com.youlai.boot.system.model.vo.LogPageVO; import com.youlai.boot.system.model.vo.VisitStatsVO; diff --git a/src/main/java/com/youlai/boot/system/controller/MenuController.java b/src/main/java/com/youlai/boot/system/controller/MenuController.java index fd897650..c35199f4 100644 --- a/src/main/java/com/youlai/boot/system/controller/MenuController.java +++ b/src/main/java/com/youlai/boot/system/controller/MenuController.java @@ -4,7 +4,7 @@ import com.youlai.boot.common.annotation.Log; import com.youlai.boot.common.annotation.RepeatSubmit; import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.common.model.Option; -import com.youlai.boot.common.result.Result; +import com.youlai.boot.core.web.Result; import com.youlai.boot.system.model.form.MenuForm; import com.youlai.boot.system.model.query.MenuQuery; import com.youlai.boot.system.model.vo.MenuVO; diff --git a/src/main/java/com/youlai/boot/system/controller/NoticeController.java b/src/main/java/com/youlai/boot/system/controller/NoticeController.java index f08198e0..3d38a08a 100644 --- a/src/main/java/com/youlai/boot/system/controller/NoticeController.java +++ b/src/main/java/com/youlai/boot/system/controller/NoticeController.java @@ -1,8 +1,8 @@ package com.youlai.boot.system.controller; import com.baomidou.mybatisplus.core.metadata.IPage; -import com.youlai.boot.common.result.PageResult; -import com.youlai.boot.common.result.Result; +import com.youlai.boot.core.web.PageResult; +import com.youlai.boot.core.web.Result; import com.youlai.boot.system.model.form.NoticeForm; import com.youlai.boot.system.model.query.NoticePageQuery; import com.youlai.boot.system.model.vo.NoticeDetailVO; diff --git a/src/main/java/com/youlai/boot/system/controller/RoleController.java b/src/main/java/com/youlai/boot/system/controller/RoleController.java index a8a062f2..fef84c40 100644 --- a/src/main/java/com/youlai/boot/system/controller/RoleController.java +++ b/src/main/java/com/youlai/boot/system/controller/RoleController.java @@ -4,8 +4,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.common.annotation.RepeatSubmit; import com.youlai.boot.common.model.Option; -import com.youlai.boot.common.result.PageResult; -import com.youlai.boot.common.result.Result; +import com.youlai.boot.core.web.PageResult; +import com.youlai.boot.core.web.Result; import com.youlai.boot.system.model.form.RoleForm; import com.youlai.boot.system.model.query.RolePageQuery; import com.youlai.boot.system.model.vo.RolePageVO; diff --git a/src/main/java/com/youlai/boot/system/controller/UserController.java b/src/main/java/com/youlai/boot/system/controller/UserController.java index 87a1c5a1..7b7d98ec 100644 --- a/src/main/java/com/youlai/boot/system/controller/UserController.java +++ b/src/main/java/com/youlai/boot/system/controller/UserController.java @@ -8,11 +8,11 @@ import com.youlai.boot.common.annotation.Log; import com.youlai.boot.common.annotation.RepeatSubmit; import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.common.model.Option; -import com.youlai.boot.common.result.ExcelResult; -import com.youlai.boot.common.result.PageResult; -import com.youlai.boot.common.result.Result; +import com.youlai.boot.core.web.ExcelResult; +import com.youlai.boot.core.web.PageResult; +import com.youlai.boot.core.web.Result; import com.youlai.boot.common.util.ExcelUtils; -import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.security.util.SecurityUtils; import com.youlai.boot.system.listener.UserImportListener; import com.youlai.boot.system.model.dto.UserExportDTO; import com.youlai.boot.system.model.dto.UserImportDTO; diff --git a/src/main/java/com/youlai/boot/system/listener/UserImportListener.java b/src/main/java/com/youlai/boot/system/listener/UserImportListener.java index 806d7e60..a699fb54 100644 --- a/src/main/java/com/youlai/boot/system/listener/UserImportListener.java +++ b/src/main/java/com/youlai/boot/system/listener/UserImportListener.java @@ -11,7 +11,7 @@ import cn.idev.excel.event.AnalysisEventListener; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.youlai.boot.common.constant.SystemConstants; import com.youlai.boot.common.enums.StatusEnum; -import com.youlai.boot.common.result.ExcelResult; +import com.youlai.boot.core.web.ExcelResult; import com.youlai.boot.system.converter.UserConverter; import com.youlai.boot.system.enums.DictCodeEnum; import com.youlai.boot.system.model.dto.UserImportDTO; 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 74d3388f..74af1bd5 100644 --- a/src/main/java/com/youlai/boot/system/mapper/UserMapper.java +++ b/src/main/java/com/youlai/boot/system/mapper/UserMapper.java @@ -7,7 +7,7 @@ import com.youlai.boot.system.model.entity.User; import com.youlai.boot.system.model.query.UserPageQuery; import com.youlai.boot.system.model.form.UserForm; import com.youlai.boot.common.annotation.DataPermission; -import com.youlai.boot.core.security.model.UserAuthCredentials; +import com.youlai.boot.security.model.UserAuthCredentials; import com.youlai.boot.system.model.dto.UserExportDTO; import org.apache.ibatis.annotations.Mapper; diff --git a/src/main/java/com/youlai/boot/system/service/MenuService.java b/src/main/java/com/youlai/boot/system/service/MenuService.java index eb0ba54b..6fbff3f3 100644 --- a/src/main/java/com/youlai/boot/system/service/MenuService.java +++ b/src/main/java/com/youlai/boot/system/service/MenuService.java @@ -1,7 +1,7 @@ package com.youlai.boot.system.service; import com.baomidou.mybatisplus.extension.service.IService; -import com.youlai.boot.shared.codegen.model.entity.GenConfig; +import com.youlai.boot.platform.codegen.model.entity.GenConfig; import com.youlai.boot.system.model.form.MenuForm; import com.youlai.boot.common.model.Option; import com.youlai.boot.system.model.entity.Menu; diff --git a/src/main/java/com/youlai/boot/system/service/UserOnlineService.java b/src/main/java/com/youlai/boot/system/service/UserOnlineService.java index d3b9794a..8200a457 100644 --- a/src/main/java/com/youlai/boot/system/service/UserOnlineService.java +++ b/src/main/java/com/youlai/boot/system/service/UserOnlineService.java @@ -2,7 +2,7 @@ package com.youlai.boot.system.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.youlai.boot.core.security.model.SysUserDetails; +import com.youlai.boot.security.model.SysUserDetails; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; 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 804569e3..9b8660ba 100644 --- a/src/main/java/com/youlai/boot/system/service/UserService.java +++ b/src/main/java/com/youlai/boot/system/service/UserService.java @@ -3,7 +3,7 @@ package com.youlai.boot.system.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.youlai.boot.common.model.Option; -import com.youlai.boot.core.security.model.UserAuthCredentials; +import com.youlai.boot.security.model.UserAuthCredentials; import com.youlai.boot.system.model.dto.CurrentUserDTO; import com.youlai.boot.system.model.dto.UserExportDTO; import com.youlai.boot.system.model.entity.User; diff --git a/src/main/java/com/youlai/boot/system/service/impl/ConfigServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/ConfigServiceImpl.java index 3c939dca..e4e12c26 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/ConfigServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/ConfigServiceImpl.java @@ -13,7 +13,7 @@ import com.youlai.boot.system.model.form.ConfigForm; import com.youlai.boot.system.model.query.ConfigPageQuery; import com.youlai.boot.system.model.vo.ConfigVO; import com.youlai.boot.system.service.ConfigService; -import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.security.util.SecurityUtils; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; diff --git a/src/main/java/com/youlai/boot/system/service/impl/DeptServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/DeptServiceImpl.java index ee7e76e1..39a60deb 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/DeptServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/DeptServiceImpl.java @@ -6,7 +6,7 @@ import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.security.util.SecurityUtils; import com.youlai.boot.system.converter.DeptConverter; import com.youlai.boot.system.mapper.DeptMapper; import com.youlai.boot.system.model.entity.Dept; diff --git a/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java index 313c564f..4b2a644e 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/MenuServiceImpl.java @@ -10,10 +10,10 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.platform.codegen.model.entity.GenConfig; +import com.youlai.boot.security.util.SecurityUtils; import com.youlai.boot.system.converter.MenuConverter; import com.youlai.boot.system.mapper.MenuMapper; -import com.youlai.boot.shared.codegen.model.entity.GenConfig; import com.youlai.boot.system.model.entity.Menu; import com.youlai.boot.system.model.form.MenuForm; import com.youlai.boot.system.model.query.MenuQuery; diff --git a/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java index e78c876e..407ef6de 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java @@ -8,7 +8,7 @@ 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.common.exception.BusinessException; -import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.security.util.SecurityUtils; import com.youlai.boot.system.converter.NoticeConverter; import com.youlai.boot.system.enums.NoticePublishStatusEnum; import com.youlai.boot.system.enums.NoticeTargetEnum; diff --git a/src/main/java/com/youlai/boot/system/service/impl/RoleServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/RoleServiceImpl.java index e56a0271..b08955df 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/RoleServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/RoleServiceImpl.java @@ -17,7 +17,7 @@ import com.youlai.boot.system.model.query.RolePageQuery; import com.youlai.boot.system.model.vo.RolePageVO; import com.youlai.boot.common.constant.SystemConstants; import com.youlai.boot.common.model.Option; -import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.security.util.SecurityUtils; import com.youlai.boot.system.service.RoleMenuService; import com.youlai.boot.system.service.RoleService; import com.youlai.boot.system.service.UserRoleService; diff --git a/src/main/java/com/youlai/boot/system/service/impl/UserNoticeServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/UserNoticeServiceImpl.java index f44489ba..aa82529c 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/UserNoticeServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/UserNoticeServiceImpl.java @@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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.core.security.util.SecurityUtils; +import com.youlai.boot.security.util.SecurityUtils; import com.youlai.boot.system.mapper.UserNoticeMapper; import com.youlai.boot.system.model.entity.UserNotice; import com.youlai.boot.system.model.query.NoticePageQuery; diff --git a/src/main/java/com/youlai/boot/system/service/impl/UserRoleServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/UserRoleServiceImpl.java index 6b27244b..148ea570 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/UserRoleServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/UserRoleServiceImpl.java @@ -3,8 +3,8 @@ package com.youlai.boot.system.service.impl; import cn.hutool.core.collection.CollectionUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.youlai.boot.core.security.token.TokenManager; -import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.security.token.TokenManager; +import com.youlai.boot.security.util.SecurityUtils; import com.youlai.boot.system.mapper.UserRoleMapper; import com.youlai.boot.system.model.entity.UserRole; import com.youlai.boot.system.service.UserRoleService; 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 9d8fafba..745c1d67 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 @@ -12,13 +12,13 @@ import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SystemConstants; import com.youlai.boot.common.exception.BusinessException; import com.youlai.boot.common.model.Option; -import com.youlai.boot.core.security.model.UserAuthCredentials; -import com.youlai.boot.core.security.service.PermissionService; -import com.youlai.boot.core.security.token.TokenManager; -import com.youlai.boot.core.security.util.SecurityUtils; -import com.youlai.boot.shared.mail.service.MailService; -import com.youlai.boot.shared.sms.enums.SmsTypeEnum; -import com.youlai.boot.shared.sms.service.SmsService; +import com.youlai.boot.platform.sms.enums.SmsTypeEnum; +import com.youlai.boot.platform.sms.service.SmsService; +import com.youlai.boot.security.model.UserAuthCredentials; +import com.youlai.boot.security.service.PermissionService; +import com.youlai.boot.security.token.TokenManager; +import com.youlai.boot.security.util.SecurityUtils; +import com.youlai.boot.platform.mail.service.MailService; import com.youlai.boot.system.converter.UserConverter; import com.youlai.boot.system.enums.DictCodeEnum; import com.youlai.boot.system.mapper.UserMapper; diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 207f1fc1..412f9494 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -158,8 +158,8 @@ springdoc: packages-to-scan: - com.youlai.boot.auth.controller - com.youlai.boot.system.controller - - com.youlai.boot.shared.file.controller - - com.youlai.boot.shared.codegen.controller + - com.youlai.boot.platform.file.controller + - com.youlai.boot.platform.codegen.controller default-flat-param-object: true # knife4j 接口文档配置 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index b1309efa..53695a35 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -155,8 +155,8 @@ springdoc: packages-to-scan: - com.youlai.boot.auth.controller - com.youlai.boot.system.controller - - com.youlai.boot.shared.file.controller - - com.youlai.boot.shared.codegen.controller + - com.youlai.boot.platform.file.controller + - com.youlai.boot.platform.codegen.controller default-flat-param-object: true # knife4j 接口文档配置 diff --git a/src/main/resources/mapper/codegen/DatabaseMapper.xml b/src/main/resources/mapper/codegen/DatabaseMapper.xml index 1fd0376a..d923000c 100644 --- a/src/main/resources/mapper/codegen/DatabaseMapper.xml +++ b/src/main/resources/mapper/codegen/DatabaseMapper.xml @@ -2,10 +2,10 @@ - + - SELECT t1.TABLE_NAME , t1.TABLE_COMMENT , @@ -33,7 +33,7 @@ CREATE_TIME DESC - SELECT TABLE_NAME , TABLE_COMMENT , @@ -47,7 +47,7 @@ AND TABLE_NAME = #{tableName} - SELECT COLUMN_NAME, DATA_TYPE, diff --git a/src/main/resources/mapper/codegen/GenConfigMapper.xml b/src/main/resources/mapper/codegen/GenConfigMapper.xml index 996587bd..02709a91 100644 --- a/src/main/resources/mapper/codegen/GenConfigMapper.xml +++ b/src/main/resources/mapper/codegen/GenConfigMapper.xml @@ -2,6 +2,6 @@ - + diff --git a/src/main/resources/mapper/codegen/GenFieldConfigMapper.xml b/src/main/resources/mapper/codegen/GenFieldConfigMapper.xml index 287e9634..0bd4db31 100644 --- a/src/main/resources/mapper/codegen/GenFieldConfigMapper.xml +++ b/src/main/resources/mapper/codegen/GenFieldConfigMapper.xml @@ -2,6 +2,6 @@ - + diff --git a/src/main/resources/mapper/system/UserMapper.xml b/src/main/resources/mapper/system/UserMapper.xml index 751147da..4e281a17 100644 --- a/src/main/resources/mapper/system/UserMapper.xml +++ b/src/main/resources/mapper/system/UserMapper.xml @@ -119,7 +119,7 @@ - + diff --git a/src/test/java/com/youlai/boot/generator/SystemCodeGenerator.java b/src/test/java/com/youlai/boot/generator/SystemCodeGenerator.java index 2a2e1834..23c018c3 100644 --- a/src/test/java/com/youlai/boot/generator/SystemCodeGenerator.java +++ b/src/test/java/com/youlai/boot/generator/SystemCodeGenerator.java @@ -39,8 +39,8 @@ public class SystemCodeGenerator { .parent("com.youlai.boot.system") .entity("model.entity") .mapper("mapper") - .service("service") - .serviceImpl("service.impl") + .service("platform") + .serviceImpl("platform.impl") .controller("controller") .pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "/src/main/resources/mapper")); } From 65a2450f3e921a32f4a01a8786b1fe7c8b6afafa Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Tue, 14 Oct 2025 16:21:15 +0800 Subject: [PATCH 53/60] =?UTF-8?q?refactor(core):=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=8C=85=E7=BB=93=E6=9E=84=E5=B9=B6=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E4=B8=9A=E5=8A=A1=E5=BC=82=E5=B8=B8=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 BusinessException 移至 core.exception 包下 - 更新所有业务异常引用路径 - 调整项目模块目录结构说明 - 升级项目版本至3.3.0 --- README.md | 47 ++++++++++--------- pom.xml | 2 +- .../model/dto/WxMiniAppPhoneLoginDTO.java | 2 +- .../boot/core/aspect/RepeatSubmitAspect.java | 2 +- .../core/exception/BusinessException.java | 2 +- .../exception/GlobalExceptionHandler.java | 1 + .../service/impl/CodegenServiceImpl.java | 2 +- .../service/impl/GenConfigServiceImpl.java | 2 +- .../file/service/impl/MinioFileService.java | 2 +- .../boot/security/token/JwtTokenManager.java | 2 +- .../security/token/RedisTokenManager.java | 2 +- .../DictEvent.java => dto/DictEventDTO.java} | 11 +++-- .../system/service/impl/DictServiceImpl.java | 2 +- .../service/impl/NoticeServiceImpl.java | 2 +- .../system/service/impl/RoleServiceImpl.java | 2 +- .../system/service/impl/UserServiceImpl.java | 2 +- .../service/impl/WebSocketServiceImpl.java | 6 +-- 17 files changed, 49 insertions(+), 42 deletions(-) rename src/main/java/com/youlai/boot/system/model/{event/DictEvent.java => dto/DictEventDTO.java} (67%) diff --git a/README.md b/README.md index f806c527..2e491ee3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ logo

youlai-boot

有来技术 - 有来技术 + 有来技术 有来技术 @@ -73,50 +73,55 @@ youlai-boot ├── sql # SQL脚本 │ ├── mysql # MySQL 脚本 ├── src # 源码目录 -│ ├── auth # 登录认证 +│ ├── auth # 认证模块(登录入口) │ ├── common # 公共模块 │ │ ├── annotation # 注解定义 │ │ ├── base # 基础类 │ │ ├── constant # 常量 │ │ ├── enums # 枚举类型 -│ │ ├── exception # 异常处理 │ │ ├── model # 数据模型 -│ │ ├── result # 结果封装 │ │ └── util # 工具类 │ ├── config # 自动装配配置 │ │ └── property # 配置属性目录 -│ ├── core # 核心功能 +│ ├── core # 核心框架 │ │ ├── aspect # 切面(日志、防重提交) +│ │ ├── exception # 异常处理 │ │ ├── filter # 过滤器(请求日志、限流) -│ │ ├── handler # 处理器(数据权限、数据填充) -│ │ └── security # Spring Security 安全模块 -│ ├── module # 业务模块 -│ │ ├── member # 会员模块【业务模块演示】 -│ │ ├── order # 订单模块【业务模块演示】 -│ │ ├── product # 商品模块【业务模块演示】 -│ ├── module # 插件扩展 -│ │ ├── knife4j # Knife4j 扩展 -│ │ ├── mybatis # Mybatis 扩展 -│ ├── shared # 共享模块 -│ │ ├── file # 文件模块 +│ │ ├── validator # 验证器 +│ │ └── web # Web响应封装(Result、PageResult等) +│ ├── platform # 平台服务(通用服务) │ │ ├── codegen # 代码生成模块 -│ │ ├── mail # 邮件模块 -│ │ ├── sms # 短信模块 -│ │ └── websocket # WebSocket 模块 +│ │ ├── file # 文件服务 +│ │ ├── mail # 邮件服务 +│ │ ├── sms # 短信服务 +│ │ └── websocket # WebSocket服务 +│ ├── plugin # 插件扩展 +│ │ ├── knife4j # Knife4j 扩展 +│ │ └── mybatis # Mybatis 扩展 +│ ├── security # 安全框架(Spring Security) +│ │ ├── exception # 安全异常 +│ │ ├── filter # 安全过滤器 +│ │ ├── handler # 安全处理器 +│ │ ├── model # 安全模型 +│ │ ├── provider # 认证提供者 +│ │ ├── service # 安全服务 +│ │ ├── token # Token管理 +│ │ └── util # 安全工具类 │ ├── system # 系统模块 │ │ ├── controller # 控制层 │ │ ├── converter # MapStruct 转换器 -│ │ ├── event # 事件处理 +│ │ ├── enums # 枚举 │ │ ├── handler # 处理器 │ │ ├── listener # 监听器 +│ │ ├── mapper # 数据库访问层 │ │ ├── model # 模型层 │ │ │ ├── bo # 业务对象 │ │ │ ├── dto # 数据传输对象 │ │ │ ├── entity # 实体对象 +│ │ │ ├── event # 事件对象 │ │ │ ├── form # 表单对象 │ │ │ ├── query # 查询参数对象 │ │ │ └── vo # 视图对象 -│ │ ├── mapper # 数据库访问层 │ │ └── service # 业务逻辑层 │ └── YouLaiBootApplication # 启动类 └── end diff --git a/pom.xml b/pom.xml index ccb0ab4d..4d1eb2f6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.youlai youlai-boot - 3.2.0 + 3.3.0 基于 Java 17 + SpringBoot 3 + Spring Security 构建的权限管理系统。 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 index 5a3e4c69..a881f271 100644 --- a/src/main/java/com/youlai/boot/auth/model/dto/WxMiniAppPhoneLoginDTO.java +++ b/src/main/java/com/youlai/boot/auth/model/dto/WxMiniAppPhoneLoginDTO.java @@ -8,7 +8,7 @@ import jakarta.validation.constraints.NotBlank; /** * 微信小程序手机号登录请求参数 * - * @author 有来技术团队 + * @author Ray.Hao * @since 2.0.0 */ @Schema(description = "微信小程序手机号登录请求参数") diff --git a/src/main/java/com/youlai/boot/core/aspect/RepeatSubmitAspect.java b/src/main/java/com/youlai/boot/core/aspect/RepeatSubmitAspect.java index c45766a4..6c0a0f4e 100644 --- a/src/main/java/com/youlai/boot/core/aspect/RepeatSubmitAspect.java +++ b/src/main/java/com/youlai/boot/core/aspect/RepeatSubmitAspect.java @@ -5,7 +5,7 @@ import cn.hutool.crypto.digest.DigestUtil; import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SecurityConstants; import com.youlai.boot.core.web.ResultCode; -import com.youlai.boot.common.exception.BusinessException; +import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.common.annotation.RepeatSubmit; import com.youlai.boot.common.util.IPUtils; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/com/youlai/boot/core/exception/BusinessException.java b/src/main/java/com/youlai/boot/core/exception/BusinessException.java index d9b2b4cf..033981c5 100644 --- a/src/main/java/com/youlai/boot/core/exception/BusinessException.java +++ b/src/main/java/com/youlai/boot/core/exception/BusinessException.java @@ -1,4 +1,4 @@ -package com.youlai.boot.common.exception; +package com.youlai.boot.core.exception; import com.youlai.boot.core.web.IResultCode; import lombok.Getter; diff --git a/src/main/java/com/youlai/boot/core/exception/GlobalExceptionHandler.java b/src/main/java/com/youlai/boot/core/exception/GlobalExceptionHandler.java index 9c97f5ad..e0599a9e 100644 --- a/src/main/java/com/youlai/boot/core/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/youlai/boot/core/exception/GlobalExceptionHandler.java @@ -2,6 +2,7 @@ package com.youlai.boot.common.exception; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.core.JsonProcessingException; +import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.core.web.Result; import com.youlai.boot.core.web.ResultCode; import jakarta.servlet.ServletException; diff --git a/src/main/java/com/youlai/boot/platform/codegen/service/impl/CodegenServiceImpl.java b/src/main/java/com/youlai/boot/platform/codegen/service/impl/CodegenServiceImpl.java index 6d8c91ec..aa4e507b 100644 --- a/src/main/java/com/youlai/boot/platform/codegen/service/impl/CodegenServiceImpl.java +++ b/src/main/java/com/youlai/boot/platform/codegen/service/impl/CodegenServiceImpl.java @@ -16,7 +16,7 @@ import com.youlai.boot.config.property.CodegenProperties; import com.youlai.boot.platform.codegen.service.GenConfigService; import com.youlai.boot.platform.codegen.service.GenFieldConfigService; import com.youlai.boot.platform.codegen.service.CodegenService; -import com.youlai.boot.common.exception.BusinessException; +import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.platform.codegen.mapper.DatabaseMapper; import com.youlai.boot.platform.codegen.model.entity.GenConfig; import com.youlai.boot.platform.codegen.model.entity.GenFieldConfig; diff --git a/src/main/java/com/youlai/boot/platform/codegen/service/impl/GenConfigServiceImpl.java b/src/main/java/com/youlai/boot/platform/codegen/service/impl/GenConfigServiceImpl.java index fe4cb49d..eaf59a40 100644 --- a/src/main/java/com/youlai/boot/platform/codegen/service/impl/GenConfigServiceImpl.java +++ b/src/main/java/com/youlai/boot/platform/codegen/service/impl/GenConfigServiceImpl.java @@ -10,7 +10,7 @@ import com.youlai.boot.common.enums.EnvEnum; import com.youlai.boot.platform.codegen.enums.FormTypeEnum; import com.youlai.boot.platform.codegen.enums.JavaTypeEnum; import com.youlai.boot.platform.codegen.enums.QueryTypeEnum; -import com.youlai.boot.common.exception.BusinessException; +import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.config.property.CodegenProperties; import com.youlai.boot.platform.codegen.converter.CodegenConverter; import com.youlai.boot.platform.codegen.mapper.DatabaseMapper; diff --git a/src/main/java/com/youlai/boot/platform/file/service/impl/MinioFileService.java b/src/main/java/com/youlai/boot/platform/file/service/impl/MinioFileService.java index 367a6c8f..be2ecafd 100644 --- a/src/main/java/com/youlai/boot/platform/file/service/impl/MinioFileService.java +++ b/src/main/java/com/youlai/boot/platform/file/service/impl/MinioFileService.java @@ -5,7 +5,7 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; -import com.youlai.boot.common.exception.BusinessException; +import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.core.web.ResultCode; import com.youlai.boot.platform.file.model.FileInfo; import com.youlai.boot.platform.file.service.FileService; diff --git a/src/main/java/com/youlai/boot/security/token/JwtTokenManager.java b/src/main/java/com/youlai/boot/security/token/JwtTokenManager.java index 1ee154e4..627a533d 100644 --- a/src/main/java/com/youlai/boot/security/token/JwtTokenManager.java +++ b/src/main/java/com/youlai/boot/security/token/JwtTokenManager.java @@ -11,7 +11,7 @@ import cn.hutool.jwt.JWTUtil; import com.youlai.boot.common.constant.JwtClaimConstants; import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SecurityConstants; -import com.youlai.boot.common.exception.BusinessException; +import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.core.web.ResultCode; import com.youlai.boot.config.property.SecurityProperties; import com.youlai.boot.security.model.AuthenticationToken; diff --git a/src/main/java/com/youlai/boot/security/token/RedisTokenManager.java b/src/main/java/com/youlai/boot/security/token/RedisTokenManager.java index 51113ede..a2dff3a5 100644 --- a/src/main/java/com/youlai/boot/security/token/RedisTokenManager.java +++ b/src/main/java/com/youlai/boot/security/token/RedisTokenManager.java @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import com.youlai.boot.common.constant.RedisConstants; -import com.youlai.boot.common.exception.BusinessException; +import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.core.web.ResultCode; import com.youlai.boot.config.property.SecurityProperties; import com.youlai.boot.security.model.AuthenticationToken; diff --git a/src/main/java/com/youlai/boot/system/model/event/DictEvent.java b/src/main/java/com/youlai/boot/system/model/dto/DictEventDTO.java similarity index 67% rename from src/main/java/com/youlai/boot/system/model/event/DictEvent.java rename to src/main/java/com/youlai/boot/system/model/dto/DictEventDTO.java index 7f30ee10..b3cae343 100644 --- a/src/main/java/com/youlai/boot/system/model/event/DictEvent.java +++ b/src/main/java/com/youlai/boot/system/model/dto/DictEventDTO.java @@ -1,15 +1,15 @@ -package com.youlai.boot.system.model.event; +package com.youlai.boot.system.model.dto; import lombok.Data; /** - * 字典更新事件 + * 字典更新事件消息 * * @author Ray.Hao * @since 3.0.0 */ @Data -public class DictEvent { +public class DictEventDTO { /** * 字典编码 */ @@ -20,8 +20,9 @@ public class DictEvent { */ private long timestamp; - public DictEvent(String dictCode) { + public DictEventDTO(String dictCode) { this.dictCode = dictCode; this.timestamp = System.currentTimeMillis(); } -} +} + diff --git a/src/main/java/com/youlai/boot/system/service/impl/DictServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/DictServiceImpl.java index 8d1f9896..2573b4d3 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/DictServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/DictServiceImpl.java @@ -4,7 +4,7 @@ import cn.hutool.core.lang.Assert; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.youlai.boot.common.exception.BusinessException; +import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.common.model.Option; import com.youlai.boot.system.converter.DictConverter; import com.youlai.boot.system.mapper.DictMapper; diff --git a/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java index 407ef6de..b4d70ea5 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java @@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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.common.exception.BusinessException; +import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.security.util.SecurityUtils; import com.youlai.boot.system.converter.NoticeConverter; import com.youlai.boot.system.enums.NoticePublishStatusEnum; diff --git a/src/main/java/com/youlai/boot/system/service/impl/RoleServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/RoleServiceImpl.java index b08955df..0efab740 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/RoleServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/RoleServiceImpl.java @@ -7,7 +7,7 @@ import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.youlai.boot.common.exception.BusinessException; +import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.system.converter.RoleConverter; import com.youlai.boot.system.mapper.RoleMapper; import com.youlai.boot.system.model.entity.Role; 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 745c1d67..827779c4 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 @@ -10,7 +10,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SystemConstants; -import com.youlai.boot.common.exception.BusinessException; +import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.common.model.Option; import com.youlai.boot.platform.sms.enums.SmsTypeEnum; import com.youlai.boot.platform.sms.service.SmsService; diff --git a/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java index addbccf5..12d443c5 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java @@ -2,7 +2,7 @@ package com.youlai.boot.system.service.impl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.youlai.boot.system.model.event.DictEvent; +import com.youlai.boot.system.model.dto.DictEventDTO; import com.youlai.boot.system.service.WebSocketService; import lombok.Data; import lombok.extern.slf4j.Slf4j; @@ -192,7 +192,7 @@ public class WebSocketServiceImpl implements WebSocketService { */ @Override public void broadcastDictChange(String dictCode) { - DictEvent event = new DictEvent(dictCode); + DictEventDTO event = new DictEventDTO(dictCode); sendDictEvent(event); } @@ -201,7 +201,7 @@ public class WebSocketServiceImpl implements WebSocketService { * * @param event 字典事件 */ - private void sendDictEvent(DictEvent event) { + private void sendDictEvent(DictEventDTO event) { if (messagingTemplate == null) { log.warn("消息模板尚未初始化,无法发送字典更新通知"); return; From 107be44cd94d760f09e57995208c00c5e91fa14e Mon Sep 17 00:00:00 2001 From: theo <971366405@qq.com> Date: Wed, 15 Oct 2025 17:55:07 +0800 Subject: [PATCH 54/60] =?UTF-8?q?refactor(core):=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E5=8C=85=E7=BB=93=E6=9E=84=E5=92=8C=E5=BC=82=E5=B8=B8=E5=A4=84?= =?UTF-8?q?=E7=90=86=E7=B1=BB=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 CaptchaTypeEnum 移至 common.enums 包 - 将 CaptchaValidationException 移至 security.exception 包- 将 GlobalExceptionHandler 移至 core.exception 包- 修正 Knife4jOpenApiCustomizer 的包路径拼写错误 - 修正 MyDataPermissionHandler 和 MyMetaObjectHandler 的包路径拼写错误- 移除 GlobalExceptionHandler 中冗余的 BusinessException 导入 --- .../java/com/youlai/boot/common/enums/CaptchaTypeEnum.java | 2 +- .../com/youlai/boot/core/exception/GlobalExceptionHandler.java | 3 +-- .../youlai/boot/plugin/knife4j/Knife4jOpenApiCustomizer.java | 2 +- .../youlai/boot/plugin/mybatis/MyDataPermissionHandler.java | 2 +- .../com/youlai/boot/plugin/mybatis/MyMetaObjectHandler.java | 2 +- .../boot/security/exception/CaptchaValidationException.java | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/youlai/boot/common/enums/CaptchaTypeEnum.java b/src/main/java/com/youlai/boot/common/enums/CaptchaTypeEnum.java index c0ac69b4..8d3fe10e 100644 --- a/src/main/java/com/youlai/boot/common/enums/CaptchaTypeEnum.java +++ b/src/main/java/com/youlai/boot/common/enums/CaptchaTypeEnum.java @@ -1,4 +1,4 @@ -package com.youlai.boot.auth.enums; +package com.youlai.boot.common.enums; /** * EasyCaptcha 验证码类型枚举 diff --git a/src/main/java/com/youlai/boot/core/exception/GlobalExceptionHandler.java b/src/main/java/com/youlai/boot/core/exception/GlobalExceptionHandler.java index e0599a9e..9b20ddb9 100644 --- a/src/main/java/com/youlai/boot/core/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/youlai/boot/core/exception/GlobalExceptionHandler.java @@ -1,8 +1,7 @@ -package com.youlai.boot.common.exception; +package com.youlai.boot.core.exception; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.core.JsonProcessingException; -import com.youlai.boot.core.exception.BusinessException; import com.youlai.boot.core.web.Result; import com.youlai.boot.core.web.ResultCode; import jakarta.servlet.ServletException; diff --git a/src/main/java/com/youlai/boot/plugin/knife4j/Knife4jOpenApiCustomizer.java b/src/main/java/com/youlai/boot/plugin/knife4j/Knife4jOpenApiCustomizer.java index abfe74be..401cb978 100644 --- a/src/main/java/com/youlai/boot/plugin/knife4j/Knife4jOpenApiCustomizer.java +++ b/src/main/java/com/youlai/boot/plugin/knife4j/Knife4jOpenApiCustomizer.java @@ -1,4 +1,4 @@ -package com.youlai.boot.pulgin.knife4j; +package com.youlai.boot.plugin.knife4j; import com.github.xiaoymin.knife4j.annotations.ApiSupport; diff --git a/src/main/java/com/youlai/boot/plugin/mybatis/MyDataPermissionHandler.java b/src/main/java/com/youlai/boot/plugin/mybatis/MyDataPermissionHandler.java index e7ff2ff8..f1d14579 100644 --- a/src/main/java/com/youlai/boot/plugin/mybatis/MyDataPermissionHandler.java +++ b/src/main/java/com/youlai/boot/plugin/mybatis/MyDataPermissionHandler.java @@ -1,4 +1,4 @@ -package com.youlai.boot.pulgin.mybatis; +package com.youlai.boot.plugin.mybatis; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.StringPool; diff --git a/src/main/java/com/youlai/boot/plugin/mybatis/MyMetaObjectHandler.java b/src/main/java/com/youlai/boot/plugin/mybatis/MyMetaObjectHandler.java index f4116517..76e73681 100644 --- a/src/main/java/com/youlai/boot/plugin/mybatis/MyMetaObjectHandler.java +++ b/src/main/java/com/youlai/boot/plugin/mybatis/MyMetaObjectHandler.java @@ -1,4 +1,4 @@ -package com.youlai.boot.pulgin.mybatis; +package com.youlai.boot.plugin.mybatis; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; diff --git a/src/main/java/com/youlai/boot/security/exception/CaptchaValidationException.java b/src/main/java/com/youlai/boot/security/exception/CaptchaValidationException.java index 5232f01c..23b5bf8e 100644 --- a/src/main/java/com/youlai/boot/security/exception/CaptchaValidationException.java +++ b/src/main/java/com/youlai/boot/security/exception/CaptchaValidationException.java @@ -1,4 +1,4 @@ -package com.youlai.boot.core.exception; +package com.youlai.boot.security.exception; import org.springframework.security.core.AuthenticationException; From b6bfc55b3b38cd62833b0c33730545f01bae623e Mon Sep 17 00:00:00 2001 From: theo <971366405@qq.com> Date: Wed, 15 Oct 2025 17:56:21 +0800 Subject: [PATCH 55/60] =?UTF-8?q?refactor(auth):=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E8=AE=A4=E8=AF=81=E6=9C=8D=E5=8A=A1=E5=92=8C=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整 AuthServiceImpl 中的包导入顺序 -修复安全配置中数据权限处理器的包路径 - 更新短信认证提供者中的验证码异常处理类路径 -优化包结构引用,提高代码可读性和维护性 --- .../youlai/boot/auth/service/impl/AuthServiceImpl.java | 8 ++++---- src/main/java/com/youlai/boot/config/MybatisConfig.java | 4 ++-- .../boot/security/provider/SmsAuthenticationProvider.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) 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 61514c7c..b19f68a1 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,22 +5,22 @@ 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.enums.CaptchaTypeEnum; -import com.youlai.boot.auth.model.vo.CaptchaVO; 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; import com.youlai.boot.common.constant.SecurityConstants; +import com.youlai.boot.common.enums.CaptchaTypeEnum; import com.youlai.boot.config.property.CaptchaProperties; +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 com.youlai.boot.platform.sms.enums.SmsTypeEnum; -import com.youlai.boot.platform.sms.service.SmsService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; diff --git a/src/main/java/com/youlai/boot/config/MybatisConfig.java b/src/main/java/com/youlai/boot/config/MybatisConfig.java index b21ae27d..a3711fbf 100644 --- a/src/main/java/com/youlai/boot/config/MybatisConfig.java +++ b/src/main/java/com/youlai/boot/config/MybatisConfig.java @@ -5,8 +5,8 @@ import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; -import com.youlai.boot.pulgin.mybatis.MyDataPermissionHandler; -import com.youlai.boot.pulgin.mybatis.MyMetaObjectHandler; +import com.youlai.boot.plugin.mybatis.MyDataPermissionHandler; +import com.youlai.boot.plugin.mybatis.MyMetaObjectHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; diff --git a/src/main/java/com/youlai/boot/security/provider/SmsAuthenticationProvider.java b/src/main/java/com/youlai/boot/security/provider/SmsAuthenticationProvider.java index 9c6b2a85..681f31e8 100644 --- a/src/main/java/com/youlai/boot/security/provider/SmsAuthenticationProvider.java +++ b/src/main/java/com/youlai/boot/security/provider/SmsAuthenticationProvider.java @@ -3,7 +3,7 @@ package com.youlai.boot.security.provider; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.youlai.boot.common.constant.RedisConstants; -import com.youlai.boot.core.exception.CaptchaValidationException; +import com.youlai.boot.security.exception.CaptchaValidationException; import com.youlai.boot.security.model.SmsAuthenticationToken; import com.youlai.boot.security.model.SysUserDetails; import com.youlai.boot.security.model.UserAuthCredentials; From a08ac338c090b8b27804a19deab1e62bdd550695 Mon Sep 17 00:00:00 2001 From: theo <971366405@qq.com> Date: Thu, 16 Oct 2025 10:25:34 +0800 Subject: [PATCH 56/60] =?UTF-8?q?refactor(security):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E8=BF=87=E6=BB=A4=E5=99=A8=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=8C=B9=E9=85=8D=E5=99=A8=E5=AE=9E=E7=8E=B0-=20?= =?UTF-8?q?=E5=B0=86=20AntPathRequestMatcher=20=E6=9B=BF=E6=8D=A2=E4=B8=BA?= =?UTF-8?q?=20PathPatternRequestMatcher-=20=E4=BD=BF=E7=94=A8=E6=96=B0?= =?UTF-8?q?=E7=9A=84=E5=8C=B9=E9=85=8D=E5=99=A8=E5=B7=A5=E5=8E=82=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=88=9B=E5=BB=BA=E7=99=BB=E5=BD=95=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=E5=99=A8-=E4=BF=9D=E6=8C=81=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E9=AA=8C=E8=AF=81=E9=80=BB=E8=BE=91=E4=B8=8D?= =?UTF-8?q?=E5=8F=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(web): 设置响应字符编码为UTF-8 - 添加 StandardCharsets 导入- 在响应处理中显式设置字符编码 - 确保响应内容正确编码避免乱码问题 --- .../java/com/youlai/boot/core/web/WebResponseHelper.java | 4 +++- .../youlai/boot/security/filter/CaptchaValidationFilter.java | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/youlai/boot/core/web/WebResponseHelper.java b/src/main/java/com/youlai/boot/core/web/WebResponseHelper.java index 00ad93f2..192ec058 100644 --- a/src/main/java/com/youlai/boot/core/web/WebResponseHelper.java +++ b/src/main/java/com/youlai/boot/core/web/WebResponseHelper.java @@ -7,6 +7,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import java.nio.charset.StandardCharsets; + /** * Web响应辅助类 *

@@ -40,7 +42,7 @@ public class WebResponseHelper { // 设置HTTP状态码 int httpStatus = mapHttpStatus(resultCode); response.setStatus(httpStatus); - + response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); // 构建响应对象 Result result = message == null ? Result.failed(resultCode) diff --git a/src/main/java/com/youlai/boot/security/filter/CaptchaValidationFilter.java b/src/main/java/com/youlai/boot/security/filter/CaptchaValidationFilter.java index 771a7555..f0a08976 100644 --- a/src/main/java/com/youlai/boot/security/filter/CaptchaValidationFilter.java +++ b/src/main/java/com/youlai/boot/security/filter/CaptchaValidationFilter.java @@ -12,7 +12,8 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpMethod; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; @@ -26,7 +27,7 @@ import java.io.IOException; */ public class CaptchaValidationFilter extends OncePerRequestFilter { - private static final AntPathRequestMatcher LOGIN_PATH_REQUEST_MATCHER = new AntPathRequestMatcher(SecurityConstants.LOGIN_PATH, HttpMethod.POST.name()); + private static final RequestMatcher LOGIN_PATH_REQUEST_MATCHER = PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST,SecurityConstants.LOGIN_PATH); public static final String CAPTCHA_CODE_PARAM_NAME = "captchaCode"; public static final String CAPTCHA_KEY_PARAM_NAME = "captchaKey"; From a5708d179efc43c2ce9743b9703c7d5ff567c29d Mon Sep 17 00:00:00 2001 From: f-404 Date: Fri, 24 Oct 2025 09:34:56 +0800 Subject: [PATCH 57/60] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90?= =?UTF-8?q?=E5=BC=95=E5=85=A5=E8=B7=AF=E5=BE=84=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: f-404 --- src/main/resources/templates/codegen/controller.java.vm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/templates/codegen/controller.java.vm b/src/main/resources/templates/codegen/controller.java.vm index 21c3b718..bdadcc47 100644 --- a/src/main/resources/templates/codegen/controller.java.vm +++ b/src/main/resources/templates/codegen/controller.java.vm @@ -8,8 +8,8 @@ import ${packageName}.${moduleName}.model.form.${entityName}Form; import ${packageName}.${moduleName}.model.query.${entityName}Query; import ${packageName}.${moduleName}.model.vo.${entityName}VO; import com.baomidou.mybatisplus.core.metadata.IPage; -import com.youlai.boot.common.result.PageResult; -import com.youlai.boot.common.result.Result; +import com.youlai.boot.core.web.PageResult; +import com.youlai.boot.core.web.Result; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; From 1aa6a15a80436e6377a6f6744ea166390a4be189 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Fri, 7 Nov 2025 21:27:52 +0800 Subject: [PATCH 58/60] =?UTF-8?q?docs(readme):=20=E6=B7=BB=E5=8A=A0=20Gite?= =?UTF-8?q?e=202025=20=E5=BC=80=E6=BA=90=E8=BD=AF=E4=BB=B6=E8=AF=84?= =?UTF-8?q?=E9=80=89=E6=8A=95=E7=A5=A8=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 2e491ee3..bdbef79a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,25 @@ +

+ +## 🎉 正在参加 Gitee 2025 最受欢迎开源软件评选 + + + 投票 + + +

+ 一票就够,不用每天投 🙏 您的支持是我们持续更新的最大动力! +

+ +

+ + 👉 点击徽章或这里投票 👈 + +

+ +
+ +![](https://foruda.gitee.com/images/1708618984641188532/a7cca095_716974.png "rainbow.png") +
logo From ffb89e50da9e4ebe94520d97f1e1c81f803a461a Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Sat, 8 Nov 2025 00:01:43 +0800 Subject: [PATCH 59/60] =?UTF-8?q?feat(websocket):=E9=87=8D=E6=9E=84=20WebS?= =?UTF-8?q?ocket=20=E9=85=8D=E7=BD=AE=E4=B8=8E=E6=9C=8D=E5=8A=A1=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../youlai/boot/config/WebSocketConfig.java | 356 ++++++++++------ .../service/impl/WebSocketServiceImpl.java | 394 ++++++++++++------ .../impl/WebSocketSessionCleanupService.java | 94 +++++ 3 files changed, 600 insertions(+), 244 deletions(-) create mode 100644 src/main/java/com/youlai/boot/system/service/impl/WebSocketSessionCleanupService.java diff --git a/src/main/java/com/youlai/boot/config/WebSocketConfig.java b/src/main/java/com/youlai/boot/config/WebSocketConfig.java index 72e2405f..2168d171 100644 --- a/src/main/java/com/youlai/boot/config/WebSocketConfig.java +++ b/src/main/java/com/youlai/boot/config/WebSocketConfig.java @@ -27,7 +27,13 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; /** - * WebSocket配置 + * WebSocket 配置类 + * + * 核心功能: + * - 配置 WebSocket 端点 + * - 配置消息代理 + * - 实现连接认证与授权 + * - 管理用户会话生命周期 * * @author Ray.Hao * @since 3.0.0 @@ -37,133 +43,251 @@ import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerCo @Slf4j public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { - private final TokenManager tokenManager; - private final WebSocketService webSocketService; + private static final String WS_ENDPOINT = "/ws"; + private static final String APP_DESTINATION_PREFIX = "/app"; + private static final String USER_DESTINATION_PREFIX = "/user"; + private static final String[] BROKER_DESTINATIONS = {"/topic", "/queue"}; - public WebSocketConfig(TokenManager tokenManager, @Lazy WebSocketService webSocketService) { - this.tokenManager = tokenManager; - this.webSocketService = webSocketService; - } + private final TokenManager tokenManager; + private final WebSocketService webSocketService; - /** - * 注册一个端点,客户端通过这个端点进行连接 - */ - @Override - public void registerStompEndpoints(StompEndpointRegistry registry) { - registry - // 注册 /ws 的端点 - .addEndpoint("/ws") - // 允许跨域 - .setAllowedOriginPatterns("*"); - } + public WebSocketConfig(TokenManager tokenManager, @Lazy WebSocketService webSocketService) { + this.tokenManager = tokenManager; + this.webSocketService = webSocketService; + log.info("✓ WebSocket 配置已加载"); + } + /** + * 注册 STOMP 端点 + * + * 客户端通过该端点建立 WebSocket 连接 + */ + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry + .addEndpoint(WS_ENDPOINT) + .setAllowedOriginPatterns("*"); // 允许跨域(生产环境建议配置具体域名) - /** - * 配置消息代理 - */ - @Override - public void configureMessageBroker(MessageBrokerRegistry registry) { - // 客户端发送消息的请求前缀 - registry.setApplicationDestinationPrefixes("/app"); + log.info("✓ STOMP 端点已注册: {}", WS_ENDPOINT); + } - // 客户端订阅消息的请求前缀,topic一般用于广播推送,queue用于点对点推送 - registry.enableSimpleBroker("/topic", "/queue"); + /** + * 配置消息代理 + * + * - /app 前缀:客户端发送消息到服务端的前缀 + * - /topic 前缀:用于广播消息 + * - /queue 前缀:用于点对点消息 + * - /user 前缀:服务端发送给特定用户的消息前缀 + */ + @Override + public void configureMessageBroker(MessageBrokerRegistry registry) { + // 客户端发送消息的请求前缀 + registry.setApplicationDestinationPrefixes(APP_DESTINATION_PREFIX); - // 服务端通知客户端的前缀,可以不设置,默认为user - registry.setUserDestinationPrefix("/user"); - } + // 启用简单消息代理,处理 /topic 和 /queue 前缀的消息 + registry.enableSimpleBroker(BROKER_DESTINATIONS); + // 服务端通知客户端的前缀 + registry.setUserDestinationPrefix(USER_DESTINATION_PREFIX); - /** - * 配置客户端入站通道拦截器 - *

- * 核心功能: - * 1. 连接建立时解析令牌并绑定用户身份 - * 2. 连接关闭时触发下线通知 - * 3. 异常Token的防御性处理 - */ - @Override - public void configureClientInboundChannel(ChannelRegistration registration) { - registration.interceptors(new ChannelInterceptor() { - @Override - public Message preSend(@NotNull Message message, @NotNull MessageChannel channel) { - StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); - if (accessor == null) { - return ChannelInterceptor.super.preSend(message, channel); + log.info("✓ 消息代理已配置: app={}, broker={}, user={}", + APP_DESTINATION_PREFIX, BROKER_DESTINATIONS, USER_DESTINATION_PREFIX); + } + + /** + * 配置客户端入站通道拦截器 + * + * 核心功能: + * 1. 连接建立时:解析 JWT Token 并绑定用户身份 + * 2. 连接关闭时:触发用户下线通知 + * 3. 安全防护:拦截无效连接请求 + */ + @Override + public void configureClientInboundChannel(ChannelRegistration registration) { + registration.interceptors(new ChannelInterceptor() { + @Override + public Message preSend(@NotNull Message message, @NotNull MessageChannel channel) { + StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); + + // 防御性检查:确保 accessor 不为空 + if (accessor == null) { + log.warn("⚠ 收到异常消息:无法获取 StompHeaderAccessor"); + return ChannelInterceptor.super.preSend(message, channel); + } + + StompCommand command = accessor.getCommand(); + if (command == null) { + return ChannelInterceptor.super.preSend(message, channel); + } + + try { + switch (command) { + case CONNECT: + handleConnect(accessor); + break; + + case DISCONNECT: + handleDisconnect(accessor); + break; + + case SUBSCRIBE: + handleSubscribe(accessor); + break; + + default: + // 其他命令不需要特殊处理 + break; + } + } catch (AuthenticationException ex) { + // 认证失败时强制关闭连接 + log.error("❌ 连接认证失败: {}", ex.getMessage()); + throw ex; + } catch (Exception ex) { + // 捕获其他未知异常 + log.error("❌ WebSocket 消息处理异常", ex); + throw new MessagingException("消息处理失败: " + ex.getMessage()); + } + + return ChannelInterceptor.super.preSend(message, channel); + } + }); + + log.info("✓ 客户端入站通道拦截器已配置"); + } + + /** + * 处理客户端连接请求 + * + * 安全校验流程: + * 1. 提取 Authorization 头 + * 2. 验证 Bearer Token 格式 + * 3. 解析并验证 JWT 有效性 + * 4. 绑定用户身份到当前会话 + * 5. 记录用户上线状态 + */ + private void handleConnect(StompHeaderAccessor accessor) { + String authorization = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION); + + // 安全检查:确保 Authorization 头存在且格式正确 + if (StrUtil.isBlank(authorization)) { + log.warn("⚠ 非法连接请求:缺少 Authorization 头"); + throw new AuthenticationCredentialsNotFoundException("缺少 Authorization 头"); } + if (!authorization.startsWith("Bearer ")) { + log.warn("⚠ 非法连接请求:Authorization 头格式错误"); + throw new BadCredentialsException("Authorization 头格式错误"); + } + + // 提取 JWT Token(移除 "Bearer " 前缀) + String token = authorization.substring(7); + + if (StrUtil.isBlank(token)) { + log.warn("⚠ 非法连接请求:Token 为空"); + throw new BadCredentialsException("Token 为空"); + } + + // 解析并验证 Token + Authentication authentication; try { - // 处理客户端连接请求 - if (StompCommand.CONNECT.equals(accessor.getCommand())) { - /* - * 安全校验流程: - * 1. 从HEADER中获取Authorization值 - * 2. 校验Bearer Token格式合法性 - * 3. 解析并验证JWT有效性 - * 4. 绑定用户身份到当前会话 - */ - String authorization = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION); - - // 防御性校验:确保Authorization头存在且格式正确 - if (StrUtil.isBlank(authorization) || !authorization.startsWith("Bearer ")) { - log.warn("非法连接请求:缺少有效的Authorization头"); - throw new AuthenticationCredentialsNotFoundException("Missing authorization header"); - } - - // 提取并处理JWT令牌(移除Bearer前缀) - String token = authorization.substring(7); - Authentication authentication = tokenManager.parseToken(token); - - // 令牌解析失败处理 - if (authentication == null) { - log.error("令牌解析失败:{}", token); - throw new BadCredentialsException("Invalid token"); - } - - // 获取用户详细信息 - SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal(); - if (userDetails == null || StrUtil.isBlank(userDetails.getUsername())) { - log.error("无效的用户凭证:{}", token); - throw new BadCredentialsException("Invalid user credentials"); - } - - String username = userDetails.getUsername(); - log.info("WebSocket连接建立:用户[{}]", username); - - // 绑定用户身份到当前会话(重要:用于@SendToUser等注解) - accessor.setUser(authentication); - - // 记录用户上线状态 - webSocketService.userConnected(username, accessor.getSessionId()); - - } - // 处理客户端断开请求 - else if (StompCommand.DISCONNECT.equals(accessor.getCommand())) { - /* - * 注意:只有成功建立过认证的连接才会触发下线事件 - * 防止未认证成功的连接产生脏数据 - */ - Authentication authentication = (Authentication) accessor.getUser(); - if (authentication != null && authentication.isAuthenticated()) { - String username = ((SysUserDetails) authentication.getPrincipal()).getUsername(); - log.info("WebSocket连接关闭:用户[{}]", username); - - // 记录用户下线状态 - webSocketService.userDisconnected(username); - } - } - } catch (AuthenticationException ex) { - // 认证失败时强制关闭连接 - log.error("连接认证失败:{}", ex.getMessage()); - throw ex; + authentication = tokenManager.parseToken(token); } catch (Exception ex) { - // 捕获其他未知异常 - log.error("WebSocket连接处理异常:", ex); - throw new MessagingException("Connection processing failed"); + log.error("❌ Token 解析失败", ex); + throw new BadCredentialsException("Token 无效: " + ex.getMessage()); } - return ChannelInterceptor.super.preSend(message, channel); - } - }); - } + // 验证解析结果 + if (authentication == null || !authentication.isAuthenticated()) { + log.warn("⚠ Token 解析失败:认证对象无效"); + throw new BadCredentialsException("Token 解析失败"); + } + + // 获取用户详细信息 + Object principal = authentication.getPrincipal(); + if (!(principal instanceof SysUserDetails)) { + log.error("❌ 无效的用户凭证类型: {}", principal.getClass().getName()); + throw new BadCredentialsException("用户凭证类型错误"); + } + + SysUserDetails userDetails = (SysUserDetails) principal; + String username = userDetails.getUsername(); + + if (StrUtil.isBlank(username)) { + log.warn("⚠ 用户名为空"); + throw new BadCredentialsException("用户名为空"); + } + + // 绑定用户身份到当前会话(重要:用于 @SendToUser 等注解) + accessor.setUser(authentication); + + // 获取会话 ID + String sessionId = accessor.getSessionId(); + if (sessionId == null) { + log.warn("⚠ 会话 ID 为空,使用临时 ID"); + sessionId = "temp-" + System.nanoTime(); + } + + // 记录用户上线状态 + try { + webSocketService.userConnected(username, sessionId); + log.info("✓ WebSocket 连接建立成功: 用户[{}], 会话[{}]", username, sessionId); + } catch (Exception ex) { + log.error("❌ 记录用户上线状态失败: 用户[{}], 会话[{}]", username, sessionId, ex); + // 不抛出异常,允许连接继续 + } + } + + /** + * 处理客户端断开连接事件 + * + * 注意: + * - 只有成功建立过认证的连接才会触发下线事件 + * - 防止未认证成功的连接产生脏数据 + */ + private void handleDisconnect(StompHeaderAccessor accessor) { + Authentication authentication = (Authentication) accessor.getUser(); + + // 防御性检查:只处理已认证的连接 + if (authentication == null || !authentication.isAuthenticated()) { + log.debug("未认证的连接断开,跳过处理"); + return; + } + + Object principal = authentication.getPrincipal(); + if (!(principal instanceof SysUserDetails)) { + log.warn("⚠ 断开连接时用户凭证类型异常"); + return; + } + + SysUserDetails userDetails = (SysUserDetails) principal; + String username = userDetails.getUsername(); + + if (StrUtil.isNotBlank(username)) { + try { + webSocketService.userDisconnected(username); + log.info("✓ WebSocket 连接断开: 用户[{}]", username); + } catch (Exception ex) { + log.error("❌ 记录用户下线状态失败: 用户[{}]", username, ex); + } + } + } + + /** + * 处理客户端订阅事件(可选) + * + * 用于记录订阅信息或实施订阅级别的权限控制 + */ + private void handleSubscribe(StompHeaderAccessor accessor) { + Authentication authentication = (Authentication) accessor.getUser(); + + if (authentication != null && authentication.isAuthenticated()) { + String destination = accessor.getDestination(); + String username = authentication.getName(); + + log.debug("用户[{}]订阅主题: {}", username, destination); + + // TODO: 这里可以实现订阅级别的权限控制 + // 例如:检查用户是否有权限订阅某个主题 + } + } } diff --git a/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java index 12d443c5..d2fedfed 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/WebSocketServiceImpl.java @@ -4,7 +4,9 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.youlai.boot.system.model.dto.DictEventDTO; import com.youlai.boot.system.service.WebSocketService; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessagingTemplate; @@ -12,12 +14,17 @@ import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** - * WebSocket服务实现类 - * 统一处理WebSocket消息发送和用户在线状态管理 + * WebSocket 服务实现类 + * + * 核心功能: + * - 用户在线状态管理(支持多设备登录) + * - 消息推送(广播、点对点) + * - 字典变更通知 * * @author Ray.Hao * @since 3.0.0 @@ -26,8 +33,23 @@ import java.util.stream.Collectors; @Slf4j public class WebSocketServiceImpl implements WebSocketService { - // 在线用户映射表,key为用户名,value为用户在线信息 - private final Map onlineUsers = new ConcurrentHashMap<>(); + // ==================== 在线用户管理 ==================== + + /** + * 用户在线会话映射表 + * Key: 用户名 + * Value: 该用户的所有会话 ID 集合(支持多设备登录) + */ + private final Map> userSessionsMap = new ConcurrentHashMap<>(); + + /** + * 会话详情映射表 + * Key: 会话 ID + * Value: 会话详细信息 + */ + private final Map sessionDetailsMap = new ConcurrentHashMap<>(); + + // ==================== 依赖注入 ==================== private SimpMessagingTemplate messagingTemplate; private final ObjectMapper objectMapper; @@ -36,67 +58,156 @@ public class WebSocketServiceImpl implements WebSocketService { public WebSocketServiceImpl(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } - + + /** + * 延迟注入 SimpMessagingTemplate,避免循环依赖 + */ @Autowired(required = false) public void setMessagingTemplate(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; - log.info("WebSocket消息模板已初始化"); + log.info("✓ WebSocket 消息模板已初始化"); } - //================================== - // 用户在线状态管理功能 - //================================== + // ==================== 用户在线状态管理 ==================== /** - * 用户上线 + * 处理用户连接事件 * * @param username 用户名 - * @param sessionId WebSocket会话ID(可选) + * @param sessionId WebSocket 会话 ID */ @Override public void userConnected(String username, String sessionId) { - // 生成会话ID(如果未提供) - String actualSessionId = sessionId != null ? sessionId : "session-" + System.nanoTime(); - UserOnlineInfo info = new UserOnlineInfo(username, actualSessionId, System.currentTimeMillis()); - onlineUsers.put(username, info); - log.info("用户[{}]上线,当前在线用户数:{}", username, onlineUsers.size()); - - // 通知在线用户状态变更 - notifyOnlineUsersChangeInternal(); + if (username == null || username.isEmpty()) { + log.warn("用户连接失败:用户名为空"); + return; + } + + if (sessionId == null || sessionId.isEmpty()) { + log.warn("用户[{}]连接失败:会话 ID 为空", username); + return; + } + + // 添加会话到用户的会话集合中(支持多设备登录) + userSessionsMap.computeIfAbsent(username, k -> ConcurrentHashMap.newKeySet()) + .add(sessionId); + + // 保存会话详情 + SessionInfo sessionInfo = new SessionInfo(username, sessionId, System.currentTimeMillis()); + sessionDetailsMap.put(sessionId, sessionInfo); + + int sessionCount = userSessionsMap.get(username).size(); + int totalOnlineUsers = userSessionsMap.size(); + + log.info("✓ 用户[{}]会话[{}]上线(该用户共 {} 个会话,系统总在线用户数:{})", + username, sessionId, sessionCount, totalOnlineUsers); + + // 广播在线用户数变更 + broadcastOnlineUserCount(); } /** - * 用户下线 + * 处理用户断开连接事件 * * @param username 用户名 */ @Override public void userDisconnected(String username) { - onlineUsers.remove(username); - log.info("用户[{}]下线,当前在线用户数:{}", username, onlineUsers.size()); - - // 通知在线用户状态变更 - notifyOnlineUsersChangeInternal(); + if (username == null || username.isEmpty()) { + return; + } + + // 获取该用户的所有会话 + Set sessions = userSessionsMap.get(username); + if (sessions == null || sessions.isEmpty()) { + log.warn("用户[{}]下线:未找到会话记录", username); + return; + } + + // 移除所有会话详情(通常一次只断开一个会话,但这里做全量清理) + sessions.forEach(sessionDetailsMap::remove); + + // 移除用户的会话记录 + userSessionsMap.remove(username); + + int totalOnlineUsers = userSessionsMap.size(); + log.info("✓ 用户[{}]下线(系统总在线用户数:{})", username, totalOnlineUsers); + + // 广播在线用户数变更 + broadcastOnlineUserCount(); + } + + /** + * 移除指定会话(单个设备下线) + * + * @param sessionId 会话 ID + */ + public void removeSession(String sessionId) { + SessionInfo sessionInfo = sessionDetailsMap.remove(sessionId); + if (sessionInfo == null) { + return; + } + + String username = sessionInfo.getUsername(); + Set sessions = userSessionsMap.get(username); + + if (sessions != null) { + sessions.remove(sessionId); + + // 如果该用户没有其他会话了,移除用户记录 + if (sessions.isEmpty()) { + userSessionsMap.remove(username); + log.info("✓ 用户[{}]最后一个会话[{}]下线", username, sessionId); + } else { + log.info("✓ 用户[{}]会话[{}]下线(还剩 {} 个会话)", + username, sessionId, sessions.size()); + } + + // 广播在线用户数变更 + broadcastOnlineUserCount(); + } } /** * 获取在线用户列表 * - * @return 在线用户名列表 + * @return 在线用户信息列表 */ - public List getOnlineUsers() { - return onlineUsers.values().stream() - .map(info -> new UserOnlineDTO(info.getUsername(), info.getLoginTime())) + public List getOnlineUsers() { + return userSessionsMap.entrySet().stream() + .map(entry -> { + String username = entry.getKey(); + Set sessions = entry.getValue(); + + // 获取该用户最早的登录时间 + long earliestLoginTime = sessions.stream() + .map(sessionDetailsMap::get) + .filter(info -> info != null) + .mapToLong(SessionInfo::getConnectTime) + .min() + .orElse(System.currentTimeMillis()); + + return new OnlineUserDTO(username, sessions.size(), earliestLoginTime); + }) .collect(Collectors.toList()); } /** * 获取在线用户数量 * - * @return 在线用户数 + * @return 在线用户数(不是会话数) */ public int getOnlineUserCount() { - return onlineUsers.size(); + return userSessionsMap.size(); + } + + /** + * 获取在线会话总数 + * + * @return 所有在线会话的总数 + */ + public int getTotalSessionCount() { + return sessionDetailsMap.size(); } /** @@ -106,172 +217,199 @@ public class WebSocketServiceImpl implements WebSocketService { * @return 是否在线 */ public boolean isUserOnline(String username) { - return onlineUsers.containsKey(username); + Set sessions = userSessionsMap.get(username); + return sessions != null && !sessions.isEmpty(); } - + /** - * 手动触发在线用户变更通知 - * 供外部手动触发通知使用 + * 获取指定用户的会话数量 + * + * @param username 用户名 + * @return 会话数量 + */ + public int getUserSessionCount(String username) { + Set sessions = userSessionsMap.get(username); + return sessions != null ? sessions.size() : 0; + } + + /** + * 手动触发在线用户数量广播 + * + * 供外部服务(如定时任务)调用 */ public void notifyOnlineUsersChange() { - log.info("手动触发在线用户数量通知,当前在线用户数:{}", onlineUsers.size()); - sendOnlineUserCount(); + log.info("手动触发在线用户数量通知,当前在线用户数:{}", getOnlineUserCount()); + broadcastOnlineUserCount(); } - + /** - * 发送在线用户数量(简化版,不包含用户详情) + * 广播在线用户数量变更(内部方法) */ - private void sendOnlineUserCount() { + private void broadcastOnlineUserCount() { if (messagingTemplate == null) { log.warn("消息模板尚未初始化,无法发送在线用户数量"); return; } - + try { - // 直接发送数量,更轻量 - int count = onlineUsers.size(); + int count = getOnlineUserCount(); messagingTemplate.convertAndSend("/topic/online-count", count); - log.debug("已发送在线用户数量: {}", count); + log.debug("✓ 已广播在线用户数量: {}", count); } catch (Exception e) { - log.error("发送在线用户数量失败", e); + log.error("广播在线用户数量失败", e); } } - - /** - * 内部通用通知方法 - * 通知所有客户端在线用户变更 - */ - private void notifyOnlineUsersChangeInternal() { - if (messagingTemplate == null) { - log.warn("消息模板尚未初始化,无法发送在线用户数量通知"); - return; - } - - // 只发送简化版数据(仅数量) - sendOnlineUserCount(); - } + + // ==================== 消息推送功能 ==================== /** - * 用户在线信息 - */ - @Data - private static class UserOnlineInfo { - private final String username; - private final String sessionId; - private final long loginTime; - } - - /** - * 用户在线DTO(用于返回给前端) - */ - @Data - public static class UserOnlineDTO { - private final String username; - private final long loginTime; - } - - /** - * 在线用户变更事件 - */ - @Data - private static class OnlineUsersChangeEvent { - private String type; - private int count; - private List users; - private long timestamp; - } - - //================================== - // WebSocket消息发送功能 - //================================== - - /** - * 向所有客户端发送字典更新事件 + * 向所有客户端广播字典更新事件 * * @param dictCode 字典编码 */ @Override public void broadcastDictChange(String dictCode) { + if (dictCode == null || dictCode.isEmpty()) { + log.warn("字典编码为空,跳过广播"); + return; + } + DictEventDTO event = new DictEventDTO(dictCode); - sendDictEvent(event); + sendDictChangeEvent(event); } /** - * 发送字典事件消息 + * 发送字典变更事件 * * @param event 字典事件 */ - private void sendDictEvent(DictEventDTO event) { + private void sendDictChangeEvent(DictEventDTO event) { if (messagingTemplate == null) { log.warn("消息模板尚未初始化,无法发送字典更新通知"); return; } - + try { String message = objectMapper.writeValueAsString(event); messagingTemplate.convertAndSend("/topic/dict", message); - log.info("已发送字典事件通知, dictCode: {}", event.getDictCode()); + log.info("✓ 已广播字典变更通知: dictCode={}", event.getDictCode()); } catch (JsonProcessingException e) { - log.error("发送字典事件失败", e); + log.error("字典事件序列化失败: dictCode={}", event.getDictCode(), e); + } catch (Exception e) { + log.error("发送字典变更通知失败: dictCode={}", event.getDictCode(), e); } } /** - * 向特定用户发送系统消息 - * - * @param username 用户名 - * @param message 消息内容 + * 向特定用户发送通知消息 + * + * @param username 目标用户名 + * @param message 消息内容 */ @Override public void sendNotification(String username, Object message) { + if (username == null || username.isEmpty()) { + log.warn("用户名为空,无法发送通知"); + return; + } + + if (message == null) { + log.warn("消息内容为空,无法发送给用户[{}]", username); + return; + } + if (messagingTemplate == null) { log.warn("消息模板尚未初始化,无法发送用户消息"); return; } - + try { String messageJson = objectMapper.writeValueAsString(message); messagingTemplate.convertAndSendToUser(username, "/queue/messages", messageJson); - log.info("已向用户[{}]发送消息", username); + log.info("✓ 已向用户[{}]发送通知", username); } catch (JsonProcessingException e) { - log.error("向用户[{}]发送消息失败", username, e); + log.error("消息序列化失败: username={}", username, e); + } catch (Exception e) { + log.error("向用户[{}]发送通知失败", username, e); } } - + /** - * 发送广播消息给所有用户 - * + * 广播系统消息给所有用户 + * * @param message 消息内容 */ - public void broadcastMessage(String message) { + public void broadcastSystemMessage(String message) { + if (message == null || message.isEmpty()) { + log.warn("消息内容为空,无法广播"); + return; + } + if (messagingTemplate == null) { log.warn("消息模板尚未初始化,无法发送广播消息"); return; } - + try { - SystemMessage systemMessage = new SystemMessage("系统", message, System.currentTimeMillis()); + SystemMessage systemMessage = new SystemMessage( + "系统通知", + message, + System.currentTimeMillis() + ); String messageJson = objectMapper.writeValueAsString(systemMessage); messagingTemplate.convertAndSend("/topic/public", messageJson); - log.info("已发送广播消息: {}", message); + log.info("✓ 已广播系统消息: {}", message); } catch (JsonProcessingException e) { - log.error("发送广播消息失败", e); + log.error("系统消息序列化失败", e); + } catch (Exception e) { + log.error("广播系统消息失败", e); } } - + + // ==================== 内部数据类 ==================== + /** - * 系统消息对象 + * 会话信息 */ @Data - public static class SystemMessage { - private String sender; - private String content; - private long timestamp; - - public SystemMessage(String sender, String content, long timestamp) { - this.sender = sender; - this.content = content; - this.timestamp = timestamp; - } + @AllArgsConstructor + @NoArgsConstructor + private static class SessionInfo { + /** 用户名 */ + private String username; + /** 会话 ID */ + private String sessionId; + /** 连接时间戳 */ + private long connectTime; } -} + + /** + * 在线用户 DTO + */ + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class OnlineUserDTO { + /** 用户名 */ + private String username; + /** 会话数量 */ + private int sessionCount; + /** 首次登录时间 */ + private long loginTime; + } + + /** + * 系统消息 + */ + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class SystemMessage { + /** 发送者 */ + private String sender; + /** 消息内容 */ + private String content; + /** 时间戳 */ + private long timestamp; + } +} diff --git a/src/main/java/com/youlai/boot/system/service/impl/WebSocketSessionCleanupService.java b/src/main/java/com/youlai/boot/system/service/impl/WebSocketSessionCleanupService.java new file mode 100644 index 00000000..f1f2042b --- /dev/null +++ b/src/main/java/com/youlai/boot/system/service/impl/WebSocketSessionCleanupService.java @@ -0,0 +1,94 @@ +package com.youlai.boot.system.service.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +/** + * WebSocket 会话清理服务 + * + * 功能: + * - 定时清理僵尸会话 + * - 监控会话状态 + * - 输出统计信息 + * + * @author Ray.Hao + * @since 3.0.0 + */ +@Service +@Slf4j +@RequiredArgsConstructor +@ConditionalOnProperty( + prefix = "websocket.session-cleanup", + name = "enabled", + havingValue = "true", + matchIfMissing = true +) +public class WebSocketSessionCleanupService { + + private final WebSocketServiceImpl webSocketService; + + /** + * 定时输出 WebSocket 会话统计信息 + * + * 每 5 分钟执行一次 + */ + @Scheduled(fixedRate = 300000, initialDelay = 60000) + public void logSessionStatistics() { + try { + int onlineUserCount = webSocketService.getOnlineUserCount(); + int totalSessionCount = webSocketService.getTotalSessionCount(); + + log.info("📊 WebSocket 统计 - 在线用户数: {}, 活跃会话数: {}", + onlineUserCount, totalSessionCount); + + // 详细信息(仅在有用户在线时输出) + if (onlineUserCount > 0) { + var onlineUsers = webSocketService.getOnlineUsers(); + onlineUsers.forEach(user -> { + log.debug(" - 用户[{}]: {} 个会话", user.getUsername(), user.getSessionCount()); + }); + } + } catch (Exception ex) { + log.error("❌ 输出会话统计信息失败", ex); + } + } + + /** + * 健康检查 + * + * 每 30 秒执行一次,用于监控服务状态 + */ + @Scheduled(fixedRate = 30000, initialDelay = 10000) + public void healthCheck() { + try { + int onlineUserCount = webSocketService.getOnlineUserCount(); + int sessionCount = webSocketService.getTotalSessionCount(); + + // 异常检测:如果会话数远大于用户数,可能存在会话泄漏 + if (sessionCount > onlineUserCount * 10 && onlineUserCount > 0) { + log.warn("⚠ 检测到异常:会话数({})远大于用户数({}×10),可能存在会话泄漏", + sessionCount, onlineUserCount); + } + } catch (Exception ex) { + log.error("❌ 健康检查失败", ex); + } + } + + /** + * 手动触发在线用户数广播 + * + * 可用于系统启动后的初始化或手动刷新 + */ + public void triggerOnlineCountBroadcast() { + try { + webSocketService.notifyOnlineUsersChange(); + log.info("✓ 手动触发在线用户数广播成功"); + } catch (Exception ex) { + log.error("❌ 手动触发在线用户数广播失败", ex); + } + } +} + From 95412501fc69777ad7db6fef970b479c9651984d Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Mon, 10 Nov 2025 08:03:08 +0800 Subject: [PATCH 60/60] =?UTF-8?q?feat(ai):=20=E6=96=B0=E5=A2=9EAI=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E7=B3=BB=E7=BB=9F=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boot/platform/ai/config/AiProperties.java | 113 ++++++++ .../ai/controller/AiCommandController.java | 99 +++++++ .../ai/mapper/AiCommandAuditMapper.java | 20 ++ .../ai/model/dto/AiCommandRequestDTO.java | 37 +++ .../ai/model/dto/AiCommandResponseDTO.java | 53 ++++ .../ai/model/dto/AiExecuteRequestDTO.java | 36 +++ .../ai/model/dto/AiExecuteResponseDTO.java | 62 +++++ .../ai/model/dto/FunctionCallDTO.java | 38 +++ .../ai/model/entity/AiCommandAudit.java | 121 ++++++++ .../AbstractOpenAiCompatibleProvider.java | 101 +++++++ .../boot/platform/ai/provider/AiProvider.java | 32 +++ .../ai/provider/AiProviderFactory.java | 51 ++++ .../ai/provider/impl/DeepSeekProvider.java | 25 ++ .../ai/provider/impl/OpenAiProvider.java | 30 ++ .../ai/provider/impl/QwenProvider.java | 25 ++ .../platform/ai/service/AiCommandService.java | 63 +++++ .../ai/service/impl/AiCommandServiceImpl.java | 262 ++++++++++++++++++ src/main/resources/application-dev.yml | 85 +++++- src/main/resources/application-prod.yml | 66 ++++- 19 files changed, 1307 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/youlai/boot/platform/ai/config/AiProperties.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/controller/AiCommandController.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/mapper/AiCommandAuditMapper.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/model/dto/AiCommandRequestDTO.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/model/dto/AiCommandResponseDTO.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteRequestDTO.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteResponseDTO.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/model/dto/FunctionCallDTO.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/model/entity/AiCommandAudit.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/provider/AbstractOpenAiCompatibleProvider.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/provider/AiProvider.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/provider/AiProviderFactory.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/provider/impl/DeepSeekProvider.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/provider/impl/OpenAiProvider.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/provider/impl/QwenProvider.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/service/AiCommandService.java create mode 100644 src/main/java/com/youlai/boot/platform/ai/service/impl/AiCommandServiceImpl.java diff --git a/src/main/java/com/youlai/boot/platform/ai/config/AiProperties.java b/src/main/java/com/youlai/boot/platform/ai/config/AiProperties.java new file mode 100644 index 00000000..69d19a02 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/config/AiProperties.java @@ -0,0 +1,113 @@ +package com.youlai.boot.platform.ai.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * AI 配置属性 + * + * 优势: + * 1. 统一管理所有提供商配置 + * 2. 添加新提供商只需在 yml 中添加配置,无需修改代码 + * 3. 类型安全,支持 IDE 提示 + * + * @author Ray.Hao + */ +@Data +@Component +@ConfigurationProperties(prefix = "ai") +public class AiProperties { + + /** + * 是否启用 AI 功能 + */ + private Boolean enabled = false; + + /** + * 当前使用的提供商(qwen、deepseek、openai 等) + */ + private String provider = "qwen"; + + /** + * 所有提供商的配置 + * Key: 提供商名称(qwen、deepseek、openai) + * Value: 提供商配置 + */ + private Map providers; + + /** + * 安全配置 + */ + private SecurityConfig security = new SecurityConfig(); + + /** + * 限流配置 + */ + private RateLimitConfig rateLimit = new RateLimitConfig(); + + /** + * 提供商配置 + */ + @Data + public static class ProviderConfig { + /** + * API Key + */ + private String apiKey; + + /** + * Base URL(统一命名,符合行业惯例) + */ + private String baseUrl; + + /** + * 模型名称 + */ + private String model; + + /** + * 提供商显示名称(可选) + */ + private String displayName; + + /** + * 超时时间(秒) + */ + private Integer timeout = 30; + } + + /** + * 安全配置 + */ + @Data + public static class SecurityConfig { + private Boolean enableAudit = true; + private Boolean dangerousOperationsConfirm = true; + private java.util.List functionWhitelist; + private java.util.List sensitiveParams; + } + + /** + * 限流配置 + */ + @Data + public static class RateLimitConfig { + private Integer maxExecutionsPerMinute = 10; + private Integer maxExecutionsPerDay = 100; + } + + /** + * 获取当前提供商配置 + */ + public ProviderConfig getCurrentProviderConfig() { + if (providers == null || !providers.containsKey(provider)) { + throw new IllegalStateException("未找到提供商配置: " + provider); + } + return providers.get(provider); + } +} + + diff --git a/src/main/java/com/youlai/boot/platform/ai/controller/AiCommandController.java b/src/main/java/com/youlai/boot/platform/ai/controller/AiCommandController.java new file mode 100644 index 00000000..3597fdca --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/controller/AiCommandController.java @@ -0,0 +1,99 @@ +package com.youlai.boot.platform.ai.controller; + +import com.youlai.boot.core.web.Result; +import com.youlai.boot.platform.ai.model.dto.*; +import com.youlai.boot.platform.ai.service.AiCommandService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +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 命令控制器 + * + * @author Ray.Hao + * @since 3.0.0 + */ +@Tag(name = "AI命令接口") +@RestController +@RequestMapping("/api/v1/ai/command") +@RequiredArgsConstructor +@Slf4j +public class AiCommandController { + + private final AiCommandService aiCommandService; + + @Operation(summary = "解析自然语言命令") + @PostMapping("/parse") + public Result parseCommand( + @RequestBody AiCommandRequestDTO request, + HttpServletRequest httpRequest + ) { + log.info("收到AI命令解析请求: {}", request.getCommand()); + + try { + AiCommandResponseDTO response = aiCommandService.parseCommand(request, httpRequest); + return Result.success(response); + } catch (Exception e) { + log.error("命令解析失败", e); + return Result.success(AiCommandResponseDTO.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 { + AiExecuteResponseDTO response = aiCommandService.executeCommand(request, httpRequest); + return Result.success(response); + } catch (Exception e) { + log.error("命令执行失败", e); + return Result.success(AiExecuteResponseDTO.builder() + .success(false) + .error(e.getMessage()) + .build()); + } + } + + @Operation(summary = "获取命令执行历史") + @GetMapping("/history") + public Result getCommandHistory( + @Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer page, + @Parameter(description = "每页数量") @RequestParam(defaultValue = "10") Integer size + ) { + return Result.success(aiCommandService.getCommandHistory(page, size)); + } + + @Operation(summary = "获取可用的函数列表") + @GetMapping("/functions") + public Result getAvailableFunctions() { + return Result.success(aiCommandService.getAvailableFunctions()); + } + + @Operation(summary = "撤销命令执行") + @PostMapping("/rollback/{auditId}") + public Result rollbackCommand( + @Parameter(description = "审计ID") @PathVariable String auditId + ) { + aiCommandService.rollbackCommand(auditId); + return Result.success("撤销成功"); + } +} + + + + + + diff --git a/src/main/java/com/youlai/boot/platform/ai/mapper/AiCommandAuditMapper.java b/src/main/java/com/youlai/boot/platform/ai/mapper/AiCommandAuditMapper.java new file mode 100644 index 00000000..c3fe86e5 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/mapper/AiCommandAuditMapper.java @@ -0,0 +1,20 @@ +package com.youlai.boot.platform.ai.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.youlai.boot.platform.ai.model.entity.AiCommandAudit; +import org.apache.ibatis.annotations.Mapper; + +/** + * AI 命令审计 Mapper + * + * @author Ray.Hao + * @since 3.0.0 + */ +@Mapper +public interface AiCommandAuditMapper extends BaseMapper { +} + + + + + diff --git a/src/main/java/com/youlai/boot/platform/ai/model/dto/AiCommandRequestDTO.java b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiCommandRequestDTO.java new file mode 100644 index 00000000..33b8a811 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiCommandRequestDTO.java @@ -0,0 +1,37 @@ +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 AiCommandRequestDTO { + + /** + * 用户输入的自然语言命令 + */ + 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/AiCommandResponseDTO.java b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiCommandResponseDTO.java new file mode 100644 index 00000000..4331b05b --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiCommandResponseDTO.java @@ -0,0 +1,53 @@ +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 AiCommandResponseDTO { + + /** + * 是否成功解析 + */ + 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/dto/AiExecuteRequestDTO.java b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteRequestDTO.java new file mode 100644 index 00000000..f091d99b --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteRequestDTO.java @@ -0,0 +1,36 @@ +package com.youlai.boot.platform.ai.model.dto; + +import lombok.Data; + +/** + * AI 命令执行请求 DTO + * + * @author Ray.Hao + * @since 3.0.0 + */ +@Data +public class AiExecuteRequestDTO { + + /** + * 要执行的函数调用 + */ + private FunctionCallDTO functionCall; + + /** + * 确认模式:auto=自动执行, manual=需要用户确认 + */ + private String confirmMode; + + /** + * 用户确认标志 + */ + private Boolean userConfirmed; + + /** + * 幂等性令牌(防止重复执行) + */ + private String idempotencyKey; +} + + + 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 new file mode 100644 index 00000000..279126d6 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/model/dto/AiExecuteResponseDTO.java @@ -0,0 +1,62 @@ +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 String auditId; + + /** + * 需要用户确认 + */ + private Boolean requiresConfirmation; + + /** + * 确认提示信息 + */ + private String confirmationPrompt; +} + + + diff --git a/src/main/java/com/youlai/boot/platform/ai/model/dto/FunctionCallDTO.java b/src/main/java/com/youlai/boot/platform/ai/model/dto/FunctionCallDTO.java new file mode 100644 index 00000000..8e08eb2b --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/model/dto/FunctionCallDTO.java @@ -0,0 +1,38 @@ +package com.youlai.boot.platform.ai.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import java.util.Map; + +/** + * 函数调用 DTO + * + * @author Ray.Hao + * @since 3.0.0 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FunctionCallDTO { + + /** + * 函数名称 + */ + private String name; + + /** + * 函数描述 + */ + private String description; + + /** + * 参数对象 + */ + private Map arguments; +} + + + diff --git a/src/main/java/com/youlai/boot/platform/ai/model/entity/AiCommandAudit.java b/src/main/java/com/youlai/boot/platform/ai/model/entity/AiCommandAudit.java new file mode 100644 index 00000000..9746f349 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/model/entity/AiCommandAudit.java @@ -0,0 +1,121 @@ +package com.youlai.boot.platform.ai.model.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import java.time.LocalDateTime; + +/** + * AI 命令审计记录 + * + * @author Ray.Hao + * @since 3.0.0 + */ +@Data +@TableName("ai_command_audit") +public class AiCommandAudit { + + /** + * 主键ID + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 用户ID + */ + private Long userId; + + /** + * 用户名 + */ + private String username; + + /** + * 原始命令 + */ + private String originalCommand; + + /** + * 解析后的函数名称 + */ + private String functionName; + + /** + * 函数参数(JSON) + */ + private String functionArguments; + + /** + * 执行状态:pending, success, failed + */ + private String executeStatus; + + /** + * 执行结果(JSON) + */ + private String executeResult; + + /** + * 错误信息 + */ + private String errorMessage; + + /** + * 影响的记录数 + */ + private Integer affectedRows; + + /** + * 是否危险操作 + */ + private Boolean isDangerous; + + /** + * 是否需要确认 + */ + private Boolean requiresConfirmation; + + /** + * 用户是否确认 + */ + private Boolean userConfirmed; + + /** + * 幂等性令牌 + */ + private String idempotencyKey; + + /** + * IP 地址 + */ + private String ipAddress; + + /** + * 用户代理 + */ + private String userAgent; + + /** + * 当前路由 + */ + private String currentRoute; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 执行时间(毫秒) + */ + private Long executionTime; + + /** + * 备注 + */ + private String remark; +} + + + diff --git a/src/main/java/com/youlai/boot/platform/ai/provider/AbstractOpenAiCompatibleProvider.java b/src/main/java/com/youlai/boot/platform/ai/provider/AbstractOpenAiCompatibleProvider.java new file mode 100644 index 00000000..bf7ca322 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/provider/AbstractOpenAiCompatibleProvider.java @@ -0,0 +1,101 @@ +package com.youlai.boot.platform.ai.provider; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.youlai.boot.platform.ai.config.AiProperties; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.TimeUnit; + +/** + * OpenAI 兼容协议的抽象提供商 + * + * 适用于:通义千问、DeepSeek、OpenAI、ChatGLM 等兼容 OpenAI API 的模型 + * + * @author Ray.Hao + */ +@Slf4j +public abstract class AbstractOpenAiCompatibleProvider implements AiProvider { + + protected final AiProperties.ProviderConfig config; + + public AbstractOpenAiCompatibleProvider(AiProperties.ProviderConfig config) { + this.config = config; + } + + @Override + public String call(String systemPrompt, String userPrompt) { + if (!isConfigValid()) { + throw new IllegalStateException(getProviderName() + " 配置无效"); + } + + try { + // 构建请求体(OpenAI 标准格式) + JSONObject requestBody = JSONUtil.createObj() + .set("model", config.getModel()) + .set("messages", JSONUtil.createArray() + .put(JSONUtil.createObj() + .set("role", "system") + .set("content", systemPrompt)) + .put(JSONUtil.createObj() + .set("role", "user") + .set("content", userPrompt)) + ) + .set("temperature", 0.7); + + log.info("📤 调用 {} API: {}/chat/completions", getProviderName(), config.getBaseUrl()); + log.debug("请求参数: {}", requestBody); + + // 发送 HTTP 请求 + HttpResponse response = HttpRequest.post(config.getBaseUrl() + "/chat/completions") + .header("Authorization", "Bearer " + config.getApiKey()) + .header("Content-Type", "application/json") + .body(requestBody.toString()) + .timeout((int) TimeUnit.SECONDS.toMillis(config.getTimeout())) + .execute(); + + // 检查响应状态 + if (!response.isOk()) { + String errorMsg = String.format("%s API 调用失败: HTTP %d - %s", + getProviderName(), response.getStatus(), response.body()); + log.error(errorMsg); + throw new RuntimeException(errorMsg); + } + + // 解析响应 + JSONObject responseJson = JSONUtil.parseObj(response.body()); + String content = responseJson.getByPath("choices[0].message.content", String.class); + + // 记录 Token 使用情况 + JSONObject usage = responseJson.getJSONObject("usage"); + if (usage != null) { + Integer inputTokens = usage.getInt("prompt_tokens"); + Integer outputTokens = usage.getInt("completion_tokens"); + Integer totalTokens = usage.getInt("total_tokens"); + log.info("✅ {} 响应成功,tokens: 输入={}, 输出={}, 总计={}", + getProviderName(), inputTokens, outputTokens, totalTokens); + } + + log.debug("📥 {} 返回内容: {}", getProviderName(), content); + return content; + + } catch (Exception e) { + String errorMsg = String.format("%s API 调用失败: %s", getProviderName(), e.getMessage()); + log.error(errorMsg, e); + throw new RuntimeException(errorMsg, e); + } + } + + @Override + public boolean isConfigValid() { + return config != null + && StrUtil.isNotBlank(config.getApiKey()) + && StrUtil.isNotBlank(config.getBaseUrl()) + && StrUtil.isNotBlank(config.getModel()); + } +} + + diff --git a/src/main/java/com/youlai/boot/platform/ai/provider/AiProvider.java b/src/main/java/com/youlai/boot/platform/ai/provider/AiProvider.java new file mode 100644 index 00000000..d6c23301 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/provider/AiProvider.java @@ -0,0 +1,32 @@ +package com.youlai.boot.platform.ai.provider; + +/** + * AI 提供商接口 + * + * 策略模式:不同提供商实现各自的调用逻辑 + * + * @author Ray.Hao + */ +public interface AiProvider { + + /** + * 调用 AI API + * + * @param systemPrompt 系统提示词 + * @param userPrompt 用户提示词 + * @return AI 响应内容 + */ + String call(String systemPrompt, String userPrompt); + + /** + * 获取提供商名称 + */ + String getProviderName(); + + /** + * 检查配置是否有效 + */ + boolean isConfigValid(); +} + + diff --git a/src/main/java/com/youlai/boot/platform/ai/provider/AiProviderFactory.java b/src/main/java/com/youlai/boot/platform/ai/provider/AiProviderFactory.java new file mode 100644 index 00000000..16b32254 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/provider/AiProviderFactory.java @@ -0,0 +1,51 @@ +package com.youlai.boot.platform.ai.provider; + +import com.youlai.boot.platform.ai.config.AiProperties; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * AI 提供商工厂 + * + * 职责:根据配置获取对应的提供商实例 + * + * @author Ray.Hao + */ +@Component +@RequiredArgsConstructor +public class AiProviderFactory { + + private final AiProperties aiProperties; + + /** + * Spring 自动注入所有 AiProvider 实现类 + * Key: Bean 名称(qwen、deepseek、openai) + * Value: 提供商实例 + */ + private final Map providers; + + /** + * 获取当前配置的提供商 + */ + public AiProvider getCurrentProvider() { + String providerName = aiProperties.getProvider(); + + if (!providers.containsKey(providerName)) { + throw new IllegalStateException("不支持的 AI 提供商: " + providerName + + ",可用提供商: " + providers.keySet()); + } + + AiProvider provider = providers.get(providerName); + + if (!provider.isConfigValid()) { + throw new IllegalStateException(provider.getProviderName() + + " 配置无效,请检查 API Key、Base URL 和 Model 是否配置"); + } + + return provider; + } +} + + diff --git a/src/main/java/com/youlai/boot/platform/ai/provider/impl/DeepSeekProvider.java b/src/main/java/com/youlai/boot/platform/ai/provider/impl/DeepSeekProvider.java new file mode 100644 index 00000000..12b80aaf --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/provider/impl/DeepSeekProvider.java @@ -0,0 +1,25 @@ +package com.youlai.boot.platform.ai.provider.impl; + +import com.youlai.boot.platform.ai.config.AiProperties; +import com.youlai.boot.platform.ai.provider.AbstractOpenAiCompatibleProvider; +import org.springframework.stereotype.Component; + +/** + * DeepSeek 提供商 + * + * @author Ray.Hao + */ +@Component("deepseek") +public class DeepSeekProvider extends AbstractOpenAiCompatibleProvider { + + public DeepSeekProvider(AiProperties aiProperties) { + super(aiProperties.getProviders().get("deepseek")); + } + + @Override + public String getProviderName() { + return config.getDisplayName() != null ? config.getDisplayName() : "DeepSeek"; + } +} + + diff --git a/src/main/java/com/youlai/boot/platform/ai/provider/impl/OpenAiProvider.java b/src/main/java/com/youlai/boot/platform/ai/provider/impl/OpenAiProvider.java new file mode 100644 index 00000000..09613e33 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/provider/impl/OpenAiProvider.java @@ -0,0 +1,30 @@ +package com.youlai.boot.platform.ai.provider.impl; + +import com.youlai.boot.platform.ai.config.AiProperties; +import com.youlai.boot.platform.ai.provider.AbstractOpenAiCompatibleProvider; +import org.springframework.stereotype.Component; + +/** + * OpenAI 提供商(GPT-4、GPT-3.5 等) + * + * 添加新提供商只需: + * 1. 继承 AbstractOpenAiCompatibleProvider + * 2. 实现 getProviderName() + * 3. 在配置文件中添加配置 + * + * @author Ray.Hao + */ +@Component("openai") +public class OpenAiProvider extends AbstractOpenAiCompatibleProvider { + + public OpenAiProvider(AiProperties aiProperties) { + super(aiProperties.getProviders().get("openai")); + } + + @Override + public String getProviderName() { + return config.getDisplayName() != null ? config.getDisplayName() : "OpenAI"; + } +} + + diff --git a/src/main/java/com/youlai/boot/platform/ai/provider/impl/QwenProvider.java b/src/main/java/com/youlai/boot/platform/ai/provider/impl/QwenProvider.java new file mode 100644 index 00000000..5a6b6dcd --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/provider/impl/QwenProvider.java @@ -0,0 +1,25 @@ +package com.youlai.boot.platform.ai.provider.impl; + +import com.youlai.boot.platform.ai.config.AiProperties; +import com.youlai.boot.platform.ai.provider.AbstractOpenAiCompatibleProvider; +import org.springframework.stereotype.Component; + +/** + * 阿里通义千问提供商 + * + * @author Ray.Hao + */ +@Component("qwen") +public class QwenProvider extends AbstractOpenAiCompatibleProvider { + + public QwenProvider(AiProperties aiProperties) { + super(aiProperties.getProviders().get("qwen")); + } + + @Override + public String getProviderName() { + return config.getDisplayName() != null ? config.getDisplayName() : "阿里通义千问"; + } +} + + diff --git a/src/main/java/com/youlai/boot/platform/ai/service/AiCommandService.java b/src/main/java/com/youlai/boot/platform/ai/service/AiCommandService.java new file mode 100644 index 00000000..f6c5b0a8 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/service/AiCommandService.java @@ -0,0 +1,63 @@ +package com.youlai.boot.platform.ai.service; + +import com.youlai.boot.platform.ai.model.dto.*; +import jakarta.servlet.http.HttpServletRequest; + +import java.util.List; +import java.util.Map; + +/** + * AI 命令服务接口 + * + * @author Ray.Hao + * @since 3.0.0 + */ +public interface AiCommandService { + + /** + * 解析自然语言命令 + * + * @param request 命令请求 + * @param httpRequest HTTP 请求 + * @return 解析结果 + */ + AiCommandResponseDTO parseCommand(AiCommandRequestDTO request, HttpServletRequest httpRequest); + + /** + * 执行已解析的命令 + * + * @param request 执行请求 + * @param httpRequest HTTP 请求 + * @return 执行结果 + */ + AiExecuteResponseDTO executeCommand(AiExecuteRequestDTO request, HttpServletRequest httpRequest); + + /** + * 获取命令执行历史 + * + * @param page 页码 + * @param size 每页数量 + * @return 历史记录 + */ + Map getCommandHistory(Integer page, Integer size); + + /** + * 获取可用的函数列表 + * + * @return 函数列表 + */ + List> getAvailableFunctions(); + + /** + * 撤销命令执行 + * + * @param auditId 审计ID + */ + void rollbackCommand(String auditId); +} + + + + + + diff --git a/src/main/java/com/youlai/boot/platform/ai/service/impl/AiCommandServiceImpl.java b/src/main/java/com/youlai/boot/platform/ai/service/impl/AiCommandServiceImpl.java new file mode 100644 index 00000000..346995b2 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/ai/service/impl/AiCommandServiceImpl.java @@ -0,0 +1,262 @@ +package com.youlai.boot.platform.ai.service.impl; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.youlai.boot.platform.ai.config.AiProperties; +import com.youlai.boot.platform.ai.model.dto.*; +import com.youlai.boot.platform.ai.model.entity.AiCommandAudit; +import com.youlai.boot.platform.ai.provider.AiProvider; +import com.youlai.boot.platform.ai.provider.AiProviderFactory; +import com.youlai.boot.platform.ai.service.AiCommandService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * AI 命令服务实现类(重构版) + * + * 重构改进: + * 1. ✅ 使用策略模式 + 工厂模式管理提供商,消除 switch-case + * 2. ✅ 配置映射化,添加新提供商只需配置,无需修改代码 + * 3. ✅ 统一命名为 base-url,符合行业惯例 + * 4. ✅ Service 层直接返回 DTO,不包装 Result(由 Controller 统一处理) + * 5. ✅ 职责清晰,扩展性强 + * + * @author Ray.Hao + * @since 3.0.0 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class AiCommandServiceImpl implements AiCommandService { + + private final AiProperties aiProperties; + private final AiProviderFactory providerFactory; + + // 审计日志存储(简化实现,实际应使用数据库) + private final Map auditStore = new HashMap<>(); + + /** + * 解析自然语言命令 + * + * 注意:直接返回 DTO,不包装 Result + * Controller 负责统一包装成 Result + */ + @Override + public AiCommandResponseDTO parseCommand(AiCommandRequestDTO request, HttpServletRequest httpRequest) { + // 检查 AI 功能是否启用 + if (!aiProperties.getEnabled()) { + throw new IllegalStateException("AI 功能未启用,请在配置文件中设置 ai.enabled=true"); + } + + try { + // 获取当前提供商(自动校验配置) + AiProvider provider = providerFactory.getCurrentProvider(); + + log.info("📤 使用 {} 解析命令: {}", provider.getProviderName(), request.getCommand()); + + // 构建提示词 + String systemPrompt = buildSystemPrompt(); + String userPrompt = buildUserPrompt(request); + + // 调用 AI API + String response = provider.call(systemPrompt, userPrompt); + + // 解析响应 + return parseAiResponse(response); + + } catch (IllegalStateException e) { + // 配置错误,抛出让 Controller 处理 + throw e; + } catch (Exception e) { + log.error("解析命令失败", e); + throw new RuntimeException("解析命令失败: " + e.getMessage(), e); + } + } + + /** + * 执行已解析的命令 + */ + @Override + public AiExecuteResponseDTO executeCommand(AiExecuteRequestDTO request, HttpServletRequest httpRequest) { + // TODO: 实现命令执行逻辑 + throw new UnsupportedOperationException("待实现"); + } + + /** + * 获取命令执行历史 + */ + @Override + public Map getCommandHistory(Integer page, Integer size) { + List allAudits = new ArrayList<>(auditStore.values()); + allAudits.sort(Comparator.comparing(AiCommandAudit::getCreateTime).reversed()); + + int total = allAudits.size(); + int start = (page - 1) * size; + int end = Math.min(start + size, total); + + List pageData = start < total ? allAudits.subList(start, end) : new ArrayList<>(); + + Map result = new HashMap<>(); + result.put("list", pageData); + result.put("total", total); + result.put("page", page); + result.put("size", size); + + return result; + } + + /** + * 获取可用的函数列表 + */ + @Override + public List> getAvailableFunctions() { + List> functions = new ArrayList<>(); + + // 用户管理函数 + functions.add(createFunctionDef( + "deleteUser", + "删除用户", + Map.of("name", "String - 用户姓名", "id", "Long - 用户ID(可选)") + )); + + functions.add(createFunctionDef( + "updateUser", + "更新用户信息", + Map.of("id", "Long - 用户ID", "nickname", "String - 昵称", "status", "Integer - 状态") + )); + + functions.add(createFunctionDef( + "queryUsers", + "查询用户列表", + Map.of("name", "String - 姓名(可选)", "status", "Integer - 状态(可选)") + )); + + // 角色管理函数 + functions.add(createFunctionDef( + "assignRole", + "分配角色给用户", + Map.of("userId", "Long - 用户ID", "roleIds", "List - 角色ID列表") + )); + + return functions; + } + + /** + * 撤销命令执行 + */ + @Override + public void rollbackCommand(String auditId) { + AiCommandAudit audit = auditStore.get(auditId); + if (audit == null) { + throw new RuntimeException("审计记录不存在"); + } + + if (!"success".equals(audit.getExecuteStatus())) { + throw new RuntimeException("只能撤销成功执行的命令"); + } + + // TODO: 实现具体的回滚逻辑 + log.info("撤销命令执行: auditId={}, function={}", auditId, audit.getFunctionName()); + throw new UnsupportedOperationException("回滚功能尚未实现"); + } + + // ==================== 私有方法 ==================== + + /** + * 构建系统提示词(包含可用函数定义) + */ + private String buildSystemPrompt() { + return """ + 你是一个专业的命令解析助手。你的任务是将用户的自然语言命令转换为结构化的函数调用。 + + 可用函数: + 1. queryUsers - 查询用户列表 + 参数:keywords(搜索关键字), status(状态), deptId(部门ID) + + 2. deleteUser - 删除用户 + 参数:userId(用户ID) + + 3. updateUser - 更新用户信息 + 参数:userId(用户ID), nickname(昵称), mobile(手机号) + + 请将命令解析为以下 JSON 格式: + { + "functionCalls": [ + { + "function": "函数名", + "parameters": { "参数名": "参数值" }, + "description": "操作说明" + } + ] + } + """; + } + + /** + * 构建用户提示词 + */ + private String buildUserPrompt(AiCommandRequestDTO request) { + return "请解析以下命令:" + request.getCommand(); + } + + /** + * 解析 AI 响应 + */ + private AiCommandResponseDTO parseAiResponse(String response) { + try { + // 提取 JSON + int jsonStart = response.indexOf("{"); + int jsonEnd = response.lastIndexOf("}") + 1; + + if (jsonStart == -1 || jsonEnd == 0) { + throw new IllegalArgumentException("AI 返回格式错误:未找到 JSON"); + } + + String jsonStr = response.substring(jsonStart, jsonEnd); + JSONObject json = JSONUtil.parseObj(jsonStr); + + // 解析函数调用列表 + List functionCalls = new ArrayList<>(); + JSONArray callsArray = json.getJSONArray("functionCalls"); + + if (callsArray != null) { + for (int i = 0; i < callsArray.size(); i++) { + JSONObject call = callsArray.getJSONObject(i); + functionCalls.add(FunctionCallDTO.builder() + .name(call.getStr("function")) + .arguments(call.getJSONObject("parameters") != null ? + call.getJSONObject("parameters").toBean(Map.class) : new HashMap<>()) + .description(call.getStr("description")) + .build()); + } + } + + return AiCommandResponseDTO.builder() + .success(true) + .functionCalls(functionCalls) + .rawResponse(response) + .build(); + + } catch (Exception e) { + log.error("解析 AI 响应失败", e); + throw new RuntimeException("解析响应失败: " + e.getMessage(), e); + } + } + + /** + * 创建函数定义 + */ + private Map createFunctionDef(String name, String description, Map parameters) { + Map func = new HashMap<>(); + func.put("name", name); + func.put("description", description); + func.put("parameters", parameters); + return func; + } +} + diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 412f9494..da7979d9 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -6,8 +6,8 @@ spring: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://www.youlai.tech:3306/youlai_boot?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true - username: youlai - password: 123456 + username: root + password: Youlai@2025 data: redis: database: 0 @@ -74,7 +74,7 @@ mybatis-plus: security: session: type: jwt # 会话方式 jwt/redis-token - access-token-time-to-live: 7200 # 访问令牌 有效期(单位:秒),默认 2 小时,-1 表示永不过期 + access-token-time-to-live: 7200 # 访问令牌 有效期(单位:秒),默认 2 小时,-1 表示永不过期 refresh-token-time-to-live: 604800 # 刷新令牌有效期(单位:秒),默认 7 天,-1 表示永不过期 jwt: secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789 # JWT密钥(HS256算法至少32字符) @@ -82,12 +82,12 @@ security: 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/logout # 开放退出登录 + - /api/v1/auth/login/** # 登录接口(账号密码登录、手机验证码登录和微信登录) + - /api/v1/auth/captcha # 验证码获取接口 + - /api/v1/auth/refresh-token # 刷新令牌接口 + - /api/v1/auth/logout # 开放退出登录 - /api/v1/auth/wx/miniapp/code-login # 微信小程序code登陆 - - /ws/** # WebSocket接口 + - /ws/** # WebSocket接口 # 非安全端点路径,完全绕过 Spring Security 的安全控制 unsecured-urls: - ${springdoc.swagger-ui.path} @@ -153,7 +153,7 @@ springdoc: api-docs: path: /v3/api-docs group-configs: - - group: '系统管理' + - group: "系统管理" paths-to-match: "/**" packages-to-scan: - com.youlai.boot.auth.controller @@ -165,9 +165,9 @@ springdoc: # knife4j 接口文档配置 knife4j: # 是否开启 Knife4j 增强功能 - enable: true # 设置为 true 表示开启增强功能 + enable: true # 设置为 true 表示开启增强功能 # 生产环境配置 - production: false # 设置为 true 表示在生产环境中不显示文档,为 false 表示显示文档(通常在开发环境中使用) + production: false # 设置为 true 表示在生产环境中不显示文档,为 false 表示显示文档(通常在开发环境中使用) setting: language: zh_cn @@ -223,3 +223,66 @@ wx: miniapp: app-id: xxxxxx app-secret: xxxxxx + +# ==================== AI 命令系统配置 ==================== +ai: + # 是否启用 AI 功能 + enabled: false + + # 当前使用的提供商:qwen、deepseek、openai + provider: qwen + + # 所有提供商配置(统一管理,扩展性强) + providers: + # 阿里通义千问(推荐:有免费额度) + qwen: + # API Key(https://bailian.console.aliyun.com/ 获取) + api-key: ${QWEN_API_KEY:sk-c2941d05bf2f411ca80424fcxxxxxxxx} + + # Base URL(OpenAI 兼容端点) + base-url: https://dashscope.aliyuncs.com/compatible-mode/v1 + + # 模型:qwen-plus(推荐)、qwen-turbo、qwen-max、qwen-long + model: qwen-plus + + # 显示名称 + display-name: 阿里通义千问 + + # 超时时间(秒) + timeout: 30 + + # DeepSeek + deepseek: + api-key: ${DEEPSEEK_API_KEY:} + base-url: https://api.deepseek.com/v1 + model: deepseek-chat + display-name: DeepSeek + timeout: 30 + + # OpenAI(添加新提供商只需配置,无需修改代码) + openai: + api-key: ${OPENAI_API_KEY:} + base-url: https://api.openai.com/v1 + model: gpt-4 + display-name: OpenAI GPT-4 + timeout: 60 + + # 安全配置 + security: + enable-audit: true + dangerous-operations-confirm: true + function-whitelist: + - deleteUser + - updateUser + - queryUsers + - assignRole + sensitive-params: + - password + - idCard + - bankCard + - token + + # 限流配置 + rate-limit: + max-executions-per-minute: 10 + max-executions-per-day: 100 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 53695a35..def022f8 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -218,4 +218,68 @@ captcha: wx: miniapp: app-id: xxxxxx - app-secret: xxxxxx \ No newline at end of file + app-secret: xxxxxx + + +# ==================== AI 命令系统配置 ==================== +ai: + # 是否启用 AI 功能 + enabled: false + + # 当前使用的提供商:qwen、deepseek、openai + provider: qwen + + # 所有提供商配置(统一管理,扩展性强) + providers: + # 阿里通义千问(推荐:有免费额度) + qwen: + # API Key(https://bailian.console.aliyun.com/ 获取) + api-key: ${QWEN_API_KEY:sk-c2941d05bf2f411ca80424fcxxxxxxxx} + + # Base URL(OpenAI 兼容端点) + base-url: https://dashscope.aliyuncs.com/compatible-mode/v1 + + # 模型:qwen-plus(推荐)、qwen-turbo、qwen-max、qwen-long + model: qwen-plus + + # 显示名称 + display-name: 阿里通义千问 + + # 超时时间(秒) + timeout: 30 + + # DeepSeek + deepseek: + api-key: ${DEEPSEEK_API_KEY:} + base-url: https://api.deepseek.com/v1 + model: deepseek-chat + display-name: DeepSeek + timeout: 30 + + # OpenAI(添加新提供商只需配置,无需修改代码) + openai: + api-key: ${OPENAI_API_KEY:} + base-url: https://api.openai.com/v1 + model: gpt-4 + display-name: OpenAI GPT-4 + timeout: 60 + + # 安全配置 + security: + enable-audit: true + dangerous-operations-confirm: true + function-whitelist: + - deleteUser + - updateUser + - queryUsers + - assignRole + sensitive-params: + - password + - idCard + - bankCard + - token + + # 限流配置 + rate-limit: + max-executions-per-minute: 10 + max-executions-per-day: 100