feat: add super comment governance and finance oversight

This commit is contained in:
2026-01-16 10:37:24 +08:00
parent f7db11a4df
commit 3af3c854c9
16 changed files with 4139 additions and 285 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 comments struct{}
// List comments
//
// @Router /super/v1/comments [get]
// @Summary List comments
// @Description List comments 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.SuperCommentItem}
// @Bind filter query
func (c *comments) List(ctx fiber.Ctx, filter *dto.SuperCommentListFilter) (*requests.Pager, error) {
return services.Super.ListComments(ctx, filter)
}
// Delete comment
//
// @Router /super/v1/comments/:id<int>/delete [post]
// @Summary Delete comment
// @Description Soft delete a comment
// @Tags Content
// @Accept json
// @Produce json
// @Param id path int64 true "Comment ID"
// @Param form body dto.SuperCommentDeleteForm false "Delete form"
// @Success 200 {string} string "Deleted"
// @Bind user local key(__ctx_user)
// @Bind id path
// @Bind form body
func (c *comments) Delete(ctx fiber.Ctx, user *models.User, id int64, form *dto.SuperCommentDeleteForm) error {
return services.Super.DeleteComment(ctx, user.ID, id, form)
}

View File

@@ -0,0 +1,74 @@
package dto
import "quyun/v2/app/requests"
// SuperCommentListFilter 超管评论列表过滤条件。
type SuperCommentListFilter 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"`
// UserID 评论用户ID精确匹配。
UserID *int64 `query:"user_id"`
// Username 评论用户用户名/昵称,模糊匹配。
Username *string `query:"username"`
// Keyword 评论内容关键字,模糊匹配。
Keyword *string `query:"keyword"`
// Status 评论状态过滤active/deleted/all
Status *string `query:"status"`
// CreatedAtFrom 创建时间起始RFC3339
CreatedAtFrom *string `query:"created_at_from"`
// CreatedAtTo 创建时间结束RFC3339
CreatedAtTo *string `query:"created_at_to"`
// Asc 升序字段id/created_at/likes
Asc *string `query:"asc"`
// Desc 降序字段id/created_at/likes
Desc *string `query:"desc"`
}
// SuperCommentItem 超管评论列表项。
type SuperCommentItem 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"`
// UserID 评论用户ID。
UserID int64 `json:"user_id"`
// Username 评论用户名称。
Username string `json:"username"`
// ReplyTo 回复评论ID0 表示一级评论)。
ReplyTo int64 `json:"reply_to"`
// Content 评论内容。
Content string `json:"content"`
// Likes 评论点赞数。
Likes int32 `json:"likes"`
// IsDeleted 是否已删除。
IsDeleted bool `json:"is_deleted"`
// CreatedAt 创建时间RFC3339
CreatedAt string `json:"created_at"`
// DeletedAt 删除时间RFC3339未删除为空
DeletedAt string `json:"deleted_at"`
}
// SuperCommentDeleteForm 超管删除评论表单。
type SuperCommentDeleteForm struct {
// Reason 删除原因(可选,用于审计记录)。
Reason string `json:"reason"`
}

View File

