feat: add content report governance

This commit is contained in:
2026-01-16 11:36:04 +08:00
parent 3af3c854c9
commit 609ca7b980
18 changed files with 2480 additions and 101 deletions

View File

@@ -0,0 +1,47 @@
package v1
import (
dto "quyun/v2/app/http/super/v1/dto"
"quyun/v2/app/requests"
"quyun/v2/app/services"
"quyun/v2/database/models"
"github.com/gofiber/fiber/v3"
)
// @provider
type contentReports struct{}
// List content reports
//
// @Router /super/v1/content-reports [get]
// @Summary List content reports
// @Description List content report records across tenants
// @Tags Content
// @Accept json
// @Produce json
// @Param page query int false "Page number"
// @Param limit query int false "Page size"
// @Success 200 {object} requests.Pager{items=[]dto.SuperContentReportItem}
// @Bind filter query
func (c *contentReports) List(ctx fiber.Ctx, filter *dto.SuperContentReportListFilter) (*requests.Pager, error) {
return services.Super.ListContentReports(ctx, filter)
}
// Process content report
//
// @Router /super/v1/content-reports/:id<int>/process [post]
// @Summary Process content report
// @Description Process a content report record
// @Tags Content
// @Accept json
// @Produce json
// @Param id path int64 true "Report ID"
// @Param form body dto.SuperContentReportProcessForm true "Process form"
// @Success 200 {string} string "Processed"
// @Bind user local key(__ctx_user)
// @Bind id path
// @Bind form body
func (c *contentReports) Process(ctx fiber.Ctx, user *models.User, id int64, form *dto.SuperContentReportProcessForm) error {
return services.Super.ProcessContentReport(ctx, user.ID, id, form)
}

View File

@@ -0,0 +1,100 @@
package dto
import "quyun/v2/app/requests"
// SuperContentReportListFilter 超管内容举报列表过滤条件。
type SuperContentReportListFilter struct {
requests.Pagination
// ID 举报ID精确匹配。
ID *int64 `query:"id"`
// TenantID 租户ID精确匹配。
TenantID *int64 `query:"tenant_id"`
// TenantCode 租户编码,模糊匹配。
TenantCode *string `query:"tenant_code"`
// TenantName 租户名称,模糊匹配。
TenantName *string `query:"tenant_name"`
// ContentID 内容ID精确匹配。
ContentID *int64 `query:"content_id"`
// ContentTitle 内容标题关键字,模糊匹配。
ContentTitle *string `query:"content_title"`
// ReporterID 举报人用户ID精确匹配。
ReporterID *int64 `query:"reporter_id"`
// ReporterName 举报人用户名/昵称,模糊匹配。
ReporterName *string `query:"reporter_name"`
// HandledBy 处理人用户ID精确匹配。
HandledBy *int64 `query:"handled_by"`
// HandledByName 处理人用户名/昵称,模糊匹配。
HandledByName *string `query:"handled_by_name"`
// Reason 举报原因关键字,模糊匹配。
Reason *string `query:"reason"`
// Keyword 举报描述关键字,模糊匹配。
Keyword *string `query:"keyword"`
// Status 处理状态pending/approved/rejected/all
Status *string `query:"status"`
// CreatedAtFrom 举报时间起始RFC3339
CreatedAtFrom *string `query:"created_at_from"`
// CreatedAtTo 举报时间结束RFC3339
CreatedAtTo *string `query:"created_at_to"`
// HandledAtFrom 处理时间起始RFC3339
HandledAtFrom *string `query:"handled_at_from"`
// HandledAtTo 处理时间结束RFC3339
HandledAtTo *string `query:"handled_at_to"`
// Asc 升序字段id/created_at/handled_at/status
Asc *string `query:"asc"`
// Desc 降序字段id/created_at/handled_at/status
Desc *string `query:"desc"`
}
// SuperContentReportItem 超管内容举报列表项。
type SuperContentReportItem struct {
// ID 举报ID。
ID int64 `json:"id"`
// TenantID 租户ID。
TenantID int64 `json:"tenant_id"`
// TenantCode 租户编码。
TenantCode string `json:"tenant_code"`
// TenantName 租户名称。
TenantName string `json:"tenant_name"`
// ContentID 内容ID。
ContentID int64 `json:"content_id"`
// ContentTitle 内容标题。
ContentTitle string `json:"content_title"`
// ContentStatus 内容状态。
ContentStatus string `json:"content_status"`
// ContentOwnerID 内容作者用户ID。
ContentOwnerID int64 `json:"content_owner_id"`
// ContentOwnerName 内容作者用户名/昵称。
ContentOwnerName string `json:"content_owner_name"`
// ReporterID 举报人用户ID。
ReporterID int64 `json:"reporter_id"`
// ReporterName 举报人用户名/昵称。
ReporterName string `json:"reporter_name"`
// Reason 举报原因。
Reason string `json:"reason"`
// Detail 举报描述。
Detail string `json:"detail"`
// Status 处理状态。
Status string `json:"status"`
// HandledBy 处理人用户ID。
HandledBy int64 `json:"handled_by"`
// HandledByName 处理人用户名/昵称。
HandledByName string `json:"handled_by_name"`
// HandledAction 处理动作block/unpublish/ignore
HandledAction string `json:"handled_action"`
// HandledReason 处理说明。
HandledReason string `json:"handled_reason"`
// CreatedAt 举报时间RFC3339
CreatedAt string `json:"created_at"`
// HandledAt 处理时间RFC3339
HandledAt string `json:"handled_at"`
}
// SuperContentReportProcessForm 超管内容举报处理表单。
type SuperContentReportProcessForm struct {
// Action 处理动作approve/reject
Action string `json:"action"`
// ContentAction 内容处置动作block/unpublish/ignore仅在 approve 时生效。
ContentAction string `json:"content_action"`
// Reason 处理说明(可选,用于审计记录)。
Reason string `json:"reason"`
}

