🎨 重构用户端前端为vue开发,完善基础类和角色相关接口

This commit is contained in:
2026-02-10 21:55:45 +08:00
parent db934ebed7
commit 56e821b222
92 changed files with 18377 additions and 21 deletions

View File

@@ -0,0 +1,392 @@
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)
}