Files
st/server/api/v1/app/character.go

393 lines
11 KiB
Go
Raw Permalink 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 app
import (
"git.echol.cn/loser/st/server/global"
"git.echol.cn/loser/st/server/middleware"
"git.echol.cn/loser/st/server/model/app/request"
"git.echol.cn/loser/st/server/model/common/response"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"io"
"strconv"
)
type CharacterApi struct{}
// GetPublicCharacterList 获取公开角色卡列表(无需鉴权)
// @Summary 获取公开角色卡列表
// @Tags 角色卡
// @Accept json
// @Produce json
// @Param page query int false "页码" default(1)
// @Param pageSize query int false "每页数量" default(20)
// @Param keyword query string false "关键词"
// @Param sortBy query string false "排序方式: newest, popular, mostChats, mostLikes"
// @Success 200 {object} response.Response{data=response.CharacterListResponse}
// @Router /app/character/public [get]
func (ca *CharacterApi) GetPublicCharacterList(c *gin.Context) {
var req request.CharacterListRequest
req.Page, _ = strconv.Atoi(c.DefaultQuery("page", "1"))
req.PageSize, _ = strconv.Atoi(c.DefaultQuery("pageSize", "20"))
req.Keyword = c.Query("keyword")
req.SortBy = c.Query("sortBy")
if req.Page < 1 {
req.Page = 1
}
if req.PageSize < 1 || req.PageSize > 100 {
req.PageSize = 20
}
// 尝试获取当前用户ID如果已登录
userID := middleware.GetOptionalAppUserID(c)
list, err := characterService.GetPublicCharacterList(req, userID)
if err != nil {
global.GVA_LOG.Error("获取公开角色卡列表失败", zap.Error(err))
response.FailWithMessage("获取列表失败", c)
return
}
response.OkWithData(list, c)
}
// GetMyCharacterList 获取我的角色卡列表
// @Summary 获取我的角色卡列表
// @Tags 角色卡
// @Accept json
// @Produce json
// @Param page query int false "页码" default(1)
// @Param pageSize query int false "每页数量" default(20)
// @Param keyword query string false "关键词"
// @Param sortBy query string false "排序方式"
// @Success 200 {object} response.Response{data=response.CharacterListResponse}
// @Router /app/character/my [get]
// @Security ApiKeyAuth
func (ca *CharacterApi) GetMyCharacterList(c *gin.Context) {
userID := middleware.GetAppUserID(c)
var req request.CharacterListRequest
req.Page, _ = strconv.Atoi(c.DefaultQuery("page", "1"))
req.PageSize, _ = strconv.Atoi(c.DefaultQuery("pageSize", "20"))
req.Keyword = c.Query("keyword")
req.SortBy = c.Query("sortBy")
if req.Page < 1 {
req.Page = 1
}
if req.PageSize < 1 || req.PageSize > 100 {
req.PageSize = 20
}
list, err := characterService.GetMyCharacterList(req, userID)
if err != nil {
global.GVA_LOG.Error("获取我的角色卡列表失败", zap.Error(err))
response.FailWithMessage("获取列表失败", c)
return
}
response.OkWithData(list, c)
}
// GetCharacterDetail 获取角色卡详情
// @Summary 获取角色卡详情
// @Tags 角色卡
// @Accept json
// @Produce json
// @Param id path int true "角色卡ID"
// @Success 200 {object} response.Response{data=response.CharacterResponse}
// @Router /app/character/:id [get]
func (ca *CharacterApi) GetCharacterDetail(c *gin.Context) {
idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
response.FailWithMessage("无效的ID", c)
return
}
// 尝试获取当前用户ID如果已登录
userID := middleware.GetOptionalAppUserID(c)
character, err := characterService.GetCharacterDetail(uint(id), userID)
if err != nil {
global.GVA_LOG.Error("获取角色卡详情失败", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithData(character, c)
}
// CreateCharacter 创建角色卡
// @Summary 创建角色卡
// @Tags 角色卡
// @Accept json
// @Produce json
// @Param data body request.CreateCharacterRequest true "角色卡信息"
// @Success 200 {object} response.Response{data=response.CharacterResponse}
// @Router /app/character [post]
// @Security ApiKeyAuth
func (ca *CharacterApi) CreateCharacter(c *gin.Context) {
userID := middleware.GetAppUserID(c)
var req request.CreateCharacterRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
character, err := characterService.CreateCharacter(req, userID)
if err != nil {
global.GVA_LOG.Error("创建角色卡失败", zap.Error(err))
response.FailWithMessage("创建失败", c)
return
}
response.OkWithData(character, c)
}
// UpdateCharacter 更新角色卡
// @Summary 更新角色卡
// @Tags 角色卡
// @Accept json
// @Produce json
// @Param data body request.UpdateCharacterRequest true "角色卡信息"
// @Success 200 {object} response.Response{data=response.CharacterResponse}
// @Router /app/character [put]
// @Security ApiKeyAuth
func (ca *CharacterApi) UpdateCharacter(c *gin.Context) {
userID := middleware.GetAppUserID(c)
var req request.UpdateCharacterRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
character, err := characterService.UpdateCharacter(req, userID)
if err != nil {
global.GVA_LOG.Error("更新角色卡失败", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithData(character, c)
}
// DeleteCharacter 删除角色卡
// @Summary 删除角色卡
// @Tags 角色卡
// @Accept json
// @Produce json
// @Param id path int true "角色卡ID"
// @Success 200 {object} response.Response
// @Router /app/character/:id [delete]
// @Security ApiKeyAuth
func (ca *CharacterApi) DeleteCharacter(c *gin.Context) {
userID := middleware.GetAppUserID(c)
idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
response.FailWithMessage("无效的ID", c)
return
}
err = characterService.DeleteCharacter(uint(id), userID)
if err != nil {
global.GVA_LOG.Error("删除角色卡失败", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
}
// ToggleFavorite 切换收藏状态
// @Summary 切换收藏状态
// @Tags 角色卡
// @Accept json
// @Produce json
// @Param data body request.CharacterActionRequest true "角色卡ID"
// @Success 200 {object} response.Response{data=map[string]bool}
// @Router /app/character/favorite [post]
// @Security ApiKeyAuth
func (ca *CharacterApi) ToggleFavorite(c *gin.Context) {
userID := middleware.GetAppUserID(c)
var req request.CharacterActionRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
isFavorited, err := characterService.ToggleFavorite(req.CharacterID, userID)
if err != nil {
global.GVA_LOG.Error("切换收藏失败", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithData(gin.H{"isFavorited": isFavorited}, c)
}
// LikeCharacter 点赞角色卡
// @Summary 点赞角色卡
// @Tags 角色卡
// @Accept json
// @Produce json
// @Param data body request.CharacterActionRequest true "角色卡ID"
// @Success 200 {object} response.Response
// @Router /app/character/like [post]
func (ca *CharacterApi) LikeCharacter(c *gin.Context) {
var req request.CharacterActionRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err := characterService.LikeCharacter(req.CharacterID)
if err != nil {
global.GVA_LOG.Error("点赞失败", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage("点赞成功", c)
}
// ExportCharacter 导出角色卡为 JSON
// @Summary 导出角色卡为 JSON
// @Tags 角色卡
// @Accept json
// @Produce json
// @Param id path int true "角色卡ID"
// @Success 200 {object} map[string]interface{}
// @Router /app/character/:id/export [get]
func (ca *CharacterApi) ExportCharacter(c *gin.Context) {
idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
response.FailWithMessage("无效的ID", c)
return
}
// 尝试获取当前用户ID如果已登录
userID := middleware.GetOptionalAppUserID(c)
exportData, err := characterService.ExportCharacter(uint(id), userID)
if err != nil {
global.GVA_LOG.Error("导出角色卡失败", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
// 设置下载文件头
c.Header("Content-Type", "application/json")
c.Header("Content-Disposition", "attachment; filename=character.json")
c.JSON(200, exportData)
}
// ExportCharacterAsPNG 导出角色卡为 PNG
// @Summary 导出角色卡为 PNG
// @Tags 角色卡
// @Accept json
// @Produce octet-stream
// @Param id path int true "角色卡ID"
// @Success 200 {file} binary
// @Router /app/character/:id/export/png [get]
func (ca *CharacterApi) ExportCharacterAsPNG(c *gin.Context) {
idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
response.FailWithMessage("无效的ID", c)
return
}
// 尝试获取当前用户ID如果已登录
userID := middleware.GetOptionalAppUserID(c)
pngData, err := characterService.ExportCharacterAsPNG(uint(id), userID)
if err != nil {
global.GVA_LOG.Error("导出 PNG 角色卡失败", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
// 设置下载文件头
c.Header("Content-Type", "image/png")
c.Header("Content-Disposition", "attachment; filename=character.png")
c.Data(200, "image/png", pngData)
}
// ImportCharacter 导入角色卡
// @Summary 导入角色卡(支持 PNG 和 JSON
// @Tags 角色卡
// @Accept multipart/form-data
// @Produce json
// @Param file formData file true "角色卡文件PNG 或 JSON"
// @Param isPublic formData bool false "是否公开"
// @Success 200 {object} response.Response{data=response.CharacterResponse}
// @Router /app/character/import [post]
// @Security ApiKeyAuth
func (ca *CharacterApi) ImportCharacter(c *gin.Context) {
userID := middleware.GetAppUserID(c)
// 获取上传的文件
file, err := c.FormFile("file")
if err != nil {
global.GVA_LOG.Error("获取上传文件失败", zap.Error(err))
response.FailWithMessage("请上传文件", c)
return
}
// 检查文件大小(最大 10MB
maxSize := int64(10 * 1024 * 1024)
if file.Size > maxSize {
response.FailWithMessage("文件大小不能超过 10MB", c)
return
}
global.GVA_LOG.Info("接收到文件上传",
zap.String("filename", file.Filename),
zap.Int64("size", file.Size))
// 读取文件内容
src, err := file.Open()
if err != nil {
global.GVA_LOG.Error("打开文件失败", zap.Error(err))
response.FailWithMessage("读取文件失败", c)
return
}
defer src.Close()
// 使用 io.ReadAll 读取完整文件内容
fileData, err := io.ReadAll(src)
if err != nil {
global.GVA_LOG.Error("读取文件内容失败", zap.Error(err))
response.FailWithMessage("读取文件内容失败: "+err.Error(), c)
return
}
global.GVA_LOG.Info("文件读取成功", zap.Int("bytes", len(fileData)))
// 获取是否公开参数
isPublic := c.DefaultPostForm("isPublic", "false") == "true"
// 导入角色卡
character, err := characterService.ImportCharacter(fileData, file.Filename, userID, isPublic)
if err != nil {
global.GVA_LOG.Error("导入角色卡失败",
zap.Error(err),
zap.String("filename", file.Filename))
response.FailWithMessage(err.Error(), c)
return
}
global.GVA_LOG.Info("角色卡导入成功",
zap.Uint("characterId", character.ID),
zap.String("name", character.Name))
response.OkWithData(character, c)
}