feat: add superadmin member review and coupon ops

This commit is contained in:
2026-01-15 11:53:52 +08:00
parent 56082bad4f
commit 8419ddede7
11 changed files with 1254 additions and 96 deletions

View File

@@ -2,8 +2,10 @@ package v1
import (
dto "quyun/v2/app/http/super/v1/dto"
v1_dto "quyun/v2/app/http/v1/dto"
"quyun/v2/app/requests"
"quyun/v2/app/services"
"quyun/v2/database/models"
"github.com/gofiber/fiber/v3"
)
@@ -26,3 +28,81 @@ type coupons struct{}
func (c *coupons) List(ctx fiber.Ctx, filter *dto.SuperCouponListFilter) (*requests.Pager, error) {
return services.Super.ListCoupons(ctx, filter)
}
// Create coupon
//
// @Router /super/v1/tenants/:tenantID<int>/coupons [post]
// @Summary Create coupon
// @Description Create coupon for tenant
// @Tags Coupon
// @Accept json
// @Produce json
// @Param tenantID path int64 true "Tenant ID"
// @Param form body v1_dto.CouponCreateForm true "Create form"
// @Success 200 {object} v1_dto.CouponItem
// @Bind tenantID path
// @Bind form body
// @Bind user local key(__ctx_user)
func (c *coupons) Create(ctx fiber.Ctx, user *models.User, tenantID int64, form *v1_dto.CouponCreateForm) (*v1_dto.CouponItem, error) {
return services.Coupon.Create(ctx, tenantID, user.ID, form)
}
// Get coupon
//
// @Router /super/v1/tenants/:tenantID<int>/coupons/:id<int> [get]
// @Summary Get coupon
// @Description Get coupon detail
// @Tags Coupon
// @Accept json
// @Produce json
// @Param tenantID path int64 true "Tenant ID"
// @Param id path int64 true "Coupon ID"
// @Success 200 {object} v1_dto.CouponItem
// @Bind tenantID path
// @Bind id path
func (c *coupons) Get(ctx fiber.Ctx, tenantID, id int64) (*v1_dto.CouponItem, error) {
return services.Coupon.Get(ctx, tenantID, id)
}
// Update coupon
//
// @Router /super/v1/tenants/:tenantID<int>/coupons/:id<int> [put]
// @Summary Update coupon
// @Description Update coupon for tenant
// @Tags Coupon
// @Accept json
// @Produce json
// @Param tenantID path int64 true "Tenant ID"
// @Param id path int64 true "Coupon ID"
// @Param form body v1_dto.CouponUpdateForm true "Update form"
// @Success 200 {object} v1_dto.CouponItem
// @Bind tenantID path
// @Bind id path
// @Bind form body
// @Bind user local key(__ctx_user)
func (c *coupons) Update(ctx fiber.Ctx, user *models.User, tenantID, id int64, form *v1_dto.CouponUpdateForm) (*v1_dto.CouponItem, error) {
return services.Coupon.Update(ctx, tenantID, user.ID, id, form)
}
// Grant coupon
//
// @Router /super/v1/tenants/:tenantID<int>/coupons/:id<int>/grant [post]
// @Summary Grant coupon
// @Description Grant coupon to users
// @Tags Coupon
// @Accept json
// @Produce json
// @Param tenantID path int64 true "Tenant ID"
// @Param id path int64 true "Coupon ID"
// @Param form body v1_dto.CouponGrantForm true "Grant form"
// @Success 200 {object} dto.SuperCouponGrantResponse
// @Bind tenantID path
// @Bind id path
// @Bind form body
func (c *coupons) Grant(ctx fiber.Ctx, tenantID, id int64, form *v1_dto.CouponGrantForm) (*dto.SuperCouponGrantResponse, error) {
granted, err := services.Coupon.Grant(ctx, tenantID, id, form.UserIDs)
if err != nil {
return nil, err
}
return &dto.SuperCouponGrantResponse{Granted: granted}, nil
}

View File

