From b46759dc730136ff620a90bd4114f4a70fdc05fa Mon Sep 17 00:00:00 2001 From: pony <1356137040@qq.com> Date: Thu, 22 Jan 2026 17:33:28 +0800 Subject: [PATCH] v2 --- .gitignore | 77 +- db/init.sql | 44 +- hertz_springboot_ui/README.md | 5 - hertz_springboot_ui/public/index1.png | Bin 13364 -> 0 bytes .../src/views/admin/system/Menu.vue | 269 ------ .../src/views/portal/About.vue | 8 - hertz_springboot/pom.xml => pom.xml | 54 +- .../main/java/com/hertz/HertzApplication.java | 0 .../com/hertz/ai/controller/AiController.java | 84 ++ .../java/com/hertz/ai/dto/ChatRequest.java | 10 + .../com/hertz/ai/entity/Conversation.java | 18 + .../java/com/hertz/ai/entity/Message.java | 21 + .../hertz/ai/mapper/ConversationMapper.java | 9 + .../com/hertz/ai/mapper/MessageMapper.java | 9 + .../java/com/hertz/ai/service/AiService.java | 8 + .../hertz/ai/service/ConversationService.java | 16 + .../hertz/ai/service/impl/AiServiceImpl.java | 71 ++ .../service/impl/ConversationServiceImpl.java | 106 +++ .../com/hertz/common/api/ApiResponse.java | 0 .../common/exception/BusinessException.java | 0 .../exception/GlobalExceptionHandler.java | 1 + .../hertz/common/filter/RequestLogFilter.java | 35 + .../monitor/controller/MonitorController.java | 22 + .../com/hertz/monitor/dto/MonitorDto.java | 89 ++ .../hertz/monitor/service/MonitorService.java | 7 + .../service/impl/MonitorServiceImpl.java | 165 ++++ .../security/CustomUserDetailsService.java | 33 + .../com/hertz/security/JwtAuthFilter.java | 6 +- .../java/com/hertz/security/JwtService.java | 0 .../com/hertz/security/SecurityConfig.java | 2 + .../com/hertz/security/SecurityUtils.java | 0 .../system/config/MybatisPlusConfig.java | 0 .../com/hertz/system/config/WebMvcConfig.java | 0 .../system/controller/AuthController.java | 8 +- .../system/controller/CaptchaController.java | 25 + .../system/controller/FileController.java | 0 .../system/controller/MenuController.java | 0 .../system/controller/RoleController.java | 0 .../system/controller/UserController.java | 7 + .../java/com/hertz/system/dto/AuthDtos.java | 14 +- .../java/com/hertz/system/dto/MenuDto.java | 0 .../java/com/hertz/system/entity/SysMenu.java | 0 .../java/com/hertz/system/entity/SysRole.java | 0 .../com/hertz/system/entity/SysRoleMenu.java | 0 .../java/com/hertz/system/entity/SysUser.java | 0 .../com/hertz/system/entity/SysUserRole.java | 0 .../hertz/system/mapper/SysMenuMapper.java | 0 .../hertz/system/mapper/SysRoleMapper.java | 0 .../system/mapper/SysRoleMenuMapper.java | 0 .../hertz/system/mapper/SysUserMapper.java | 0 .../system/mapper/SysUserRoleMapper.java | 0 .../hertz/system/service/AuthzService.java | 0 .../hertz/system/service/CaptchaService.java | 8 + .../com/hertz/system/service/MenuService.java | 0 .../com/hertz/system/service/RoleService.java | 0 .../com/hertz/system/service/UserService.java | 2 + .../system/service/impl/AuthzServiceImpl.java | 0 .../service/impl/CaptchaServiceImpl.java | 54 ++ .../system/service/impl/MenuServiceImpl.java | 0 .../system/service/impl/RoleServiceImpl.java | 0 .../system/service/impl/UserServiceImpl.java | 10 + .../main/resources/application.yml | 20 + src/main/resources/schema/ai_schema.sql | 26 + src/main/resources/schema/monitor_schema.sql | 14 + .../main/resources/schema}/schema.sql | 2 +- .../java/com/hertz/HertzApplicationTests.java | 0 .../.env.development => ui/.env.dev | 0 {hertz_springboot_ui => ui}/index.html | 2 +- {hertz_springboot_ui => ui}/jsconfig.json | 0 {hertz_springboot_ui => ui}/package-lock.json | 13 + {hertz_springboot_ui => ui}/package.json | 1 + .../public/index.png => ui/public/favicon.png | Bin {hertz_springboot_ui => ui}/public/logo.png | Bin {hertz_springboot_ui => ui}/src/App.vue | 0 {hertz_springboot_ui => ui}/src/api/auth.js | 5 + ui/src/api/chat.js | 39 + {hertz_springboot_ui => ui}/src/api/common.js | 0 {hertz_springboot_ui => ui}/src/api/http.js | 8 +- ui/src/api/monitor.js | 6 + {hertz_springboot_ui => ui}/src/api/system.js | 4 + ui/src/assets/img/default_avatar.png | Bin 0 -> 6187 bytes .../src/assets/img/profile_bg.jpg | Bin .../src/assets/style.css | 75 +- ui/src/components/ContentPage.vue | 66 ++ .../src/components/Error.vue | 18 +- .../src/layouts/AdminLayout.vue | 4 +- .../src/layouts/PortalLayout.vue | 16 +- {hertz_springboot_ui => ui}/src/main.js | 0 .../src/router/index.js | 49 +- .../src/router/setupGuards.js | 0 .../src/router/utils.js | 0 .../src/stores/auth.js | 0 .../src/stores/menu.js | 0 .../src/views/Login.vue | 36 +- .../src/views/Profile.vue | 0 .../src/views/Register.vue | 32 +- .../src/views/admin/Dashboard.vue | 12 +- ui/src/views/admin/system/Menu.vue | 258 ++++++ .../src/views/admin/system/Role.vue | 39 +- .../src/views/admin/system/User.vue | 75 +- ui/src/views/portal/About.vue | 120 +++ ui/src/views/portal/Chat.vue | 838 ++++++++++++++++++ .../src/views/portal/Home.vue | 0 ui/src/views/portal/Monitor.vue | 287 ++++++ {hertz_springboot_ui => ui}/vite.config.js | 0 105 files changed, 2929 insertions(+), 433 deletions(-) delete mode 100644 hertz_springboot_ui/README.md delete mode 100644 hertz_springboot_ui/public/index1.png delete mode 100644 hertz_springboot_ui/src/views/admin/system/Menu.vue delete mode 100644 hertz_springboot_ui/src/views/portal/About.vue rename hertz_springboot/pom.xml => pom.xml (69%) rename {hertz_springboot/src => src}/main/java/com/hertz/HertzApplication.java (100%) create mode 100644 src/main/java/com/hertz/ai/controller/AiController.java create mode 100644 src/main/java/com/hertz/ai/dto/ChatRequest.java create mode 100644 src/main/java/com/hertz/ai/entity/Conversation.java create mode 100644 src/main/java/com/hertz/ai/entity/Message.java create mode 100644 src/main/java/com/hertz/ai/mapper/ConversationMapper.java create mode 100644 src/main/java/com/hertz/ai/mapper/MessageMapper.java create mode 100644 src/main/java/com/hertz/ai/service/AiService.java create mode 100644 src/main/java/com/hertz/ai/service/ConversationService.java create mode 100644 src/main/java/com/hertz/ai/service/impl/AiServiceImpl.java create mode 100644 src/main/java/com/hertz/ai/service/impl/ConversationServiceImpl.java rename {hertz_springboot/src => src}/main/java/com/hertz/common/api/ApiResponse.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/common/exception/BusinessException.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/common/exception/GlobalExceptionHandler.java (97%) create mode 100644 src/main/java/com/hertz/common/filter/RequestLogFilter.java create mode 100644 src/main/java/com/hertz/monitor/controller/MonitorController.java create mode 100644 src/main/java/com/hertz/monitor/dto/MonitorDto.java create mode 100644 src/main/java/com/hertz/monitor/service/MonitorService.java create mode 100644 src/main/java/com/hertz/monitor/service/impl/MonitorServiceImpl.java create mode 100644 src/main/java/com/hertz/security/CustomUserDetailsService.java rename {hertz_springboot/src => src}/main/java/com/hertz/security/JwtAuthFilter.java (88%) rename {hertz_springboot/src => src}/main/java/com/hertz/security/JwtService.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/security/SecurityConfig.java (95%) rename {hertz_springboot/src => src}/main/java/com/hertz/security/SecurityUtils.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/config/MybatisPlusConfig.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/config/WebMvcConfig.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/controller/AuthController.java (92%) create mode 100644 src/main/java/com/hertz/system/controller/CaptchaController.java rename {hertz_springboot/src => src}/main/java/com/hertz/system/controller/FileController.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/controller/MenuController.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/controller/RoleController.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/controller/UserController.java (95%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/dto/AuthDtos.java (76%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/dto/MenuDto.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/entity/SysMenu.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/entity/SysRole.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/entity/SysRoleMenu.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/entity/SysUser.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/entity/SysUserRole.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/mapper/SysMenuMapper.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/mapper/SysRoleMapper.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/mapper/SysRoleMenuMapper.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/mapper/SysUserMapper.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/mapper/SysUserRoleMapper.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/service/AuthzService.java (100%) create mode 100644 src/main/java/com/hertz/system/service/CaptchaService.java rename {hertz_springboot/src => src}/main/java/com/hertz/system/service/MenuService.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/service/RoleService.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/service/UserService.java (95%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/service/impl/AuthzServiceImpl.java (100%) create mode 100644 src/main/java/com/hertz/system/service/impl/CaptchaServiceImpl.java rename {hertz_springboot/src => src}/main/java/com/hertz/system/service/impl/MenuServiceImpl.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/service/impl/RoleServiceImpl.java (100%) rename {hertz_springboot/src => src}/main/java/com/hertz/system/service/impl/UserServiceImpl.java (96%) rename {hertz_springboot/src => src}/main/resources/application.yml (64%) create mode 100644 src/main/resources/schema/ai_schema.sql create mode 100644 src/main/resources/schema/monitor_schema.sql rename {hertz_springboot/src/main/resources => src/main/resources/schema}/schema.sql (99%) rename {hertz_springboot/src => src}/test/java/com/hertz/HertzApplicationTests.java (100%) rename hertz_springboot_ui/.env.development => ui/.env.dev (100%) rename {hertz_springboot_ui => ui}/index.html (82%) rename {hertz_springboot_ui => ui}/jsconfig.json (100%) rename {hertz_springboot_ui => ui}/package-lock.json (99%) rename {hertz_springboot_ui => ui}/package.json (94%) rename hertz_springboot_ui/public/index.png => ui/public/favicon.png (100%) rename {hertz_springboot_ui => ui}/public/logo.png (100%) rename {hertz_springboot_ui => ui}/src/App.vue (100%) rename {hertz_springboot_ui => ui}/src/api/auth.js (82%) create mode 100644 ui/src/api/chat.js rename {hertz_springboot_ui => ui}/src/api/common.js (100%) rename {hertz_springboot_ui => ui}/src/api/http.js (73%) create mode 100644 ui/src/api/monitor.js rename {hertz_springboot_ui => ui}/src/api/system.js (95%) create mode 100644 ui/src/assets/img/default_avatar.png rename {hertz_springboot_ui => ui}/src/assets/img/profile_bg.jpg (100%) rename {hertz_springboot_ui => ui}/src/assets/style.css (76%) create mode 100644 ui/src/components/ContentPage.vue rename {hertz_springboot_ui => ui}/src/components/Error.vue (93%) rename {hertz_springboot_ui => ui}/src/layouts/AdminLayout.vue (97%) rename {hertz_springboot_ui => ui}/src/layouts/PortalLayout.vue (91%) rename {hertz_springboot_ui => ui}/src/main.js (100%) rename {hertz_springboot_ui => ui}/src/router/index.js (52%) rename {hertz_springboot_ui => ui}/src/router/setupGuards.js (100%) rename {hertz_springboot_ui => ui}/src/router/utils.js (100%) rename {hertz_springboot_ui => ui}/src/stores/auth.js (100%) rename {hertz_springboot_ui => ui}/src/stores/menu.js (100%) rename {hertz_springboot_ui => ui}/src/views/Login.vue (72%) rename {hertz_springboot_ui => ui}/src/views/Profile.vue (100%) rename {hertz_springboot_ui => ui}/src/views/Register.vue (75%) rename {hertz_springboot_ui => ui}/src/views/admin/Dashboard.vue (98%) create mode 100644 ui/src/views/admin/system/Menu.vue rename {hertz_springboot_ui => ui}/src/views/admin/system/Role.vue (88%) rename {hertz_springboot_ui => ui}/src/views/admin/system/User.vue (83%) create mode 100644 ui/src/views/portal/About.vue create mode 100644 ui/src/views/portal/Chat.vue rename {hertz_springboot_ui => ui}/src/views/portal/Home.vue (100%) create mode 100644 ui/src/views/portal/Monitor.vue rename {hertz_springboot_ui => ui}/vite.config.js (100%) diff --git a/.gitignore b/.gitignore index f8d850d..6f7b3f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,56 +1,45 @@ -# --- Common --- -.DS_Store -Thumbs.db -Desktop.ini -*.log -*.tmp -*.bak -*.swp - -# --- IDEs & Editors --- +# IDEs .idea/ .vscode/ *.iml -*.ipr *.iws +*.ipr .classpath .project .settings/ .factorypath -# --- Java / Maven (Backend) --- +# OS +.DS_Store +Thumbs.db +ehthumbs.db +Desktop.ini + +# Maven target/ -*.class -*.jar -*.war -*.ear -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar +.mvn/ +mvnw +mvnw.cmd +*.log -# --- Node / Vue (Frontend) --- -node_modules/ -dist/ -dist-ssr/ -coverage/ -*.local -.npm/ -# Logs -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* +# Frontend (Vue/Vite) +ui/node_modules/ +ui/dist/ +ui/npm-debug.log* +ui/yarn-debug.log* +ui/yarn-error.log* +ui/pnpm-debug.log* +ui/.env.local +ui/.env.*.local +ui/.DS_Store +ui/coverage/ +ui/.nyc_output/ -# --- Application Specific --- -# Uploaded files -uploads/ -# Ignore local environment override files if any -.env.local -.env.development.local -.env.test.local -.env.production.local +# Application Logs +logs/ +*.log + +# Temp files +*.tmp +*.bak +*.swp diff --git a/db/init.sql b/db/init.sql index 55a97b0..ffc397b 100644 --- a/db/init.sql +++ b/db/init.sql @@ -11,12 +11,48 @@ Target Server Version : 80040 (8.0.40) File Encoding : 65001 - Date: 20/01/2026 15:33:14 + Date: 22/01/2026 14:58:04 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; +-- ---------------------------- +-- Table structure for ai_conversations +-- ---------------------------- +DROP TABLE IF EXISTS `ai_conversations`; +CREATE TABLE `ai_conversations` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `user_id` bigint NOT NULL COMMENT '用户ID', + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '对话标题', + `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_user_id`(`user_id` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 38 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '对话记录表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of ai_conversations +-- ---------------------------- + +-- ---------------------------- +-- Table structure for ai_messages +-- ---------------------------- +DROP TABLE IF EXISTS `ai_messages`; +CREATE TABLE `ai_messages` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `conversation_id` bigint NOT NULL COMMENT '所属对话ID', + `role` enum('user','assistant') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '消息角色', + `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '消息内容', + `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_conversation_id`(`conversation_id` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 159 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '对话消息表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of ai_messages +-- ---------------------------- + -- ---------------------------- -- Table structure for sys_menu -- ---------------------------- @@ -107,13 +143,13 @@ CREATE TABLE `sys_user` ( PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `uk_username`(`username` ASC) USING BTREE, INDEX `idx_status`(`status` ASC) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '系统用户表' ROW_FORMAT = Dynamic; +) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '系统用户表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of sys_user -- ---------------------------- -INSERT INTO `sys_user` VALUES (1, 'hertz', '$2a$10$Gker6.ggCxG3wfZ13rE/Eu7aDnB.DX2JmP6h6vct30RTtBr9.q5Pq', '赫兹', '', '18888888888', 'hertz@hertz.com', 1, 1, '2026-01-19 17:30:21', '2026-01-20 15:33:01'); -INSERT INTO `sys_user` VALUES (2, 'demo', '$2a$10$PSIz9pWXAwXfB32HWSxTjeGhVi0bixsSKxzeX8YAdKnRRXPxJC3Xe', 'demo', '', '13888888888', 'demo@hertz.com', 1, 1, '2026-01-19 17:30:21', '2026-01-20 15:33:04'); +INSERT INTO `sys_user` VALUES (1, 'hertz', '$2a$10$Gker6.ggCxG3wfZ13rE/Eu7aDnB.DX2JmP6h6vct30RTtBr9.q5Pq', '赫兹', '/uploads/avatar/2026/01/20/56b8d363d1c743d6afb749faa0b4f5cb.png', '18888888888', 'hertz@hertz.com', 1, 1, '2026-01-19 17:30:21', '2026-01-22 14:29:46'); +INSERT INTO `sys_user` VALUES (2, 'demo', '$2a$10$PSIz9pWXAwXfB32HWSxTjeGhVi0bixsSKxzeX8YAdKnRRXPxJC3Xe', 'demo', '/uploads/avatar/2026/01/20/e19931fe65664b1ba31bf3db85cc6519.jpg', '13888888888', 'demo@hertz.com', 1, 1, '2026-01-19 17:30:21', '2026-01-20 17:57:47'); -- ---------------------------- -- Table structure for sys_user_role diff --git a/hertz_springboot_ui/README.md b/hertz_springboot_ui/README.md deleted file mode 100644 index 33895ab..0000000 --- a/hertz_springboot_ui/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Vue 3 + TypeScript + Vite - -This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` - diff --git a/hertz_springboot_ui/src/views/portal/About.vue b/hertz_springboot_ui/src/views/portal/About.vue deleted file mode 100644 index 901b1b5..0000000 --- a/hertz_springboot_ui/src/views/portal/About.vue +++ /dev/null @@ -1,8 +0,0 @@ - - - \ No newline at end of file diff --git a/hertz_springboot/pom.xml b/pom.xml similarity index 69% rename from hertz_springboot/pom.xml rename to pom.xml index 6c61e00..cb86e90 100644 --- a/hertz_springboot/pom.xml +++ b/pom.xml @@ -12,9 +12,9 @@ com.hertz - hertz-springboot-backend + hertz-springboot 0.0.1-SNAPSHOT - hertz-springboot-backend + hertz-springboot Hertz 权限管理系统后端 @@ -22,8 +22,21 @@ 21 3.5.8 0.12.6 + 1.0.0-M5 + + + + org.springframework.ai + spring-ai-bom + ${spring-ai.version} + pom + import + + + + org.springframework.boot @@ -38,6 +51,15 @@ spring-boot-starter-security + + org.springframework.ai + spring-ai-ollama-spring-boot-starter + + + org.springframework.boot + spring-boot-starter-actuator + + com.baomidou mybatis-plus-spring-boot3-starter @@ -74,6 +96,23 @@ runtime + + org.springframework.boot + spring-boot-starter-data-redis + + + cn.hutool + hutool-all + 5.8.25 + + + + + com.github.oshi + oshi-core + 6.6.5 + + org.springframework.boot spring-boot-starter-test @@ -86,6 +125,17 @@ + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + diff --git a/hertz_springboot/src/main/java/com/hertz/HertzApplication.java b/src/main/java/com/hertz/HertzApplication.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/HertzApplication.java rename to src/main/java/com/hertz/HertzApplication.java diff --git a/src/main/java/com/hertz/ai/controller/AiController.java b/src/main/java/com/hertz/ai/controller/AiController.java new file mode 100644 index 0000000..f96d2e5 --- /dev/null +++ b/src/main/java/com/hertz/ai/controller/AiController.java @@ -0,0 +1,84 @@ +package com.hertz.ai.controller; + +import com.hertz.ai.dto.ChatRequest; +import com.hertz.ai.entity.Conversation; +import com.hertz.ai.entity.Message; +import com.hertz.ai.service.AiService; +import com.hertz.ai.service.ConversationService; +import com.hertz.common.api.ApiResponse; +import com.hertz.security.SecurityUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; + +import java.util.List; + +@RestController +@RequestMapping("/api/ai") +@RequiredArgsConstructor +public class AiController { + + private final AiService aiService; + private final ConversationService conversationService; + + @PostMapping("/chat") + public ApiResponse chat(@RequestBody ChatRequest request) { + if (request.getConversationId() != null) { + conversationService.saveMessage(request.getConversationId(), "user", request.getMessage()); + } + String response = aiService.chat(request.getMessage(), request.getTemperature()); + if (request.getConversationId() != null) { + conversationService.saveMessage(request.getConversationId(), "assistant", response); + } + return ApiResponse.ok(response); + } + + @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux streamChat(@RequestBody ChatRequest request) { + // Frontend handles message persistence + return aiService.streamChat(request.getMessage(), request.getTemperature()); + } + + @PostMapping("/conversations") + public ApiResponse createConversation(@RequestBody Conversation conversation) { + Long userId = SecurityUtils.getCurrentUserId(); + return ApiResponse.ok(conversationService.createConversation(conversation.getTitle(), userId)); + } + + @GetMapping("/conversations") + public ApiResponse> listConversations() { + Long userId = SecurityUtils.getCurrentUserId(); + return ApiResponse.ok(conversationService.getConversations(userId)); + } + + @DeleteMapping("/conversations/{id}") + public ApiResponse deleteConversation(@PathVariable Long id) { + Long userId = SecurityUtils.getCurrentUserId(); + conversationService.deleteConversation(id, userId); + return ApiResponse.ok(); + } + + @PutMapping("/conversations/{id}") + public ApiResponse updateConversation(@PathVariable Long id, @RequestBody Conversation conversation) { + Long userId = SecurityUtils.getCurrentUserId(); + conversationService.updateConversationTitle(id, conversation.getTitle(), userId); + return ApiResponse.ok(); + } + + @GetMapping("/conversations/search") + public ApiResponse> searchConversations(@RequestParam String query) { + Long userId = SecurityUtils.getCurrentUserId(); + return ApiResponse.ok(conversationService.searchConversations(query, userId)); + } + + @PostMapping("/conversations/{id}/messages") + public ApiResponse saveMessage(@PathVariable Long id, @RequestBody Message message) { + return ApiResponse.ok(conversationService.saveMessage(id, message.getRole(), message.getContent())); + } + + @GetMapping("/conversations/{id}/messages") + public ApiResponse> getMessages(@PathVariable Long id) { + return ApiResponse.ok(conversationService.getMessages(id)); + } +} diff --git a/src/main/java/com/hertz/ai/dto/ChatRequest.java b/src/main/java/com/hertz/ai/dto/ChatRequest.java new file mode 100644 index 0000000..3567ed9 --- /dev/null +++ b/src/main/java/com/hertz/ai/dto/ChatRequest.java @@ -0,0 +1,10 @@ +package com.hertz.ai.dto; + +import lombok.Data; + +@Data +public class ChatRequest { + private String message; + private Double temperature; + private Long conversationId; +} diff --git a/src/main/java/com/hertz/ai/entity/Conversation.java b/src/main/java/com/hertz/ai/entity/Conversation.java new file mode 100644 index 0000000..c26cae3 --- /dev/null +++ b/src/main/java/com/hertz/ai/entity/Conversation.java @@ -0,0 +1,18 @@ +package com.hertz.ai.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.time.LocalDateTime; +import lombok.Data; + +@Data +@TableName("ai_conversations") +public class Conversation { + @TableId(type = IdType.AUTO) + private Long id; + private Long userId; + private String title; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; +} diff --git a/src/main/java/com/hertz/ai/entity/Message.java b/src/main/java/com/hertz/ai/entity/Message.java new file mode 100644 index 0000000..6417e41 --- /dev/null +++ b/src/main/java/com/hertz/ai/entity/Message.java @@ -0,0 +1,21 @@ +package com.hertz.ai.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.time.LocalDateTime; +import lombok.Data; + +@Data +@TableName("ai_messages") +public class Message { + @TableId(type = IdType.AUTO) + private Long id; + private Long conversationId; + /** + * user or assistant + */ + private String role; + private String content; + private LocalDateTime createdAt; +} diff --git a/src/main/java/com/hertz/ai/mapper/ConversationMapper.java b/src/main/java/com/hertz/ai/mapper/ConversationMapper.java new file mode 100644 index 0000000..fe2f731 --- /dev/null +++ b/src/main/java/com/hertz/ai/mapper/ConversationMapper.java @@ -0,0 +1,9 @@ +package com.hertz.ai.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.hertz.ai.entity.Conversation; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ConversationMapper extends BaseMapper { +} diff --git a/src/main/java/com/hertz/ai/mapper/MessageMapper.java b/src/main/java/com/hertz/ai/mapper/MessageMapper.java new file mode 100644 index 0000000..c0d36ce --- /dev/null +++ b/src/main/java/com/hertz/ai/mapper/MessageMapper.java @@ -0,0 +1,9 @@ +package com.hertz.ai.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.hertz.ai.entity.Message; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface MessageMapper extends BaseMapper { +} diff --git a/src/main/java/com/hertz/ai/service/AiService.java b/src/main/java/com/hertz/ai/service/AiService.java new file mode 100644 index 0000000..f2a9d69 --- /dev/null +++ b/src/main/java/com/hertz/ai/service/AiService.java @@ -0,0 +1,8 @@ +package com.hertz.ai.service; + +import reactor.core.publisher.Flux; + +public interface AiService { + String chat(String message, Double temperature); + Flux streamChat(String message, Double temperature); +} diff --git a/src/main/java/com/hertz/ai/service/ConversationService.java b/src/main/java/com/hertz/ai/service/ConversationService.java new file mode 100644 index 0000000..1bc24c3 --- /dev/null +++ b/src/main/java/com/hertz/ai/service/ConversationService.java @@ -0,0 +1,16 @@ +package com.hertz.ai.service; + +import com.hertz.ai.entity.Conversation; +import com.hertz.ai.entity.Message; +import java.util.List; + +public interface ConversationService { + Conversation createConversation(String title, Long userId); + List getConversations(Long userId); + void deleteConversation(Long id, Long userId); + List searchConversations(String query, Long userId); + Message saveMessage(Long conversationId, String role, String content); + List getMessages(Long conversationId); + Conversation getConversation(Long id); + void updateConversationTitle(Long id, String title, Long userId); +} diff --git a/src/main/java/com/hertz/ai/service/impl/AiServiceImpl.java b/src/main/java/com/hertz/ai/service/impl/AiServiceImpl.java new file mode 100644 index 0000000..d7cdc65 --- /dev/null +++ b/src/main/java/com/hertz/ai/service/impl/AiServiceImpl.java @@ -0,0 +1,71 @@ +package com.hertz.ai.service.impl; + +import com.hertz.ai.service.AiService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.ollama.OllamaChatModel; +import org.springframework.ai.ollama.api.OllamaOptions; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; + +import java.util.List; + +@Service +@Slf4j +@RequiredArgsConstructor +public class AiServiceImpl implements AiService { + + private final OllamaChatModel chatModel; + private static final String SYSTEM_PROMPT = "你是赫兹官网的AI助手。赫兹是一个基于Spring Boot的权限控制管理系统框架,用于构建高性能、可扩展的应用程序。" + + "你可以回答关于赫兹的问题,如:赫兹的架构、性能优化、微服务设计模式等。" + + "如果用户有其他问题,也请尽力回答。"; + + @Override + public String chat(String message, Double temperature) { + long startTime = System.currentTimeMillis(); + try { + log.info("Starting AI chat request. Message length: {}", message.length()); + + OllamaOptions options = OllamaOptions.builder() + .temperature(temperature != null ? temperature : 0.7) + .build(); + + List messages = List.of( + new SystemMessage(SYSTEM_PROMPT), + new UserMessage(message) + ); + + String response = chatModel.call(new Prompt(messages, options)).getResult().getOutput().getContent(); + + log.info("AI chat request completed in {}ms", System.currentTimeMillis() - startTime); + return response; + } catch (Exception e) { + log.error("AI chat request failed", e); + throw new RuntimeException("AI service unavailable: " + e.getMessage()); + } + } + + @Override + public Flux streamChat(String message, Double temperature) { + OllamaOptions options = OllamaOptions.builder() + .temperature(temperature != null ? temperature : 0.7) + .build(); + + List messages = List.of( + new SystemMessage(SYSTEM_PROMPT), + new UserMessage(message) + ); + + return chatModel.stream(new Prompt(messages, options)) + .map(response -> { + String content = response.getResult().getOutput().getContent(); + return content != null ? content : ""; + }) + .doOnError(e -> log.error("Error in AI stream", e)) + .onErrorResume(e -> Flux.just(" [Error: " + e.getMessage() + "]")); + } +} diff --git a/src/main/java/com/hertz/ai/service/impl/ConversationServiceImpl.java b/src/main/java/com/hertz/ai/service/impl/ConversationServiceImpl.java new file mode 100644 index 0000000..5e11851 --- /dev/null +++ b/src/main/java/com/hertz/ai/service/impl/ConversationServiceImpl.java @@ -0,0 +1,106 @@ +package com.hertz.ai.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.hertz.ai.entity.Conversation; +import com.hertz.ai.entity.Message; +import com.hertz.ai.mapper.ConversationMapper; +import com.hertz.ai.mapper.MessageMapper; +import com.hertz.ai.service.ConversationService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ConversationServiceImpl implements ConversationService { + + private final ConversationMapper conversationMapper; + private final MessageMapper messageMapper; + + @Override + @Transactional + public Conversation createConversation(String title, Long userId) { + Conversation conversation = new Conversation(); + conversation.setUserId(userId); + conversation.setTitle(title != null && !title.isEmpty() ? title : "New Chat"); + conversation.setCreatedAt(LocalDateTime.now()); + conversation.setUpdatedAt(LocalDateTime.now()); + conversationMapper.insert(conversation); + return conversation; + } + + @Override + public List getConversations(Long userId) { + return conversationMapper.selectList(new LambdaQueryWrapper() + .eq(Conversation::getUserId, userId) + .orderByDesc(Conversation::getUpdatedAt)); + } + + @Override + @Transactional + public void deleteConversation(Long id, Long userId) { + Conversation conversation = conversationMapper.selectById(id); + if (conversation != null && conversation.getUserId().equals(userId)) { + messageMapper.delete(new LambdaQueryWrapper() + .eq(Message::getConversationId, id)); + conversationMapper.deleteById(id); + } else { + throw new RuntimeException("Conversation not found or access denied"); + } + } + + @Override + public List searchConversations(String query, Long userId) { + return conversationMapper.selectList(new LambdaQueryWrapper() + .eq(Conversation::getUserId, userId) + .like(Conversation::getTitle, query) + .orderByDesc(Conversation::getUpdatedAt)); + } + + @Override + @Transactional + public Message saveMessage(Long conversationId, String role, String content) { + Message message = new Message(); + message.setConversationId(conversationId); + message.setRole(role); + message.setContent(content); + message.setCreatedAt(LocalDateTime.now()); + messageMapper.insert(message); + + Conversation conversation = conversationMapper.selectById(conversationId); + if (conversation != null) { + conversation.setUpdatedAt(LocalDateTime.now()); + conversationMapper.updateById(conversation); + } + + return message; + } + + @Override + public List getMessages(Long conversationId) { + return messageMapper.selectList(new LambdaQueryWrapper() + .eq(Message::getConversationId, conversationId) + .orderByAsc(Message::getCreatedAt)); + } + + @Override + public Conversation getConversation(Long id) { + return conversationMapper.selectById(id); + } + + @Override + @Transactional + public void updateConversationTitle(Long id, String title, Long userId) { + Conversation conversation = conversationMapper.selectById(id); + if (conversation != null && conversation.getUserId().equals(userId)) { + conversation.setTitle(title); + conversation.setUpdatedAt(LocalDateTime.now()); + conversationMapper.updateById(conversation); + } else { + throw new RuntimeException("Conversation not found or access denied"); + } + } +} diff --git a/hertz_springboot/src/main/java/com/hertz/common/api/ApiResponse.java b/src/main/java/com/hertz/common/api/ApiResponse.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/common/api/ApiResponse.java rename to src/main/java/com/hertz/common/api/ApiResponse.java diff --git a/hertz_springboot/src/main/java/com/hertz/common/exception/BusinessException.java b/src/main/java/com/hertz/common/exception/BusinessException.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/common/exception/BusinessException.java rename to src/main/java/com/hertz/common/exception/BusinessException.java diff --git a/hertz_springboot/src/main/java/com/hertz/common/exception/GlobalExceptionHandler.java b/src/main/java/com/hertz/common/exception/GlobalExceptionHandler.java similarity index 97% rename from hertz_springboot/src/main/java/com/hertz/common/exception/GlobalExceptionHandler.java rename to src/main/java/com/hertz/common/exception/GlobalExceptionHandler.java index f34d956..5649b79 100644 --- a/hertz_springboot/src/main/java/com/hertz/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/hertz/common/exception/GlobalExceptionHandler.java @@ -54,6 +54,7 @@ public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ApiResponse handleException(Exception e) { + e.printStackTrace(); // 打印堆栈信息到控制台 return ApiResponse.fail(50000, "系统异常"); } } diff --git a/src/main/java/com/hertz/common/filter/RequestLogFilter.java b/src/main/java/com/hertz/common/filter/RequestLogFilter.java new file mode 100644 index 0000000..c431765 --- /dev/null +++ b/src/main/java/com/hertz/common/filter/RequestLogFilter.java @@ -0,0 +1,35 @@ +package com.hertz.common.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +@Slf4j +public class RequestLogFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + long startTime = System.currentTimeMillis(); + String method = request.getMethod(); + String uri = request.getRequestURI(); + + try { + filterChain.doFilter(request, response); + } finally { + long endTime = System.currentTimeMillis(); + long timeTaken = endTime - startTime; + int status = response.getStatus(); + + log.info("Request: [{}] {} | Status: {} | Time: {}ms", method, uri, status, timeTaken); + } + } +} diff --git a/src/main/java/com/hertz/monitor/controller/MonitorController.java b/src/main/java/com/hertz/monitor/controller/MonitorController.java new file mode 100644 index 0000000..56d42e1 --- /dev/null +++ b/src/main/java/com/hertz/monitor/controller/MonitorController.java @@ -0,0 +1,22 @@ +package com.hertz.monitor.controller; + +import com.hertz.common.api.ApiResponse; +import com.hertz.monitor.dto.MonitorDto; +import com.hertz.monitor.service.MonitorService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/monitor") +@RequiredArgsConstructor +public class MonitorController { + + private final MonitorService monitorService; + + @GetMapping("/server") + public ApiResponse getServerInfo() { + return ApiResponse.ok(monitorService.getServerInfo()); + } +} diff --git a/src/main/java/com/hertz/monitor/dto/MonitorDto.java b/src/main/java/com/hertz/monitor/dto/MonitorDto.java new file mode 100644 index 0000000..dbbe843 --- /dev/null +++ b/src/main/java/com/hertz/monitor/dto/MonitorDto.java @@ -0,0 +1,89 @@ +package com.hertz.monitor.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MonitorDto { + private CpuInfo cpu; + private MemInfo mem; + private JvmInfo jvm; + private SysInfo sys; + private List disks; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class CpuInfo { + private int cpuNum; + private double total; + private double sys; + private double used; + private double wait; + private double free; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class MemInfo { + private double total; + private double used; + private double free; + private double usage; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class JvmInfo { + private double total; + private double max; + private double free; + private String version; + private String home; + private double usage; + private String startTime; + private String runTime; + private String inputArgs; + private String name; + private long gcCount; + private long gcTime; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SysInfo { + private String computerName; + private String computerIp; + private String userDir; + private String osName; + private String osArch; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class DiskInfo { + private String dirName; + private String sysTypeName; + private String typeName; + private String total; + private String free; + private String used; + private double usage; + } +} diff --git a/src/main/java/com/hertz/monitor/service/MonitorService.java b/src/main/java/com/hertz/monitor/service/MonitorService.java new file mode 100644 index 0000000..6e0ae81 --- /dev/null +++ b/src/main/java/com/hertz/monitor/service/MonitorService.java @@ -0,0 +1,7 @@ +package com.hertz.monitor.service; + +import com.hertz.monitor.dto.MonitorDto; + +public interface MonitorService { + MonitorDto getServerInfo(); +} diff --git a/src/main/java/com/hertz/monitor/service/impl/MonitorServiceImpl.java b/src/main/java/com/hertz/monitor/service/impl/MonitorServiceImpl.java new file mode 100644 index 0000000..e8de52d --- /dev/null +++ b/src/main/java/com/hertz/monitor/service/impl/MonitorServiceImpl.java @@ -0,0 +1,165 @@ +package com.hertz.monitor.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.NumberUtil; +import com.hertz.monitor.dto.MonitorDto; +import com.hertz.monitor.service.MonitorService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.CentralProcessor.TickType; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.Util; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Properties; + +@Slf4j +@Service +public class MonitorServiceImpl implements MonitorService { + + private final SystemInfo si = new SystemInfo(); + + @Override + public MonitorDto getServerInfo() { + HardwareAbstractionLayer hal = si.getHardware(); + OperatingSystem os = si.getOperatingSystem(); + + return MonitorDto.builder() + .cpu(getCpuInfo(hal.getProcessor())) + .mem(getMemInfo(hal.getMemory())) + .sys(getSysInfo()) + .jvm(getJvmInfo()) + .disks(getDiskInfo(os)) + .build(); + } + + private MonitorDto.CpuInfo getCpuInfo(CentralProcessor processor) { + long[] prevTicks = processor.getSystemCpuLoadTicks(); + Util.sleep(1000); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; + long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; + long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; + long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; + long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; + long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; + long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + + // Prevent division by zero + if (totalCpu == 0) totalCpu = 1; + + return MonitorDto.CpuInfo.builder() + .cpuNum(processor.getLogicalProcessorCount()) + .total(totalCpu) + .sys(NumberUtil.round(cSys * 100.0 / totalCpu, 2).doubleValue()) + .used(NumberUtil.round(user * 100.0 / totalCpu, 2).doubleValue()) + .wait(NumberUtil.round(iowait * 100.0 / totalCpu, 2).doubleValue()) + .free(NumberUtil.round(idle * 100.0 / totalCpu, 2).doubleValue()) + .build(); + } + + private MonitorDto.MemInfo getMemInfo(GlobalMemory memory) { + double total = NumberUtil.div(memory.getTotal(), (1024 * 1024 * 1024), 2); + double free = NumberUtil.div(memory.getAvailable(), (1024 * 1024 * 1024), 2); + double used = NumberUtil.sub(total, free); + double usage = NumberUtil.mul(NumberUtil.div(used, total, 4), 100); + + return MonitorDto.MemInfo.builder() + .total(total) + .used(used) + .free(free) + .usage(usage) + .build(); + } + + private MonitorDto.SysInfo getSysInfo() { + Properties props = System.getProperties(); + return MonitorDto.SysInfo.builder() + .computerName(NetUtil.getLocalHostName()) + .computerIp(NetUtil.getLocalhostStr()) + .osName(props.getProperty("os.name")) + .osArch(props.getProperty("os.arch")) + .userDir(props.getProperty("user.dir")) + .build(); + } + + private MonitorDto.JvmInfo getJvmInfo() { + Properties props = System.getProperties(); + RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); + long time = runtime.getStartTime(); + + // GC Info + long gcCount = 0; + long gcTime = 0; + List gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); + for (GarbageCollectorMXBean gcBean : gcBeans) { + long count = gcBean.getCollectionCount(); + long t = gcBean.getCollectionTime(); + if (count > 0) gcCount += count; + if (t > 0) gcTime += t; + } + + double total = NumberUtil.div(Runtime.getRuntime().totalMemory(), (1024 * 1024), 2); + double max = NumberUtil.div(Runtime.getRuntime().maxMemory(), (1024 * 1024), 2); + double free = NumberUtil.div(Runtime.getRuntime().freeMemory(), (1024 * 1024), 2); + double used = total - free; + double usage = NumberUtil.mul(NumberUtil.div(used, total, 4), 100); + + return MonitorDto.JvmInfo.builder() + .total(total) + .max(max) + .free(free) + .version(props.getProperty("java.version")) + .home(props.getProperty("java.home")) + .name(ManagementFactory.getRuntimeMXBean().getVmName()) + .usage(usage) + .startTime(DateUtil.formatDateTime(new Date(time))) + .runTime(DateUtil.formatBetween(new Date(time), new Date())) + .inputArgs(runtime.getInputArguments().toString()) + .gcCount(gcCount) + .gcTime(gcTime) + .build(); + } + + private List getDiskInfo(OperatingSystem os) { + List list = new ArrayList<>(); + FileSystem fileSystem = os.getFileSystem(); + List fileStores = fileSystem.getFileStores(); + for (OSFileStore fs : fileStores) { + long free = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + long used = total - free; + + if (total == 0) continue; + + double usage = NumberUtil.mul(NumberUtil.div(used, total, 4), 100); + + MonitorDto.DiskInfo disk = MonitorDto.DiskInfo.builder() + .dirName(fs.getMount()) + .sysTypeName(fs.getType()) + .typeName(fs.getName()) + .total(FileUtil.readableFileSize(total)) + .free(FileUtil.readableFileSize(free)) + .used(FileUtil.readableFileSize(used)) + .usage(usage) + .build(); + list.add(disk); + } + return list; + } +} diff --git a/src/main/java/com/hertz/security/CustomUserDetailsService.java b/src/main/java/com/hertz/security/CustomUserDetailsService.java new file mode 100644 index 0000000..c564bf8 --- /dev/null +++ b/src/main/java/com/hertz/security/CustomUserDetailsService.java @@ -0,0 +1,33 @@ +package com.hertz.security; + +import com.hertz.system.service.UserService; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service +public class CustomUserDetailsService implements UserDetailsService { + + private final UserService userService; + + public CustomUserDetailsService(UserService userService) { + this.userService = userService; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + var user = userService.findByUsername(username); + if (user == null) { + throw new UsernameNotFoundException("User not found: " + username); + } + // We are using JWT for auth, so this is mostly to satisfy Spring Security's default config + // or if we wanted to support Basic Auth/Form Login alongside JWT. + // For now, we return a minimal UserDetails implementation. + return org.springframework.security.core.userdetails.User + .withUsername(user.getUsername()) + .password(user.getPassword()) + .roles("USER") // Default role, actual authorities are loaded in JwtAuthFilter + .build(); + } +} diff --git a/hertz_springboot/src/main/java/com/hertz/security/JwtAuthFilter.java b/src/main/java/com/hertz/security/JwtAuthFilter.java similarity index 88% rename from hertz_springboot/src/main/java/com/hertz/security/JwtAuthFilter.java rename to src/main/java/com/hertz/security/JwtAuthFilter.java index f7bf564..85a7ccd 100644 --- a/hertz_springboot/src/main/java/com/hertz/security/JwtAuthFilter.java +++ b/src/main/java/com/hertz/security/JwtAuthFilter.java @@ -47,7 +47,11 @@ public class JwtAuthFilter extends OncePerRequestFilter { var auth = new UsernamePasswordAuthenticationToken(username, null, authorities); auth.setDetails(userId); SecurityContextHolder.getContext().setAuthentication(auth); - } catch (Exception ignored) { + // System.out.println("DEBUG: JWT Auth Success for user: " + username); + } catch (Exception e) { + System.err.println("DEBUG: JWT Auth Failed: " + e.getMessage()); + e.printStackTrace(); + logger.error("JWT Authentication failed: " + e.getMessage(), e); SecurityContextHolder.clearContext(); } diff --git a/hertz_springboot/src/main/java/com/hertz/security/JwtService.java b/src/main/java/com/hertz/security/JwtService.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/security/JwtService.java rename to src/main/java/com/hertz/security/JwtService.java diff --git a/hertz_springboot/src/main/java/com/hertz/security/SecurityConfig.java b/src/main/java/com/hertz/security/SecurityConfig.java similarity index 95% rename from hertz_springboot/src/main/java/com/hertz/security/SecurityConfig.java rename to src/main/java/com/hertz/security/SecurityConfig.java index fa24fd0..3fc585d 100644 --- a/hertz_springboot/src/main/java/com/hertz/security/SecurityConfig.java +++ b/src/main/java/com/hertz/security/SecurityConfig.java @@ -1,5 +1,6 @@ package com.hertz.security; +import jakarta.servlet.DispatcherType; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; @@ -47,6 +48,7 @@ public class SecurityConfig { .cors(Customizer.withDefaults()) .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth + .dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll() .requestMatchers("/api/auth/**", "/error", "/uploads/**").permitAll() .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .anyRequest().authenticated() diff --git a/hertz_springboot/src/main/java/com/hertz/security/SecurityUtils.java b/src/main/java/com/hertz/security/SecurityUtils.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/security/SecurityUtils.java rename to src/main/java/com/hertz/security/SecurityUtils.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/config/MybatisPlusConfig.java b/src/main/java/com/hertz/system/config/MybatisPlusConfig.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/config/MybatisPlusConfig.java rename to src/main/java/com/hertz/system/config/MybatisPlusConfig.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/config/WebMvcConfig.java b/src/main/java/com/hertz/system/config/WebMvcConfig.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/config/WebMvcConfig.java rename to src/main/java/com/hertz/system/config/WebMvcConfig.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/controller/AuthController.java b/src/main/java/com/hertz/system/controller/AuthController.java similarity index 92% rename from hertz_springboot/src/main/java/com/hertz/system/controller/AuthController.java rename to src/main/java/com/hertz/system/controller/AuthController.java index 8e40ee7..b5edab3 100644 --- a/hertz_springboot/src/main/java/com/hertz/system/controller/AuthController.java +++ b/src/main/java/com/hertz/system/controller/AuthController.java @@ -6,6 +6,7 @@ import com.hertz.security.JwtService; import com.hertz.security.SecurityUtils; import com.hertz.system.dto.AuthDtos; import com.hertz.system.mapper.SysRoleMapper; +import com.hertz.system.service.CaptchaService; import com.hertz.system.service.UserService; import jakarta.validation.Valid; import java.util.List; @@ -24,21 +25,25 @@ public class AuthController { private final PasswordEncoder passwordEncoder; private final JwtService jwtService; private final SysRoleMapper roleMapper; + private final CaptchaService captchaService; public AuthController( UserService userService, PasswordEncoder passwordEncoder, JwtService jwtService, - SysRoleMapper roleMapper + SysRoleMapper roleMapper, + CaptchaService captchaService ) { this.userService = userService; this.passwordEncoder = passwordEncoder; this.jwtService = jwtService; this.roleMapper = roleMapper; + this.captchaService = captchaService; } @PostMapping("/register") public ApiResponse register(@Valid @RequestBody AuthDtos.RegisterRequest req) { + captchaService.validateCaptcha(req.uuid(), req.code()); var user = userService.register(req.username(), req.password(), req.nickname()); return ApiResponse.ok(new AuthDtos.MeResponse( user.getId(), @@ -54,6 +59,7 @@ public class AuthController { @PostMapping("/login") public ApiResponse login(@Valid @RequestBody AuthDtos.LoginRequest req) { + captchaService.validateCaptcha(req.uuid(), req.code()); var user = userService.findByUsername(req.username()); if (user == null || user.getStatus() == null || user.getStatus() != 1) { throw new BusinessException(40003, "用户名或密码错误"); diff --git a/src/main/java/com/hertz/system/controller/CaptchaController.java b/src/main/java/com/hertz/system/controller/CaptchaController.java new file mode 100644 index 0000000..1bcec03 --- /dev/null +++ b/src/main/java/com/hertz/system/controller/CaptchaController.java @@ -0,0 +1,25 @@ +package com.hertz.system.controller; + +import com.hertz.common.api.ApiResponse; +import com.hertz.system.dto.AuthDtos; +import com.hertz.system.service.CaptchaService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/auth/captcha") +public class CaptchaController { + + private final CaptchaService captchaService; + + public CaptchaController(CaptchaService captchaService) { + this.captchaService = captchaService; + } + + @GetMapping + public ApiResponse getCaptcha() { + var res = captchaService.generateCaptcha(); + return ApiResponse.ok(res); + } +} diff --git a/hertz_springboot/src/main/java/com/hertz/system/controller/FileController.java b/src/main/java/com/hertz/system/controller/FileController.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/controller/FileController.java rename to src/main/java/com/hertz/system/controller/FileController.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/controller/MenuController.java b/src/main/java/com/hertz/system/controller/MenuController.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/controller/MenuController.java rename to src/main/java/com/hertz/system/controller/MenuController.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/controller/RoleController.java b/src/main/java/com/hertz/system/controller/RoleController.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/controller/RoleController.java rename to src/main/java/com/hertz/system/controller/RoleController.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/controller/UserController.java b/src/main/java/com/hertz/system/controller/UserController.java similarity index 95% rename from hertz_springboot/src/main/java/com/hertz/system/controller/UserController.java rename to src/main/java/com/hertz/system/controller/UserController.java index 4b23be6..b9bce12 100644 --- a/hertz_springboot/src/main/java/com/hertz/system/controller/UserController.java +++ b/src/main/java/com/hertz/system/controller/UserController.java @@ -135,6 +135,13 @@ public class UserController { return ApiResponse.ok(); } + @DeleteMapping + @PreAuthorize("hasRole('ADMIN') or hasAuthority('system:user:remove')") + public ApiResponse deleteBatch(@RequestBody List ids) { + userService.deleteUsers(ids); + return ApiResponse.ok(); + } + @GetMapping("/{id}/roles") @PreAuthorize("hasRole('ADMIN') or hasAuthority('system:user:view')") public ApiResponse> roleIds(@PathVariable("id") long userId) { diff --git a/hertz_springboot/src/main/java/com/hertz/system/dto/AuthDtos.java b/src/main/java/com/hertz/system/dto/AuthDtos.java similarity index 76% rename from hertz_springboot/src/main/java/com/hertz/system/dto/AuthDtos.java rename to src/main/java/com/hertz/system/dto/AuthDtos.java index 93ece57..1798568 100644 --- a/hertz_springboot/src/main/java/com/hertz/system/dto/AuthDtos.java +++ b/src/main/java/com/hertz/system/dto/AuthDtos.java @@ -6,14 +6,18 @@ import java.util.List; public class AuthDtos { public record LoginRequest( @NotBlank String username, - @NotBlank String password + @NotBlank String password, + String uuid, + String code ) { } public record RegisterRequest( @NotBlank String username, @NotBlank String password, - String nickname + String nickname, + String uuid, + String code ) { } @@ -51,5 +55,11 @@ public class AuthDtos { @NotBlank(message = "新密码不能为空") String newPassword ) { } + + public record CaptchaResponse( + @com.fasterxml.jackson.annotation.JsonProperty("uuid") String uuid, + @com.fasterxml.jackson.annotation.JsonProperty("img") String img + ) { + } } diff --git a/hertz_springboot/src/main/java/com/hertz/system/dto/MenuDto.java b/src/main/java/com/hertz/system/dto/MenuDto.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/dto/MenuDto.java rename to src/main/java/com/hertz/system/dto/MenuDto.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/entity/SysMenu.java b/src/main/java/com/hertz/system/entity/SysMenu.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/entity/SysMenu.java rename to src/main/java/com/hertz/system/entity/SysMenu.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/entity/SysRole.java b/src/main/java/com/hertz/system/entity/SysRole.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/entity/SysRole.java rename to src/main/java/com/hertz/system/entity/SysRole.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/entity/SysRoleMenu.java b/src/main/java/com/hertz/system/entity/SysRoleMenu.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/entity/SysRoleMenu.java rename to src/main/java/com/hertz/system/entity/SysRoleMenu.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/entity/SysUser.java b/src/main/java/com/hertz/system/entity/SysUser.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/entity/SysUser.java rename to src/main/java/com/hertz/system/entity/SysUser.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/entity/SysUserRole.java b/src/main/java/com/hertz/system/entity/SysUserRole.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/entity/SysUserRole.java rename to src/main/java/com/hertz/system/entity/SysUserRole.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/mapper/SysMenuMapper.java b/src/main/java/com/hertz/system/mapper/SysMenuMapper.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/mapper/SysMenuMapper.java rename to src/main/java/com/hertz/system/mapper/SysMenuMapper.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/mapper/SysRoleMapper.java b/src/main/java/com/hertz/system/mapper/SysRoleMapper.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/mapper/SysRoleMapper.java rename to src/main/java/com/hertz/system/mapper/SysRoleMapper.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/mapper/SysRoleMenuMapper.java b/src/main/java/com/hertz/system/mapper/SysRoleMenuMapper.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/mapper/SysRoleMenuMapper.java rename to src/main/java/com/hertz/system/mapper/SysRoleMenuMapper.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/mapper/SysUserMapper.java b/src/main/java/com/hertz/system/mapper/SysUserMapper.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/mapper/SysUserMapper.java rename to src/main/java/com/hertz/system/mapper/SysUserMapper.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/mapper/SysUserRoleMapper.java b/src/main/java/com/hertz/system/mapper/SysUserRoleMapper.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/mapper/SysUserRoleMapper.java rename to src/main/java/com/hertz/system/mapper/SysUserRoleMapper.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/service/AuthzService.java b/src/main/java/com/hertz/system/service/AuthzService.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/service/AuthzService.java rename to src/main/java/com/hertz/system/service/AuthzService.java diff --git a/src/main/java/com/hertz/system/service/CaptchaService.java b/src/main/java/com/hertz/system/service/CaptchaService.java new file mode 100644 index 0000000..b1c8aa9 --- /dev/null +++ b/src/main/java/com/hertz/system/service/CaptchaService.java @@ -0,0 +1,8 @@ +package com.hertz.system.service; + +import com.hertz.system.dto.AuthDtos; + +public interface CaptchaService { + AuthDtos.CaptchaResponse generateCaptcha(); + void validateCaptcha(String uuid, String code); +} diff --git a/hertz_springboot/src/main/java/com/hertz/system/service/MenuService.java b/src/main/java/com/hertz/system/service/MenuService.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/service/MenuService.java rename to src/main/java/com/hertz/system/service/MenuService.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/service/RoleService.java b/src/main/java/com/hertz/system/service/RoleService.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/service/RoleService.java rename to src/main/java/com/hertz/system/service/RoleService.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/service/UserService.java b/src/main/java/com/hertz/system/service/UserService.java similarity index 95% rename from hertz_springboot/src/main/java/com/hertz/system/service/UserService.java rename to src/main/java/com/hertz/system/service/UserService.java index 0cc4e6f..11a6145 100644 --- a/hertz_springboot/src/main/java/com/hertz/system/service/UserService.java +++ b/src/main/java/com/hertz/system/service/UserService.java @@ -22,6 +22,8 @@ public interface UserService { void deleteUser(Long id); + void deleteUsers(List ids); + void updateUserRoles(long userId, List roleIds); List getUserRoleIds(long userId); diff --git a/hertz_springboot/src/main/java/com/hertz/system/service/impl/AuthzServiceImpl.java b/src/main/java/com/hertz/system/service/impl/AuthzServiceImpl.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/service/impl/AuthzServiceImpl.java rename to src/main/java/com/hertz/system/service/impl/AuthzServiceImpl.java diff --git a/src/main/java/com/hertz/system/service/impl/CaptchaServiceImpl.java b/src/main/java/com/hertz/system/service/impl/CaptchaServiceImpl.java new file mode 100644 index 0000000..f073c81 --- /dev/null +++ b/src/main/java/com/hertz/system/service/impl/CaptchaServiceImpl.java @@ -0,0 +1,54 @@ +package com.hertz.system.service.impl; + +import cn.hutool.captcha.CaptchaUtil; +import cn.hutool.captcha.LineCaptcha; +import cn.hutool.core.lang.UUID; +import com.hertz.common.exception.BusinessException; +import com.hertz.system.dto.AuthDtos; +import com.hertz.system.service.CaptchaService; +import java.util.concurrent.TimeUnit; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; + +@Service +public class CaptchaServiceImpl implements CaptchaService { + + private final StringRedisTemplate redisTemplate; + + public CaptchaServiceImpl(StringRedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + @Override + public AuthDtos.CaptchaResponse generateCaptcha() { + // Width: 120, Height: 40, Code Count: 4, Interference Line Count: 10 + LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(120, 40, 4, 10); + String uuid = UUID.fastUUID().toString(true); + String code = lineCaptcha.getCode(); + + // Store in Redis with 5 minutes expiration + redisTemplate.opsForValue().set("captcha:" + uuid, code, 5, TimeUnit.MINUTES); + + return new AuthDtos.CaptchaResponse(uuid, lineCaptcha.getImageBase64Data()); + } + + @Override + public void validateCaptcha(String uuid, String code) { + if (uuid == null || uuid.isBlank() || code == null || code.isBlank()) { + throw new BusinessException(40001, "验证码不能为空"); + } + String key = "captcha:" + uuid; + String cachedCode = redisTemplate.opsForValue().get(key); + + if (cachedCode == null) { + throw new BusinessException(40002, "验证码已过期"); + } + + if (!code.equalsIgnoreCase(cachedCode)) { + throw new BusinessException(40003, "验证码错误"); + } + + // Validate once, then delete to prevent replay + redisTemplate.delete(key); + } +} diff --git a/hertz_springboot/src/main/java/com/hertz/system/service/impl/MenuServiceImpl.java b/src/main/java/com/hertz/system/service/impl/MenuServiceImpl.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/service/impl/MenuServiceImpl.java rename to src/main/java/com/hertz/system/service/impl/MenuServiceImpl.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/service/impl/RoleServiceImpl.java b/src/main/java/com/hertz/system/service/impl/RoleServiceImpl.java similarity index 100% rename from hertz_springboot/src/main/java/com/hertz/system/service/impl/RoleServiceImpl.java rename to src/main/java/com/hertz/system/service/impl/RoleServiceImpl.java diff --git a/hertz_springboot/src/main/java/com/hertz/system/service/impl/UserServiceImpl.java b/src/main/java/com/hertz/system/service/impl/UserServiceImpl.java similarity index 96% rename from hertz_springboot/src/main/java/com/hertz/system/service/impl/UserServiceImpl.java rename to src/main/java/com/hertz/system/service/impl/UserServiceImpl.java index 73dd436..45c77ae 100644 --- a/hertz_springboot/src/main/java/com/hertz/system/service/impl/UserServiceImpl.java +++ b/src/main/java/com/hertz/system/service/impl/UserServiceImpl.java @@ -145,6 +145,16 @@ public class UserServiceImpl implements UserService { userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, id)); } + @Override + @Transactional + public void deleteUsers(List ids) { + if (ids == null || ids.isEmpty()) { + return; + } + userMapper.deleteByIds(ids); + userRoleMapper.delete(new LambdaQueryWrapper().in(SysUserRole::getUserId, ids)); + } + @Override @Transactional public void updateUserRoles(long userId, List roleIds) { diff --git a/hertz_springboot/src/main/resources/application.yml b/src/main/resources/application.yml similarity index 64% rename from hertz_springboot/src/main/resources/application.yml rename to src/main/resources/application.yml index c4009ba..06ce376 100644 --- a/hertz_springboot/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -11,11 +11,25 @@ spring: multipart: max-file-size: 2MB max-request-size: 10MB + ai: + ollama: + base-url: http://localhost:11434 + chat: + # 如果需要快速响应,建议切换为标准模型,如: llama3.1, qwen2.5:7b, deepseek-llm:7b + model: deepseek-llm:7b + options: + temperature: 0.7 datasource: url: jdbc:mysql://localhost:3306/hertz_springboot?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver + data: + redis: + host: localhost + port: 6379 + password: + database: 0 mybatis-plus: configuration: @@ -28,3 +42,9 @@ app: upload: root-path: d:\LocalFile\hertz_springboot\uploads avatar-path: avatar/ + +management: + endpoints: + web: + exposure: + include: health,info,metrics diff --git a/src/main/resources/schema/ai_schema.sql b/src/main/resources/schema/ai_schema.sql new file mode 100644 index 0000000..bc03fef --- /dev/null +++ b/src/main/resources/schema/ai_schema.sql @@ -0,0 +1,26 @@ + +-- ---------------------------- +-- Table structure for conversations +-- ---------------------------- +CREATE TABLE IF NOT EXISTS `ai_conversations` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `user_id` bigint NOT NULL COMMENT '用户ID', + `title` varchar(255) NOT NULL COMMENT '对话标题', + `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='对话记录表'; + +-- ---------------------------- +-- Table structure for messages +-- ---------------------------- +CREATE TABLE IF NOT EXISTS `ai_messages` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `conversation_id` bigint NOT NULL COMMENT '所属对话ID', + `role` enum('user','assistant') NOT NULL COMMENT '消息角色', + `content` text NOT NULL COMMENT '消息内容', + `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_conversation_id` (`conversation_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='对话消息表'; diff --git a/src/main/resources/schema/monitor_schema.sql b/src/main/resources/schema/monitor_schema.sql new file mode 100644 index 0000000..3e1807f --- /dev/null +++ b/src/main/resources/schema/monitor_schema.sql @@ -0,0 +1,14 @@ +-- ---------------------------- +-- Table structure for sys_monitor_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_monitor_log`; +CREATE TABLE `sys_monitor_log` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `cpu_usage` double NOT NULL COMMENT 'CPU使用率(%)', + `memory_usage` double NOT NULL COMMENT '内存使用率(%)', + `memory_total` bigint NOT NULL COMMENT '总内存(字节)', + `memory_used` bigint NOT NULL COMMENT '已用内存(字节)', + `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录时间', + PRIMARY KEY (`id`), + KEY `idx_created_at` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统监控日志表'; diff --git a/hertz_springboot/src/main/resources/schema.sql b/src/main/resources/schema/schema.sql similarity index 99% rename from hertz_springboot/src/main/resources/schema.sql rename to src/main/resources/schema/schema.sql index ef388e8..ea0531e 100644 --- a/hertz_springboot/src/main/resources/schema.sql +++ b/src/main/resources/schema/schema.sql @@ -104,4 +104,4 @@ INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (1, 2), (1, 3), (1, 4), - (1, 5); + (1, 5); \ No newline at end of file diff --git a/hertz_springboot/src/test/java/com/hertz/HertzApplicationTests.java b/src/test/java/com/hertz/HertzApplicationTests.java similarity index 100% rename from hertz_springboot/src/test/java/com/hertz/HertzApplicationTests.java rename to src/test/java/com/hertz/HertzApplicationTests.java diff --git a/hertz_springboot_ui/.env.development b/ui/.env.dev similarity index 100% rename from hertz_springboot_ui/.env.development rename to ui/.env.dev diff --git a/hertz_springboot_ui/index.html b/ui/index.html similarity index 82% rename from hertz_springboot_ui/index.html rename to ui/index.html index eb8bfcc..c9e0db4 100644 --- a/hertz_springboot_ui/index.html +++ b/ui/index.html @@ -2,7 +2,7 @@ - + Hertz Admin diff --git a/hertz_springboot_ui/jsconfig.json b/ui/jsconfig.json similarity index 100% rename from hertz_springboot_ui/jsconfig.json rename to ui/jsconfig.json diff --git a/hertz_springboot_ui/package-lock.json b/ui/package-lock.json similarity index 99% rename from hertz_springboot_ui/package-lock.json rename to ui/package-lock.json index 66c359f..51cd060 100644 --- a/hertz_springboot_ui/package-lock.json +++ b/ui/package-lock.json @@ -11,6 +11,7 @@ "@element-plus/icons-vue": "^2.3.2", "axios": "^1.13.2", "element-plus": "^2.13.1", + "marked": "^17.0.1", "pinia": "^3.0.4", "vue": "^3.5.24", "vue-router": "^4.6.4" @@ -1656,6 +1657,18 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/marked": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz", + "integrity": "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", diff --git a/hertz_springboot_ui/package.json b/ui/package.json similarity index 94% rename from hertz_springboot_ui/package.json rename to ui/package.json index 65f9064..d21df5b 100644 --- a/hertz_springboot_ui/package.json +++ b/ui/package.json @@ -12,6 +12,7 @@ "@element-plus/icons-vue": "^2.3.2", "axios": "^1.13.2", "element-plus": "^2.13.1", + "marked": "^17.0.1", "pinia": "^3.0.4", "vue": "^3.5.24", "vue-router": "^4.6.4" diff --git a/hertz_springboot_ui/public/index.png b/ui/public/favicon.png similarity index 100% rename from hertz_springboot_ui/public/index.png rename to ui/public/favicon.png diff --git a/hertz_springboot_ui/public/logo.png b/ui/public/logo.png similarity index 100% rename from hertz_springboot_ui/public/logo.png rename to ui/public/logo.png diff --git a/hertz_springboot_ui/src/App.vue b/ui/src/App.vue similarity index 100% rename from hertz_springboot_ui/src/App.vue rename to ui/src/App.vue diff --git a/hertz_springboot_ui/src/api/auth.js b/ui/src/api/auth.js similarity index 82% rename from hertz_springboot_ui/src/api/auth.js rename to ui/src/api/auth.js index 1f5583f..a69ec1c 100644 --- a/hertz_springboot_ui/src/api/auth.js +++ b/ui/src/api/auth.js @@ -1,5 +1,10 @@ import { http } from './http' +export async function getCaptcha() { + const { data } = await http.get('/api/auth/captcha') + return data.data +} + export async function login(req) { const { data } = await http.post('/api/auth/login', req) return data.data diff --git a/ui/src/api/chat.js b/ui/src/api/chat.js new file mode 100644 index 0000000..7936b89 --- /dev/null +++ b/ui/src/api/chat.js @@ -0,0 +1,39 @@ +import { http as request } from './http' + +export const chatApi = { + // Create Conversation + async createConversation(title) { + const { data } = await request.post('api/ai/conversations', { title }) + return data.data + }, + // Get List + async getConversations() { + const { data } = await request.get('api/ai/conversations') + return data.data + }, + // Delete + async deleteConversation(id) { + const { data } = await request.delete(`api/ai/conversations/${id}`) + return data.data + }, + // Update Title + async updateConversation(id, title) { + const { data } = await request.put(`api/ai/conversations/${id}`, { title }) + return data.data + }, + // Search + async searchConversations(query) { + const { data } = await request.get('api/ai/conversations/search', { params: { query } }) + return data.data + }, + // Save Message + async saveMessage(conversationId, role, content) { + const { data } = await request.post(`api/ai/conversations/${conversationId}/messages`, { role, content }) + return data.data + }, + // Get Messages + async getMessages(conversationId) { + const { data } = await request.get(`api/ai/conversations/${conversationId}/messages`) + return data.data + } +} diff --git a/hertz_springboot_ui/src/api/common.js b/ui/src/api/common.js similarity index 100% rename from hertz_springboot_ui/src/api/common.js rename to ui/src/api/common.js diff --git a/hertz_springboot_ui/src/api/http.js b/ui/src/api/http.js similarity index 73% rename from hertz_springboot_ui/src/api/http.js rename to ui/src/api/http.js index 57b7e44..040bcb2 100644 --- a/hertz_springboot_ui/src/api/http.js +++ b/ui/src/api/http.js @@ -26,12 +26,14 @@ http.interceptors.response.use( import('../router') .then(({ default: router }) => { const current = router.currentRoute.value - if (current.path !== '/403') { - router.replace({ path: '/403', query: { from: current.fullPath } }) + const path = current.path + const target = path.startsWith('/admin') ? '/admin/403' : '/portal/403' + if (path !== target) { + router.replace({ path: target, query: { from: current.fullPath } }) } }) .catch(() => { - if (window.location.pathname !== '/403') window.location.replace('/403') + if (window.location.pathname !== '/portal/403') window.location.replace('/portal/403') }) } return Promise.reject(err) diff --git a/ui/src/api/monitor.js b/ui/src/api/monitor.js new file mode 100644 index 0000000..bd2b3ba --- /dev/null +++ b/ui/src/api/monitor.js @@ -0,0 +1,6 @@ +import { http } from './http' + +export async function getServerInfo() { + const { data } = await http.get('/api/monitor/server') + return data +} diff --git a/hertz_springboot_ui/src/api/system.js b/ui/src/api/system.js similarity index 95% rename from hertz_springboot_ui/src/api/system.js rename to ui/src/api/system.js index b0c6b62..7110516 100644 --- a/hertz_springboot_ui/src/api/system.js +++ b/ui/src/api/system.js @@ -39,6 +39,10 @@ export async function deleteUser(id) { await http.delete(`/api/system/users/${id}`) } +export async function deleteUsers(ids) { + await http.delete('/api/system/users', { data: ids }) +} + export async function fetchRoles() { const { data } = await http.get('/api/system/roles') return data.data diff --git a/ui/src/assets/img/default_avatar.png b/ui/src/assets/img/default_avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..187c7f1f3fcf96a9482b16e5fc51773eb864aabc GIT binary patch literal 6187 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 YVD_8g&A-|@4e4=MAgvL#CYNsh2Wv#f-~a#s literal 0 HcmV?d00001 diff --git a/hertz_springboot_ui/src/assets/img/profile_bg.jpg b/ui/src/assets/img/profile_bg.jpg similarity index 100% rename from hertz_springboot_ui/src/assets/img/profile_bg.jpg rename to ui/src/assets/img/profile_bg.jpg diff --git a/hertz_springboot_ui/src/assets/style.css b/ui/src/assets/style.css similarity index 76% rename from hertz_springboot_ui/src/assets/style.css rename to ui/src/assets/style.css index 0ff6052..a9e0c33 100644 --- a/hertz_springboot_ui/src/assets/style.css +++ b/ui/src/assets/style.css @@ -1,10 +1,10 @@ :root { /* iOS Color Palette */ - --ios-primary: #007AFF; - --ios-primary-light: #47a0ff; + --ios-primary: #3542ec; + --ios-primary-light: #0011FF; --ios-success: #34C759; --ios-warning: #FF9500; - --ios-danger: #FF3B30; + --ios-danger: #FF4040; --ios-gray: #8E8E93; --ios-bg: #F2F4F8; --ios-surface: rgba(255, 255, 255, 0.7); @@ -56,7 +56,7 @@ body { .el-card__header { border-bottom: 1px solid var(--ios-border) !important; - padding: 16px 24px !important; + padding: 10px 24px !important; font-weight: 600; font-size: 16px; } @@ -68,7 +68,19 @@ body { transition: all 0.2s ease !important; } -.el-button:not(.is-circle):not(.is-text) { +.el-button.is-link:hover { + color: var(--ios-primary-light); +} + + +.el-button--primary { + --el-button-hover-bg-color: var(--ios-primary-light); + --el-button-hover-border-color: var(--ios-primary-light); + --el-button-active-bg-color: var(--ios-primary-light); + --el-button-active-border-color: var(--ios-primary-light); +} + +.el-button:not(.is-circle):not(.is-text):not(.is-link) { height: 36px !important; padding: 0 20px !important; } @@ -111,6 +123,14 @@ body { background-color: rgba(0, 122, 255, 0.03) !important; } +.el-checkbox__inner { + width: 15px; + height: 15px; + border-radius: 4px; +} + + + /* Dialog Styling */ .el-dialog { border-radius: 20px !important; @@ -240,3 +260,48 @@ html, body { scrollbar-width: none; -ms-overflow-style: none; } + +/* MessageBox Styling */ +.el-message-box { + border-radius: var(--ios-radius) !important; + border: none !important; + box-shadow: var(--ios-shadow-hover) !important; + background-color: rgba(255, 255, 255, 0.95) !important; + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + padding: 0 !important; + width: 400px !important; + max-width: 90vw; +} + +.el-message-box__header { + padding: 20px 24px 10px !important; +} + +.el-message-box__title { + font-weight: 600; + font-size: 18px !important; +} + +.el-message-box__headerbtn { + top: 20px !important; + right: 20px !important; +} + +.el-message-box__content { + padding: 10px 24px 20px !important; + font-size: 15px !important; + color: #1d1d1f; +} + +.el-message-box__btns { + padding: 0 24px 24px !important; + display: flex; + justify-content: flex-end; + gap: 12px; +} + +.el-message-box__btns .el-button { + margin-left: 0 !important; + min-width: 80px; +} diff --git a/ui/src/components/ContentPage.vue b/ui/src/components/ContentPage.vue new file mode 100644 index 0000000..d649248 --- /dev/null +++ b/ui/src/components/ContentPage.vue @@ -0,0 +1,66 @@ + + + diff --git a/hertz_springboot_ui/src/components/Error.vue b/ui/src/components/Error.vue similarity index 93% rename from hertz_springboot_ui/src/components/Error.vue rename to ui/src/components/Error.vue index bd393bb..f481060 100644 --- a/hertz_springboot_ui/src/components/Error.vue +++ b/ui/src/components/Error.vue @@ -3,18 +3,18 @@
{{ codeText }}
-
+
{{ displayTitle }}
{{ displaySubTitle }}
-
+
@@ -97,11 +97,9 @@ function switchAccount() { display: flex; justify-content: center; align-items: center; - height: 100vh; - padding: 16px; - background: radial-gradient(circle at top left, #e3f2fd, transparent 45%), - radial-gradient(circle at bottom right, #f3e5f5, transparent 45%), - #f2f4f8; + min-height: 100%; + padding: 32px 16px; + flex: 1; } .error-card { diff --git a/hertz_springboot_ui/src/layouts/AdminLayout.vue b/ui/src/layouts/AdminLayout.vue similarity index 97% rename from hertz_springboot_ui/src/layouts/AdminLayout.vue rename to ui/src/layouts/AdminLayout.vue index 5d51c43..51d233e 100644 --- a/hertz_springboot_ui/src/layouts/AdminLayout.vue +++ b/ui/src/layouts/AdminLayout.vue @@ -1,7 +1,7 @@ + diff --git a/hertz_springboot_ui/src/views/admin/system/Role.vue b/ui/src/views/admin/system/Role.vue similarity index 88% rename from hertz_springboot_ui/src/views/admin/system/Role.vue rename to ui/src/views/admin/system/Role.vue index 7e759f1..36913cf 100644 --- a/hertz_springboot_ui/src/views/admin/system/Role.vue +++ b/ui/src/views/admin/system/Role.vue @@ -1,15 +1,17 @@