diff --git a/internal/api/handler/export_handler.go b/internal/api/handler/export_handler.go index 44b8020..1ffd0d5 100644 --- a/internal/api/handler/export_handler.go +++ b/internal/api/handler/export_handler.go @@ -20,6 +20,21 @@ func NewExportHandler(exportService *service.ExportService) *ExportHandler { return &ExportHandler{exportService: exportService} } +// ExportUsers 导出用户 +// @Summary 导出用户数据 +// @Description 导出用户数据为 CSV 或 Excel 格式 +// @Tags 数据导入导出 +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param format query string false "导出格式" default(csv) Enums(csv, excel) +// @Param fields query string false "导出字段,逗号分隔" +// @Param keyword query string false "关键词过滤" +// @Param status query int false "用户状态过滤" +// @Success 200 {file} file "用户数据文件" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/exports/users [get] func (h *ExportHandler) ExportUsers(c *gin.Context) { format := c.DefaultQuery("format", "csv") fieldsStr := c.Query("fields") @@ -57,6 +72,20 @@ func (h *ExportHandler) ExportUsers(c *gin.Context) { c.Data(http.StatusOK, contentType, data) } +// ImportUsers 导入用户 +// @Summary 导入用户数据 +// @Description 从 CSV 或 Excel 文件导入用户数据 +// @Tags 数据导入导出 +// @Accept multipart/form-data +// @Produce json +// @Security BearerAuth +// @Param file formData file true "导入文件" +// @Param format query string false "文件格式" default(csv) Enums(csv, excel) +// @Success 200 {object} Response "导入结果" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/exports/users [post] func (h *ExportHandler) ImportUsers(c *gin.Context) { file, _, err := c.Request.FormFile("file") if err != nil { @@ -84,6 +113,17 @@ func (h *ExportHandler) ImportUsers(c *gin.Context) { }) } +// GetImportTemplate 获取导入模板 +// @Summary 获取用户导入模板 +// @Description 下载用户批量导入的 CSV 或 Excel 模板 +// @Tags 数据导入导出 +// @Produce json +// @Security BearerAuth +// @Param format query string false "模板格式" default(csv) Enums(csv, excel) +// @Success 200 {file} file "导入模板文件" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/exports/template [get] func (h *ExportHandler) GetImportTemplate(c *gin.Context) { format := c.DefaultQuery("format", "csv") data, filename, contentType, err := h.exportService.GetImportTemplateByFormat(format) diff --git a/internal/api/handler/sms_handler.go b/internal/api/handler/sms_handler.go index 755ddc5..4ce8356 100644 --- a/internal/api/handler/sms_handler.go +++ b/internal/api/handler/sms_handler.go @@ -14,6 +14,16 @@ type SMSHandler struct { smsCodeService *service.SMSCodeService } +// SMSLoginRequest 短信登录请求 +type SMSLoginRequest struct { + Phone string `json:"phone" binding:"required"` + Code string `json:"code" binding:"required"` + DeviceID string `json:"device_id"` + DeviceName string `json:"device_name"` + DeviceBrowser string `json:"device_browser"` + DeviceOS string `json:"device_os"` +} + // NewSMSHandler creates a new SMSHandler (stub, no SMS configured) func NewSMSHandler() *SMSHandler { return &SMSHandler{} @@ -27,7 +37,17 @@ func NewSMSHandlerWithService(authService *service.AuthService, smsCodeService * } } -// SendCode 发送短信验证码(用于注册/登录) +// SendCode 发送短信验证码 +// @Summary 发送短信验证码 +// @Description 向指定手机号发送短信验证码(用于注册或登录) +// @Tags 短信验证 +// @Accept json +// @Produce json +// @Param request body service.SendCodeRequest true "发送验证码请求" +// @Success 200 {object} Response "发送成功" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 503 {object} Response "短信服务未配置" +// @Router /api/v1/sms/send [post] func (h *SMSHandler) SendCode(c *gin.Context) { if h.smsCodeService == nil { c.JSON(http.StatusServiceUnavailable, gin.H{"code": 503, "message": "SMS service not configured"}) @@ -53,22 +73,25 @@ func (h *SMSHandler) SendCode(c *gin.Context) { }) } -// LoginByCode 短信验证码登录(带设备信息以支持设备信任链路) +// LoginByCode 短信验证码登录 +// @Summary 短信验证码登录 +// @Description 使用手机号和短信验证码登录(带设备信息以支持设备信任链路) +// @Tags 短信验证 +// @Accept json +// @Produce json +// @Param request body SMSLoginRequest true "登录请求" +// @Success 200 {object} Response "登录成功" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "验证码错误" +// @Failure 503 {object} Response "短信登录未配置" +// @Router /api/v1/sms/login [post] func (h *SMSHandler) LoginByCode(c *gin.Context) { if h.authService == nil { c.JSON(http.StatusServiceUnavailable, gin.H{"code": 503, "message": "SMS login not configured"}) return } - var req struct { - Phone string `json:"phone" binding:"required"` - Code string `json:"code" binding:"required"` - DeviceID string `json:"device_id"` - DeviceName string `json:"device_name"` - DeviceBrowser string `json:"device_browser"` - DeviceOS string `json:"device_os"` - } - + var req SMSLoginRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return diff --git a/internal/api/handler/sso_handler.go b/internal/api/handler/sso_handler.go index 12e8da2..18a3d43 100644 --- a/internal/api/handler/sso_handler.go +++ b/internal/api/handler/sso_handler.go @@ -34,7 +34,22 @@ type AuthorizeRequest struct { } // Authorize 处理 SSO 授权请求 -// GET /api/v1/sso/authorize?client_id=xxx&redirect_uri=xxx&response_type=code&scope=openid&state=xxx +// @Summary SSO 授权 +// @Description 处理 SSO 授权请求,返回授权码或访问令牌 +// @Tags SSO +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param client_id query string true "客户端ID" +// @Param redirect_uri query string true "回调地址" +// @Param response_type query string true "响应类型" Enums(code, token) +// @Param scope query string false "授权范围" +// @Param state query string false "状态参数" +// @Success 302 {string} string "重定向到回调地址" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/sso/authorize [get] func (h *SSOHandler) Authorize(c *gin.Context) { var req AuthorizeRequest if err := c.ShouldBindQuery(&req); err != nil { @@ -138,8 +153,22 @@ type TokenResponse struct { Scope string `json:"scope"` } -// Token 处理 Token 请求(授权码模式第二步) -// POST /api/v1/sso/token +// Token 处理 Token 请求 +// @Summary 获取 Access Token +// @Description 使用授权码获取 Access Token(授权码模式第二步) +// @Tags SSO +// @Accept json +// @Produce json +// @Param grant_type formData string true "授权类型" Enums(authorization_code) +// @Param code formData string false "授权码" +// @Param redirect_uri formData string false "回调地址" +// @Param client_id formData string true "客户端ID" +// @Param client_secret formData string true "客户端密钥" +// @Success 200 {object} TokenResponse "访问令牌响应" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "客户端认证失败" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/sso/token [post] func (h *SSOHandler) Token(c *gin.Context) { var req TokenRequest if err := c.ShouldBind(&req); err != nil { @@ -205,7 +234,17 @@ type IntrospectResponse struct { } // Introspect 验证 access token -// POST /api/v1/sso/introspect +// @Summary 验证 Access Token +// @Description 验证 Access Token 的有效性并返回相关信息 +// @Tags SSO +// @Accept json +// @Produce json +// @Param token formData string true "Access Token" +// @Param client_id formData string false "客户端ID" +// @Success 200 {object} IntrospectResponse "Token信息" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/sso/introspect [post] func (h *SSOHandler) Introspect(c *gin.Context) { var req IntrospectRequest if err := c.ShouldBind(&req); err != nil { @@ -234,7 +273,16 @@ type RevokeRequest struct { } // Revoke 撤销 access token -// POST /api/v1/sso/revoke +// @Summary 撤销 Access Token +// @Description 撤销指定的 Access Token +// @Tags SSO +// @Accept json +// @Produce json +// @Param token formData string true "Access Token" +// @Success 200 {object} Response "撤销成功" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/sso/revoke [post] func (h *SSOHandler) Revoke(c *gin.Context) { var req RevokeRequest if err := c.ShouldBind(&req); err != nil { @@ -253,8 +301,16 @@ type UserInfoResponse struct { Username string `json:"username"` } -// UserInfo 获取当前用户信息(SSO 专用) -// GET /api/v1/sso/userinfo +// UserInfo 获取当前用户信息 +// @Summary 获取 SSO 用户信息 +// @Description 获取当前通过 SSO 授权的用户信息 +// @Tags SSO +// @Produce json +// @Security BearerAuth +// @Success 200 {object} Response{data=UserInfoResponse} "用户信息" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/sso/userinfo [get] func (h *SSOHandler) UserInfo(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { diff --git a/internal/api/handler/theme_handler.go b/internal/api/handler/theme_handler.go index df6b298..ac95ff6 100644 --- a/internal/api/handler/theme_handler.go +++ b/internal/api/handler/theme_handler.go @@ -20,6 +20,18 @@ func NewThemeHandler(themeService *service.ThemeService) *ThemeHandler { } // CreateTheme 创建主题 +// @Summary 创建主题 +// @Description 创建新的主题配置 +// @Tags 主题管理 +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param request body service.CreateThemeRequest true "主题信息" +// @Success 201 {object} Response{data=domain.Theme} "主题创建成功" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/themes [post] func (h *ThemeHandler) CreateTheme(c *gin.Context) { var req service.CreateThemeRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -41,6 +53,19 @@ func (h *ThemeHandler) CreateTheme(c *gin.Context) { } // UpdateTheme 更新主题 +// @Summary 更新主题 +// @Description 更新指定主题的配置 +// @Tags 主题管理 +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param id path int true "主题ID" +// @Param request body service.UpdateThemeRequest true "更新信息" +// @Success 200 {object} Response{data=domain.Theme} "主题更新成功" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/themes/{id} [put] func (h *ThemeHandler) UpdateTheme(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { @@ -68,6 +93,17 @@ func (h *ThemeHandler) UpdateTheme(c *gin.Context) { } // DeleteTheme 删除主题 +// @Summary 删除主题 +// @Description 删除指定的主题 +// @Tags 主题管理 +// @Produce json +// @Security BearerAuth +// @Param id path int true "主题ID" +// @Success 200 {object} Response "主题删除成功" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/themes/{id} [delete] func (h *ThemeHandler) DeleteTheme(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { @@ -87,6 +123,17 @@ func (h *ThemeHandler) DeleteTheme(c *gin.Context) { } // GetTheme 获取主题 +// @Summary 获取主题 +// @Description 根据ID获取主题详情 +// @Tags 主题管理 +// @Produce json +// @Security BearerAuth +// @Param id path int true "主题ID" +// @Success 200 {object} Response{data=domain.Theme} "主题详情" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/themes/{id} [get] func (h *ThemeHandler) GetTheme(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { @@ -107,7 +154,16 @@ func (h *ThemeHandler) GetTheme(c *gin.Context) { }) } -// ListThemes 获取所有主题 +// ListThemes 获取主题列表 +// @Summary 获取主题列表 +// @Description 获取所有已启用的主题 +// @Tags 主题管理 +// @Produce json +// @Security BearerAuth +// @Success 200 {object} Response{data=[]domain.Theme} "主题列表" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/themes [get] func (h *ThemeHandler) ListThemes(c *gin.Context) { themes, err := h.themeService.ListThemes(c.Request.Context()) if err != nil { @@ -123,6 +179,15 @@ func (h *ThemeHandler) ListThemes(c *gin.Context) { } // ListAllThemes 获取所有主题(包括禁用的) +// @Summary 获取所有主题 +// @Description 获取所有主题(包括已禁用的) +// @Tags 主题管理 +// @Produce json +// @Security BearerAuth +// @Success 200 {object} Response{data=[]domain.Theme} "主题列表" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/themes/all [get] func (h *ThemeHandler) ListAllThemes(c *gin.Context) { themes, err := h.themeService.ListAllThemes(c.Request.Context()) if err != nil { @@ -138,6 +203,15 @@ func (h *ThemeHandler) ListAllThemes(c *gin.Context) { } // GetDefaultTheme 获取默认主题 +// @Summary 获取默认主题 +// @Description 获取系统默认主题 +// @Tags 主题管理 +// @Produce json +// @Security BearerAuth +// @Success 200 {object} Response{data=domain.Theme} "默认主题" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/themes/default [get] func (h *ThemeHandler) GetDefaultTheme(c *gin.Context) { theme, err := h.themeService.GetDefaultTheme(c.Request.Context()) if err != nil { @@ -153,6 +227,17 @@ func (h *ThemeHandler) GetDefaultTheme(c *gin.Context) { } // SetDefaultTheme 设置默认主题 +// @Summary 设置默认主题 +// @Description 将指定主题设为系统默认主题 +// @Tags 主题管理 +// @Produce json +// @Security BearerAuth +// @Param id path int true "主题ID" +// @Success 200 {object} Response "设置成功" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/themes/{id}/default [put] func (h *ThemeHandler) SetDefaultTheme(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { @@ -171,7 +256,14 @@ func (h *ThemeHandler) SetDefaultTheme(c *gin.Context) { }) } -// GetActiveTheme 获取当前生效的主题(公开接口) +// GetActiveTheme 获取当前生效的主题 +// @Summary 获取当前生效的主题 +// @Description 获取当前系统正在使用的主题(公开接口) +// @Tags 主题管理 +// @Produce json +// @Success 200 {object} Response{data=domain.Theme} "当前生效主题" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/themes/active [get] func (h *ThemeHandler) GetActiveTheme(c *gin.Context) { theme, err := h.themeService.GetActiveTheme(c.Request.Context()) if err != nil { diff --git a/internal/api/handler/webhook_handler.go b/internal/api/handler/webhook_handler.go index 9a282c1..c7ec067 100644 --- a/internal/api/handler/webhook_handler.go +++ b/internal/api/handler/webhook_handler.go @@ -19,6 +19,19 @@ func NewWebhookHandler(webhookService *service.WebhookService) *WebhookHandler { return &WebhookHandler{webhookService: webhookService} } +// CreateWebhook 创建 Webhook +// @Summary 创建 Webhook +// @Description 创建新的 Webhook 配置 +// @Tags Webhook管理 +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param request body service.CreateWebhookRequest true "Webhook信息" +// @Success 201 {object} Response{data=domain.Webhook} "Webhook创建成功" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/webhooks [post] func (h *WebhookHandler) CreateWebhook(c *gin.Context) { var req service.CreateWebhookRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -38,6 +51,19 @@ func (h *WebhookHandler) CreateWebhook(c *gin.Context) { c.JSON(http.StatusCreated, gin.H{"code": 0, "message": "success", "data": webhook}) } +// ListWebhooks 获取 Webhook 列表 +// @Summary 获取 Webhook 列表 +// @Description 获取当前用户的 Webhook 配置列表 +// @Tags Webhook管理 +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param page query int false "页码" default(1) +// @Param page_size query int false "每页数量" default(20) +// @Success 200 {object} Response "Webhook列表" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/webhooks [get] func (h *WebhookHandler) ListWebhooks(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) @@ -70,6 +96,20 @@ func (h *WebhookHandler) ListWebhooks(c *gin.Context) { }) } +// UpdateWebhook 更新 Webhook +// @Summary 更新 Webhook +// @Description 更新指定 Webhook 的配置 +// @Tags Webhook管理 +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param id path int true "Webhook ID" +// @Param request body service.UpdateWebhookRequest true "更新信息" +// @Success 200 {object} Response "更新成功" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/webhooks/{id} [put] func (h *WebhookHandler) UpdateWebhook(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { @@ -91,6 +131,18 @@ func (h *WebhookHandler) UpdateWebhook(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"code": 0, "message": "更新成功"}) } +// DeleteWebhook 删除 Webhook +// @Summary 删除 Webhook +// @Description 删除指定的 Webhook 配置 +// @Tags Webhook管理 +// @Produce json +// @Security BearerAuth +// @Param id path int true "Webhook ID" +// @Success 200 {object} Response "删除成功" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/webhooks/{id} [delete] func (h *WebhookHandler) DeleteWebhook(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { @@ -106,6 +158,19 @@ func (h *WebhookHandler) DeleteWebhook(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"code": 0, "message": "删除成功"}) } +// GetWebhookDeliveries 获取 Webhook 投递记录 +// @Summary 获取 Webhook 投递记录 +// @Description 获取指定 Webhook 的最近投递记录 +// @Tags Webhook管理 +// @Produce json +// @Security BearerAuth +// @Param id path int true "Webhook ID" +// @Param limit query int false "返回记录数量" default(20) +// @Success 200 {object} Response "投递记录列表" +// @Failure 400 {object} Response "请求参数错误" +// @Failure 401 {object} Response "未认证" +// @Failure 500 {object} Response "服务器错误" +// @Router /api/v1/webhooks/{id}/deliveries [get] func (h *WebhookHandler) GetWebhookDeliveries(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil {