134 lines
4.7 KiB
Go
134 lines
4.7 KiB
Go
package httpapi
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"net/http"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"data-terminal-cloud/internal/concurrency"
|
||
|
|
"data-terminal-cloud/internal/contentmodel"
|
||
|
|
)
|
||
|
|
|
||
|
|
func ListContentModelsHandler(service *contentmodel.Service) http.Handler {
|
||
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
principal, ok := webPrincipal(w, r)
|
||
|
|
if !ok {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
tenantID := strings.TrimSpace(r.Header.Get("X-Tenant-Id"))
|
||
|
|
if !validUUID(tenantID) {
|
||
|
|
writeValidation(w, r, "租户参数不正确", FieldError{Field: "X-Tenant-Id", Message: "必须是有效UUID"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
items, err := service.List(r.Context(), principal, tenantID)
|
||
|
|
if err != nil {
|
||
|
|
writeContentModelError(w, r, err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
w.Header().Set("Cache-Control", "no-store")
|
||
|
|
WriteJSON(w, r, http.StatusOK, map[string]any{"items": items})
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func CreateContentModelHandler(service *contentmodel.Service) http.Handler {
|
||
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
principal, ok := webPrincipal(w, r)
|
||
|
|
if !ok {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
var input contentmodel.CreateInput
|
||
|
|
if !decodeWebJSON(w, r, &input, "大模型配置参数不正确") {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
input.TenantID = strings.TrimSpace(r.Header.Get("X-Tenant-Id"))
|
||
|
|
item, err := service.Create(r.Context(), principal, input, RequestID(r.Context()))
|
||
|
|
if err != nil {
|
||
|
|
writeContentModelError(w, r, err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
w.Header().Set("Cache-Control", "no-store")
|
||
|
|
w.Header().Set("ETag", concurrency.FormatETag(item.Version))
|
||
|
|
WriteJSON(w, r, http.StatusCreated, item)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestContentModelHandler(service *contentmodel.Service) http.Handler {
|
||
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
principal, ok := resourcePrincipal(w, r, "id")
|
||
|
|
if !ok {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
item, err := service.Test(r.Context(), principal, strings.TrimSpace(r.Header.Get("X-Tenant-Id")), r.PathValue("id"), RequestID(r.Context()))
|
||
|
|
if err != nil {
|
||
|
|
writeContentModelError(w, r, err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
w.Header().Set("Cache-Control", "no-store")
|
||
|
|
w.Header().Set("ETag", concurrency.FormatETag(item.Version))
|
||
|
|
WriteJSON(w, r, http.StatusOK, item)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func ActivateContentModelHandler(service *contentmodel.Service) http.Handler {
|
||
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
principal, ok := resourcePrincipal(w, r, "id")
|
||
|
|
if !ok {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
version, err := concurrency.ParseIfMatch(r)
|
||
|
|
if err != nil {
|
||
|
|
writeETagError(w, r, err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
item, err := service.Activate(r.Context(), principal, strings.TrimSpace(r.Header.Get("X-Tenant-Id")), r.PathValue("id"), RequestID(r.Context()), version)
|
||
|
|
if err != nil {
|
||
|
|
writeContentModelError(w, r, err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
w.Header().Set("Cache-Control", "no-store")
|
||
|
|
w.Header().Set("ETag", concurrency.FormatETag(item.Version))
|
||
|
|
WriteJSON(w, r, http.StatusOK, item)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func RevokeContentModelHandler(service *contentmodel.Service) http.Handler {
|
||
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
principal, ok := resourcePrincipal(w, r, "id")
|
||
|
|
if !ok {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
version, err := concurrency.ParseIfMatch(r)
|
||
|
|
if err != nil {
|
||
|
|
writeETagError(w, r, err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
item, err := service.Revoke(r.Context(), principal, strings.TrimSpace(r.Header.Get("X-Tenant-Id")), r.PathValue("id"), RequestID(r.Context()), version)
|
||
|
|
if err != nil {
|
||
|
|
writeContentModelError(w, r, err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
w.Header().Set("Cache-Control", "no-store")
|
||
|
|
w.Header().Set("ETag", concurrency.FormatETag(item.Version))
|
||
|
|
WriteJSON(w, r, http.StatusOK, item)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeContentModelError(w http.ResponseWriter, r *http.Request, err error) {
|
||
|
|
switch {
|
||
|
|
case errors.Is(err, contentmodel.ErrInvalid):
|
||
|
|
writeValidation(w, r, "大模型配置参数不正确", FieldError{Field: "model", Message: "请检查供应商、地址、模型ID、密钥和生成参数"})
|
||
|
|
case errors.Is(err, contentmodel.ErrAccessDenied):
|
||
|
|
WriteError(w, r, http.StatusForbidden, "CONTENT_MODEL_ACCESS_DENIED", "只有家庭负责人或管理员可以管理总结模型")
|
||
|
|
case errors.Is(err, contentmodel.ErrNotFound):
|
||
|
|
WriteError(w, r, http.StatusNotFound, "RESOURCE_NOT_FOUND", "大模型配置不存在")
|
||
|
|
case errors.Is(err, contentmodel.ErrVersion):
|
||
|
|
WriteError(w, r, http.StatusPreconditionFailed, "VERSION_CONFLICT", "配置已更新,请刷新后重试")
|
||
|
|
case errors.Is(err, contentmodel.ErrTestRequired):
|
||
|
|
WriteError(w, r, http.StatusConflict, "MODEL_TEST_REQUIRED", "请先通过连通性测试再激活")
|
||
|
|
case errors.Is(err, contentmodel.ErrActiveRevoke):
|
||
|
|
WriteError(w, r, http.StatusConflict, "ACTIVE_MODEL_CANNOT_REMOVE", "请先激活另一个模型,再移除当前模型")
|
||
|
|
default:
|
||
|
|
WriteError(w, r, http.StatusInternalServerError, "INTERNAL_ERROR", "大模型配置处理失败")
|
||
|
|
}
|
||
|
|
}
|