2025-11-19 02:22:04 +08:00
|
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"strconv"
|
|
|
|
|
|
"github.com/ctwj/urldb/db/converter"
|
|
|
|
|
|
"github.com/ctwj/urldb/db/dto"
|
|
|
|
|
|
"github.com/ctwj/urldb/db/entity"
|
|
|
|
|
|
"github.com/ctwj/urldb/db/repo"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
|
"github.com/go-playground/validator/v10"
|
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type ReportHandler struct {
|
2025-11-19 08:32:01 +08:00
|
|
|
|
reportRepo repo.ReportRepository
|
|
|
|
|
|
resourceRepo repo.ResourceRepository
|
|
|
|
|
|
validate *validator.Validate
|
2025-11-19 02:22:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-19 08:32:01 +08:00
|
|
|
|
func NewReportHandler(reportRepo repo.ReportRepository, resourceRepo repo.ResourceRepository) *ReportHandler {
|
2025-11-19 02:22:04 +08:00
|
|
|
|
return &ReportHandler{
|
2025-11-19 08:32:01 +08:00
|
|
|
|
reportRepo: reportRepo,
|
|
|
|
|
|
resourceRepo: resourceRepo,
|
|
|
|
|
|
validate: validator.New(),
|
2025-11-19 02:22:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateReport 创建举报
|
|
|
|
|
|
// @Summary 创建举报
|
|
|
|
|
|
// @Description 提交资源举报
|
|
|
|
|
|
// @Tags Report
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param request body dto.ReportCreateRequest true "举报信息"
|
|
|
|
|
|
// @Success 200 {object} Response{data=dto.ReportResponse}
|
|
|
|
|
|
// @Failure 400 {object} Response
|
|
|
|
|
|
// @Failure 500 {object} Response
|
|
|
|
|
|
// @Router /reports [post]
|
|
|
|
|
|
func (h *ReportHandler) CreateReport(c *gin.Context) {
|
|
|
|
|
|
var req dto.ReportCreateRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
|
ErrorResponse(c, "参数错误: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证参数
|
|
|
|
|
|
if err := h.validate.Struct(req); err != nil {
|
|
|
|
|
|
ErrorResponse(c, "参数验证失败: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建举报实体
|
|
|
|
|
|
report := &entity.Report{
|
|
|
|
|
|
ResourceKey: req.ResourceKey,
|
|
|
|
|
|
Reason: req.Reason,
|
|
|
|
|
|
Description: req.Description,
|
|
|
|
|
|
Contact: req.Contact,
|
|
|
|
|
|
UserAgent: req.UserAgent,
|
|
|
|
|
|
IPAddress: req.IPAddress,
|
|
|
|
|
|
Status: "pending", // 默认为待处理
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 保存到数据库
|
|
|
|
|
|
if err := h.reportRepo.Create(report); err != nil {
|
|
|
|
|
|
ErrorResponse(c, "创建举报失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 返回响应
|
|
|
|
|
|
response := converter.ReportToResponse(report)
|
|
|
|
|
|
SuccessResponse(c, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetReport 获取举报详情
|
|
|
|
|
|
// @Summary 获取举报详情
|
|
|
|
|
|
// @Description 根据ID获取举报详情
|
|
|
|
|
|
// @Tags Report
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param id path int true "举报ID"
|
|
|
|
|
|
// @Success 200 {object} Response{data=dto.ReportResponse}
|
|
|
|
|
|
// @Failure 400 {object} Response
|
|
|
|
|
|
// @Failure 404 {object} Response
|
|
|
|
|
|
// @Failure 500 {object} Response
|
|
|
|
|
|
// @Router /reports/{id} [get]
|
|
|
|
|
|
func (h *ReportHandler) GetReport(c *gin.Context) {
|
|
|
|
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "无效的ID", http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
report, err := h.reportRepo.GetByID(uint(id))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if err == gorm.ErrRecordNotFound {
|
|
|
|
|
|
ErrorResponse(c, "举报不存在", http.StatusNotFound)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
ErrorResponse(c, "获取举报失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response := converter.ReportToResponse(report)
|
|
|
|
|
|
SuccessResponse(c, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ListReports 获取举报列表
|
|
|
|
|
|
// @Summary 获取举报列表
|
|
|
|
|
|
// @Description 获取举报列表(支持分页和状态筛选)
|
|
|
|
|
|
// @Tags Report
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param page query int false "页码" default(1)
|
|
|
|
|
|
// @Param page_size query int false "每页数量" default(10)
|
|
|
|
|
|
// @Param status query string false "处理状态"
|
|
|
|
|
|
// @Success 200 {object} Response{data=object{items=[]dto.ReportResponse,total=int}}
|
|
|
|
|
|
// @Failure 400 {object} Response
|
|
|
|
|
|
// @Failure 500 {object} Response
|
|
|
|
|
|
// @Router /reports [get]
|
|
|
|
|
|
func (h *ReportHandler) ListReports(c *gin.Context) {
|
|
|
|
|
|
var req dto.ReportListRequest
|
|
|
|
|
|
if err := c.ShouldBindQuery(&req); err != nil {
|
|
|
|
|
|
ErrorResponse(c, "参数错误: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置默认值
|
|
|
|
|
|
if req.Page == 0 {
|
|
|
|
|
|
req.Page = 1
|
|
|
|
|
|
}
|
|
|
|
|
|
if req.PageSize == 0 {
|
|
|
|
|
|
req.PageSize = 10
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证参数
|
|
|
|
|
|
if err := h.validate.Struct(req); err != nil {
|
|
|
|
|
|
ErrorResponse(c, "参数验证失败: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reports, total, err := h.reportRepo.List(req.Status, req.Page, req.PageSize)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "获取举报列表失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-19 08:32:01 +08:00
|
|
|
|
// 获取每个举报关联的资源
|
|
|
|
|
|
var reportResponses []*dto.ReportResponse
|
|
|
|
|
|
for _, report := range reports {
|
|
|
|
|
|
// 通过资源key查找关联的资源
|
|
|
|
|
|
resources, err := h.getResourcesByResourceKey(report.ResourceKey)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
// 如果获取资源失败,仍然返回基本的举报信息
|
|
|
|
|
|
reportResponses = append(reportResponses, converter.ReportToResponse(report))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 使用包含资源详情的转换函数
|
|
|
|
|
|
response := converter.ReportToResponseWithResources(report, resources)
|
|
|
|
|
|
reportResponses = append(reportResponses, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PageResponse(c, reportResponses, total, req.Page, req.PageSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// getResourcesByResourceKey 根据资源key获取关联的资源列表
|
|
|
|
|
|
func (h *ReportHandler) getResourcesByResourceKey(resourceKey string) ([]*entity.Resource, error) {
|
|
|
|
|
|
// 从资源仓库获取与key关联的所有资源
|
|
|
|
|
|
resources, err := h.resourceRepo.FindByResourceKey(resourceKey)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 将 []entity.Resource 转换为 []*entity.Resource
|
|
|
|
|
|
var resourcePointers []*entity.Resource
|
|
|
|
|
|
for i := range resources {
|
|
|
|
|
|
resourcePointers = append(resourcePointers, &resources[i])
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return resourcePointers, nil
|
2025-11-19 02:22:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateReport 更新举报状态
|
|
|
|
|
|
// @Summary 更新举报状态
|
|
|
|
|
|
// @Description 更新举报处理状态
|
|
|
|
|
|
// @Tags Report
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param id path int true "举报ID"
|
|
|
|
|
|
// @Param request body dto.ReportUpdateRequest true "更新信息"
|
|
|
|
|
|
// @Success 200 {object} Response{data=dto.ReportResponse}
|
|
|
|
|
|
// @Failure 400 {object} Response
|
|
|
|
|
|
// @Failure 404 {object} Response
|
|
|
|
|
|
// @Failure 500 {object} Response
|
|
|
|
|
|
// @Router /reports/{id} [put]
|
|
|
|
|
|
func (h *ReportHandler) UpdateReport(c *gin.Context) {
|
|
|
|
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "无效的ID", http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var req dto.ReportUpdateRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
|
ErrorResponse(c, "参数错误: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证参数
|
|
|
|
|
|
if err := h.validate.Struct(req); err != nil {
|
|
|
|
|
|
ErrorResponse(c, "参数验证失败: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前举报
|
|
|
|
|
|
_, err = h.reportRepo.GetByID(uint(id))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if err == gorm.ErrRecordNotFound {
|
|
|
|
|
|
ErrorResponse(c, "举报不存在", http.StatusNotFound)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
ErrorResponse(c, "获取举报失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新状态
|
|
|
|
|
|
processedBy := uint(0) // 从上下文获取当前用户ID,如果存在的话
|
|
|
|
|
|
if currentUser := c.GetUint("user_id"); currentUser > 0 {
|
|
|
|
|
|
processedBy = currentUser
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := h.reportRepo.UpdateStatus(uint(id), req.Status, &processedBy, req.Note); err != nil {
|
|
|
|
|
|
ErrorResponse(c, "更新举报状态失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取更新后的举报信息
|
|
|
|
|
|
updatedReport, err := h.reportRepo.GetByID(uint(id))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "获取更新后举报信息失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SuccessResponse(c, converter.ReportToResponse(updatedReport))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DeleteReport 删除举报
|
|
|
|
|
|
// @Summary 删除举报
|
|
|
|
|
|
// @Description 删除举报记录
|
|
|
|
|
|
// @Tags Report
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param id path int true "举报ID"
|
|
|
|
|
|
// @Success 200 {object} Response
|
|
|
|
|
|
// @Failure 400 {object} Response
|
|
|
|
|
|
// @Failure 500 {object} Response
|
|
|
|
|
|
// @Router /reports/{id} [delete]
|
|
|
|
|
|
func (h *ReportHandler) DeleteReport(c *gin.Context) {
|
|
|
|
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "无效的ID", http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := h.reportRepo.Delete(uint(id)); err != nil {
|
|
|
|
|
|
ErrorResponse(c, "删除举报失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SuccessResponse(c, nil)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetReportByResource 获取某个资源的举报列表
|
|
|
|
|
|
// @Summary 获取资源举报列表
|
|
|
|
|
|
// @Description 获取某个资源的所有举报记录
|
|
|
|
|
|
// @Tags Report
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param resource_key path string true "资源Key"
|
|
|
|
|
|
// @Success 200 {object} Response{data=[]dto.ReportResponse}
|
|
|
|
|
|
// @Failure 400 {object} Response
|
|
|
|
|
|
// @Failure 500 {object} Response
|
|
|
|
|
|
// @Router /reports/resource/{resource_key} [get]
|
|
|
|
|
|
func (h *ReportHandler) GetReportByResource(c *gin.Context) {
|
|
|
|
|
|
resourceKey := c.Param("resource_key")
|
|
|
|
|
|
if resourceKey == "" {
|
|
|
|
|
|
ErrorResponse(c, "资源Key不能为空", http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reports, err := h.reportRepo.GetByResourceKey(resourceKey)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "获取资源举报失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SuccessResponse(c, converter.ReportsToResponse(reports))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// RegisterReportRoutes 注册举报相关路由
|
2025-11-19 08:32:01 +08:00
|
|
|
|
func RegisterReportRoutes(router *gin.RouterGroup, reportRepo repo.ReportRepository, resourceRepo repo.ResourceRepository) {
|
|
|
|
|
|
handler := NewReportHandler(reportRepo, resourceRepo)
|
2025-11-19 02:22:04 +08:00
|
|
|
|
|
|
|
|
|
|
reports := router.Group("/reports")
|
|
|
|
|
|
{
|
|
|
|
|
|
reports.POST("", handler.CreateReport) // 创建举报
|
|
|
|
|
|
reports.GET("/:id", handler.GetReport) // 获取举报详情
|
|
|
|
|
|
reports.GET("", handler.ListReports) // 获取举报列表
|
|
|
|
|
|
reports.PUT("/:id", handler.UpdateReport) // 更新举报状态
|
|
|
|
|
|
reports.DELETE("/:id", handler.DeleteReport) // 删除举报
|
|
|
|
|
|
reports.GET("/resource/:resource_key", handler.GetReportByResource) // 获取资源举报列表
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|