View File

@@ -31,6 +31,13 @@ func Provide(opts ...opt.Option) error {
}); err != nil {
return err
}
if err := container.Container.Provide(func() (*contentReports, error) {
obj := &contentReports{}
return obj, nil
}); err != nil {
return err
}
if err := container.Container.Provide(func() (*contents, error) {
obj := &contents{}
@@ -98,6 +105,7 @@ func Provide(opts ...opt.Option) error {
assets *assets,
auditLogs *auditLogs,
comments *comments,
contentReports *contentReports,
contents *contents,
coupons *coupons,
creatorApplications *creatorApplications,
@@ -117,6 +125,7 @@ func Provide(opts ...opt.Option) error {
assets: assets,
auditLogs: auditLogs,
comments: comments,
contentReports: contentReports,
contents: contents,
coupons: coupons,
creatorApplications: creatorApplications,

View File

@@ -28,6 +28,7 @@ type Routes struct {
assets *assets
auditLogs *auditLogs
comments *comments
contentReports *contentReports
contents *contents
coupons *coupons
creatorApplications *creatorApplications
@@ -94,6 +95,19 @@ func (r *Routes) Register(router fiber.Router) {
PathParam[int64]("id"),
Body[dto.SuperCommentDeleteForm]("form"),
))
// Register routes for controller: contentReports
r.log.Debugf("Registering route: Get /super/v1/content-reports -> contentReports.List")
router.Get("/super/v1/content-reports"[len(r.Path()):], DataFunc1(
r.contentReports.List,
Query[dto.SuperContentReportListFilter]("filter"),
))
r.log.Debugf("Registering route: Post /super/v1/content-reports/:id<int>/process -> contentReports.Process")
router.Post("/super/v1/content-reports/:id<int>/process"[len(r.Path()):], Func3(
r.contentReports.Process,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
Body[dto.SuperContentReportProcessForm]("form"),
))
// Register routes for controller: contents
r.log.Debugf("Registering route: Get /super/v1/contents -> contents.List")
router.Get("/super/v1/contents"[len(r.Path()):], DataFunc1(