// 项目素材库、素材文件夹与角色(含定妆表现、音色)接口。 package handler import ( "net/http" "strings" "yingce/backend/internal/service" "github.com/gin-gonic/gin" ) func registerProjectAssetRoutes(r *gin.RouterGroup, svc *service.Service) { r.GET("/projects/:id/assets", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } pageParam, hasPage := c.GetQuery("page") pageSizeParam, hasPageSize := c.GetQuery("pageSize") if hasPage || hasPageSize { page, pageErr := parsePositiveQueryInt(pageParam, 1) if pageErr != nil { fail(c, http.StatusBadRequest, pageErr) return } pageSize, pageSizeErr := parsePositiveQueryInt(pageSizeParam, 40) if pageSizeErr != nil { fail(c, http.StatusBadRequest, pageSizeErr) return } var folderID *string if value, present := c.GetQuery("folderId"); present { folderID = &value } assets, pageErr := svc.ProjectAssetsPage(user.ID, c.Param("id"), page, pageSize, c.Query("category"), c.Query("mediaType"), c.Query("status"), folderID, c.Query("q")) if pageErr != nil { failService(c, pageErr) return } ok(c, assets) return } assets, err := svc.FilterProjectAssets(user.ID, c.Param("id"), service.ProjectAssetFilter{Category: c.Query("category"), MediaType: c.Query("mediaType"), Status: c.Query("status"), Usage: c.Query("usage")}) if err != nil { failService(c, err) return } ok(c, gin.H{"assets": assets}) }) r.GET("/projects/:id/asset-folders", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } folders, err := svc.ProjectAssetFolders(user.ID, c.Param("id")) if err != nil { if service.IsProjectNotFound(err) { fail(c, http.StatusNotFound, err) return } failService(c, err) return } ok(c, gin.H{"folders": folders}) }) r.POST("/projects/:id/asset-folders", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 32<<10) var req service.CreateProjectAssetFolderRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } folder, err := svc.CreateProjectAssetFolder(user.ID, c.Param("id"), req) if err != nil { if service.IsProjectNotFound(err) { fail(c, http.StatusNotFound, err) return } failService(c, err) return } ok(c, gin.H{"folder": folder}) }) r.PATCH("/projects/:id/asset-folders/:folderId", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 32<<10) var req service.UpdateProjectAssetFolderRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } folder, err := svc.UpdateProjectAssetFolder(user.ID, c.Param("id"), c.Param("folderId"), req) if err != nil { if service.IsProjectNotFound(err) { fail(c, http.StatusNotFound, err) return } failService(c, err) return } ok(c, gin.H{"folder": folder}) }) r.DELETE("/projects/:id/asset-folders/:folderId", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } if err := svc.DeleteProjectAssetFolder(user.ID, c.Param("id"), c.Param("folderId")); err != nil { if service.IsProjectNotFound(err) { fail(c, http.StatusNotFound, err) return } failService(c, err) return } ok(c, gin.H{"id": c.Param("folderId")}) }) r.POST("/projects/:id/characters", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 256<<10) var req service.CreateProjectCharacterRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } character, err := svc.CreateProjectCharacter(user.ID, c.Param("id"), req) if err != nil { failService(c, err) return } ok(c, character) }) r.GET("/projects/:id/characters/:assetId", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } character, err := svc.ProjectCharacter(user.ID, c.Param("id"), c.Param("assetId")) if err != nil { failService(c, err) return } ok(c, character) }) r.PATCH("/projects/:id/characters/:assetId", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 256<<10) var req service.UpdateProjectCharacterRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } character, err := svc.UpdateProjectCharacter(user.ID, c.Param("id"), c.Param("assetId"), req) if err != nil { failService(c, err) return } ok(c, character) }) r.PUT("/projects/:id/characters/:assetId/representations", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 128<<10) var req service.ReplaceCharacterRepresentationsRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } character, err := svc.ReplaceProjectCharacterRepresentations(user.ID, c.Param("id"), c.Param("assetId"), req) if err != nil { failService(c, err) return } ok(c, character) }) r.PUT("/projects/:id/characters/:assetId/voice", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 64<<10) var req service.BindCharacterVoiceRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } character, err := svc.BindProjectCharacterVoice(user.ID, c.Param("id"), c.Param("assetId"), req) if err != nil { failService(c, err) return } ok(c, character) }) r.DELETE("/projects/:id/characters/:assetId/voice", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } character, err := svc.UnbindProjectCharacterVoice(user.ID, c.Param("id"), c.Param("assetId")) if err != nil { failService(c, err) return } ok(c, character) }) r.POST("/projects/:id/assets", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 64<<10) var req service.LinkProjectAssetRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } asset, err := svc.LinkProjectAsset(user.ID, c.Param("id"), req) if err != nil { failService(c, err) return } ok(c, gin.H{"asset": asset}) }) r.DELETE("/projects/:id/assets/:assetId", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } if err := svc.UnlinkProjectAsset(user.ID, c.Param("id"), c.Param("assetId")); err != nil { failService(c, err) return } ok(c, gin.H{"id": c.Param("assetId")}) }) r.PATCH("/projects/:id/assets/:assetId", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 64<<10) var req service.UpdateProjectAssetRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } asset, err := svc.UpdateProjectAsset(user.ID, c.Param("id"), c.Param("assetId"), req) if err != nil { failService(c, err) return } ok(c, gin.H{"asset": asset}) }) r.POST("/projects/:id/assets/:assetId/versions", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 256<<10) var req service.CreateAssetVersionRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } version, err := svc.CreateProjectAssetVersion(user.ID, c.Param("id"), c.Param("assetId"), req) if err != nil { failService(c, err) return } ok(c, gin.H{"version": version}) }) } // registerCharacterLibraryRoutes 注册个人角色库接口。角色库由画布和短剧共用,不受短剧功能开关限制。 func registerCharacterLibraryRoutes(r *gin.RouterGroup, svc *service.Service) { r.GET("/characters", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } page, pageSize, parseErr := parsePaginationQuery(c, 12) if parseErr != nil { fail(c, http.StatusBadRequest, parseErr) return } if pageSize > 48 { pageSize = 48 } ids := []string{} for _, id := range strings.Split(c.Query("ids"), ",") { if id = strings.TrimSpace(id); id != "" { ids = append(ids, id) } } characters, err := svc.ListCharacters(user.ID, c.Query("q"), page, pageSize, ids) if err != nil { failService(c, err) return } ok(c, characters) }) r.POST("/characters", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 256<<10) var req service.CreateCharacterRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } character, err := svc.CreateCharacter(user.ID, req) if err != nil { failService(c, err) return } ok(c, character) }) r.GET("/characters/:assetId", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } character, err := svc.Character(user.ID, c.Param("assetId")) if err != nil { failService(c, err) return } ok(c, character) }) r.PATCH("/characters/:assetId", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 256<<10) var req service.UpdateProjectCharacterRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } character, err := svc.UpdateCharacter(user.ID, c.Param("assetId"), req) if err != nil { failService(c, err) return } ok(c, character) }) r.PUT("/characters/:assetId/representations", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 128<<10) var req service.ReplaceCharacterRepresentationsRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } character, err := svc.ReplaceCharacterRepresentations(user.ID, c.Param("assetId"), req) if err != nil { failService(c, err) return } ok(c, character) }) r.PUT("/characters/:assetId/voice", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 64<<10) var req service.BindCharacterVoiceRequest if err := c.ShouldBindJSON(&req); err != nil { fail(c, http.StatusBadRequest, err) return } character, err := svc.BindCharacterVoice(user.ID, c.Param("assetId"), req) if err != nil { failService(c, err) return } ok(c, character) }) r.DELETE("/characters/:assetId/voice", func(c *gin.Context) { user, err := currentUser(c, svc) if err != nil { failService(c, err) return } character, err := svc.UnbindCharacterVoice(user.ID, c.Param("assetId")) if err != nil { failService(c, err) return } ok(c, character) }) }