easyai-ai-gateway/apps/api/internal/httpapi/catalog_handlers.go
chensipeng 918dfbfee1 docs(api): 补全 OpenAPI 注释与生成文档
为接口、模型与脚本补齐 Swagger/OpenAPI 注释,生成最新文档,并增加一键生成与查看入口。
2026-05-14 18:18:27 +08:00

324 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package httpapi
import (
"encoding/json"
"errors"
"net/http"
"strings"
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
)
// listCatalogProviders godoc
// @Summary 列出目录供应商
// @Description 返回模型目录使用的供应商元数据;公共路径和管理路径返回同一结构。
// @Tags catalog
// @Produce json
// @Success 200 {object} CatalogProviderListResponse
// @Failure 500 {object} ErrorEnvelope
// @Router /api/v1/public/catalog/providers [get]
// @Router /api/admin/catalog/providers [get]
func (s *Server) listCatalogProviders(w http.ResponseWriter, r *http.Request) {
items, err := s.store.ListCatalogProviders(r.Context())
if err != nil {
s.logger.Error("list catalog providers failed", "error", err)
writeError(w, http.StatusInternalServerError, "list catalog providers failed")
return
}
writeJSON(w, http.StatusOK, map[string]any{"items": items})
}
// createCatalogProvider godoc
// @Summary 创建目录供应商
// @Description 管理端新增模型目录供应商providerKey 和 displayName 必填。
// @Tags catalog
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param input body store.CatalogProviderInput true "目录供应商请求"
// @Success 201 {object} store.CatalogProvider
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/catalog/providers [post]
func (s *Server) createCatalogProvider(w http.ResponseWriter, r *http.Request) {
var input store.CatalogProviderInput
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
return
}
if strings.TrimSpace(input.ProviderKey) == "" || strings.TrimSpace(input.DisplayName) == "" {
writeError(w, http.StatusBadRequest, "providerKey and displayName are required")
return
}
item, err := s.store.CreateCatalogProvider(r.Context(), input)
if err != nil {
if store.IsUniqueViolation(err) {
writeError(w, http.StatusConflict, "provider key or code already exists")
return
}
s.logger.Error("create catalog provider failed", "error", err)
writeError(w, http.StatusInternalServerError, "create catalog provider failed")
return
}
writeJSON(w, http.StatusCreated, item)
}
// updateCatalogProvider godoc
// @Summary 更新目录供应商
// @Description 管理端更新目录供应商展示信息、图标和元数据。
// @Tags catalog
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param providerID path string true "目录供应商 ID"
// @Param input body store.CatalogProviderInput true "目录供应商请求"
// @Success 200 {object} store.CatalogProvider
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/catalog/providers/{providerID} [patch]
func (s *Server) updateCatalogProvider(w http.ResponseWriter, r *http.Request) {
var input store.CatalogProviderInput
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
return
}
if strings.TrimSpace(input.ProviderKey) == "" || strings.TrimSpace(input.DisplayName) == "" {
writeError(w, http.StatusBadRequest, "providerKey and displayName are required")
return
}
item, err := s.store.UpdateCatalogProvider(r.Context(), r.PathValue("providerID"), input)
if err != nil {
if store.IsNotFound(err) {
writeError(w, http.StatusNotFound, "catalog provider not found")
return
}
if store.IsUniqueViolation(err) {
writeError(w, http.StatusConflict, "provider key or code already exists")
return
}
s.logger.Error("update catalog provider failed", "error", err)
writeError(w, http.StatusInternalServerError, "update catalog provider failed")
return
}
writeJSON(w, http.StatusOK, item)
}
// deleteCatalogProvider godoc
// @Summary 删除目录供应商
// @Description 管理端删除目录供应商。
// @Tags catalog
// @Produce json
// @Security BearerAuth
// @Param providerID path string true "目录供应商 ID"
// @Success 204 "No Content"
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/catalog/providers/{providerID} [delete]
func (s *Server) deleteCatalogProvider(w http.ResponseWriter, r *http.Request) {
if err := s.store.DeleteCatalogProvider(r.Context(), r.PathValue("providerID")); err != nil {
if store.IsNotFound(err) {
writeError(w, http.StatusNotFound, "catalog provider not found")
return
}
s.logger.Error("delete catalog provider failed", "error", err)
writeError(w, http.StatusInternalServerError, "delete catalog provider failed")
return
}
w.WriteHeader(http.StatusNoContent)
}
// listBaseModels godoc
// @Summary 列出基础模型
// @Description 返回基础模型目录;公共路径和管理路径返回同一结构。
// @Tags catalog
// @Produce json
// @Success 200 {object} BaseModelListResponse
// @Failure 500 {object} ErrorEnvelope
// @Router /api/v1/public/catalog/base-models [get]
// @Router /api/admin/catalog/base-models [get]
func (s *Server) listBaseModels(w http.ResponseWriter, r *http.Request) {
items, err := s.store.ListBaseModels(r.Context())
if err != nil {
s.logger.Error("list base models failed", "error", err)
writeError(w, http.StatusInternalServerError, "list base models failed")
return
}
writeJSON(w, http.StatusOK, map[string]any{"items": items})
}
// createBaseModel godoc
// @Summary 创建基础模型
// @Description 管理端新增基础模型目录项providerKey、providerModelName 和 modelType 必填。
// @Tags catalog
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param input body store.BaseModelInput true "基础模型请求"
// @Success 201 {object} store.BaseModel
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/catalog/base-models [post]
func (s *Server) createBaseModel(w http.ResponseWriter, r *http.Request) {
var input store.BaseModelInput
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
return
}
if !validBaseModelInput(input) {
writeError(w, http.StatusBadRequest, "providerKey, providerModelName and modelType are required")
return
}
item, err := s.store.CreateBaseModel(r.Context(), input)
if err != nil {
if store.IsUniqueViolation(err) {
writeError(w, http.StatusConflict, "canonical model key already exists")
return
}
s.logger.Error("create base model failed", "error", err)
writeError(w, http.StatusInternalServerError, "create base model failed")
return
}
writeJSON(w, http.StatusCreated, item)
}
// updateBaseModel godoc
// @Summary 更新基础模型
// @Description 管理端更新基础模型目录项及能力、图标、默认快照等元数据。
// @Tags catalog
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param baseModelID path string true "基础模型 ID"
// @Param input body store.BaseModelInput true "基础模型请求"
// @Success 200 {object} store.BaseModel
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/catalog/base-models/{baseModelID} [patch]
func (s *Server) updateBaseModel(w http.ResponseWriter, r *http.Request) {
var input store.BaseModelInput
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
return
}
if !validBaseModelInput(input) {
writeError(w, http.StatusBadRequest, "providerKey, providerModelName and modelType are required")
return
}
item, err := s.store.UpdateBaseModel(r.Context(), r.PathValue("baseModelID"), input)
if err != nil {
if store.IsNotFound(err) {
writeError(w, http.StatusNotFound, "base model not found")
return
}
if store.IsUniqueViolation(err) {
writeError(w, http.StatusConflict, "canonical model key already exists")
return
}
s.logger.Error("update base model failed", "error", err)
writeError(w, http.StatusInternalServerError, "update base model failed")
return
}
writeJSON(w, http.StatusOK, item)
}
// resetBaseModel godoc
// @Summary 重置基础模型
// @Description 将指定基础模型恢复为系统默认快照;无默认快照时返回 409。
// @Tags catalog
// @Produce json
// @Security BearerAuth
// @Param baseModelID path string true "基础模型 ID"
// @Success 200 {object} store.BaseModel
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/catalog/base-models/{baseModelID}/reset [post]
func (s *Server) resetBaseModel(w http.ResponseWriter, r *http.Request) {
item, err := s.store.ResetBaseModelToDefault(r.Context(), r.PathValue("baseModelID"))
if err != nil {
if store.IsNotFound(err) {
writeError(w, http.StatusNotFound, "base model not found")
return
}
if errors.Is(err, store.ErrProtectedDefault) {
writeError(w, http.StatusConflict, "base model has no system default snapshot")
return
}
s.logger.Error("reset base model failed", "error", err)
writeError(w, http.StatusInternalServerError, "reset base model failed")
return
}
writeJSON(w, http.StatusOK, item)
}
// resetAllBaseModels godoc
// @Summary 重置全部基础模型
// @Description 将所有具备系统默认快照的基础模型恢复为默认配置。
// @Tags catalog
// @Produce json
// @Security BearerAuth
// @Success 200 {object} BaseModelListResponse
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/catalog/base-models/reset-all [post]
func (s *Server) resetAllBaseModels(w http.ResponseWriter, r *http.Request) {
items, err := s.store.ResetAllBaseModelsToDefault(r.Context())
if err != nil {
s.logger.Error("reset all base models failed", "error", err)
writeError(w, http.StatusInternalServerError, "reset all base models failed")
return
}
writeJSON(w, http.StatusOK, map[string]any{"items": items})
}
// deleteBaseModel godoc
// @Summary 删除基础模型
// @Description 管理端删除基础模型目录项。
// @Tags catalog
// @Produce json
// @Security BearerAuth
// @Param baseModelID path string true "基础模型 ID"
// @Success 204 "No Content"
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/catalog/base-models/{baseModelID} [delete]
func (s *Server) deleteBaseModel(w http.ResponseWriter, r *http.Request) {
if err := s.store.DeleteBaseModel(r.Context(), r.PathValue("baseModelID")); err != nil {
if store.IsNotFound(err) {
writeError(w, http.StatusNotFound, "base model not found")
return
}
s.logger.Error("delete base model failed", "error", err)
writeError(w, http.StatusInternalServerError, "delete base model failed")
return
}
w.WriteHeader(http.StatusNoContent)
}
func validBaseModelInput(input store.BaseModelInput) bool {
return strings.TrimSpace(input.ProviderKey) != "" &&
strings.TrimSpace(input.ProviderModelName) != "" &&
len(input.ModelType) > 0
}