package app import ( "encoding/json" "fmt" "io" "net/http" "git.echol.cn/loser/st/server/global" "git.echol.cn/loser/st/server/middleware" "git.echol.cn/loser/st/server/model/app" "git.echol.cn/loser/st/server/model/app/request" "git.echol.cn/loser/st/server/model/app/response" sysResponse "git.echol.cn/loser/st/server/model/common/response" "git.echol.cn/loser/st/server/service" "github.com/gin-gonic/gin" "go.uber.org/zap" ) type WorldInfoApi struct{} var worldInfoService = service.ServiceGroupApp.AppServiceGroup.WorldInfoService // CreateWorldBook 创建世界书 // @Summary 创建世界书 // @Description 创建一个新的世界书 // @Tags 世界书管理 // @Accept json // @Produce json // @Param data body request.CreateWorldBookRequest true "世界书信息" // @Success 200 {object} response.Response{data=app.AIWorldInfo} // @Router /app/worldbook [post] func (a *WorldInfoApi) CreateWorldBook(c *gin.Context) { userID := middleware.GetAppUserID(c) var req request.CreateWorldBookRequest if err := c.ShouldBindJSON(&req); err != nil { sysResponse.FailWithMessage(err.Error(), c) return } book, err := worldInfoService.CreateWorldBook(userID, &req) if err != nil { global.GVA_LOG.Error("创建世界书失败", zap.Error(err)) sysResponse.FailWithMessage("创建失败: "+err.Error(), c) return } sysResponse.OkWithData(response.ToWorldBookResponse(book), c) } // UpdateWorldBook 更新世界书 // @Summary 更新世界书 // @Description 更新世界书信息 // @Tags 世界书管理 // @Accept json // @Produce json // @Param id path int true "世界书ID" // @Param data body request.UpdateWorldBookRequest true "世界书信息" // @Success 200 {object} response.Response // @Router /app/worldbook/:id [put] func (a *WorldInfoApi) UpdateWorldBook(c *gin.Context) { userID := middleware.GetAppUserID(c) bookID := c.GetUint("id") var req request.UpdateWorldBookRequest if err := c.ShouldBindJSON(&req); err != nil { sysResponse.FailWithMessage(err.Error(), c) return } if err := worldInfoService.UpdateWorldBook(userID, bookID, &req); err != nil { global.GVA_LOG.Error("更新世界书失败", zap.Error(err)) sysResponse.FailWithMessage("更新失败: "+err.Error(), c) return } sysResponse.OkWithMessage("更新成功", c) } // DeleteWorldBook 删除世界书 // @Summary 删除世界书 // @Description 删除世界书 // @Tags 世界书管理 // @Accept json // @Produce json // @Param id path int true "世界书ID" // @Success 200 {object} response.Response // @Router /app/worldbook/:id [delete] func (a *WorldInfoApi) DeleteWorldBook(c *gin.Context) { userID := middleware.GetAppUserID(c) bookID := c.GetUint("id") if err := worldInfoService.DeleteWorldBook(userID, bookID); err != nil { global.GVA_LOG.Error("删除世界书失败", zap.Error(err)) sysResponse.FailWithMessage("删除失败: "+err.Error(), c) return } sysResponse.OkWithMessage("删除成功", c) } // GetWorldBook 获取世界书详情 // @Summary 获取世界书详情 // @Description 获取世界书详细信息 // @Tags 世界书管理 // @Accept json // @Produce json // @Param id path int true "世界书ID" // @Success 200 {object} response.Response{data=response.WorldBookResponse} // @Router /app/worldbook/:id [get] func (a *WorldInfoApi) GetWorldBook(c *gin.Context) { userID := middleware.GetAppUserID(c) bookID := c.GetUint("id") book, err := worldInfoService.GetWorldBook(userID, bookID) if err != nil { global.GVA_LOG.Error("获取世界书失败", zap.Error(err)) sysResponse.FailWithMessage("获取失败: "+err.Error(), c) return } sysResponse.OkWithData(response.ToWorldBookResponse(book), c) } // GetWorldBookList 获取世界书列表 // @Summary 获取世界书列表 // @Description 获取世界书列表 // @Tags 世界书管理 // @Accept json // @Produce json // @Param data query request.WorldBookListRequest true "查询参数" // @Success 200 {object} response.Response{data=response.WorldBookListResponse} // @Router /app/worldbook/list [get] func (a *WorldInfoApi) GetWorldBookList(c *gin.Context) { userID := middleware.GetAppUserID(c) var req request.WorldBookListRequest if err := c.ShouldBindQuery(&req); err != nil { sysResponse.FailWithMessage(err.Error(), c) return } // 默认分页 if req.Page < 1 { req.Page = 1 } if req.PageSize < 1 { req.PageSize = 10 } result, err := worldInfoService.GetWorldBookList(userID, &req) if err != nil { global.GVA_LOG.Error("获取世界书列表失败", zap.Error(err)) sysResponse.FailWithMessage("获取失败: "+err.Error(), c) return } sysResponse.OkWithData(result, c) } // CreateWorldEntry 创建世界书条目 // @Summary 创建世界书条目 // @Description 在世界书中创建一个新条目 // @Tags 世界书管理 // @Accept json // @Produce json // @Param data body request.CreateWorldEntryRequest true "条目信息" // @Success 200 {object} response.Response // @Router /app/worldbook/entry [post] func (a *WorldInfoApi) CreateWorldEntry(c *gin.Context) { userID := middleware.GetAppUserID(c) var req request.CreateWorldEntryRequest if err := c.ShouldBindJSON(&req); err != nil { sysResponse.FailWithMessage(err.Error(), c) return } if err := worldInfoService.CreateWorldEntry(userID, req.BookID, &req.Entry); err != nil { global.GVA_LOG.Error("创建条目失败", zap.Error(err)) sysResponse.FailWithMessage("创建失败: "+err.Error(), c) return } sysResponse.OkWithMessage("创建成功", c) } // UpdateWorldEntry 更新世界书条目 // @Summary 更新世界书条目 // @Description 更新世界书条目信息 // @Tags 世界书管理 // @Accept json // @Produce json // @Param data body request.UpdateWorldEntryRequest true "条目信息" // @Success 200 {object} response.Response // @Router /app/worldbook/entry [put] func (a *WorldInfoApi) UpdateWorldEntry(c *gin.Context) { userID := middleware.GetAppUserID(c) var req request.UpdateWorldEntryRequest if err := c.ShouldBindJSON(&req); err != nil { sysResponse.FailWithMessage(err.Error(), c) return } if err := worldInfoService.UpdateWorldEntry(userID, req.BookID, &req.Entry); err != nil { global.GVA_LOG.Error("更新条目失败", zap.Error(err)) sysResponse.FailWithMessage("更新失败: "+err.Error(), c) return } sysResponse.OkWithMessage("更新成功", c) } // DeleteWorldEntry 删除世界书条目 // @Summary 删除世界书条目 // @Description 删除世界书条目 // @Tags 世界书管理 // @Accept json // @Produce json // @Param data body request.DeleteWorldEntryRequest true "删除参数" // @Success 200 {object} response.Response // @Router /app/worldbook/entry [delete] func (a *WorldInfoApi) DeleteWorldEntry(c *gin.Context) { userID := middleware.GetAppUserID(c) var req request.DeleteWorldEntryRequest if err := c.ShouldBindJSON(&req); err != nil { sysResponse.FailWithMessage(err.Error(), c) return } if err := worldInfoService.DeleteWorldEntry(userID, req.BookID, req.EntryID); err != nil { global.GVA_LOG.Error("删除条目失败", zap.Error(err)) sysResponse.FailWithMessage("删除失败: "+err.Error(), c) return } sysResponse.OkWithMessage("删除成功", c) } // LinkCharactersToWorldBook 关联角色到世界书 // @Summary 关联角色到世界书 // @Description 将角色关联到世界书 // @Tags 世界书管理 // @Accept json // @Produce json // @Param data body request.LinkCharacterRequest true "关联参数" // @Success 200 {object} response.Response // @Router /app/worldbook/link [post] func (a *WorldInfoApi) LinkCharactersToWorldBook(c *gin.Context) { userID := middleware.GetAppUserID(c) var req request.LinkCharacterRequest if err := c.ShouldBindJSON(&req); err != nil { sysResponse.FailWithMessage(err.Error(), c) return } if err := worldInfoService.LinkCharactersToWorldBook(userID, req.BookID, req.CharacterIDs); err != nil { global.GVA_LOG.Error("关联角色失败", zap.Error(err)) sysResponse.FailWithMessage("关联失败: "+err.Error(), c) return } sysResponse.OkWithMessage("关联成功", c) } // ImportWorldBook 导入世界书 // @Summary 导入世界书 // @Description 从文件导入世界书 // @Tags 世界书管理 // @Accept multipart/form-data // @Produce json // @Param file formData file true "世界书文件(JSON)" // @Param bookName formData string false "世界书名称" // @Success 200 {object} response.Response{data=app.AIWorldInfo} // @Router /app/worldbook/import [post] func (a *WorldInfoApi) ImportWorldBook(c *gin.Context) { userID := middleware.GetAppUserID(c) // 获取文件 file, err := c.FormFile("file") if err != nil { sysResponse.FailWithMessage("请上传世界书文件", c) return } // 文件大小限制(10MB) if file.Size > 10<<20 { sysResponse.FailWithMessage("文件大小不能超过 10MB", c) return } // 读取文件内容 src, err := file.Open() if err != nil { global.GVA_LOG.Error("打开文件失败", zap.Error(err)) sysResponse.FailWithMessage("文件读取失败", c) return } defer src.Close() fileData, err := io.ReadAll(src) if err != nil { global.GVA_LOG.Error("读取文件内容失败", zap.Error(err)) sysResponse.FailWithMessage("文件读取失败", c) return } // 获取世界书名称 bookName := c.PostForm("bookName") // 导入世界书 book, err := worldInfoService.ImportWorldBook(userID, bookName, fileData, "json") if err != nil { global.GVA_LOG.Error("导入世界书失败", zap.Error(err)) sysResponse.FailWithMessage("导入失败: "+err.Error(), c) return } sysResponse.OkWithData(response.ToWorldBookResponse(book), c) } // ExportWorldBook 导出世界书 // @Summary 导出世界书 // @Description 导出世界书为 JSON 文件 // @Tags 世界书管理 // @Accept json // @Produce application/json // @Param id path int true "世界书ID" // @Success 200 {object} response.WorldBookExportData // @Router /app/worldbook/:id/export [get] func (a *WorldInfoApi) ExportWorldBook(c *gin.Context) { userID := middleware.GetAppUserID(c) bookID := c.GetUint("id") exportData, err := worldInfoService.ExportWorldBook(userID, bookID) if err != nil { global.GVA_LOG.Error("导出世界书失败", zap.Error(err)) sysResponse.FailWithMessage("导出失败: "+err.Error(), c) return } // 设置响应头 filename := fmt.Sprintf("%s.json", exportData.Name) c.Header("Content-Type", "application/json") c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename)) // 直接返回 JSON 数据 c.JSON(http.StatusOK, exportData) } // MatchWorldInfo 匹配世界书条目(用于聊天) // @Summary 匹配世界书条目 // @Description 根据消息内容匹配激活的世界书条目 // @Tags 世界书管理 // @Accept json // @Produce json // @Param data body request.MatchWorldInfoRequest true "匹配参数" // @Success 200 {object} response.Response{data=response.MatchWorldInfoResponse} // @Router /app/worldbook/match [post] func (a *WorldInfoApi) MatchWorldInfo(c *gin.Context) { userID := middleware.GetAppUserID(c) var req request.MatchWorldInfoRequest if err := c.ShouldBindJSON(&req); err != nil { sysResponse.FailWithMessage(err.Error(), c) return } // 默认值 if req.ScanDepth < 1 { req.ScanDepth = 10 } if req.MaxTokens < 100 { req.MaxTokens = 2000 } result, err := worldInfoService.MatchWorldInfo(userID, &req) if err != nil { global.GVA_LOG.Error("匹配世界书失败", zap.Error(err)) sysResponse.FailWithMessage("匹配失败: "+err.Error(), c) return } sysResponse.OkWithData(result, c) } // GetCharacterWorldBooks 获取角色关联的世界书列表 // @Summary 获取角色关联的世界书列表 // @Description 获取特定角色关联的所有世界书 // @Tags 世界书管理 // @Accept json // @Produce json // @Param characterId path int true "角色ID" // @Success 200 {object} response.Response{data=[]response.WorldBookResponse} // @Router /app/worldbook/character/:characterId [get] func (a *WorldInfoApi) GetCharacterWorldBooks(c *gin.Context) { userID := middleware.GetAppUserID(c) characterID := c.GetUint("characterId") var books []app.AIWorldInfo err := global.GVA_DB. Where("user_id = ? AND (is_global = true OR ? = ANY(linked_chars))", userID, fmt.Sprintf("%d", characterID)). Find(&books).Error if err != nil { global.GVA_LOG.Error("获取角色世界书失败", zap.Error(err)) sysResponse.FailWithMessage("获取失败: "+err.Error(), c) return } result := make([]response.WorldBookResponse, 0, len(books)) for i := range books { result = append(result, response.ToWorldBookResponse(&books[i])) } sysResponse.OkWithData(result, c) } // DuplicateWorldBook 复制世界书 // @Summary 复制世界书 // @Description 创建世界书的副本 // @Tags 世界书管理 // @Accept json // @Produce json // @Param id path int true "世界书ID" // @Success 200 {object} response.Response{data=app.AIWorldInfo} // @Router /app/worldbook/:id/duplicate [post] func (a *WorldInfoApi) DuplicateWorldBook(c *gin.Context) { userID := middleware.GetAppUserID(c) bookID := c.GetUint("id") // 获取原世界书 book, err := worldInfoService.GetWorldBook(userID, bookID) if err != nil { global.GVA_LOG.Error("获取世界书失败", zap.Error(err)) sysResponse.FailWithMessage("获取失败: "+err.Error(), c) return } // 解析条目 var entries []app.AIWorldInfoEntry if err := json.Unmarshal([]byte(book.Entries), &entries); err != nil { global.GVA_LOG.Error("解析条目失败", zap.Error(err)) sysResponse.FailWithMessage("解析失败", c) return } // 创建副本 req := request.CreateWorldBookRequest{ BookName: book.BookName + " (副本)", IsGlobal: book.IsGlobal, Entries: entries, LinkedChars: book.LinkedChars, } newBook, err := worldInfoService.CreateWorldBook(userID, &req) if err != nil { global.GVA_LOG.Error("复制世界书失败", zap.Error(err)) sysResponse.FailWithMessage("复制失败: "+err.Error(), c) return } sysResponse.OkWithData(response.ToWorldBookResponse(newBook), c) }