@@ -454,6 +454,59 @@ type TenantUser struct {
UpdatedAt string `json:"updated_at"`
}
// SuperTenantJoinRequestListFilter 超管成员申请列表过滤条件。
type SuperTenantJoinRequestListFilter struct {
requests.Pagination
// 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"`
// Status 申请状态pending/approved/rejected
Status *consts.TenantJoinRequestStatus `query:"status"`
// CreatedAtFrom 申请时间起始RFC3339
CreatedAtFrom *string `query:"created_at_from"`
// CreatedAtTo 申请时间结束RFC3339
CreatedAtTo *string `query:"created_at_to"`
}
// SuperTenantJoinRequestItem 超管成员申请列表项。
type SuperTenantJoinRequestItem 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"`
// Status 申请状态。
Status string `json:"status"`
// StatusDescription 状态描述(用于展示)。
StatusDescription string `json:"status_description"`
// Reason 申请说明。
Reason string `json:"reason"`
// DecidedAt 审核时间RFC3339
DecidedAt string `json:"decided_at"`
// DecidedOperatorUserID 审核操作者ID。
DecidedOperatorUserID int64 `json:"decided_operator_user_id"`
// DecidedReason 审核备注/原因。
DecidedReason string `json:"decided_reason"`
// CreatedAt 申请时间RFC3339
CreatedAt string `json:"created_at"`
// UpdatedAt 更新时间RFC3339
UpdatedAt string `json:"updated_at"`
}
// Order Related
type SuperOrderItem struct {
// ID 订单ID。

View File

@@ -73,3 +73,9 @@ type SuperCouponItem struct {
// UpdatedAt 更新时间RFC3339
UpdatedAt string `json:"updated_at"`
}
// SuperCouponGrantResponse 超管优惠券发放结果。
type SuperCouponGrantResponse struct {
// Granted 实际发放数量。
Granted int `json:"granted"`
}

View File

@@ -6,6 +6,7 @@ package v1
import (
dto "quyun/v2/app/http/super/v1/dto"
v1_dto "quyun/v2/app/http/v1/dto"
"quyun/v2/app/middlewares"
"quyun/v2/database/models"
@@ -87,6 +88,34 @@ func (r *Routes) Register(router fiber.Router) {
r.coupons.List,
Query[dto.SuperCouponListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /super/v1/tenants/:tenantID<int>/coupons/:id<int> -> coupons.Get")
router.Get("/super/v1/tenants/:tenantID<int>/coupons/:id<int>"[len(r.Path()):], DataFunc2(
r.coupons.Get,
PathParam[int64]("tenantID"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Post /super/v1/tenants/:tenantID<int>/coupons -> coupons.Create")
router.Post("/super/v1/tenants/:tenantID<int>/coupons"[len(r.Path()):], DataFunc3(
r.coupons.Create,
Local[*models.User]("__ctx_user"),
PathParam[int64]("tenantID"),
Body[v1_dto.CouponCreateForm]("form"),
))
r.log.Debugf("Registering route: Post /super/v1/tenants/:tenantID<int>/coupons/:id<int>/grant -> coupons.Grant")
router.Post("/super/v1/tenants/:tenantID<int>/coupons/:id<int>/grant"[len(r.Path()):], DataFunc3(
r.coupons.Grant,
PathParam[int64]("tenantID"),
PathParam[int64]("id"),
Body[v1_dto.CouponGrantForm]("form"),
))
r.log.Debugf("Registering route: Put /super/v1/tenants/:tenantID<int>/coupons/:id<int> -> coupons.Update")
router.Put("/super/v1/tenants/:tenantID<int>/coupons/:id<int>"[len(r.Path()):], DataFunc4(
r.coupons.Update,
Local[*models.User]("__ctx_user"),
PathParam[int64]("tenantID"),
PathParam[int64]("id"),
Body[v1_dto.CouponUpdateForm]("form"),
))
// Register routes for controller: creators
r.log.Debugf("Registering route: Get /super/v1/creators -> creators.List")
router.Get("/super/v1/creators"[len(r.Path()):], DataFunc1(
@@ -126,6 +155,11 @@ func (r *Routes) Register(router fiber.Router) {
Body[dto.SuperReportExportForm]("form"),
))
// Register routes for controller: tenants
r.log.Debugf("Registering route: Get /super/v1/tenant-join-requests -> tenants.ListJoinRequests")
router.Get("/super/v1/tenant-join-requests"[len(r.Path()):], DataFunc1(
r.tenants.ListJoinRequests,
Query[dto.SuperTenantJoinRequestListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /super/v1/tenants -> tenants.List")
router.Get("/super/v1/tenants"[len(r.Path()):], DataFunc1(
r.tenants.List,
@@ -163,11 +197,24 @@ func (r *Routes) Register(router fiber.Router) {
PathParam[int64]("id"),
Body[dto.TenantStatusUpdateForm]("form"),
))
r.log.Debugf("Registering route: Post /super/v1/tenant-join-requests/:id<int>/review -> tenants.ReviewJoinRequest")
router.Post("/super/v1/tenant-join-requests/:id<int>/review"[len(r.Path()):], Func3(
r.tenants.ReviewJoinRequest,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
Body[v1_dto.TenantJoinReviewForm]("form"),
))
r.log.Debugf("Registering route: Post /super/v1/tenants -> tenants.Create")
router.Post("/super/v1/tenants"[len(r.Path()):], Func1(
r.tenants.Create,
Body[dto.TenantCreateForm]("form"),
))
r.log.Debugf("Registering route: Post /super/v1/tenants/:tenantID<int>/invites -> tenants.CreateInvite")
router.Post("/super/v1/tenants/:tenantID<int>/invites"[len(r.Path()):], DataFunc2(
r.tenants.CreateInvite,
PathParam[int64]("tenantID"),
Body[v1_dto.TenantInviteCreateForm]("form"),
))
// Register routes for controller: users
r.log.Debugf("Registering route: Get /super/v1/users -> users.List")
router.Get("/super/v1/users"[len(r.Path()):], DataFunc1(

View File

@@ -2,8 +2,10 @@ package v1
import (
dto "quyun/v2/app/http/super/v1/dto"
v1_dto "quyun/v2/app/http/v1/dto"
"quyun/v2/app/requests"
"quyun/v2/app/services"
"quyun/v2/database/models"
"github.com/gofiber/fiber/v3"
)
@@ -62,6 +64,57 @@ func (c *tenants) ListUsers(ctx fiber.Ctx, tenantID int64, filter *dto.SuperTena
return services.Super.ListTenantUsers(ctx, tenantID, filter)
}
// List tenant join requests
//
// @Router /super/v1/tenant-join-requests [get]
// @Summary List tenant join requests
// @Description List tenant join requests across tenants
// @Tags Tenant
// @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.SuperTenantJoinRequestItem}
// @Bind filter query
func (c *tenants) ListJoinRequests(ctx fiber.Ctx, filter *dto.SuperTenantJoinRequestListFilter) (*requests.Pager, error) {
return services.Super.ListTenantJoinRequests(ctx, filter)
}
// Review tenant join request
//
// @Router /super/v1/tenant-join-requests/:id<int>/review [post]
// @Summary Review tenant join request
// @Description Approve or reject a tenant join request
// @Tags Tenant
// @Accept json
// @Produce json
// @Param id path int64 true "Join request ID"
// @Param form body v1_dto.TenantJoinReviewForm true "Review form"
// @Success 200 {string} string "Reviewed"
// @Bind user local key(__ctx_user)
// @Bind id path
// @Bind form body
func (c *tenants) ReviewJoinRequest(ctx fiber.Ctx, user *models.User, id int64, form *v1_dto.TenantJoinReviewForm) error {
return services.Super.ReviewTenantJoinRequest(ctx, user.ID, id, form)
}
// Create tenant invite
//
// @Router /super/v1/tenants/:tenantID<int>/invites [post]
// @Summary Create tenant invite
// @Description Create tenant invite code
// @Tags Tenant
// @Accept json
// @Produce json
// @Param tenantID path int64 true "Tenant ID"
// @Param form body v1_dto.TenantInviteCreateForm true "Invite form"
// @Success 200 {object} v1_dto.TenantInviteItem
// @Bind tenantID path
// @Bind form body
func (c *tenants) CreateInvite(ctx fiber.Ctx, tenantID int64, form *v1_dto.TenantInviteCreateForm) (*v1_dto.TenantInviteItem, error) {
return services.Super.CreateTenantInvite(ctx, tenantID, form)
}
// Create tenant
//
// @Router /super/v1/tenants [post]