package handler import ( "errors" "io" "net/http" "path/filepath" "strconv" "strings" "github.com/Tencent/WeKnora/internal/application/service" apperrors "github.com/Tencent/WeKnora/internal/errors" "github.com/Tencent/WeKnora/internal/logger" "github.com/Tencent/WeKnora/internal/types" "github.com/gin-gonic/gin" ) type DigitalHumanHandler struct{ service *service.DigitalHumanService } func NewDigitalHumanHandler(svc *service.DigitalHumanService) *DigitalHumanHandler { return &DigitalHumanHandler{service: svc} } func digitalHumanHTTPError(c *gin.Context, err error) { var inputError *types.DigitalHumanInputError switch { case errors.Is(err, types.ErrDigitalHumanNotFound): c.Error(apperrors.NewNotFoundError(err.Error())) case errors.Is(err, types.ErrDigitalHumanIdentity): c.Error(apperrors.NewUnauthorizedError(err.Error())) case errors.Is(err, types.ErrDigitalHumanConflict): c.Error(apperrors.NewConflictError(err.Error())) case errors.As(err, &inputError): c.Error(apperrors.NewBadRequestError(inputError.Error())) default: logger.ErrorWithFields(c.Request.Context(), err, map[string]interface{}{"feature": "digital_human"}) c.Error(apperrors.NewInternalServerError("数字人操作失败,请稍后重试")) } } func (h *DigitalHumanHandler) List(c *gin.Context) { offset, _ := strconv.Atoi(c.Query("offset")) limit, _ := strconv.Atoi(c.Query("limit")) data, err := h.service.List(c.Request.Context(), offset, limit) if err != nil { digitalHumanHTTPError(c, err) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": data}) } func (h *DigitalHumanHandler) Get(c *gin.Context) { data, err := h.service.Get(c.Request.Context(), c.Param("id")) if err != nil { digitalHumanHTTPError(c, err) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": data}) } func (h *DigitalHumanHandler) Create(c *gin.Context) { c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 2<<20) var input service.DigitalHumanInput if err := c.ShouldBindJSON(&input); err != nil { c.Error(apperrors.NewBadRequestError("请填写有效的数字人资料")) return } data, err := h.service.Create(c.Request.Context(), input) if err != nil { digitalHumanHTTPError(c, err) return } c.JSON(http.StatusCreated, gin.H{"success": true, "data": data}) } func (h *DigitalHumanHandler) Update(c *gin.Context) { c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 2<<20) var input service.DigitalHumanInput if err := c.ShouldBindJSON(&input); err != nil { c.Error(apperrors.NewBadRequestError("请填写有效的数字人资料")) return } data, err := h.service.Update(c.Request.Context(), c.Param("id"), input) if err != nil { digitalHumanHTTPError(c, err) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": data}) } func (h *DigitalHumanHandler) Delete(c *gin.Context) { if err := h.service.Delete(c.Request.Context(), c.Param("id")); err != nil { digitalHumanHTTPError(c, err) return } c.JSON(http.StatusOK, gin.H{"success": true}) } func (h *DigitalHumanHandler) Review(c *gin.Context) { c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 32<<10) var input struct { Version int `json:"version"` Approve *bool `json:"approve"` Note string `json:"note"` } if err := c.ShouldBindJSON(&input); err != nil || input.Approve == nil || input.Version < 1 { c.Error(apperrors.NewBadRequestError("请选择审核版本和通过或退回")) return } data, err := h.service.Review(c.Request.Context(), c.Param("id"), input.Version, *input.Approve, input.Note) if err != nil { digitalHumanHTTPError(c, err) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": data}) } func (h *DigitalHumanHandler) Bind(c *gin.Context) { c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 4096) var input struct { AgentID string `json:"agent_id"` } if err := c.ShouldBindJSON(&input); err != nil || input.AgentID == "" { c.Error(apperrors.NewBadRequestError("请选择 Agent")) return } if err := h.service.Bind(c.Request.Context(), c.Param("id"), input.AgentID); err != nil { digitalHumanHTTPError(c, err) return } c.JSON(http.StatusOK, gin.H{"success": true}) } func (h *DigitalHumanHandler) Unbind(c *gin.Context) { if err := h.service.Unbind(c.Request.Context(), c.Param("id"), c.Param("agent_id")); err != nil { digitalHumanHTTPError(c, err) return } c.JSON(http.StatusOK, gin.H{"success": true}) } func (h *DigitalHumanHandler) Distill(c *gin.Context) { var input service.DigitalHumanDistillInput if strings.HasPrefix(c.ContentType(), "multipart/form-data") { // Authorize before accepting or parsing potentially large private documents. if err := h.service.CheckOwner(c.Request.Context(), c.Param("id")); err != nil { digitalHumanHTTPError(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, service.DigitalHumanUploadLimit) if err := c.Request.ParseMultipartForm(4 << 20); err != nil { c.Error(apperrors.NewBadRequestError("上传材料总大小不能超过50MB")) return } defer c.Request.MultipartForm.RemoveAll() if len(c.Request.MultipartForm.File["files"]) > service.DigitalHumanFileCountLimit { c.Error(apperrors.NewBadRequestError("每次最多上传99份材料")) return } input.Advice, input.ModelID = c.PostForm("advice"), c.PostForm("model_id") input.Revision, _ = strconv.ParseInt(c.PostForm("revision"), 10, 64) if text := strings.TrimSpace(c.PostForm("text")); text != "" { input.Sources = append(input.Sources, types.DigitalHumanSource{Name: "补充原稿", Text: text}) } for _, header := range c.Request.MultipartForm.File["files"] { if !service.DigitalHumanUploadSupported(header.Filename) { c.Error(apperrors.NewBadRequestError("支持 Word、PDF、PPT、MD、TXT、SRT、VTT、RTF、ODT、CSV、JSON 和 HTML 材料")) return } file, err := header.Open() if err != nil { c.Error(apperrors.NewBadRequestError("无法读取上传材料")) return } data, readErr := io.ReadAll(io.LimitReader(file, service.DigitalHumanFileLimit+1)) file.Close() if readErr != nil || len(data) > service.DigitalHumanFileLimit || len(data) == 0 { c.Error(apperrors.NewBadRequestError("单份材料须有内容且不能超过20MB")) return } input.Files = append(input.Files, service.DigitalHumanUpload{Name: filepath.Base(header.Filename), Data: data}) } } else { c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 2<<20) if err := c.ShouldBindJSON(&input); err != nil { c.Error(apperrors.NewBadRequestError("请提供有效的蒸馏原稿和建议")) return } } job, err := h.service.Distill(c.Request.Context(), c.Param("id"), input) if err != nil { digitalHumanHTTPError(c, err) return } c.JSON(http.StatusAccepted, gin.H{"success": true, "data": job}) } func (h *DigitalHumanHandler) Job(c *gin.Context) { job, err := h.service.Job(c.Request.Context(), c.Param("id"), c.Param("job_id")) if err != nil { digitalHumanHTTPError(c, err) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": job}) }