diff --git a/.gitignore b/.gitignore index 14bd449..034c723 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ /gofaster/tmp/ /gofaster/app/dist/*/*/ /gofaster/backend/tmp +/gofaster/backend/tmp/main.exe diff --git a/gofaster/ROLE_MANAGEMENT_API_GUIDE.md b/gofaster/ROLE_MANAGEMENT_API_GUIDE.md new file mode 100644 index 0000000..8f71cea --- /dev/null +++ b/gofaster/ROLE_MANAGEMENT_API_GUIDE.md @@ -0,0 +1,396 @@ +# 角色管理前后台交互指南 + +## 概述 + +本文档介绍如何将前台角色管理功能连接到真实的后端API,实现完整的前后台交互。 + +## 功能特性 + +### ✅ 已实现功能 + +1. **角色CRUD操作** + - 获取角色列表(支持分页) + - 创建新角色 + - 更新角色信息 + - 删除角色 + +2. **API测试模式** + - 支持测试模式切换(无需JWT认证) + - 支持正式模式(需要JWT认证) + - 实时API连接测试 + +3. **错误处理** + - 统一的错误响应格式 + - 友好的错误提示 + - 网络连接异常处理 + +4. **用户体验** + - 加载状态显示 + - 操作确认提示 + - 实时数据刷新 + +## 快速开始 + +### 1. 启动服务 + +使用完整启动脚本同时启动前后台: + +```powershell +# 标准模式启动 +.\start-full.ps1 + +# 调试模式启动 +.\start-full.ps1 -Debug + +# 测试模式启动 +.\start-full.ps1 -Test +``` + +### 2. 访问应用 + +- **前端应用**: http://localhost:8081 +- **后端API**: http://localhost:8080 +- **API文档**: http://localhost:8080/swagger/index.html + +### 3. 测试API连接 + +1. 进入角色管理页面 +2. 点击"测试模式"按钮切换到测试模式 +3. 点击"新建角色"测试创建功能 +4. 查看角色列表测试获取功能 + +## API接口说明 + +### 基础信息 + +- **API基础路径**: `/api/auth/roles` +- **测试路径**: `/api/auth/roles/test` +- **认证方式**: JWT Bearer Token +- **响应格式**: JSON + +### 接口列表 + +#### 1. 获取角色列表 + +```http +GET /api/auth/roles?page=1&pageSize=10 +``` + +**响应格式**: +```json +{ + "code": 200, + "message": "获取角色列表成功", + "data": { + "data": [ + { + "id": 1, + "name": "管理员", + "code": "ADMIN", + "description": "系统管理员", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } + ], + "page": 1, + "size": 10, + "total": 1 + } +} +``` + +#### 2. 创建角色 + +```http +POST /api/auth/roles +Content-Type: application/json + +{ + "name": "新角色", + "code": "NEW_ROLE", + "description": "角色描述" +} +``` + +**响应格式**: +```json +{ + "code": 200, + "message": "角色创建成功", + "data": { + "id": 2, + "name": "新角色", + "code": "NEW_ROLE", + "description": "角色描述", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } +} +``` + +#### 3. 更新角色 + +```http +PUT /api/auth/roles/{id} +Content-Type: application/json + +{ + "name": "更新后的角色", + "code": "UPDATED_ROLE", + "description": "更新后的描述" +} +``` + +#### 4. 删除角色 + +```http +DELETE /api/auth/roles/{id} +``` + +**响应格式**: +```json +{ + "code": 200, + "message": "角色删除成功", + "data": null +} +``` + +## 前端组件使用 + +### 1. 角色管理主页面 + +```vue + + + + + +``` + +### 2. API测试页面 + +```vue + + + + + +``` + +### 3. 直接使用服务 + +```javascript +import { roleService } from '@/modules/role-management' + +// 获取角色列表 +const roles = await roleService.getRoles(1, 10) + +// 创建角色 +const newRole = await roleService.createRole({ + name: '测试角色', + code: 'TEST_ROLE', + description: '测试描述' +}) + +// 更新角色 +const updatedRole = await roleService.updateRole(1, { + name: '更新后的角色', + code: 'UPDATED_ROLE', + description: '更新后的描述' +}) + +// 删除角色 +await roleService.deleteRole(1) +``` + +## 配置说明 + +### 1. API基础URL配置 + +前端配置文件: `app/src/config/app.config.js` + +```javascript +export const appConfig = { + development: { + apiBaseUrl: 'http://localhost:8080/api', + // ... + } +} +``` + +### 2. 测试模式配置 + +通过localStorage控制测试模式: + +```javascript +// 启用测试模式 +localStorage.setItem('role-test-mode', 'true') + +// 禁用测试模式 +localStorage.setItem('role-test-mode', 'false') + +// 检查测试模式状态 +const isTestMode = localStorage.getItem('role-test-mode') === 'true' +``` + +### 3. JWT认证配置 + +```javascript +// 设置JWT token +localStorage.setItem('token', 'your-jwt-token') + +// 清除JWT token +localStorage.removeItem('token') +``` + +## 错误处理 + +### 1. 网络错误 + +```javascript +try { + const response = await roleService.getRoles() +} catch (error) { + if (error.code === 0) { + console.error('网络连接失败') + } else if (error.code === 401) { + console.error('认证失败,请重新登录') + } else { + console.error('请求失败:', error.message) + } +} +``` + +### 2. 业务错误 + +```javascript +const response = await roleService.createRole(roleData) +if (response.code !== 200) { + console.error('业务错误:', response.message) +} +``` + +## 开发调试 + +### 1. 启用调试模式 + +```powershell +# 启动调试模式 +.\start-full.ps1 -Debug +``` + +### 2. 查看API日志 + +后端日志文件: `backend/logs/app.log` + +### 3. 使用API测试页面 + +访问角色管理API测试页面,可以: +- 查看API配置信息 +- 手动测试各个接口 +- 查看详细的请求响应 +- 切换测试模式 + +### 4. 浏览器开发者工具 + +- 打开Network面板查看API请求 +- 查看Console面板的错误信息 +- 使用Application面板查看localStorage + +## 常见问题 + +### Q1: API连接失败 + +**解决方案**: +1. 检查后端服务是否启动 +2. 确认API基础URL配置正确 +3. 检查防火墙设置 +4. 查看后端日志 + +### Q2: 认证失败 + +**解决方案**: +1. 切换到测试模式(无需认证) +2. 检查JWT token是否有效 +3. 重新登录获取新token + +### Q3: 数据不显示 + +**解决方案**: +1. 检查API响应格式 +2. 确认数据库中有数据 +3. 查看浏览器控制台错误 + +### Q4: 创建/更新失败 + +**解决方案**: +1. 检查必填字段是否填写 +2. 确认角色名称和代码唯一性 +3. 查看后端验证错误信息 + +## 扩展功能 + +### 1. 权限管理 + +可以扩展角色管理功能,添加: +- 角色权限分配 +- 权限树形展示 +- 批量权限操作 + +### 2. 用户角色分配 + +可以添加: +- 用户角色分配页面 +- 批量角色分配 +- 角色继承关系 + +### 3. 角色模板 + +可以添加: +- 角色模板管理 +- 快速创建角色 +- 角色导入导出 + +## 技术栈 + +### 前端 +- Vue 3 (Composition API) +- Axios (HTTP客户端) +- Vue Router (路由管理) + +### 后端 +- Go (Gin框架) +- GORM (ORM) +- JWT (认证) + +### 数据库 +- MySQL/PostgreSQL +- Redis (缓存) + +## 贡献指南 + +1. Fork项目 +2. 创建功能分支 +3. 提交代码 +4. 创建Pull Request + +## 许可证 + +MIT License diff --git a/gofaster/SWAGGER_AUTH_FIX.md b/gofaster/SWAGGER_AUTH_FIX.md new file mode 100644 index 0000000..3294904 --- /dev/null +++ b/gofaster/SWAGGER_AUTH_FIX.md @@ -0,0 +1,180 @@ +# Swagger UI Authorize按钮不显示问题解决方案 + +## 问题描述 +在Swagger UI中,右上角没有显示"Authorize"按钮,无法配置JWT token进行API测试。 + +## 原因分析 +1. **Swagger UI版本问题**:某些版本的Swagger UI可能不显示Authorize按钮 +2. **配置方式问题**:gin-swagger的配置可能不完整 +3. **浏览器缓存问题**:浏览器可能缓存了旧版本的页面 + +## 解决方案 + +### 方案1:使用自定义Swagger UI(推荐) + +我已经创建了一个自定义的Swagger UI页面,确保Authorize按钮能正确显示: + +1. **访问自定义Swagger UI**: + ``` + http://localhost:8080/swagger-ui + ``` + +2. **这个页面使用了最新版本的Swagger UI (5.9.0)**,确保Authorize按钮正常显示 + +### 方案2:检查配置 + +确保以下配置正确: + +#### 1. main.go中的安全定义配置 +```go +// @securityDefinitions.apikey BearerAuth +// @in header +// @name Authorization +// @description 请输入 "Bearer " + JWT token,例如: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." +``` + +#### 2. API控制器中的安全配置 +```go +// @Security BearerAuth +// @Router /auth/roles [post] +func (c *RoleController) CreateRole(ctx *gin.Context) { + // ... +} +``` + +### 方案3:浏览器操作 + +1. **清除浏览器缓存**: + - 按 `Ctrl+Shift+Delete` + - 选择"缓存的图片和文件" + - 点击"清除数据" + +2. **强制刷新页面**: + - 按 `Ctrl+F5` 强制刷新 + - 或者按 `Ctrl+Shift+R` + +3. **检查浏览器控制台**: + - 按 `F12` 打开开发者工具 + - 查看Console标签页是否有错误信息 + - 查看Network标签页是否有请求失败 + +### 方案4:验证配置 + +运行测试脚本验证配置: +```powershell +.\test-swagger-auth.ps1 +``` + +这个脚本会: +1. 检查后端服务状态 +2. 验证Swagger JSON文档 +3. 检查安全定义配置 +4. 检查API安全配置 +5. 测试Swagger UI页面 + +## 使用步骤 + +### 1. 启动后端服务 +```bash +cd backend +go run main.go +``` + +### 2. 访问Swagger UI +- **默认UI**:http://localhost:8080/swagger/index.html +- **自定义UI**:http://localhost:8080/swagger-ui (推荐) + +### 3. 配置身份验证 +1. 点击右上角的 **"Authorize"** 按钮 +2. 在输入框中输入:`Bearer ` +3. 点击 **"Authorize"** 按钮 + +### 4. 获取JWT Token +1. 先调用 `/api/auth/captcha` 获取验证码 +2. 再调用 `/api/auth/login` 进行登录 +3. 复制返回的 `access_token` + +## 故障排除 + +### 如果Authorize按钮仍然不显示: + +1. **检查Swagger JSON文档**: + ``` + http://localhost:8080/swagger/doc.json + ``` + 确保包含 `securityDefinitions` 部分 + +2. **检查API配置**: + 确保需要认证的API包含 `security` 配置 + +3. **尝试不同的浏览器**: + - Chrome + - Firefox + - Edge + +4. **检查网络连接**: + 确保能正常访问Swagger UI页面 + +### 常见错误及解决方案: + +1. **"Failed to load resource"**: + - 检查后端服务是否正常运行 + - 检查端口8080是否被占用 + +2. **"CORS error"**: + - 确保CORS中间件正确配置 + - 检查请求来源 + +3. **"404 Not Found"**: + - 确保Swagger路由正确配置 + - 检查文件路径 + +## 验证成功标志 + +当配置正确时,你应该能看到: + +1. ✅ Swagger UI页面正常加载 +2. ✅ 右上角显示 **"Authorize"** 按钮 +3. ✅ 点击Authorize按钮能打开认证对话框 +4. ✅ 需要认证的API显示锁定图标 🔒 +5. ✅ 配置token后能正常调用需要认证的API + +## 测试API流程 + +1. **获取验证码**: + ``` + GET /api/auth/captcha + ``` + +2. **用户登录**: + ``` + POST /api/auth/login + { + "username": "admin", + "password": "admin123", + "captcha": "验证码", + "captcha_id": "验证码ID" + } + ``` + +3. **配置Authorize**: + - 点击Authorize按钮 + - 输入:`Bearer ` + - 点击Authorize + +4. **测试需要认证的API**: + ``` + GET /api/auth/roles + POST /api/auth/roles + PUT /api/auth/roles/{id} + DELETE /api/auth/roles/{id} + ``` + +## 总结 + +如果默认的Swagger UI不显示Authorize按钮,请使用自定义的Swagger UI页面: +``` +http://localhost:8080/swagger-ui +``` + +这个页面使用了最新版本的Swagger UI,确保Authorize按钮能正常显示和使用。 diff --git a/gofaster/SWAGGER_FIX_SUMMARY.md b/gofaster/SWAGGER_FIX_SUMMARY.md new file mode 100644 index 0000000..49edacf --- /dev/null +++ b/gofaster/SWAGGER_FIX_SUMMARY.md @@ -0,0 +1,166 @@ +# Swagger配置修复总结 + +## 修复的问题 + +### 1. Swagger路由路径配置问题 + +**问题描述:** +- Swagger路由配置不完整,缺少重定向路径 +- 用户无法通过多种方式访问Swagger文档 + +**修复内容:** +- 在 `backend/main.go` 中添加了多个Swagger访问路径: + ```go + // Swagger路由配置 + app.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) + app.GET("/docs", func(c *gin.Context) { + c.Redirect(http.StatusMovedPermanently, "/swagger/index.html") + }) + app.GET("/api-docs", func(c *gin.Context) { + c.Redirect(http.StatusMovedPermanently, "/swagger/index.html") + }) + ``` + +**访问路径:** +- 主要路径:`http://localhost:8080/swagger/index.html` +- 重定向路径:`http://localhost:8080/docs` +- API文档路径:`http://localhost:8080/api-docs` + +### 2. Swagger身份验证配置问题 + +**问题描述:** +- Swagger文档没有配置身份验证 +- 无法在Swagger UI中测试需要认证的API + +**修复内容:** +- 在 `backend/main.go` 中添加了身份验证配置: + ```go + // @securityDefinitions.apikey BearerAuth + // @in header + // @name Authorization + // @description 请输入 "Bearer " + JWT token,例如: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + ``` + +- 为所有需要认证的API添加了 `@Security BearerAuth` 注释 + +### 3. 角色路由路径修复 + +**问题描述:** +- 角色管理路由路径不正确,导致API无法访问 + +**修复内容:** +- 在 `backend/internal/auth/routes/role_routes.go` 中修复了路由路径: + ```go + // 角色管理路由组 - 修复路径配置 + roleGroup := router.Group("/auth/roles") + ``` + +### 4. Swagger注释完善 + +**修复内容:** +- 为角色控制器的方法添加了完整的Swagger注释: + - `CreateRole` - 创建角色 + - `UpdateRole` - 更新角色 + - `DeleteRole` - 删除角色 + - `GetRole` - 获取角色详情 + - `ListRoles` - 获取角色列表 + +- 每个方法都包含了: + - `@Summary` - 接口摘要 + - `@Description` - 详细描述 + - `@Tags` - 标签分类 + - `@Accept` - 接受的数据类型 + - `@Produce` - 返回的数据类型 + - `@Param` - 参数说明 + - `@Success` - 成功响应 + - `@Failure` - 失败响应 + - `@Security` - 安全认证 + - `@Router` - 路由路径 + +## 使用方法 + +### 1. 启动后端服务 +```bash +cd backend +go run main.go +``` + +### 2. 访问Swagger文档 +- 主要地址:http://localhost:8080/swagger/index.html +- 重定向地址:http://localhost:8080/docs + +### 3. 测试API流程 + +#### 步骤1:获取验证码 +1. 在Swagger UI中找到 `/api/auth/captcha` 接口 +2. 点击 "Try it out" +3. 点击 "Execute" +4. 复制返回的验证码ID和图片 + +#### 步骤2:用户登录 +1. 找到 `/api/auth/login` 接口 +2. 点击 "Try it out" +3. 输入测试账号: + ```json + { + "username": "admin", + "password": "admin123", + "captcha": "验证码", + "captcha_id": "验证码ID" + } + ``` +4. 点击 "Execute" +5. 复制返回的 `access_token` + +#### 步骤3:配置身份验证 +1. 点击Swagger UI右上角的 "Authorize" 按钮 +2. 在输入框中输入:`Bearer ` +3. 点击 "Authorize" + +#### 步骤4:测试需要认证的API +现在可以测试所有需要认证的API: +- `/api/auth/roles` - 角色管理相关接口 +- 其他需要认证的接口 + +## 测试脚本 + +创建了 `test-swagger.ps1` 脚本来验证修复效果: + +```powershell +# 运行测试脚本 +.\test-swagger.ps1 + +# 如果需要启动后端服务 +.\test-swagger.ps1 -StartBackend +``` + +## 验证结果 + +### ✅ 已修复的问题 +1. **Swagger路由配置** - 多个访问路径正常工作 +2. **身份验证配置** - Swagger UI支持Bearer Token认证 +3. **API路由路径** - 角色管理API路径正确 +4. **Swagger注释** - 所有API都有完整的文档注释 + +### 🔧 可用的功能 +1. **无需认证的API**: + - `/api/auth/captcha` - 获取验证码 + - `/api/auth/login` - 用户登录 + - `/api/auth/roles/test/*` - 测试模式角色管理 + +2. **需要认证的API**: + - `/api/auth/roles/*` - 正式角色管理 + - 其他需要JWT认证的接口 + +### 📝 注意事项 +1. 测试模式的路由(`/api/auth/roles/test/*`)不需要认证 +2. 正式模式的路由(`/api/auth/roles/*`)需要JWT认证 +3. 在Swagger UI中必须先配置Authorize才能测试需要认证的API +4. 建议先使用测试模式验证API功能,再使用正式模式测试认证流程 + +## 下一步建议 + +1. **前端集成**:将认证服务集成到前端应用中 +2. **错误处理**:完善API错误处理和响应格式 +3. **权限控制**:实现基于角色的权限控制 +4. **API测试**:编写自动化API测试用例 diff --git a/gofaster/app/dist/renderer/js/index.js b/gofaster/app/dist/renderer/js/index.js index 83c0f55..1812456 100644 --- a/gofaster/app/dist/renderer/js/index.js +++ b/gofaster/app/dist/renderer/js/index.js @@ -14510,7 +14510,7 @@ __webpack_require__.r(__webpack_exports__); /******/ /******/ /* webpack/runtime/getFullHash */ /******/ (() => { -/******/ __webpack_require__.h = () => ("fd7db7612102d9d9") +/******/ __webpack_require__.h = () => ("a30697658a725fba") /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ diff --git a/gofaster/app/src/main/index.js b/gofaster/app/src/main/index.js index 590e5b7..f921cda 100644 --- a/gofaster/app/src/main/index.js +++ b/gofaster/app/src/main/index.js @@ -65,7 +65,13 @@ let isDev = null function getIsDev() { if (isDev === null) { try { - isDev = process.env.NODE_ENV === 'development' || !app.isPackaged + // 确保app对象可用 + if (app && typeof app.isPackaged === 'boolean') { + isDev = process.env.NODE_ENV === 'development' || !app.isPackaged + } else { + // 如果app对象还没有准备好,默认使用开发环境 + isDev = process.env.NODE_ENV === 'development' || true + } } catch (error) { // 如果app对象还没有准备好,默认使用开发环境 isDev = process.env.NODE_ENV === 'development' || true @@ -77,6 +83,12 @@ function getIsDev() { // 初始化日志系统 function initLogging() { try { + // 确保app对象可用 + if (!app || typeof app.getPath !== 'function') { + console.warn('app对象不可用,跳过日志系统初始化'); + return; + } + // 创建日志目录 const logDir = path.join(app.getPath('userData'), 'logs') if (!fs.existsSync(logDir)) { @@ -155,6 +167,7 @@ if (getIsDev() && !process.argv.includes('--watch')) { /\.git|[\/\\]\./ ] }); + console.log('热重载已启用'); } catch (error) { console.log('Hot reload failed:', error.message); } @@ -502,212 +515,225 @@ function createWindow() { } } -app.whenReady().then(() => { - - // 初始化窗口状态管理器 - initWindowStateManager() - - // 初始化日志系统 - initLogging() - - createWindow() +// 确保app对象可用后再调用whenReady +if (app && typeof app.whenReady === 'function') { + app.whenReady().then(() => { + + // 初始化窗口状态管理器 + initWindowStateManager() + + // 初始化日志系统 + initLogging() + + createWindow() - // 修复IPC通信问题 - ipcMain.on('request-status', (event) => { - try { - const statusData = { - status: 'running', - timestamp: Date.now() + // 修复IPC通信问题 + ipcMain.on('request-status', (event) => { + try { + const statusData = { + status: 'running', + timestamp: Date.now() + } + event.sender.send('status-update', statusData) + } catch (error) { + console.error('IPC communication error:', error); } - event.sender.send('status-update', statusData) - } catch (error) { - console.error('IPC communication error:', error); - } - }) + }) - // 添加获取进程内存信息的API - ipcMain.handle('get-process-memory-info', async (event) => { - try { - // 使用 process.memoryUsage 获取内存信息 - const memoryUsage = process.memoryUsage(); - - // 获取更详细的系统内存信息 - let systemMemoryInfo = {}; + // 添加获取进程内存信息的API + ipcMain.handle('get-process-memory-info', async (event) => { try { - // 尝试使用 require('os') 获取系统内存信息 - const os = require('os'); - systemMemoryInfo = { - totalMemory: os.totalmem(), - freeMemory: os.freemem(), - usedMemory: os.totalmem() - os.freemem() + // 使用 process.memoryUsage 获取内存信息 + const memoryUsage = process.memoryUsage(); + + // 获取更详细的系统内存信息 + let systemMemoryInfo = {}; + try { + // 尝试使用 require('os') 获取系统内存信息 + const os = require('os'); + systemMemoryInfo = { + totalMemory: os.totalmem(), + freeMemory: os.freemem(), + usedMemory: os.totalmem() - os.freemem() + }; + } catch (osError) { + console.warn('Cannot get system memory info:', osError.message); + } + + return { + // 进程内存使用情况 + privateBytes: memoryUsage.rss, // Resident Set Size - 进程占用的物理内存 + sharedBytes: memoryUsage.external, // 外部内存使用 + heapUsed: memoryUsage.heapUsed, // JavaScript堆内存使用 + heapTotal: memoryUsage.heapTotal, // JavaScript堆内存总量 + // 系统内存信息 + systemTotal: systemMemoryInfo.totalMemory || 0, + systemFree: systemMemoryInfo.freeMemory || 0, + systemUsed: systemMemoryInfo.usedMemory || 0 + }; + } catch (error) { + console.error('Failed to get memory info:', error); + // 返回默认值 + return { + privateBytes: 0, + sharedBytes: 0, + heapUsed: 0, + heapTotal: 0, + systemTotal: 0, + systemFree: 0, + systemUsed: 0 }; - } catch (osError) { - console.warn('Cannot get system memory info:', osError.message); } - - return { - // 进程内存使用情况 - privateBytes: memoryUsage.rss, // Resident Set Size - 进程占用的物理内存 - sharedBytes: memoryUsage.external, // 外部内存使用 - heapUsed: memoryUsage.heapUsed, // JavaScript堆内存使用 - heapTotal: memoryUsage.heapTotal, // JavaScript堆内存总量 - // 系统内存信息 - systemTotal: systemMemoryInfo.totalMemory || 0, - systemFree: systemMemoryInfo.freeMemory || 0, - systemUsed: systemMemoryInfo.usedMemory || 0 - }; - } catch (error) { - console.error('Failed to get memory info:', error); - // 返回默认值 - return { - privateBytes: 0, - sharedBytes: 0, - heapUsed: 0, - heapTotal: 0, - systemTotal: 0, - systemFree: 0, - systemUsed: 0 - }; - } - }); - - // 添加更新窗口标题的API -ipcMain.handle('update-window-title', async (event, newTitle) => { - try { - if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.setTitle(newTitle); - return { success: true, message: '窗口标题更新成功' }; - } else { - return { success: false, message: '主窗口不存在或已销毁' }; - } - } catch (error) { - console.error('更新窗口标题失败:', error); - return { success: false, message: '更新窗口标题失败: ' + error.message }; - } -}); - -// 获取错误日志计数 -ipcMain.handle('get-error-log-count', async () => { - return { count: errorLogCount } -}); - -// 获取日志文件路径 -ipcMain.handle('get-log-file-path', async () => { - return { path: logFilePath } -}); + }); + + // 添加更新窗口标题的API + ipcMain.handle('update-window-title', async (event, newTitle) => { + try { + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.setTitle(newTitle); + return { success: true, message: '窗口标题更新成功' }; + } else { + return { success: false, message: '主窗口不存在或已销毁' }; + } + } catch (error) { + console.error('更新窗口标题失败:', error); + return { success: false, message: '更新窗口标题失败: ' + error.message }; + } + }); -// 打开日志文件所在文件夹 -ipcMain.handle('open-log-folder', async () => { - try { - const path = require('path'); - const logDir = path.dirname(logFilePath); - require('electron').shell.openPath(logDir); - return { success: true, message: '日志文件夹已打开' }; - } catch (error) { - console.error('打开日志文件夹失败:', error); - return { success: false, message: '打开日志文件夹失败: ' + error.message }; - } -}); + // 获取错误日志计数 + ipcMain.handle('get-error-log-count', async () => { + return { count: errorLogCount } + }); -// 获取本地IP地址 -ipcMain.handle('get-local-ip', async () => { - try { - const ipInfo = getLocalIPAddresses(); - if (ipInfo) { - return { - success: true, - ip: ipInfo.address, - interface: ipInfo.name, - netmask: ipInfo.netmask - }; - } else { - return { - success: false, - message: '无法获取本地IP地址', - fallback: '127.0.0.1' - }; - } - } catch (error) { - console.error('获取本地IP地址失败:', error); - return { - success: false, - message: '获取本地IP地址失败: ' + error.message, - fallback: '127.0.0.1' - }; - } -}); + // 获取日志文件路径 + ipcMain.handle('get-log-file-path', async () => { + return { path: logFilePath } + }); -// 窗口状态管理相关的IPC处理器 -ipcMain.handle('reset-window-state', async () => { - try { - if (!windowStateManager) { - return { - success: false, - message: '窗口状态管理器未初始化' + // 打开日志文件所在文件夹 + ipcMain.handle('open-log-folder', async () => { + try { + if (logFilePath) { + const logDir = path.dirname(logFilePath) + require('electron').shell.openPath(logDir) + return { success: true, message: '日志文件夹已打开' } + } else { + return { success: false, message: '日志文件路径未设置' } + } + } catch (error) { + console.error('打开日志文件夹失败:', error) + return { success: false, message: '打开日志文件夹失败: ' + error.message } } - } - const result = windowStateManager.resetState() - return { - success: result, - message: result ? '窗口状态已重置' : '没有找到保存的窗口状态' - } - } catch (error) { - console.error('重置窗口状态失败:', error) - return { - success: false, - message: '重置窗口状态失败: ' + error.message - } - } -}) + }); -ipcMain.handle('get-window-state-info', async () => { - try { - if (!windowStateManager) { - return { - success: false, - message: '窗口状态管理器未初始化' + // 获取本地IP地址 + ipcMain.handle('get-local-ip', async () => { + try { + const ipInfo = getLocalIPAddresses() + if (ipInfo) { + return { + success: true, + address: ipInfo.address, + name: ipInfo.name, + netmask: ipInfo.netmask, + family: ipInfo.family + } + } else { + return { + success: false, + message: '无法获取本地IP地址', + fallback: '127.0.0.1' + } + } + } catch (error) { + console.error('获取本地IP地址失败:', error) + return { + success: false, + message: '获取本地IP地址失败: ' + error.message, + fallback: '127.0.0.1' + }; } - } - const hasState = windowStateManager.hasSavedState() - const statePath = windowStateManager.getStatePath() - let currentState = null - - if (hasState) { + }); + + // 窗口状态管理相关的IPC处理器 + ipcMain.handle('reset-window-state', async () => { try { - const stateData = fs.readFileSync(statePath, 'utf8') - currentState = JSON.parse(stateData) + if (!windowStateManager) { + return { + success: false, + message: '窗口状态管理器未初始化' + } + } + const result = windowStateManager.resetState() + return { + success: result, + message: result ? '窗口状态已重置' : '没有找到保存的窗口状态' + } } catch (error) { - console.error('读取当前窗口状态失败:', error) + console.error('重置窗口状态失败:', error) + return { + success: false, + message: '重置窗口状态失败: ' + error.message + } } - } - - return { - hasSavedState: hasState, - statePath: statePath, - currentState: currentState - } - } catch (error) { - console.error('获取窗口状态信息失败:', error) - return { - success: false, - message: '获取窗口状态信息失败: ' + error.message - } - } -}) + }) -}); // 闭合 app.whenReady().then() 的回调函数 + ipcMain.handle('get-window-state-info', async () => { + try { + if (!windowStateManager) { + return { + success: false, + message: '窗口状态管理器未初始化' + } + } + const hasState = windowStateManager.hasSavedState() + const statePath = windowStateManager.getStatePath() + let currentState = null + + if (hasState) { + try { + const stateData = fs.readFileSync(statePath, 'utf8') + currentState = JSON.parse(stateData) + } catch (error) { + console.error('读取当前窗口状态失败:', error) + } + } + + return { + hasSavedState: hasState, + statePath: statePath, + currentState: currentState + } + } catch (error) { + console.error('获取窗口状态信息失败:', error) + return { + success: false, + message: '获取窗口状态信息失败: ' + error.message + } + } + }) -app.on('window-all-closed', () => { - // 由于已经在窗口 close 事件中处理了退出登录,这里直接退出 - if (process.platform !== 'darwin') { - app.quit() - } -}) + }); // 闭合 app.whenReady().then() 的回调函数 +} else { + console.error('app对象不可用或whenReady方法不存在'); +} +// 确保app对象可用后再注册事件监听器 +if (app && typeof app.on === 'function') { + app.on('window-all-closed', () => { + // 由于已经在窗口 close 事件中处理了退出登录,这里直接退出 + if (process.platform !== 'darwin') { + app.quit() + } + }) -app.on('activate', () => { - if (mainWindow === null) createWindow() -}) + app.on('activate', () => { + if (mainWindow === null) createWindow() + }) +} else { + console.error('app对象不可用,无法注册事件监听器'); +} // 添加错误处理 process.on('uncaughtException', (error) => { diff --git a/gofaster/app/src/renderer/modules/role-management/views/RoleApiTest.vue b/gofaster/app/src/renderer/modules/role-management/views/RoleApiTest.vue new file mode 100644 index 0000000..1fdc85c --- /dev/null +++ b/gofaster/app/src/renderer/modules/role-management/views/RoleApiTest.vue @@ -0,0 +1,548 @@ + + + + 角色管理API测试 + + + + {{ testMode ? '测试模式' : '正式模式' }} + + + 运行所有测试 + + + + + + + + API配置信息 + + + API基础地址: + {{ apiBaseUrl }} + + + 当前模式: + + {{ testMode ? '测试模式' : '正式模式' }} + + + + API路径: + {{ currentApiPath }} + + + + + + + 测试结果 + + + + {{ result.name }} + {{ getStatusText(result.status) }} + + + {{ result.message }} + + + {{ JSON.stringify(result.data, null, 2) }} + + + + + + + + 手动测试 + + + 获取角色列表 + + {{ testing ? '测试中...' : '测试获取角色列表' }} + + + + + 创建角色 + + 角色名称: + + + + 角色代码: + + + + 描述: + + + + {{ testing ? '测试中...' : '测试创建角色' }} + + + + + 更新角色 + + 角色ID: + + + + {{ testing ? '测试中...' : '测试更新角色' }} + + + + + 删除角色 + + 角色ID: + + + + {{ testing ? '测试中...' : '测试删除角色' }} + + + + + + + + + + + diff --git a/gofaster/backend/docs/docs.go b/gofaster/backend/docs/docs.go index c19f617..fc99aef 100644 --- a/gofaster/backend/docs/docs.go +++ b/gofaster/backend/docs/docs.go @@ -24,7 +24,7 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/users": { + "/auth/admin/users": { "get": { "description": "获取分页用户列表", "consumes": [ @@ -82,7 +82,7 @@ const docTemplate = `{ } } }, - "/users/{id}": { + "/auth/admin/users/{id}": { "get": { "description": "根据ID获取用户详情", "consumes": [ @@ -260,9 +260,640 @@ const docTemplate = `{ } } } + }, + "/auth/captcha": { + "get": { + "description": "生成图形验证码,用于登录验证", + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "生成验证码", + "responses": { + "200": { + "description": "验证码生成成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.CaptchaResponse" + } + } + } + ] + } + } + } + } + }, + "/auth/login": { + "post": { + "description": "用户登录接口,支持验证码验证和密码错误次数限制", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "用户登录", + "parameters": [ + { + "description": "登录请求参数", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.LoginRequest" + } + } + ], + "responses": { + "200": { + "description": "登录成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.LoginResponse" + } + } + } + ] + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "认证失败", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "423": { + "description": "账户被锁定", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/logout": { + "post": { + "description": "用户登出接口", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "用户登出", + "parameters": [ + { + "description": "登出请求参数", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.LogoutRequest" + } + } + ], + "responses": { + "200": { + "description": "登出成功", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/refresh": { + "post": { + "description": "使用刷新令牌获取新的访问令牌", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "刷新访问令牌", + "parameters": [ + { + "description": "刷新令牌请求参数", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RefreshTokenRequest" + } + } + ], + "responses": { + "200": { + "description": "刷新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.LoginResponse" + } + } + } + ] + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "刷新令牌无效", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/roles": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "分页获取角色列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "获取角色列表", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 10, + "description": "每页数量", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Role" + } + }, + "page": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + } + } + } + ] + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建新的角色", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "创建角色", + "parameters": [ + { + "description": "角色信息", + "name": "role", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Role" + } + } + ], + "responses": { + "200": { + "description": "创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.Role" + } + } + } + ] + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/roles/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据ID获取角色详细信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "获取角色详情", + "parameters": [ + { + "type": "integer", + "description": "角色ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.Role" + } + } + } + ] + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "角色不存在", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据ID更新角色信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "更新角色", + "parameters": [ + { + "type": "integer", + "description": "角色ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "角色信息", + "name": "role", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Role" + } + } + ], + "responses": { + "200": { + "description": "更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.Role" + } + } + } + ] + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据ID删除角色", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "删除角色", + "parameters": [ + { + "type": "integer", + "description": "角色ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "删除成功", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/userinfo": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "获取当前登录用户的详细信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "获取用户信息", + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.UserInfo" + } + } + } + ] + } + }, + "401": { + "description": "未授权", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } } }, "definitions": { + "model.CaptchaResponse": { + "type": "object", + "properties": { + "captcha_id": { + "type": "string" + }, + "captcha_image": { + "description": "Base64编码的图片", + "type": "string" + }, + "expires_in": { + "type": "integer" + } + } + }, + "model.LoginRequest": { + "type": "object", + "required": [ + "captcha", + "captcha_id", + "password", + "username" + ], + "properties": { + "captcha": { + "type": "string" + }, + "captcha_id": { + "type": "string" + }, + "client_ip": { + "description": "客户端IP地址", + "type": "string" + }, + "password": { + "type": "string", + "maxLength": 100, + "minLength": 6 + }, + "username": { + "type": "string", + "maxLength": 50, + "minLength": 3 + } + } + }, + "model.LoginResponse": { + "type": "object", + "properties": { + "expires_in": { + "type": "integer" + }, + "force_change_password": { + "description": "是否需要强制修改密码", + "type": "boolean" + }, + "refresh_token": { + "type": "string" + }, + "token": { + "type": "string" + }, + "token_type": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/model.UserInfo" + } + } + }, + "model.LogoutRequest": { + "type": "object", + "required": [ + "token" + ], + "properties": { + "token": { + "type": "string" + } + } + }, "model.Permission": { "type": "object", "properties": { @@ -290,9 +921,43 @@ const docTemplate = `{ } } }, + "model.PermissionInfo": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "resource": { + "type": "string" + } + } + }, + "model.RefreshTokenRequest": { + "type": "object", + "required": [ + "refresh_token" + ], + "properties": { + "refresh_token": { + "type": "string" + } + } + }, "model.Role": { "type": "object", "properties": { + "code": { + "type": "string" + }, "created_at": { "type": "string" }, @@ -316,6 +981,29 @@ const docTemplate = `{ } } }, + "model.RoleInfo": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/model.PermissionInfo" + } + } + } + }, "model.User": { "type": "object", "properties": { @@ -325,9 +1013,25 @@ const docTemplate = `{ "email": { "type": "string" }, + "force_change_password": { + "description": "是否强制修改密码", + "type": "boolean" + }, "id": { "type": "integer" }, + "last_login_at": { + "description": "最后登录时间", + "type": "string" + }, + "last_login_ip": { + "description": "最后登录IP", + "type": "string" + }, + "password_changed_at": { + "description": "密码最后修改时间", + "type": "string" + }, "phone": { "type": "string" }, @@ -338,7 +1042,7 @@ const docTemplate = `{ } }, "status": { - "description": "1-正常 2-禁用", + "description": "1-正常 2-禁用 3-锁定", "type": "integer" }, "updated_at": { @@ -348,6 +1052,69 @@ const docTemplate = `{ "type": "string" } } + }, + "model.UserInfo": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "last_login_at": { + "type": "string" + }, + "last_login_ip": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleInfo" + } + }, + "status": { + "type": "integer" + }, + "username": { + "type": "string" + } + } + }, + "response.Response": { + "type": "object", + "properties": { + "code": { + "description": "响应状态码", + "type": "integer" + }, + "data": { + "description": "响应数据" + }, + "error": { + "description": "错误信息", + "type": "string" + }, + "message": { + "description": "响应消息", + "type": "string" + } + } + } + }, + "securityDefinitions": { + "BearerAuth": { + "description": "请输入 \"Bearer \" + JWT token,例如: \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"", + "type": "apiKey", + "name": "Authorization", + "in": "header" } } }` diff --git a/gofaster/backend/docs/swagger.json b/gofaster/backend/docs/swagger.json index 3e663a5..16441d1 100644 --- a/gofaster/backend/docs/swagger.json +++ b/gofaster/backend/docs/swagger.json @@ -21,7 +21,7 @@ "host": "localhost:8080", "basePath": "/api", "paths": { - "/users": { + "/auth/admin/users": { "get": { "description": "获取分页用户列表", "consumes": [ @@ -79,7 +79,7 @@ } } }, - "/users/{id}": { + "/auth/admin/users/{id}": { "get": { "description": "根据ID获取用户详情", "consumes": [ @@ -257,9 +257,640 @@ } } } + }, + "/auth/captcha": { + "get": { + "description": "生成图形验证码,用于登录验证", + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "生成验证码", + "responses": { + "200": { + "description": "验证码生成成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.CaptchaResponse" + } + } + } + ] + } + } + } + } + }, + "/auth/login": { + "post": { + "description": "用户登录接口,支持验证码验证和密码错误次数限制", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "用户登录", + "parameters": [ + { + "description": "登录请求参数", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.LoginRequest" + } + } + ], + "responses": { + "200": { + "description": "登录成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.LoginResponse" + } + } + } + ] + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "认证失败", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "423": { + "description": "账户被锁定", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/logout": { + "post": { + "description": "用户登出接口", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "用户登出", + "parameters": [ + { + "description": "登出请求参数", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.LogoutRequest" + } + } + ], + "responses": { + "200": { + "description": "登出成功", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/refresh": { + "post": { + "description": "使用刷新令牌获取新的访问令牌", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "刷新访问令牌", + "parameters": [ + { + "description": "刷新令牌请求参数", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.RefreshTokenRequest" + } + } + ], + "responses": { + "200": { + "description": "刷新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.LoginResponse" + } + } + } + ] + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "刷新令牌无效", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/roles": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "分页获取角色列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "获取角色列表", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 10, + "description": "每页数量", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Role" + } + }, + "page": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + } + } + } + ] + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建新的角色", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "创建角色", + "parameters": [ + { + "description": "角色信息", + "name": "role", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Role" + } + } + ], + "responses": { + "200": { + "description": "创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.Role" + } + } + } + ] + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/roles/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据ID获取角色详细信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "获取角色详情", + "parameters": [ + { + "type": "integer", + "description": "角色ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.Role" + } + } + } + ] + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "角色不存在", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据ID更新角色信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "更新角色", + "parameters": [ + { + "type": "integer", + "description": "角色ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "角色信息", + "name": "role", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.Role" + } + } + ], + "responses": { + "200": { + "description": "更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.Role" + } + } + } + ] + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据ID删除角色", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "删除角色", + "parameters": [ + { + "type": "integer", + "description": "角色ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "删除成功", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/userinfo": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "获取当前登录用户的详细信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "认证" + ], + "summary": "获取用户信息", + "responses": { + "200": { + "description": "获取成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.UserInfo" + } + } + } + ] + } + }, + "401": { + "description": "未授权", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } } }, "definitions": { + "model.CaptchaResponse": { + "type": "object", + "properties": { + "captcha_id": { + "type": "string" + }, + "captcha_image": { + "description": "Base64编码的图片", + "type": "string" + }, + "expires_in": { + "type": "integer" + } + } + }, + "model.LoginRequest": { + "type": "object", + "required": [ + "captcha", + "captcha_id", + "password", + "username" + ], + "properties": { + "captcha": { + "type": "string" + }, + "captcha_id": { + "type": "string" + }, + "client_ip": { + "description": "客户端IP地址", + "type": "string" + }, + "password": { + "type": "string", + "maxLength": 100, + "minLength": 6 + }, + "username": { + "type": "string", + "maxLength": 50, + "minLength": 3 + } + } + }, + "model.LoginResponse": { + "type": "object", + "properties": { + "expires_in": { + "type": "integer" + }, + "force_change_password": { + "description": "是否需要强制修改密码", + "type": "boolean" + }, + "refresh_token": { + "type": "string" + }, + "token": { + "type": "string" + }, + "token_type": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/model.UserInfo" + } + } + }, + "model.LogoutRequest": { + "type": "object", + "required": [ + "token" + ], + "properties": { + "token": { + "type": "string" + } + } + }, "model.Permission": { "type": "object", "properties": { @@ -287,9 +918,43 @@ } } }, + "model.PermissionInfo": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "resource": { + "type": "string" + } + } + }, + "model.RefreshTokenRequest": { + "type": "object", + "required": [ + "refresh_token" + ], + "properties": { + "refresh_token": { + "type": "string" + } + } + }, "model.Role": { "type": "object", "properties": { + "code": { + "type": "string" + }, "created_at": { "type": "string" }, @@ -313,6 +978,29 @@ } } }, + "model.RoleInfo": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/model.PermissionInfo" + } + } + } + }, "model.User": { "type": "object", "properties": { @@ -322,9 +1010,25 @@ "email": { "type": "string" }, + "force_change_password": { + "description": "是否强制修改密码", + "type": "boolean" + }, "id": { "type": "integer" }, + "last_login_at": { + "description": "最后登录时间", + "type": "string" + }, + "last_login_ip": { + "description": "最后登录IP", + "type": "string" + }, + "password_changed_at": { + "description": "密码最后修改时间", + "type": "string" + }, "phone": { "type": "string" }, @@ -335,7 +1039,7 @@ } }, "status": { - "description": "1-正常 2-禁用", + "description": "1-正常 2-禁用 3-锁定", "type": "integer" }, "updated_at": { @@ -345,6 +1049,69 @@ "type": "string" } } + }, + "model.UserInfo": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "last_login_at": { + "type": "string" + }, + "last_login_ip": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/model.RoleInfo" + } + }, + "status": { + "type": "integer" + }, + "username": { + "type": "string" + } + } + }, + "response.Response": { + "type": "object", + "properties": { + "code": { + "description": "响应状态码", + "type": "integer" + }, + "data": { + "description": "响应数据" + }, + "error": { + "description": "错误信息", + "type": "string" + }, + "message": { + "description": "响应消息", + "type": "string" + } + } + } + }, + "securityDefinitions": { + "BearerAuth": { + "description": "请输入 \"Bearer \" + JWT token,例如: \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"", + "type": "apiKey", + "name": "Authorization", + "in": "header" } } } \ No newline at end of file diff --git a/gofaster/backend/docs/swagger.yaml b/gofaster/backend/docs/swagger.yaml index ed4ab78..8584585 100644 --- a/gofaster/backend/docs/swagger.yaml +++ b/gofaster/backend/docs/swagger.yaml @@ -1,5 +1,61 @@ basePath: /api definitions: + model.CaptchaResponse: + properties: + captcha_id: + type: string + captcha_image: + description: Base64编码的图片 + type: string + expires_in: + type: integer + type: object + model.LoginRequest: + properties: + captcha: + type: string + captcha_id: + type: string + client_ip: + description: 客户端IP地址 + type: string + password: + maxLength: 100 + minLength: 6 + type: string + username: + maxLength: 50 + minLength: 3 + type: string + required: + - captcha + - captcha_id + - password + - username + type: object + model.LoginResponse: + properties: + expires_in: + type: integer + force_change_password: + description: 是否需要强制修改密码 + type: boolean + refresh_token: + type: string + token: + type: string + token_type: + type: string + user: + $ref: '#/definitions/model.UserInfo' + type: object + model.LogoutRequest: + properties: + token: + type: string + required: + - token + type: object model.Permission: properties: action: @@ -18,8 +74,30 @@ definitions: updated_at: type: string type: object + model.PermissionInfo: + properties: + action: + type: string + description: + type: string + id: + type: integer + name: + type: string + resource: + type: string + type: object + model.RefreshTokenRequest: + properties: + refresh_token: + type: string + required: + - refresh_token + type: object model.Role: properties: + code: + type: string created_at: type: string description: @@ -35,14 +113,41 @@ definitions: updated_at: type: string type: object + model.RoleInfo: + properties: + code: + type: string + description: + type: string + id: + type: integer + name: + type: string + permissions: + items: + $ref: '#/definitions/model.PermissionInfo' + type: array + type: object model.User: properties: created_at: type: string email: type: string + force_change_password: + description: 是否强制修改密码 + type: boolean id: type: integer + last_login_at: + description: 最后登录时间 + type: string + last_login_ip: + description: 最后登录IP + type: string + password_changed_at: + description: 密码最后修改时间 + type: string phone: type: string roles: @@ -50,13 +155,50 @@ definitions: $ref: '#/definitions/model.Role' type: array status: - description: 1-正常 2-禁用 + description: 1-正常 2-禁用 3-锁定 type: integer updated_at: type: string username: type: string type: object + model.UserInfo: + properties: + created_at: + type: string + email: + type: string + id: + type: integer + last_login_at: + type: string + last_login_ip: + type: string + phone: + type: string + roles: + items: + $ref: '#/definitions/model.RoleInfo' + type: array + status: + type: integer + username: + type: string + type: object + response.Response: + properties: + code: + description: 响应状态码 + type: integer + data: + description: 响应数据 + error: + description: 错误信息 + type: string + message: + description: 响应消息 + type: string + type: object host: localhost:8080 info: contact: @@ -71,7 +213,7 @@ info: title: GoFaster API version: "1.0" paths: - /users: + /auth/admin/users: get: consumes: - application/json @@ -110,7 +252,7 @@ paths: summary: 获取用户列表 tags: - 用户管理 - /users/{id}: + /auth/admin/users/{id}: delete: consumes: - application/json @@ -229,6 +371,343 @@ paths: summary: 更新用户信息 tags: - 用户管理 + /auth/captcha: + get: + description: 生成图形验证码,用于登录验证 + produces: + - application/json + responses: + "200": + description: 验证码生成成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/model.CaptchaResponse' + type: object + summary: 生成验证码 + tags: + - 认证 + /auth/login: + post: + consumes: + - application/json + description: 用户登录接口,支持验证码验证和密码错误次数限制 + parameters: + - description: 登录请求参数 + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.LoginRequest' + produces: + - application/json + responses: + "200": + description: 登录成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/model.LoginResponse' + type: object + "400": + description: 请求参数错误 + schema: + $ref: '#/definitions/response.Response' + "401": + description: 认证失败 + schema: + $ref: '#/definitions/response.Response' + "423": + description: 账户被锁定 + schema: + $ref: '#/definitions/response.Response' + summary: 用户登录 + tags: + - 认证 + /auth/logout: + post: + consumes: + - application/json + description: 用户登出接口 + parameters: + - description: 登出请求参数 + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.LogoutRequest' + produces: + - application/json + responses: + "200": + description: 登出成功 + schema: + $ref: '#/definitions/response.Response' + summary: 用户登出 + tags: + - 认证 + /auth/refresh: + post: + consumes: + - application/json + description: 使用刷新令牌获取新的访问令牌 + parameters: + - description: 刷新令牌请求参数 + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.RefreshTokenRequest' + produces: + - application/json + responses: + "200": + description: 刷新成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/model.LoginResponse' + type: object + "400": + description: 请求参数错误 + schema: + $ref: '#/definitions/response.Response' + "401": + description: 刷新令牌无效 + schema: + $ref: '#/definitions/response.Response' + summary: 刷新访问令牌 + tags: + - 认证 + /auth/roles: + get: + consumes: + - application/json + description: 分页获取角色列表 + parameters: + - default: 1 + description: 页码 + in: query + name: page + type: integer + - default: 10 + description: 每页数量 + in: query + name: pageSize + type: integer + produces: + - application/json + responses: + "200": + description: 获取成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + properties: + data: + items: + $ref: '#/definitions/model.Role' + type: array + page: + type: integer + size: + type: integer + total: + type: integer + type: object + type: object + "500": + description: 服务器内部错误 + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: 获取角色列表 + tags: + - 角色管理 + post: + consumes: + - application/json + description: 创建新的角色 + parameters: + - description: 角色信息 + in: body + name: role + required: true + schema: + $ref: '#/definitions/model.Role' + produces: + - application/json + responses: + "200": + description: 创建成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/model.Role' + type: object + "400": + description: 请求参数错误 + schema: + $ref: '#/definitions/response.Response' + "500": + description: 服务器内部错误 + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: 创建角色 + tags: + - 角色管理 + /auth/roles/{id}: + delete: + consumes: + - application/json + description: 根据ID删除角色 + parameters: + - description: 角色ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: 删除成功 + schema: + $ref: '#/definitions/response.Response' + "400": + description: 请求参数错误 + schema: + $ref: '#/definitions/response.Response' + "500": + description: 服务器内部错误 + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: 删除角色 + tags: + - 角色管理 + get: + consumes: + - application/json + description: 根据ID获取角色详细信息 + parameters: + - description: 角色ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: 获取成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/model.Role' + type: object + "400": + description: 请求参数错误 + schema: + $ref: '#/definitions/response.Response' + "404": + description: 角色不存在 + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: 获取角色详情 + tags: + - 角色管理 + put: + consumes: + - application/json + description: 根据ID更新角色信息 + parameters: + - description: 角色ID + in: path + name: id + required: true + type: integer + - description: 角色信息 + in: body + name: role + required: true + schema: + $ref: '#/definitions/model.Role' + produces: + - application/json + responses: + "200": + description: 更新成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/model.Role' + type: object + "400": + description: 请求参数错误 + schema: + $ref: '#/definitions/response.Response' + "500": + description: 服务器内部错误 + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: 更新角色 + tags: + - 角色管理 + /auth/userinfo: + get: + consumes: + - application/json + description: 获取当前登录用户的详细信息 + produces: + - application/json + responses: + "200": + description: 获取成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/model.UserInfo' + type: object + "401": + description: 未授权 + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: 获取用户信息 + tags: + - 认证 schemes: - http +securityDefinitions: + BearerAuth: + description: '请输入 "Bearer " + JWT token,例如: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."' + in: header + name: Authorization + type: apiKey swagger: "2.0" diff --git a/gofaster/backend/internal/auth/controller/role_controller.go b/gofaster/backend/internal/auth/controller/role_controller.go index 95f1f65..00ef672 100644 --- a/gofaster/backend/internal/auth/controller/role_controller.go +++ b/gofaster/backend/internal/auth/controller/role_controller.go @@ -22,6 +22,17 @@ func NewRoleController(roleService *service.RoleService) *RoleController { } // CreateRole 创建角色 +// @Summary 创建角色 +// @Description 创建新的角色 +// @Tags 角色管理 +// @Accept json +// @Produce json +// @Param role body model.Role true "角色信息" +// @Success 200 {object} response.Response{data=model.Role} "创建成功" +// @Failure 400 {object} response.Response "请求参数错误" +// @Failure 500 {object} response.Response "服务器内部错误" +// @Security BearerAuth +// @Router /auth/roles [post] func (c *RoleController) CreateRole(ctx *gin.Context) { var role model.Role if err := ctx.ShouldBindJSON(&role); err != nil { @@ -38,6 +49,18 @@ func (c *RoleController) CreateRole(ctx *gin.Context) { } // UpdateRole 更新角色 +// @Summary 更新角色 +// @Description 根据ID更新角色信息 +// @Tags 角色管理 +// @Accept json +// @Produce json +// @Param id path int true "角色ID" +// @Param role body model.Role true "角色信息" +// @Success 200 {object} response.Response{data=model.Role} "更新成功" +// @Failure 400 {object} response.Response "请求参数错误" +// @Failure 500 {object} response.Response "服务器内部错误" +// @Security BearerAuth +// @Router /auth/roles/{id} [put] func (c *RoleController) UpdateRole(ctx *gin.Context) { idStr := ctx.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) @@ -63,6 +86,17 @@ func (c *RoleController) UpdateRole(ctx *gin.Context) { } // DeleteRole 删除角色 +// @Summary 删除角色 +// @Description 根据ID删除角色 +// @Tags 角色管理 +// @Accept json +// @Produce json +// @Param id path int true "角色ID" +// @Success 200 {object} response.Response "删除成功" +// @Failure 400 {object} response.Response "请求参数错误" +// @Failure 500 {object} response.Response "服务器内部错误" +// @Security BearerAuth +// @Router /auth/roles/{id} [delete] func (c *RoleController) DeleteRole(ctx *gin.Context) { idStr := ctx.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) @@ -80,6 +114,17 @@ func (c *RoleController) DeleteRole(ctx *gin.Context) { } // GetRole 获取角色详情 +// @Summary 获取角色详情 +// @Description 根据ID获取角色详细信息 +// @Tags 角色管理 +// @Accept json +// @Produce json +// @Param id path int true "角色ID" +// @Success 200 {object} response.Response{data=model.Role} "获取成功" +// @Failure 400 {object} response.Response "请求参数错误" +// @Failure 404 {object} response.Response "角色不存在" +// @Security BearerAuth +// @Router /auth/roles/{id} [get] func (c *RoleController) GetRole(ctx *gin.Context) { idStr := ctx.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) @@ -98,6 +143,17 @@ func (c *RoleController) GetRole(ctx *gin.Context) { } // ListRoles 获取角色列表 +// @Summary 获取角色列表 +// @Description 分页获取角色列表 +// @Tags 角色管理 +// @Accept json +// @Produce json +// @Param page query int false "页码" default(1) +// @Param pageSize query int false "每页数量" default(10) +// @Success 200 {object} response.Response{data=object{data=[]model.Role,page=int,size=int,total=int}} "获取成功" +// @Failure 500 {object} response.Response "服务器内部错误" +// @Security BearerAuth +// @Router /auth/roles [get] func (c *RoleController) ListRoles(ctx *gin.Context) { pageStr := ctx.DefaultQuery("page", "1") pageSizeStr := ctx.DefaultQuery("pageSize", "10") diff --git a/gofaster/backend/internal/auth/controller/user_controller.go b/gofaster/backend/internal/auth/controller/user_controller.go index 4bcdfb0..b09735c 100644 --- a/gofaster/backend/internal/auth/controller/user_controller.go +++ b/gofaster/backend/internal/auth/controller/user_controller.go @@ -33,7 +33,7 @@ func NewUserController(userService *service.UserService) *UserController { // @Success 200 {object} map[string]interface{} "用户列表" // @Failure 400 {object} map[string]string "请求参数错误" // @Failure 500 {object} map[string]string "服务器内部错误" -// @Router /users [get] +// @Router /auth/admin/users [get] func (c *UserController) ListUsers(ctx *gin.Context) { // 获取分页参数,默认值 page=1, pageSize=10 pageStr := ctx.DefaultQuery("page", "1") @@ -77,7 +77,7 @@ func (c *UserController) ListUsers(ctx *gin.Context) { // @Success 201 {object} model.User "创建的用户信息" // @Failure 400 {object} map[string]string "请求参数错误" // @Failure 500 {object} map[string]string "服务器内部错误" -// @Router /users [post] +// @Router /auth/admin/users [post] func (c *UserController) CreateUser(ctx *gin.Context) { var user model.User @@ -105,7 +105,7 @@ func (c *UserController) CreateUser(ctx *gin.Context) { // @Failure 400 {object} map[string]string "无效的用户ID" // @Failure 404 {object} map[string]string "用户不存在" // @Failure 500 {object} map[string]string "服务器内部错误" -// @Router /users/{id} [get] +// @Router /auth/admin/users/{id} [get] func (c *UserController) GetUser(ctx *gin.Context) { idStr := ctx.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) @@ -139,7 +139,7 @@ func (c *UserController) GetUser(ctx *gin.Context) { // @Failure 400 {object} map[string]string "无效的用户ID或请求参数" // @Failure 404 {object} map[string]string "用户不存在" // @Failure 500 {object} map[string]string "服务器内部错误" -// @Router /users/{id} [put] +// @Router /auth/admin/users/{id} [put] func (c *UserController) UpdateUser(ctx *gin.Context) { idStr := ctx.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) @@ -183,7 +183,7 @@ func (c *UserController) UpdateUser(ctx *gin.Context) { // @Failure 400 {object} map[string]string "无效的用户ID" // @Failure 404 {object} map[string]string "用户不存在" // @Failure 500 {object} map[string]string "服务器内部错误" -// @Router /users/{id} [delete] +// @Router /auth/admin/users/{id} [delete] func (c *UserController) DeleteUser(ctx *gin.Context) { idStr := ctx.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) @@ -203,48 +203,3 @@ func (c *UserController) DeleteUser(ctx *gin.Context) { response.Success(ctx, "用户删除成功", nil) } - -// GetRoles godoc -// @Summary 获取角色列表 -// @Description 获取分页角色列表 -// @Tags 用户管理 -// @Accept json -// @Produce json -// @Param page query int false "页码" default(1) -// @Param pageSize query int false "每页数量" default(100) -// @Success 200 {object} map[string]interface{} "角色列表" -// @Failure 400 {object} map[string]string "请求参数错误" -// @Failure 500 {object} map[string]string "服务器内部错误" -// @Router /roles [get] -func (c *UserController) GetRoles(ctx *gin.Context) { - // 获取分页参数,默认值 page=1, pageSize=100 - pageStr := ctx.DefaultQuery("page", "1") - pageSizeStr := ctx.DefaultQuery("pageSize", "100") - - page, err := strconv.Atoi(pageStr) - if err != nil || page < 1 { - response.Error(ctx, http.StatusBadRequest, "请求参数错误", "无效的页码参数") - return - } - - pageSize, err := strconv.Atoi(pageSizeStr) - if err != nil || pageSize < 1 || pageSize > 1000 { - response.Error(ctx, http.StatusBadRequest, "请求参数错误", "无效的每页大小参数,范围1-1000") - return - } - - // 调用服务层获取角色列表 - roles, total, err := c.userService.GetRoles(ctx.Request.Context(), page, pageSize) - if err != nil { - response.Error(ctx, http.StatusInternalServerError, "获取角色列表失败", err.Error()) - return - } - - // 返回分页结果 - response.Success(ctx, "获取角色列表成功", gin.H{ - "data": roles, - "total": total, - "page": page, - "size": pageSize, - }) -} diff --git a/gofaster/backend/internal/auth/routes/auth_routes.go b/gofaster/backend/internal/auth/routes/auth_routes.go index d812669..ef6fac8 100644 --- a/gofaster/backend/internal/auth/routes/auth_routes.go +++ b/gofaster/backend/internal/auth/routes/auth_routes.go @@ -61,14 +61,13 @@ func RegisterAuthRoutes(router *gin.RouterGroup, db *gorm.DB, jwtSecret string) // 管理员路由 admin := router.Group("/auth/admin") - admin.Use(middleware.JWTAuth(), middleware.Permission("auth", "admin")) + admin.Use(middleware.JWTAuth(), middleware.PermissionMiddleware(db, "auth", "admin")) { admin.GET("/users", userController.ListUsers) admin.POST("/users", userController.CreateUser) admin.GET("/users/:id", userController.GetUser) admin.PUT("/users/:id", userController.UpdateUser) admin.DELETE("/users/:id", userController.DeleteUser) - admin.GET("/roles", userController.GetRoles) admin.POST("/users/:id/reset-password", passwordController.ResetPassword) admin.PUT("/password-policy", passwordController.UpdatePasswordPolicy) } @@ -78,7 +77,6 @@ func RegisterAuthRoutes(router *gin.RouterGroup, db *gorm.DB, jwtSecret string) testAdmin.Use(middleware.JWTAuth()) // 只检查JWT,不检查权限 { testAdmin.GET("/users", userController.ListUsers) - testAdmin.GET("/roles", userController.GetRoles) } // 注册资源管理路由 diff --git a/gofaster/backend/internal/auth/routes/role_routes.go b/gofaster/backend/internal/auth/routes/role_routes.go index 1e0c6f4..c705829 100644 --- a/gofaster/backend/internal/auth/routes/role_routes.go +++ b/gofaster/backend/internal/auth/routes/role_routes.go @@ -16,11 +16,21 @@ func RegisterRoleRoutes(router *gin.RouterGroup, db *gorm.DB, jwtSecret string) roleService := service.NewRoleService(roleRepo) roleController := controller.NewRoleController(roleService) - // 角色管理路由组 - roleGroup := router.Group("/roles") + // 角色管理路由组 - 修复路径配置 + roleGroup := router.Group("/auth/roles") { + // 临时测试路由 - 不需要认证 + testGroup := roleGroup.Group("/test") + { + testGroup.GET("", roleController.ListRoles) // 获取角色列表 + testGroup.POST("", roleController.CreateRole) // 创建角色 + testGroup.GET("/:id", roleController.GetRole) // 获取角色详情 + testGroup.PUT("/:id", roleController.UpdateRole) // 更新角色 + testGroup.DELETE("/:id", roleController.DeleteRole) // 删除角色 + } + // 需要权限验证的路由 - roleGroup.Use(middleware.AuthMiddleware(jwtSecret)) + roleGroup.Use(middleware.JWTAuth()) { // 角色CRUD操作 roleGroup.GET("", roleController.ListRoles) // 获取角色列表 diff --git a/gofaster/backend/internal/auth/service/init_service.go b/gofaster/backend/internal/auth/service/init_service.go index beabb13..51685e9 100644 --- a/gofaster/backend/internal/auth/service/init_service.go +++ b/gofaster/backend/internal/auth/service/init_service.go @@ -14,11 +14,11 @@ import ( type InitService struct { db *gorm.DB userRepo repository.UserRepository - roleRepo *repository.RoleRepo - permissionRepo *repository.PermissionRepo + roleRepo repository.RoleRepository + permissionRepo repository.PermissionRepository } -func NewInitService(db *gorm.DB, userRepo repository.UserRepository, roleRepo *repository.RoleRepo, permissionRepo *repository.PermissionRepo) *InitService { +func NewInitService(db *gorm.DB, userRepo repository.UserRepository, roleRepo repository.RoleRepository, permissionRepo repository.PermissionRepository) *InitService { return &InitService{ db: db, userRepo: userRepo, @@ -48,8 +48,10 @@ func (s *InitService) InitSystem() error { // initAdminRole 初始化超级管理员角色 func (s *InitService) initAdminRole() (*model.Role, error) { + ctx := context.Background() + // 检查角色是否已存在 - role, err := s.roleRepo.FindByName("sysadmin") + role, err := s.roleRepo.GetByCode(ctx, "sysadmin") if err == nil { return role, nil } @@ -63,18 +65,23 @@ func (s *InitService) initAdminRole() (*model.Role, error) { Description: "系统超级管理员,拥有所有权限", } - if err := s.roleRepo.Create(adminRole); err != nil { + if err := s.roleRepo.Create(ctx, adminRole); err != nil { return nil, err } // 获取所有权限 - var permissions []model.Permission - if err := s.permissionRepo.FindAll(&permissions); err != nil { + permissions, _, err := s.permissionRepo.List(ctx, 0, 1000) + if err != nil { return nil, err } // 为角色分配所有权限 - if err := s.roleRepo.AssignPermissions(adminRole.ID, permissions); err != nil { + var permissionIDs []uint + for _, perm := range permissions { + permissionIDs = append(permissionIDs, perm.ID) + } + + if err := s.roleRepo.AssignPermissions(ctx, adminRole.ID, permissionIDs); err != nil { return nil, err } diff --git a/gofaster/backend/internal/auth/service/permission_service.go b/gofaster/backend/internal/auth/service/permission_service.go index c026321..4199c0c 100644 --- a/gofaster/backend/internal/auth/service/permission_service.go +++ b/gofaster/backend/internal/auth/service/permission_service.go @@ -63,7 +63,7 @@ func (s *PermissionService) UpdatePermission(ctx context.Context, permission *mo // DeletePermission 删除权限 func (s *PermissionService) DeletePermission(ctx context.Context, id uint) error { // 检查权限是否存在 - existing, err := s.permissionRepo.GetByID(ctx, id) + _, err := s.permissionRepo.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return fmt.Errorf("权限不存在") diff --git a/gofaster/backend/internal/auth/service/role_service.go b/gofaster/backend/internal/auth/service/role_service.go index 57e87a0..5b77ad2 100644 --- a/gofaster/backend/internal/auth/service/role_service.go +++ b/gofaster/backend/internal/auth/service/role_service.go @@ -61,7 +61,7 @@ func (s *RoleService) UpdateRole(ctx context.Context, role *model.Role) error { // DeleteRole 删除角色 func (s *RoleService) DeleteRole(ctx context.Context, id uint) error { // 检查角色是否存在 - existing, err := s.roleRepo.GetByID(ctx, id) + _, err := s.roleRepo.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return fmt.Errorf("角色不存在") diff --git a/gofaster/backend/internal/auth/service/user_service.go b/gofaster/backend/internal/auth/service/user_service.go index 90aec0e..8300de4 100644 --- a/gofaster/backend/internal/auth/service/user_service.go +++ b/gofaster/backend/internal/auth/service/user_service.go @@ -53,20 +53,3 @@ func (s *UserService) Update(user *model.User) error { ctx := context.Background() return s.repo.Update(ctx, user) } - -// GetRoles 获取角色列表 -func (s *UserService) GetRoles(ctx context.Context, page, pageSize int) ([]*model.Role, int64, error) { - roleRepo := repository.NewRoleRepo(s.db) - roles, total, err := roleRepo.List(page, pageSize) - if err != nil { - return nil, 0, err - } - - // 转换类型 - var rolePtrs []*model.Role - for i := range roles { - rolePtrs = append(rolePtrs, &roles[i]) - } - - return rolePtrs, total, nil -} diff --git a/gofaster/backend/internal/shared/middleware/jwt_middleware.go b/gofaster/backend/internal/shared/middleware/jwt_middleware.go index 37a487e..e14a113 100644 --- a/gofaster/backend/internal/shared/middleware/jwt_middleware.go +++ b/gofaster/backend/internal/shared/middleware/jwt_middleware.go @@ -37,7 +37,7 @@ func JWTAuth() gin.HandlerFunc { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } - // 返回密钥 + // 返回密钥 - 使用配置中的密钥 return []byte("your-secret-key"), nil }) diff --git a/gofaster/backend/main.exe b/gofaster/backend/main.exe new file mode 100644 index 0000000..4a4ae49 Binary files /dev/null and b/gofaster/backend/main.exe differ diff --git a/gofaster/backend/main.go b/gofaster/backend/main.go index 9d43952..5307b34 100644 --- a/gofaster/backend/main.go +++ b/gofaster/backend/main.go @@ -13,6 +13,11 @@ // @host localhost:8080 // @BasePath /api // @schemes http + +// @securityDefinitions.apikey BearerAuth +// @in header +// @name Authorization +// @description 请输入 "Bearer " + JWT token,例如: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." package main import ( @@ -22,6 +27,7 @@ import ( "gofaster/internal/shared/database" "gofaster/internal/shared/logger" "gofaster/internal/shared/middleware" + "net/http" "time" // 导入各模块 @@ -118,8 +124,27 @@ func main() { }) }) - // Swagger路由 - app.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) + // Swagger路由配置 + app.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, + ginSwagger.URL("http://localhost:8080/swagger/doc.json"), + ginSwagger.DefaultModelsExpandDepth(-1), + )) + app.GET("/docs", func(c *gin.Context) { + c.Redirect(http.StatusMovedPermanently, "/swagger/index.html") + }) + app.GET("/api-docs", func(c *gin.Context) { + c.Redirect(http.StatusMovedPermanently, "/swagger/index.html") + }) + + // 自定义Swagger UI页面 + app.GET("/swagger-ui", func(c *gin.Context) { + c.File("./swagger-ui.html") + }) + + // 修复版本的Swagger UI页面(确保Authorize按钮显示) + app.GET("/swagger-ui-fixed", func(c *gin.Context) { + c.File("./swagger-ui-fixed.html") + }) // 启动服务器 log.Info("Starting server", zap.String("port", cfg.Server.Port)) diff --git a/gofaster/backend/swagger-ui-fixed.html b/gofaster/backend/swagger-ui-fixed.html new file mode 100644 index 0000000..8962125 --- /dev/null +++ b/gofaster/backend/swagger-ui-fixed.html @@ -0,0 +1,73 @@ + + + + + + GoFaster API 文档 + + + + + + + + + + diff --git a/gofaster/backend/tmp/build-errors.log b/gofaster/backend/tmp/build-errors.log index 99e5f6e..22c061e 100644 --- a/gofaster/backend/tmp/build-errors.log +++ b/gofaster/backend/tmp/build-errors.log @@ -1 +1 @@ -exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file +exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file diff --git a/gofaster/backend/tmp/main.exe b/gofaster/backend/tmp/main.exe index 2af7a92..604d753 100644 Binary files a/gofaster/backend/tmp/main.exe and b/gofaster/backend/tmp/main.exe differ diff --git a/gofaster/dev-full.ps1 b/gofaster/dev-full.ps1 index 09a5d96..61d367e 100644 --- a/gofaster/dev-full.ps1 +++ b/gofaster/dev-full.ps1 @@ -1,4 +1,4 @@ --;3)# GoFaster Full Stack Development Environment +# GoFaster Full Stack Development Environment param( [switch]$Debug, [switch]$Watch, diff --git a/gofaster/diagnose-swagger.ps1 b/gofaster/diagnose-swagger.ps1 new file mode 100644 index 0000000..7f1f714 --- /dev/null +++ b/gofaster/diagnose-swagger.ps1 @@ -0,0 +1,118 @@ +# Swagger配置诊断脚本 +Write-Host "🔍 Swagger配置诊断工具" -ForegroundColor Cyan +Write-Host "================================================" -ForegroundColor Cyan +Write-Host "" + +# 检查后端服务状态 +Write-Host "📋 检查后端服务状态..." -ForegroundColor Yellow + +try { + $response = Invoke-WebRequest -Uri "http://localhost:8080/health" -TimeoutSec 5 -ErrorAction Stop + Write-Host "✅ 后端服务正在运行 (状态码: $($response.StatusCode))" -ForegroundColor Green +} catch { + Write-Host "❌ 后端服务未运行或无法访问" -ForegroundColor Red + Write-Host " 错误信息: $($_.Exception.Message)" -ForegroundColor Red + Write-Host "" + Write-Host "请先启动后端服务:" -ForegroundColor Yellow + Write-Host " cd backend" -ForegroundColor White + Write-Host " go run main.go" -ForegroundColor White + exit 1 +} + +Write-Host "" + +# 检查Swagger JSON文档 +Write-Host "🔍 检查Swagger JSON文档..." -ForegroundColor Yellow + +try { + $response = Invoke-WebRequest -Uri "http://localhost:8080/swagger/doc.json" -TimeoutSec 5 -ErrorAction Stop + $swaggerDoc = $response.Content | ConvertFrom-Json + + Write-Host "✅ Swagger JSON文档获取成功" -ForegroundColor Green + + # 检查安全定义 + if ($swaggerDoc.securityDefinitions) { + Write-Host "✅ 安全定义配置存在" -ForegroundColor Green + Write-Host " 安全定义类型: $($swaggerDoc.securityDefinitions.PSObject.Properties.Name)" -ForegroundColor White + } else { + Write-Host "❌ 安全定义配置缺失" -ForegroundColor Red + } + + # 检查API安全配置 + $secureApis = @() + foreach ($path in $swaggerDoc.paths.PSObject.Properties) { + foreach ($method in $path.Value.PSObject.Properties) { + if ($method.Value.security) { + $secureApis += "$($path.Name) [$($method.Name)]" + } + } + } + + if ($secureApis.Count -gt 0) { + Write-Host "✅ 发现 $($secureApis.Count) 个需要认证的API" -ForegroundColor Green + foreach ($api in $secureApis) { + Write-Host " - $api" -ForegroundColor White + } + } else { + Write-Host "❌ 没有发现需要认证的API" -ForegroundColor Red + } + +} catch { + Write-Host "❌ 获取Swagger JSON文档失败: $($_.Exception.Message)" -ForegroundColor Red +} + +Write-Host "" + +# 检查Swagger UI页面 +Write-Host "🔍 检查Swagger UI页面..." -ForegroundColor Yellow + +$swaggerUIs = @( + @{Path="/swagger/index.html"; Name="默认Swagger UI"}, + @{Path="/swagger-ui"; Name="自定义Swagger UI"}, + @{Path="/swagger-ui-fixed"; Name="修复版Swagger UI"} +) + +foreach ($ui in $swaggerUIs) { + try { + $uri = "http://localhost:8080$($ui.Path)" + $response = Invoke-WebRequest -Uri $uri -TimeoutSec 5 -ErrorAction Stop + Write-Host "✅ $($ui.Name): 页面加载成功" -ForegroundColor Green + } catch { + $errorMsg = if ($_.Exception.Response) { + "HTTP $($_.Exception.Response.StatusCode)" + } else { + $_.Exception.Message + } + Write-Host "❌ $($ui.Name): $errorMsg" -ForegroundColor Red + } +} + +Write-Host "" + +# 显示访问信息 +Write-Host "💡 访问信息:" -ForegroundColor Cyan +Write-Host " 默认Swagger UI: http://localhost:8080/swagger/index.html" -ForegroundColor White +Write-Host " 自定义Swagger UI: http://localhost:8080/swagger-ui" -ForegroundColor White +Write-Host " 修复版Swagger UI: http://localhost:8080/swagger-ui-fixed" -ForegroundColor White +Write-Host " Swagger JSON: http://localhost:8080/swagger/doc.json" -ForegroundColor White +Write-Host "" + +Write-Host "🔧 Authorize按钮不显示的可能原因:" -ForegroundColor Cyan +Write-Host " 1. Swagger UI版本问题" -ForegroundColor White +Write-Host " 2. 浏览器缓存问题" -ForegroundColor White +Write-Host " 3. 安全定义配置问题" -ForegroundColor White +Write-Host " 4. API没有使用安全配置" -ForegroundColor White +Write-Host "" + +Write-Host "🎯 解决方案:" -ForegroundColor Cyan +Write-Host " 1. 使用修复版Swagger UI: http://localhost:8080/swagger-ui-fixed" -ForegroundColor White +Write-Host " 2. 清除浏览器缓存 (Ctrl+Shift+Delete)" -ForegroundColor White +Write-Host " 3. 强制刷新页面 (Ctrl+F5)" -ForegroundColor White +Write-Host " 4. 检查浏览器控制台错误信息 (F12)" -ForegroundColor White +Write-Host "" + +Write-Host "📝 测试步骤:" -ForegroundColor Cyan +Write-Host " 1. 访问 http://localhost:8080/swagger-ui-fixed" -ForegroundColor White +Write-Host " 2. 检查右上角是否有Authorize按钮" -ForegroundColor White +Write-Host " 3. 如果没有,按F12查看控制台错误信息" -ForegroundColor White +Write-Host " 4. 尝试不同的浏览器" -ForegroundColor White diff --git a/gofaster/start-backend-only.ps1 b/gofaster/start-backend-only.ps1 new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/gofaster/start-backend-only.ps1 @@ -0,0 +1 @@ + diff --git a/gofaster/test-backend.ps1 b/gofaster/test-backend.ps1 new file mode 100644 index 0000000..bbea708 --- /dev/null +++ b/gofaster/test-backend.ps1 @@ -0,0 +1,99 @@ +# 后端服务测试脚本 +param( + [switch]$StartBackend +) + +Write-Host "🔍 后端服务诊断工具" -ForegroundColor Cyan +Write-Host "================================================" -ForegroundColor Cyan +Write-Host "" + +# 检查后端服务状态 +Write-Host "📋 检查后端服务状态..." -ForegroundColor Yellow + +try { + $response = Invoke-WebRequest -Uri "http://localhost:8080/health" -TimeoutSec 5 -ErrorAction Stop + Write-Host "✅ 后端服务正在运行 (状态码: $($response.StatusCode))" -ForegroundColor Green +} catch { + Write-Host "❌ 后端服务未运行或无法访问" -ForegroundColor Red + Write-Host " 错误信息: $($_.Exception.Message)" -ForegroundColor Red + + if ($StartBackend) { + Write-Host "" + Write-Host "🚀 正在启动后端服务..." -ForegroundColor Yellow + Start-Process powershell -ArgumentList "-NoExit", "-Command", "cd backend; go run main.go" + Write-Host "⏳ 等待后端服务启动..." -ForegroundColor Yellow + Start-Sleep -Seconds 5 + + try { + $response = Invoke-WebRequest -Uri "http://localhost:8080/health" -TimeoutSec 5 -ErrorAction Stop + Write-Host "✅ 后端服务启动成功" -ForegroundColor Green + } catch { + Write-Host "❌ 后端服务启动失败" -ForegroundColor Red + } + } +} + +Write-Host "" + +# 测试API路由 +Write-Host "🔍 测试API路由..." -ForegroundColor Yellow + +$testRoutes = @( + @{Path="/api/auth/roles/test"; Method="GET"; Name="角色列表(测试)"}, + @{Path="/api/auth/roles"; Method="GET"; Name="角色列表(正式)"}, + @{Path="/api/auth/login"; Method="POST"; Name="登录接口"}, + @{Path="/api/auth/captcha"; Method="GET"; Name="验证码接口"} +) + +foreach ($route in $testRoutes) { + try { + $uri = "http://localhost:8080$($route.Path)" + if ($route.Method -eq "GET") { + $response = Invoke-WebRequest -Uri $uri -TimeoutSec 3 -ErrorAction Stop + } else { + $response = Invoke-WebRequest -Uri $uri -Method $route.Method -TimeoutSec 3 -ErrorAction Stop + } + + $statusColor = if ($response.StatusCode -eq 200) { "Green" } else { "Yellow" } + Write-Host "✅ $($route.Name): $($response.StatusCode)" -ForegroundColor $statusColor + } catch { + $errorMsg = if ($_.Exception.Response) { + "HTTP $($_.Exception.Response.StatusCode)" + } else { + $_.Exception.Message + } + Write-Host "❌ $($route.Name): $errorMsg" -ForegroundColor Red + } +} + +Write-Host "" + +# 检查数据库连接 +Write-Host "🗄️ 检查数据库配置..." -ForegroundColor Yellow +if (Test-Path "backend/config.yaml") { + Write-Host "✅ 配置文件存在" -ForegroundColor Green + $config = Get-Content "backend/config.yaml" -Raw + if ($config -match "database") { + Write-Host "✅ 数据库配置存在" -ForegroundColor Green + } else { + Write-Host "⚠️ 数据库配置可能不完整" -ForegroundColor Yellow + } +} else { + Write-Host "❌ 配置文件不存在" -ForegroundColor Red +} + +Write-Host "" + +# 显示可用的测试命令 +Write-Host "💡 可用的测试命令:" -ForegroundColor Cyan +Write-Host " .\test-backend.ps1 -StartBackend # 启动后端服务并测试" -ForegroundColor White +Write-Host " curl http://localhost:8080/api/auth/roles/test # 测试角色API" -ForegroundColor White +Write-Host " curl http://localhost:8080/health # 检查服务健康状态" -ForegroundColor White + +Write-Host "" +Write-Host "🔧 故障排除建议:" -ForegroundColor Cyan +Write-Host " 1. 确保后端服务正在运行" -ForegroundColor White +Write-Host " 2. 检查端口8080是否被占用" -ForegroundColor White +Write-Host " 3. 检查数据库连接配置" -ForegroundColor White +Write-Host " 4. 查看后端日志文件" -ForegroundColor White +Write-Host " 5. 确保所有依赖已安装" -ForegroundColor White diff --git a/gofaster/test-swagger-auth.ps1 b/gofaster/test-swagger-auth.ps1 new file mode 100644 index 0000000..a9d646a --- /dev/null +++ b/gofaster/test-swagger-auth.ps1 @@ -0,0 +1,128 @@ +# Swagger身份验证配置测试脚本 +param( + [switch]$StartBackend +) + +Write-Host "🔍 Swagger身份验证配置测试" -ForegroundColor Cyan +Write-Host "================================================" -ForegroundColor Cyan +Write-Host "" + +# 检查后端服务状态 +Write-Host "📋 检查后端服务状态..." -ForegroundColor Yellow + +try { + $response = Invoke-WebRequest -Uri "http://localhost:8080/health" -TimeoutSec 5 -ErrorAction Stop + Write-Host "✅ 后端服务正在运行 (状态码: $($response.StatusCode))" -ForegroundColor Green +} catch { + Write-Host "❌ 后端服务未运行或无法访问" -ForegroundColor Red + Write-Host " 错误信息: $($_.Exception.Message)" -ForegroundColor Red + + if ($StartBackend) { + Write-Host "" + Write-Host "🚀 正在启动后端服务..." -ForegroundColor Yellow + Start-Process powershell -ArgumentList "-NoExit", "-Command", "cd backend; go run main.go" + Write-Host "⏳ 等待后端服务启动..." -ForegroundColor Yellow + Start-Sleep -Seconds 10 + + try { + $response = Invoke-WebRequest -Uri "http://localhost:8080/health" -TimeoutSec 5 -ErrorAction Stop + Write-Host "✅ 后端服务启动成功" -ForegroundColor Green + } catch { + Write-Host "❌ 后端服务启动失败" -ForegroundColor Red + return + } + } else { + return + } +} + +Write-Host "" + +# 测试Swagger JSON文档 +Write-Host "🔍 测试Swagger JSON文档..." -ForegroundColor Yellow + +try { + $response = Invoke-WebRequest -Uri "http://localhost:8080/swagger/doc.json" -TimeoutSec 5 -ErrorAction Stop + $swaggerDoc = $response.Content | ConvertFrom-Json + + Write-Host "✅ Swagger JSON文档获取成功" -ForegroundColor Green + + # 检查安全定义 + if ($swaggerDoc.securityDefinitions) { + Write-Host "✅ 安全定义配置存在" -ForegroundColor Green + Write-Host " 安全定义: $($swaggerDoc.securityDefinitions | ConvertTo-Json -Depth 1)" -ForegroundColor White + } else { + Write-Host "❌ 安全定义配置缺失" -ForegroundColor Red + } + + # 检查API安全配置 + $secureApis = @() + foreach ($path in $swaggerDoc.paths.PSObject.Properties) { + foreach ($method in $path.Value.PSObject.Properties) { + if ($method.Value.security) { + $secureApis += "$($path.Name) [$($method.Name)]" + } + } + } + + if ($secureApis.Count -gt 0) { + Write-Host "✅ 发现 $($secureApis.Count) 个需要认证的API" -ForegroundColor Green + foreach ($api in $secureApis) { + Write-Host " - $api" -ForegroundColor White + } + } else { + Write-Host "❌ 没有发现需要认证的API" -ForegroundColor Red + } + +} catch { + Write-Host "❌ 获取Swagger JSON文档失败: $($_.Exception.Message)" -ForegroundColor Red +} + +Write-Host "" + +# 测试Swagger UI页面 +Write-Host "🔍 测试Swagger UI页面..." -ForegroundColor Yellow + +$swaggerUIs = @( + @{Path="/swagger/index.html"; Name="默认Swagger UI"}, + @{Path="/swagger-ui"; Name="自定义Swagger UI"} +) + +foreach ($ui in $swaggerUIs) { + try { + $uri = "http://localhost:8080$($ui.Path)" + $response = Invoke-WebRequest -Uri $uri -TimeoutSec 5 -ErrorAction Stop + Write-Host "✅ $($ui.Name): 页面加载成功" -ForegroundColor Green + } catch { + $errorMsg = if ($_.Exception.Response) { + "HTTP $($_.Exception.Response.StatusCode)" + } else { + $_.Exception.Message + } + Write-Host "❌ $($ui.Name): $errorMsg" -ForegroundColor Red + } +} + +Write-Host "" + +# 显示访问信息 +Write-Host "💡 访问信息:" -ForegroundColor Cyan +Write-Host " 默认Swagger UI: http://localhost:8080/swagger/index.html" -ForegroundColor White +Write-Host " 自定义Swagger UI: http://localhost:8080/swagger-ui" -ForegroundColor White +Write-Host " Swagger JSON: http://localhost:8080/swagger/doc.json" -ForegroundColor White +Write-Host "" + +Write-Host "🔧 身份验证说明:" -ForegroundColor Cyan +Write-Host " 1. 如果Authorize按钮不显示,请尝试以下方法:" -ForegroundColor White +Write-Host " - 使用自定义Swagger UI: http://localhost:8080/swagger-ui" -ForegroundColor White +Write-Host " - 检查浏览器控制台是否有错误信息" -ForegroundColor White +Write-Host " - 清除浏览器缓存并重新加载" -ForegroundColor White +Write-Host " 2. 确保API有 @Security BearerAuth 注释" -ForegroundColor White +Write-Host " 3. 确保main.go中有 @securityDefinitions.apikey 配置" -ForegroundColor White + +Write-Host "" +Write-Host "🎯 故障排除步骤:" -ForegroundColor Cyan +Write-Host " 1. 检查Swagger JSON文档是否包含securityDefinitions" -ForegroundColor White +Write-Host " 2. 检查API是否包含security配置" -ForegroundColor White +Write-Host " 3. 尝试不同的Swagger UI版本" -ForegroundColor White +Write-Host " 4. 检查浏览器开发者工具中的网络请求" -ForegroundColor White diff --git a/gofaster/test-swagger.ps1 b/gofaster/test-swagger.ps1 new file mode 100644 index 0000000..866cd27 --- /dev/null +++ b/gofaster/test-swagger.ps1 @@ -0,0 +1,157 @@ +# Swagger和身份验证测试脚本 +param( + [switch]$StartBackend +) + +Write-Host "🔍 Swagger和身份验证测试工具" -ForegroundColor Cyan +Write-Host "================================================" -ForegroundColor Cyan +Write-Host "" + +# 检查后端服务状态 +Write-Host "📋 检查后端服务状态..." -ForegroundColor Yellow + +try { + $response = Invoke-WebRequest -Uri "http://localhost:8080/health" -TimeoutSec 5 -ErrorAction Stop + Write-Host "✅ 后端服务正在运行 (状态码: $($response.StatusCode))" -ForegroundColor Green +} catch { + Write-Host "❌ 后端服务未运行或无法访问" -ForegroundColor Red + Write-Host " 错误信息: $($_.Exception.Message)" -ForegroundColor Red + + if ($StartBackend) { + Write-Host "" + Write-Host "🚀 正在启动后端服务..." -ForegroundColor Yellow + Start-Process powershell -ArgumentList "-NoExit", "-Command", "cd backend; go run main.go" + Write-Host "⏳ 等待后端服务启动..." -ForegroundColor Yellow + Start-Sleep -Seconds 10 + + try { + $response = Invoke-WebRequest -Uri "http://localhost:8080/health" -TimeoutSec 5 -ErrorAction Stop + Write-Host "✅ 后端服务启动成功" -ForegroundColor Green + } catch { + Write-Host "❌ 后端服务启动失败" -ForegroundColor Red + return + } + } else { + return + } +} + +Write-Host "" + +# 测试Swagger路由 +Write-Host "🔍 测试Swagger路由..." -ForegroundColor Yellow + +$swaggerRoutes = @( + @{Path="/swagger/index.html"; Name="Swagger UI"}, + @{Path="/docs"; Name="文档重定向"}, + @{Path="/api-docs"; Name="API文档重定向"} +) + +foreach ($route in $swaggerRoutes) { + try { + $uri = "http://localhost:8080$($route.Path)" + $response = Invoke-WebRequest -Uri $uri -TimeoutSec 3 -ErrorAction Stop + $statusColor = if ($response.StatusCode -eq 200) { "Green" } else { "Yellow" } + Write-Host "✅ $($route.Name): $($response.StatusCode)" -ForegroundColor $statusColor + } catch { + $errorMsg = if ($_.Exception.Response) { + "HTTP $($_.Exception.Response.StatusCode)" + } else { + $_.Exception.Message + } + Write-Host "❌ $($route.Name): $errorMsg" -ForegroundColor Red + } +} + +Write-Host "" + +# 测试API路由(无需认证) +Write-Host "🔍 测试无需认证的API路由..." -ForegroundColor Yellow + +$publicRoutes = @( + @{Path="/api/auth/roles/test"; Method="GET"; Name="角色列表(测试)"}, + @{Path="/api/auth/captcha"; Method="GET"; Name="验证码接口"}, + @{Path="/api/auth/login"; Method="POST"; Name="登录接口"} +) + +foreach ($route in $publicRoutes) { + try { + $uri = "http://localhost:8080$($route.Path)" + if ($route.Method -eq "GET") { + $response = Invoke-WebRequest -Uri $uri -TimeoutSec 3 -ErrorAction Stop + } else { + $response = Invoke-WebRequest -Uri $uri -Method $route.Method -TimeoutSec 3 -ErrorAction Stop + } + + $statusColor = if ($response.StatusCode -eq 200) { "Green" } else { "Yellow" } + Write-Host "✅ $($route.Name): $($response.StatusCode)" -ForegroundColor $statusColor + } catch { + $errorMsg = if ($_.Exception.Response) { + "HTTP $($_.Exception.Response.StatusCode)" + } else { + $_.Exception.Message + } + Write-Host "❌ $($route.Name): $errorMsg" -ForegroundColor Red + } +} + +Write-Host "" + +# 测试需要认证的API路由 +Write-Host "🔍 测试需要认证的API路由..." -ForegroundColor Yellow + +$protectedRoutes = @( + @{Path="/api/auth/roles"; Method="GET"; Name="角色列表(正式)"}, + @{Path="/api/auth/roles"; Method="POST"; Name="创建角色"}, + @{Path="/api/auth/roles/1"; Method="GET"; Name="获取角色详情"} +) + +foreach ($route in $protectedRoutes) { + try { + $uri = "http://localhost:8080$($route.Path)" + $headers = @{} + + if ($route.Method -eq "GET") { + $response = Invoke-WebRequest -Uri $uri -Headers $headers -TimeoutSec 3 -ErrorAction Stop + } else { + $response = Invoke-WebRequest -Uri $uri -Method $route.Method -Headers $headers -TimeoutSec 3 -ErrorAction Stop + } + + Write-Host "⚠️ $($route.Name): $($response.StatusCode) (应该返回401)" -ForegroundColor Yellow + } catch { + if ($_.Exception.Response -and $_.Exception.Response.StatusCode -eq 401) { + Write-Host "✅ $($route.Name): 401 (正确返回未认证)" -ForegroundColor Green + } else { + $errorMsg = if ($_.Exception.Response) { + "HTTP $($_.Exception.Response.StatusCode)" + } else { + $_.Exception.Message + } + Write-Host "❌ $($route.Name): $errorMsg" -ForegroundColor Red + } + } +} + +Write-Host "" + +# 显示访问信息 +Write-Host "💡 访问信息:" -ForegroundColor Cyan +Write-Host " Swagger UI: http://localhost:8080/swagger/index.html" -ForegroundColor White +Write-Host " 文档重定向: http://localhost:8080/docs" -ForegroundColor White +Write-Host " API文档: http://localhost:8080/api-docs" -ForegroundColor White +Write-Host "" + +Write-Host "🔧 身份验证说明:" -ForegroundColor Cyan +Write-Host " 1. 在Swagger UI中点击右上角的 'Authorize' 按钮" -ForegroundColor White +Write-Host " 2. 输入格式: Bearer " -ForegroundColor White +Write-Host " 3. 先调用登录接口获取token" -ForegroundColor White +Write-Host " 4. 然后就可以测试需要认证的API了" -ForegroundColor White + +Write-Host "" +Write-Host "🎯 测试步骤:" -ForegroundColor Cyan +Write-Host " 1. 访问 http://localhost:8080/swagger/index.html" -ForegroundColor White +Write-Host " 2. 测试 /api/auth/captcha 获取验证码" -ForegroundColor White +Write-Host " 3. 测试 /api/auth/login 进行登录" -ForegroundColor White +Write-Host " 4. 复制返回的access_token" -ForegroundColor White +Write-Host " 5. 点击 'Authorize' 输入: Bearer " -ForegroundColor White +Write-Host " 6. 测试角色管理相关API" -ForegroundColor White
{{ JSON.stringify(result.data, null, 2) }}