@@ -0,0 +1,177 @@
package dto
import (
"quyun/v2/app/requests"
"quyun/v2/pkg/consts"
)
// SuperLedgerListFilter 超管资金流水过滤条件。
type SuperLedgerListFilter 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"`
// UserID 用户ID精确匹配。
UserID *int64 `query:"user_id"`
// Username 用户名/昵称,模糊匹配。
Username *string `query:"username"`
// OrderID 关联订单ID精确匹配。
OrderID *int64 `query:"order_id"`
// Type 流水类型过滤。
Type *consts.TenantLedgerType `query:"type"`
// AmountMin 金额下限(分)。
AmountMin *int64 `query:"amount_min"`
// AmountMax 金额上限(分)。
AmountMax *int64 `query:"amount_max"`
// CreatedAtFrom 创建时间起始RFC3339
CreatedAtFrom *string `query:"created_at_from"`
// CreatedAtTo 创建时间结束RFC3339
CreatedAtTo *string `query:"created_at_to"`
// Asc 升序字段id/created_at/amount
Asc *string `query:"asc"`
// Desc 降序字段id/created_at/amount
Desc *string `query:"desc"`
}
// SuperLedgerItem 超管资金流水项。
type SuperLedgerItem 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"`
// UserID 关联用户ID。
UserID int64 `json:"user_id"`
// Username 关联用户名。
Username string `json:"username"`
// OrderID 关联订单ID。
OrderID int64 `json:"order_id"`
// Type 流水类型。
Type consts.TenantLedgerType `json:"type"`
// TypeDescription 流水类型描述(用于展示)。
TypeDescription string `json:"type_description"`
// Amount 变动金额(分)。
Amount int64 `json:"amount"`
// BalanceBefore 变更前可用余额(分)。
BalanceBefore int64 `json:"balance_before"`
// BalanceAfter 变更后可用余额(分)。
BalanceAfter int64 `json:"balance_after"`
// FrozenBefore 变更前冻结余额(分)。
FrozenBefore int64 `json:"frozen_before"`
// FrozenAfter 变更后冻结余额(分)。
FrozenAfter int64 `json:"frozen_after"`
// Remark 流水备注说明。
Remark string `json:"remark"`
// OperatorUserID 操作者用户ID0 表示系统)。
OperatorUserID int64 `json:"operator_user_id"`
// BizRefType 业务引用类型(可选)。
BizRefType string `json:"biz_ref_type"`
// BizRefID 业务引用ID可选
BizRefID int64 `json:"biz_ref_id"`
// CreatedAt 创建时间RFC3339
CreatedAt string `json:"created_at"`
// UpdatedAt 更新时间RFC3339
UpdatedAt string `json:"updated_at"`
}
// SuperBalanceAnomalyFilter 余额异常筛选条件。
type SuperBalanceAnomalyFilter struct {
requests.Pagination
// UserID 用户ID精确匹配。
UserID *int64 `query:"user_id"`
// Username 用户名/昵称,模糊匹配。
Username *string `query:"username"`
// Issue 异常类型negative_balance/negative_frozen
Issue *string `query:"issue"`
// Asc 升序字段id/balance/balance_frozen
Asc *string `query:"asc"`
// Desc 降序字段id/balance/balance_frozen
Desc *string `query:"desc"`
}
// SuperBalanceAnomalyItem 余额异常项。
type SuperBalanceAnomalyItem struct {
// UserID 用户ID。
UserID int64 `json:"user_id"`
// Username 用户名。
Username string `json:"username"`
// Balance 可用余额(分)。
Balance int64 `json:"balance"`
// BalanceFrozen 冻结余额(分)。
BalanceFrozen int64 `json:"balance_frozen"`
// Issue 异常类型标识。
Issue string `json:"issue"`
// IssueDescription 异常描述说明。
IssueDescription string `json:"issue_description"`
// CreatedAt 用户创建时间RFC3339
CreatedAt string `json:"created_at"`
}
// SuperOrderAnomalyFilter 订单异常筛选条件。
type SuperOrderAnomalyFilter 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"`
// UserID 用户ID精确匹配。
UserID *int64 `query:"user_id"`
// Username 用户名/昵称,模糊匹配。
Username *string `query:"username"`
// Type 订单类型过滤。
Type *consts.OrderType `query:"type"`
// Issue 异常类型missing_paid_at/missing_refunded_at
Issue *string `query:"issue"`
// CreatedAtFrom 创建时间起始RFC3339
CreatedAtFrom *string `query:"created_at_from"`
// CreatedAtTo 创建时间结束RFC3339
CreatedAtTo *string `query:"created_at_to"`
// Asc 升序字段id/created_at/amount_paid
Asc *string `query:"asc"`
// Desc 降序字段id/created_at/amount_paid
Desc *string `query:"desc"`
}
// SuperOrderAnomalyItem 订单异常项。
type SuperOrderAnomalyItem struct {
// OrderID 订单ID。
OrderID int64 `json:"order_id"`
// TenantID 租户ID。
TenantID int64 `json:"tenant_id"`
// TenantCode 租户编码。
TenantCode string `json:"tenant_code"`
// TenantName 租户名称。
TenantName string `json:"tenant_name"`
// UserID 用户ID。
UserID int64 `json:"user_id"`
// Username 用户名。
Username string `json:"username"`
// Type 订单类型。
Type consts.OrderType `json:"type"`
// Status 订单状态。
Status consts.OrderStatus `json:"status"`
// AmountPaid 实付金额(分)。
AmountPaid int64 `json:"amount_paid"`
// Issue 异常类型标识。
Issue string `json:"issue"`
// IssueDescription 异常描述说明。
IssueDescription string `json:"issue_description"`
// CreatedAt 创建时间RFC3339
CreatedAt string `json:"created_at"`
// PaidAt 支付时间RFC3339
PaidAt string `json:"paid_at"`
// RefundedAt 退款时间RFC3339
RefundedAt string `json:"refunded_at"`
}

View File

