2025-07-10 13:58:28 +08:00
|
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"strconv"
|
|
|
|
|
|
|
2025-07-18 09:42:07 +08:00
|
|
|
|
"github.com/ctwj/urldb/db/converter"
|
|
|
|
|
|
"github.com/ctwj/urldb/db/dto"
|
|
|
|
|
|
"github.com/ctwj/urldb/db/entity"
|
|
|
|
|
|
"github.com/ctwj/urldb/utils"
|
2025-07-10 13:58:28 +08:00
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// GetTags 获取标签列表
|
|
|
|
|
|
func GetTags(c *gin.Context) {
|
2025-07-16 08:29:49 +08:00
|
|
|
|
// 获取分页参数
|
|
|
|
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
|
|
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
|
|
|
|
|
search := c.Query("search")
|
|
|
|
|
|
|
|
|
|
|
|
var tags []entity.Tag
|
|
|
|
|
|
var total int64
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
|
|
if search != "" {
|
2025-08-09 17:26:52 +08:00
|
|
|
|
// 搜索标签(按资源数量排序)
|
|
|
|
|
|
tags, total, err = repoManager.TagRepository.SearchOrderByResourceCount(search, page, pageSize)
|
2025-07-16 08:29:49 +08:00
|
|
|
|
} else {
|
2025-08-09 17:26:52 +08:00
|
|
|
|
// 分页查询(按资源数量排序)
|
|
|
|
|
|
tags, total, err = repoManager.TagRepository.FindWithPaginationOrderByResourceCount(page, pageSize)
|
2025-07-16 08:29:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-10 13:58:28 +08:00
|
|
|
|
if err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-16 08:29:49 +08:00
|
|
|
|
// 获取每个标签的资源数量
|
|
|
|
|
|
resourceCounts := make(map[uint]int64)
|
|
|
|
|
|
for _, tag := range tags {
|
|
|
|
|
|
count, err := repoManager.TagRepository.GetResourceCount(tag.ID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
resourceCounts[tag.ID] = count
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
responses := converter.ToTagResponseList(tags, resourceCounts)
|
|
|
|
|
|
|
|
|
|
|
|
// 返回分页格式的响应
|
|
|
|
|
|
SuccessResponse(c, gin.H{
|
|
|
|
|
|
"items": responses,
|
|
|
|
|
|
"total": total,
|
|
|
|
|
|
"page": page,
|
|
|
|
|
|
"page_size": pageSize,
|
|
|
|
|
|
})
|
2025-07-10 13:58:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateTag 创建标签
|
|
|
|
|
|
func CreateTag(c *gin.Context) {
|
|
|
|
|
|
var req dto.CreateTagRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, err.Error(), http.StatusBadRequest)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-03 10:50:25 +08:00
|
|
|
|
// 首先检查是否存在已删除的同名标签
|
|
|
|
|
|
deletedTag, err := repoManager.TagRepository.FindByNameIncludingDeleted(req.Name)
|
|
|
|
|
|
if err == nil && deletedTag.DeletedAt.Valid {
|
|
|
|
|
|
// 如果存在已删除的同名标签,则恢复它
|
|
|
|
|
|
err = repoManager.TagRepository.RestoreDeletedTag(deletedTag.ID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "恢复已删除标签失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重新获取恢复后的标签
|
|
|
|
|
|
restoredTag, err := repoManager.TagRepository.FindByID(deletedTag.ID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "获取恢复的标签失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新标签信息
|
|
|
|
|
|
restoredTag.Description = req.Description
|
|
|
|
|
|
restoredTag.CategoryID = req.CategoryID
|
|
|
|
|
|
err = repoManager.TagRepository.UpdateWithNulls(restoredTag)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "更新恢复的标签失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SuccessResponse(c, gin.H{
|
|
|
|
|
|
"message": "标签恢复成功",
|
|
|
|
|
|
"tag": converter.ToTagResponse(restoredTag, 0),
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果不存在已删除的同名标签,则创建新标签
|
2025-07-10 13:58:28 +08:00
|
|
|
|
tag := &entity.Tag{
|
|
|
|
|
|
Name: req.Name,
|
|
|
|
|
|
Description: req.Description,
|
2025-07-16 10:54:00 +08:00
|
|
|
|
CategoryID: req.CategoryID,
|
2025-07-10 13:58:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-03 10:50:25 +08:00
|
|
|
|
err = repoManager.TagRepository.Create(tag)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
if err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 17:45:16 +08:00
|
|
|
|
SuccessResponse(c, gin.H{
|
2025-07-10 13:58:28 +08:00
|
|
|
|
"message": "标签创建成功",
|
2025-07-16 08:29:49 +08:00
|
|
|
|
"tag": converter.ToTagResponse(tag, 0),
|
2025-07-10 13:58:28 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 17:45:16 +08:00
|
|
|
|
// GetTagByID 根据ID获取标签详情
|
|
|
|
|
|
func GetTagByID(c *gin.Context) {
|
|
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
id, err := strconv.ParseUint(idStr, 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "无效的ID", http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tag, err := repoManager.TagRepository.FindByID(uint(id))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "标签不存在", http.StatusNotFound)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-16 08:29:49 +08:00
|
|
|
|
// 获取资源数量
|
|
|
|
|
|
resourceCount, _ := repoManager.TagRepository.GetResourceCount(tag.ID)
|
|
|
|
|
|
response := converter.ToTagResponse(tag, resourceCount)
|
2025-07-11 17:45:16 +08:00
|
|
|
|
SuccessResponse(c, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-10 13:58:28 +08:00
|
|
|
|
// UpdateTag 更新标签
|
|
|
|
|
|
func UpdateTag(c *gin.Context) {
|
|
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
id, err := strconv.ParseUint(idStr, 10, 32)
|
|
|
|
|
|
if err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, "无效的ID", http.StatusBadRequest)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var req dto.UpdateTagRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, err.Error(), http.StatusBadRequest)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tag, err := repoManager.TagRepository.FindByID(uint(id))
|
|
|
|
|
|
if err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, "标签不存在", http.StatusNotFound)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-16 10:54:00 +08:00
|
|
|
|
// 添加调试信息
|
2025-07-17 17:42:52 +08:00
|
|
|
|
utils.Debug("更新标签 - ID: %d, 请求CategoryID: %v, 当前CategoryID: %v", id, req.CategoryID, tag.CategoryID)
|
2025-07-16 10:54:00 +08:00
|
|
|
|
|
2025-07-10 13:58:28 +08:00
|
|
|
|
if req.Name != "" {
|
|
|
|
|
|
tag.Name = req.Name
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.Description != "" {
|
|
|
|
|
|
tag.Description = req.Description
|
|
|
|
|
|
}
|
2025-07-16 10:54:00 +08:00
|
|
|
|
// 处理CategoryID,包括设置为null的情况
|
|
|
|
|
|
tag.CategoryID = req.CategoryID
|
|
|
|
|
|
|
|
|
|
|
|
// 添加调试信息
|
2025-07-17 17:42:52 +08:00
|
|
|
|
utils.Debug("更新后CategoryID: %v", tag.CategoryID)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
|
2025-07-16 10:54:00 +08:00
|
|
|
|
// 使用专门的更新方法,确保能更新null值
|
|
|
|
|
|
err = repoManager.TagRepository.UpdateWithNulls(tag)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
if err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 17:45:16 +08:00
|
|
|
|
SuccessResponse(c, gin.H{"message": "标签更新成功"})
|
2025-07-10 13:58:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DeleteTag 删除标签
|
|
|
|
|
|
func DeleteTag(c *gin.Context) {
|
|
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
id, err := strconv.ParseUint(idStr, 10, 32)
|
|
|
|
|
|
if err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, "无效的ID", http.StatusBadRequest)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err = repoManager.TagRepository.Delete(uint(id))
|
|
|
|
|
|
if err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 17:45:16 +08:00
|
|
|
|
SuccessResponse(c, gin.H{"message": "标签删除成功"})
|
2025-07-10 13:58:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 17:45:16 +08:00
|
|
|
|
// GetTagByID 根据ID获取标签详情(使用全局repoManager)
|
|
|
|
|
|
func GetTagByIDGlobal(c *gin.Context) {
|
2025-07-10 13:58:28 +08:00
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
id, err := strconv.ParseUint(idStr, 10, 32)
|
|
|
|
|
|
if err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, "无效的ID", http.StatusBadRequest)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tag, err := repoManager.TagRepository.FindByID(uint(id))
|
|
|
|
|
|
if err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, "标签不存在", http.StatusNotFound)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-16 08:29:49 +08:00
|
|
|
|
// 获取资源数量
|
|
|
|
|
|
resourceCount, _ := repoManager.TagRepository.GetResourceCount(tag.ID)
|
|
|
|
|
|
response := converter.ToTagResponse(tag, resourceCount)
|
2025-07-11 17:45:16 +08:00
|
|
|
|
SuccessResponse(c, response)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 17:45:16 +08:00
|
|
|
|
// GetTags 获取标签列表(使用全局repoManager)
|
|
|
|
|
|
func GetTagsGlobal(c *gin.Context) {
|
|
|
|
|
|
tags, err := repoManager.TagRepository.FindAll()
|
2025-07-10 13:58:28 +08:00
|
|
|
|
if err != nil {
|
2025-07-11 17:45:16 +08:00
|
|
|
|
ErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-16 08:29:49 +08:00
|
|
|
|
// 获取每个标签的资源数量
|
|
|
|
|
|
resourceCounts := make(map[uint]int64)
|
|
|
|
|
|
for _, tag := range tags {
|
|
|
|
|
|
count, err := repoManager.TagRepository.GetResourceCount(tag.ID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
resourceCounts[tag.ID] = count
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
responses := converter.ToTagResponseList(tags, resourceCounts)
|
2025-07-11 17:45:16 +08:00
|
|
|
|
SuccessResponse(c, responses)
|
2025-07-10 13:58:28 +08:00
|
|
|
|
}
|
2025-07-16 10:54:00 +08:00
|
|
|
|
|
|
|
|
|
|
// GetTagsByCategory 根据分类ID获取标签列表
|
|
|
|
|
|
func GetTagsByCategory(c *gin.Context) {
|
|
|
|
|
|
categoryIDStr := c.Param("categoryId")
|
|
|
|
|
|
categoryID, err := strconv.ParseUint(categoryIDStr, 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "无效的分类ID", http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取分页参数
|
|
|
|
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
|
|
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
|
|
|
|
|
|
|
|
|
|
|
tags, total, err := repoManager.TagRepository.FindByCategoryIDPaginated(uint(categoryID), page, pageSize)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取每个标签的资源数量
|
|
|
|
|
|
resourceCounts := make(map[uint]int64)
|
|
|
|
|
|
for _, tag := range tags {
|
|
|
|
|
|
count, err := repoManager.TagRepository.GetResourceCount(tag.ID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
resourceCounts[tag.ID] = count
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
responses := converter.ToTagResponseList(tags, resourceCounts)
|
|
|
|
|
|
|
|
|
|
|
|
// 返回分页格式的响应
|
|
|
|
|
|
SuccessResponse(c, gin.H{
|
|
|
|
|
|
"items": responses,
|
|
|
|
|
|
"total": total,
|
|
|
|
|
|
"page": page,
|
|
|
|
|
|
"page_size": pageSize,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|