@@ -0,0 +1,60 @@
package v1
import (
dto "quyun/v2/app/http/super/v1/dto"
"quyun/v2/app/requests"
"quyun/v2/app/services"
"github.com/gofiber/fiber/v3"
)
// @provider
type finance struct{}
// List ledgers
//
// @Router /super/v1/finance/ledgers [get]
// @Summary List ledgers
// @Description List tenant ledgers across tenants
// @Tags Finance
// @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.SuperLedgerItem}
// @Bind filter query
func (c *finance) ListLedgers(ctx fiber.Ctx, filter *dto.SuperLedgerListFilter) (*requests.Pager, error) {
return services.Super.ListLedgers(ctx, filter)
}
// List balance anomalies
//
// @Router /super/v1/finance/anomalies/balances [get]
// @Summary List balance anomalies
// @Description List balance anomalies across users
// @Tags Finance
// @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.SuperBalanceAnomalyItem}
// @Bind filter query
func (c *finance) ListBalanceAnomalies(ctx fiber.Ctx, filter *dto.SuperBalanceAnomalyFilter) (*requests.Pager, error) {
return services.Super.ListBalanceAnomalies(ctx, filter)
}
// List order anomalies
//
// @Router /super/v1/finance/anomalies/orders [get]
// @Summary List order anomalies
// @Description List order anomalies across tenants
// @Tags Finance
// @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.SuperOrderAnomalyItem}
// @Bind filter query
func (c *finance) ListOrderAnomalies(ctx fiber.Ctx, filter *dto.SuperOrderAnomalyFilter) (*requests.Pager, error) {
return services.Super.ListOrderAnomalies(ctx, filter)
}

View File

@@ -24,6 +24,13 @@ func Provide(opts ...opt.Option) error {
}); err != nil {
return err
}
if err := container.Container.Provide(func() (*comments, error) {
obj := &comments{}
return obj, nil
}); err != nil {
return err
}
if err := container.Container.Provide(func() (*contents, error) {
obj := &contents{}
@@ -52,6 +59,13 @@ func Provide(opts ...opt.Option) error {
}); err != nil {
return err
}
if err := container.Container.Provide(func() (*finance, error) {
obj := &finance{}
return obj, nil
}); err != nil {
return err
}
if err := container.Container.Provide(func() (*notifications, error) {
obj := &notifications{}
@@ -83,10 +97,12 @@ func Provide(opts ...opt.Option) error {
if err := container.Container.Provide(func(
assets *assets,
auditLogs *auditLogs,
comments *comments,
contents *contents,
coupons *coupons,
creatorApplications *creatorApplications,
creators *creators,
finance *finance,
middlewares *middlewares.Middlewares,
notifications *notifications,
orders *orders,
@@ -100,10 +116,12 @@ func Provide(opts ...opt.Option) error {
obj := &Routes{
assets: assets,
auditLogs: auditLogs,
comments: comments,
contents: contents,
coupons: coupons,
creatorApplications: creatorApplications,
creators: creators,
finance: finance,
middlewares: middlewares,
notifications: notifications,
orders: orders,

View File

@@ -27,10 +27,12 @@ type Routes struct {
// Controller instances
assets *assets
auditLogs *auditLogs
comments *comments
contents *contents
coupons *coupons
creatorApplications *creatorApplications
creators *creators
finance *finance
notifications *notifications
orders *orders
payoutAccounts *payoutAccounts
@@ -79,6 +81,19 @@ func (r *Routes) Register(router fiber.Router) {
r.auditLogs.List,
Query[dto.SuperAuditLogListFilter]("filter"),
))
// Register routes for controller: comments
r.log.Debugf("Registering route: Get /super/v1/comments -> comments.List")
router.Get("/super/v1/comments"[len(r.Path()):], DataFunc1(
r.comments.List,
Query[dto.SuperCommentListFilter]("filter"),
))
r.log.Debugf("Registering route: Post /super/v1/comments/:id<int>/delete -> comments.Delete")
router.Post("/super/v1/comments/:id<int>/delete"[len(r.Path()):], Func3(
r.comments.Delete,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
Body[dto.SuperCommentDeleteForm]("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(
@@ -186,6 +201,22 @@ func (r *Routes) Register(router fiber.Router) {
r.creators.List,
Query[dto.TenantListFilter]("filter"),
))
// Register routes for controller: finance
r.log.Debugf("Registering route: Get /super/v1/finance/anomalies/balances -> finance.ListBalanceAnomalies")
router.Get("/super/v1/finance/anomalies/balances"[len(r.Path()):], DataFunc1(
r.finance.ListBalanceAnomalies,
Query[dto.SuperBalanceAnomalyFilter]("filter"),
))
r.log.Debugf("Registering route: Get /super/v1/finance/anomalies/orders -> finance.ListOrderAnomalies")
router.Get("/super/v1/finance/anomalies/orders"[len(r.Path()):], DataFunc1(
r.finance.ListOrderAnomalies,
Query[dto.SuperOrderAnomalyFilter]("filter"),
))
r.log.Debugf("Registering route: Get /super/v1/finance/ledgers -> finance.ListLedgers")
router.Get("/super/v1/finance/ledgers"[len(r.Path()):], DataFunc1(
r.finance.ListLedgers,
Query[dto.SuperLedgerListFilter]("filter"),
))
// Register routes for controller: notifications
r.log.Debugf("Registering route: Get /super/v1/notifications -> notifications.List")
router.Get("/super/v1/notifications"[len(r.Path()):], DataFunc1(