feat: switch to global auth and tenant route prefix

This commit is contained in:
2026-01-26 18:04:05 +08:00
parent 8addf6f900
commit cde4fb8594
25 changed files with 479 additions and 7196 deletions

View File

@@ -3,8 +3,6 @@ package auth
import (
"quyun/v2/app/http/v1/dto"
"quyun/v2/app/services"
"quyun/v2/database/models"
"quyun/v2/pkg/consts"
"github.com/gofiber/fiber/v3"
)
@@ -14,7 +12,7 @@ type Auth struct{}
// SendOTP sends an OTP to the provided phone number.
//
// @Router /t/:tenantCode/v1/auth/otp [post]
// @Router /v1/auth/otp [post]
// @Summary Send OTP
// @Description Send OTP to phone number
// @Tags Auth
@@ -27,9 +25,7 @@ func (a *Auth) SendOTP(ctx fiber.Ctx, form *dto.SendOTPForm) error {
return services.User.SendOTP(ctx, form.Phone)
}
// Login logs in or registers a user with OTP.
//
// @Router /t/:tenantCode/v1/auth/login [post]
// @Router /v1/auth/login [post]
// @Summary Login or Register with OTP
// @Description Login or register user using phone number and OTP
// @Tags Auth
@@ -39,11 +35,5 @@ func (a *Auth) SendOTP(ctx fiber.Ctx, form *dto.SendOTPForm) error {
// @Success 200 {object} dto.LoginResponse
// @Bind form body
func (a *Auth) Login(ctx fiber.Ctx, form *dto.LoginForm) (*dto.LoginResponse, error) {
tenantID := int64(0)
if t := ctx.Locals(consts.CtxKeyTenant); t != nil {
if tenant, ok := t.(*models.Tenant); ok {
tenantID = tenant.ID
}
}
return services.User.LoginWithOTP(ctx, tenantID, form.Phone, form.OTP)
return services.User.LoginWithOTP(ctx, 0, form.Phone, form.OTP)
}

View File

@@ -42,13 +42,13 @@ func (r *Routes) Name() string {
// Each route is registered with its corresponding controller action and parameter bindings.
func (r *Routes) Register(router fiber.Router) {
// Register routes for controller: Auth
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/auth/login -> auth.Login")
router.Post("/t/:tenantCode/v1/auth/login"[len(r.Path()):], DataFunc1(
r.log.Debugf("Registering route: Post /v1/auth/login -> auth.Login")
router.Post("/v1/auth/login"[len(r.Path()):], DataFunc1(
r.auth.Login,
Body[dto.LoginForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/auth/otp -> auth.SendOTP")
router.Post("/t/:tenantCode/v1/auth/otp"[len(r.Path()):], Func1(
r.log.Debugf("Registering route: Post /v1/auth/otp -> auth.SendOTP")
router.Post("/v1/auth/otp"[len(r.Path()):], Func1(
r.auth.SendOTP,
Body[dto.SendOTPForm]("form"),
))

View File

@@ -1,11 +1,9 @@
package auth
func (r *Routes) Path() string {
return "/t/:tenantCode/v1/auth"
return "/v1/auth"
}
func (r *Routes) Middlewares() []any {
return []any{
r.middlewares.TenantResolver,
}
return []any{}
}

View File

@@ -13,7 +13,7 @@ import (
// @provider
type Common struct{}
// @Router /t/:tenantCode/v1/upload [post]
// @Router /v1/t/:tenantCode/upload [post]
// @Summary Upload file
// @Description Upload file
// @Tags Common
@@ -41,7 +41,7 @@ func (c *Common) Upload(
// Get options (enums)
//
// @Router /t/:tenantCode/v1/common/options [get]
// @Router /v1/t/:tenantCode/common/options [get]
// @Summary Get options
// @Description Get global options (enums)
// @Tags Common
@@ -54,7 +54,7 @@ func (c *Common) GetOptions(ctx fiber.Ctx) (*dto.OptionsResponse, error) {
// Check file hash for deduplication
//
// @Router /t/:tenantCode/v1/upload/check [get]
// @Router /v1/t/:tenantCode/upload/check [get]
// @Summary Check hash
// @Description Check if file hash exists
// @Tags Common
@@ -69,7 +69,7 @@ func (c *Common) CheckHash(ctx fiber.Ctx, user *models.User, hash string) (*dto.
return services.Common.CheckHash(ctx, tenantID, user.ID, hash)
}
// @Router /t/:tenantCode/v1/upload/init [post]
// @Router /v1/t/:tenantCode/upload/init [post]
// @Summary Init multipart upload
// @Description Initialize multipart upload
// @Tags Common
@@ -84,7 +84,7 @@ func (c *Common) InitUpload(ctx fiber.Ctx, user *models.User, form *dto.UploadIn
return services.Common.InitUpload(ctx.Context(), tenantID, user.ID, form)
}
// @Router /t/:tenantCode/v1/upload/part [post]
// @Router /v1/t/:tenantCode/upload/part [post]
// @Summary Upload part
// @Description Upload a part
// @Tags Common
@@ -101,7 +101,7 @@ func (c *Common) UploadPart(ctx fiber.Ctx, user *models.User, file *multipart.Fi
return services.Common.UploadPart(ctx.Context(), tenantID, user.ID, file, form)
}
// @Router /t/:tenantCode/v1/upload/complete [post]
// @Router /v1/t/:tenantCode/upload/complete [post]
// @Summary Complete upload
// @Description Complete multipart upload
// @Tags Common
@@ -116,7 +116,7 @@ func (c *Common) CompleteUpload(ctx fiber.Ctx, user *models.User, form *dto.Uplo
return services.Common.CompleteUpload(ctx.Context(), tenantID, user.ID, form)
}
// @Router /t/:tenantCode/v1/upload/:uploadId [delete]
// @Router /v1/t/:tenantCode/upload/:uploadId [delete]
// @Summary Abort upload
// @Description Abort multipart upload
// @Tags Common
@@ -131,7 +131,7 @@ func (c *Common) AbortUpload(ctx fiber.Ctx, user *models.User, uploadId string)
return services.Common.AbortUpload(ctx.Context(), tenantID, user.ID, uploadId)
}
// @Router /t/:tenantCode/v1/media-assets/:id<int> [delete]
// @Router /v1/t/:tenantCode/media-assets/:id<int> [delete]
// @Summary Delete media asset
// @Description Delete media asset
// @Tags Common

View File

@@ -14,7 +14,7 @@ type Content struct{}
// List contents (Explore / Search)
//
// @Router /t/:tenantCode/v1/contents [get]
// @Router /v1/t/:tenantCode/contents [get]
// @Summary List contents
// @Description List contents with filtering and pagination
// @Tags Content
@@ -44,7 +44,7 @@ func (c *Content) List(
// Get content detail
//
// @Router /t/:tenantCode/v1/contents/:id<int> [get]
// @Router /v1/t/:tenantCode/contents/:id<int> [get]
// @Summary Get content detail
// @Description Get content detail by ID
// @Tags Content
@@ -61,7 +61,7 @@ func (c *Content) Get(ctx fiber.Ctx, id int64) (*dto.ContentDetail, error) {
// Get comments for a content
//
// @Router /t/:tenantCode/v1/contents/:id<int>/comments [get]
// @Router /v1/t/:tenantCode/contents/:id<int>/comments [get]
// @Summary Get comments
// @Description Get comments for a content
// @Tags Content
@@ -80,7 +80,7 @@ func (c *Content) ListComments(ctx fiber.Ctx, id int64, page int) (*requests.Pag
// Post a comment
//
// @Router /t/:tenantCode/v1/contents/:id<int>/comments [post]
// @Router /v1/t/:tenantCode/contents/:id<int>/comments [post]
// @Summary Post comment
// @Description Post a comment to a content
// @Tags Content
@@ -99,7 +99,7 @@ func (c *Content) CreateComment(ctx fiber.Ctx, id int64, form *dto.CommentCreate
// Like a comment
//
// @Router /t/:tenantCode/v1/comments/:id<int>/like [post]
// @Router /v1/t/:tenantCode/comments/:id<int>/like [post]
// @Summary Like comment
// @Description Like a comment
// @Tags Content
@@ -116,7 +116,7 @@ func (c *Content) LikeComment(ctx fiber.Ctx, id int64) error {
// Add like
//
// @Router /t/:tenantCode/v1/contents/:id<int>/like [post]
// @Router /v1/t/:tenantCode/contents/:id<int>/like [post]
// @Summary Add like
// @Tags Content
// @Param id path int64 true "Content ID"
@@ -130,7 +130,7 @@ func (c *Content) AddLike(ctx fiber.Ctx, id int64) error {
// Remove like
//
// @Router /t/:tenantCode/v1/contents/:id<int>/like [delete]
// @Router /v1/t/:tenantCode/contents/:id<int>/like [delete]
// @Summary Remove like
// @Tags Content
// @Param id path int64 true "Content ID"
@@ -144,7 +144,7 @@ func (c *Content) RemoveLike(ctx fiber.Ctx, id int64) error {
// Add favorite
//
// @Router /t/:tenantCode/v1/contents/:id<int>/favorite [post]
// @Router /v1/t/:tenantCode/contents/:id<int>/favorite [post]
// @Summary Add favorite
// @Tags Content
// @Param id path int64 true "Content ID"
@@ -158,7 +158,7 @@ func (c *Content) AddFavorite(ctx fiber.Ctx, id int64) error {
// Remove favorite
//
// @Router /t/:tenantCode/v1/contents/:id<int>/favorite [delete]
// @Router /v1/t/:tenantCode/contents/:id<int>/favorite [delete]
// @Summary Remove favorite
// @Tags Content
// @Param id path int64 true "Content ID"
@@ -172,7 +172,7 @@ func (c *Content) RemoveFavorite(ctx fiber.Ctx, id int64) error {
// List curated topics
//
// @Router /t/:tenantCode/v1/topics [get]
// @Router /v1/t/:tenantCode/topics [get]
// @Summary List topics
// @Description List curated topics
// @Tags Content

View File

@@ -3,7 +3,6 @@ package v1
import (
"quyun/v2/app/errorx"
"quyun/v2/app/http/v1/dto"
"quyun/v2/app/requests"
"quyun/v2/app/services"
"quyun/v2/database/models"
@@ -15,514 +14,36 @@ type Creator struct{}
// Apply to become a creator
//
// @Router /t/:tenantCode/v1/creator/apply [post]
// @Summary Apply creator
// @Description Apply to become a creator
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param form body dto.ApplyForm true "Apply form"
// @Success 200 {string} string "Application submitted"
// @Bind user local key(__ctx_user)
// @Bind form body
func (c *Creator) Apply(ctx fiber.Ctx, user *models.User, form *dto.ApplyForm) error {
tenantID := getTenantID(ctx)
return services.Creator.Apply(ctx, tenantID, user.ID, form)
}
// @Router /v1/t/:tenantCode/creator/apply [post]
// @Router /v1/t/:tenantCode/creator/members/:id<int>/review [post]
// @Router /v1/t/:tenantCode/creator/members/invite [post]
// @Router /v1/t/:tenantCode/creator/members [get]
// @Router /v1/t/:tenantCode/creator/members/invites [get]
// @Router /v1/t/:tenantCode/creator/members/invites/:id<int> [delete]
// @Router /v1/t/:tenantCode/creator/members/join-requests [get]
// @Router /v1/t/:tenantCode/creator/members/:id<int> [delete]
// @Router /v1/t/:tenantCode/creator/reports/overview [get]
// @Router /v1/t/:tenantCode/creator/reports/export [post]
// @Router /v1/t/:tenantCode/creator/dashboard [get]
// @Router /v1/t/:tenantCode/creator/contents/:id<int> [get]
// @Router /v1/t/:tenantCode/creator/contents [get]
// @Router /v1/t/:tenantCode/creator/contents [post]
// @Router /v1/t/:tenantCode/creator/contents/:id<int> [put]
// @Router /v1/t/:tenantCode/creator/contents/:id<int> [delete]
// @Router /v1/t/:tenantCode/creator/orders [get]
// @Router /v1/t/:tenantCode/creator/orders/:id<int>/refund [post]
// @Router /v1/t/:tenantCode/creator/settings [get]
// @Router /v1/t/:tenantCode/creator/settings [put]
// @Router /v1/t/:tenantCode/creator/payout-accounts [get]
// @Router /v1/t/:tenantCode/creator/payout-accounts [post]
// @Router /v1/t/:tenantCode/creator/payout-accounts [delete]
// @Router /v1/t/:tenantCode/creator/withdraw [post]
// @Router /v1/t/:tenantCode/creator/coupons [post]
// @Router /v1/t/:tenantCode/creator/coupons [get]
// @Router /v1/t/:tenantCode/creator/coupons/:id<int> [get]
// @Router /v1/t/:tenantCode/creator/coupons/:id<int> [put]
// @Router /v1/t/:tenantCode/creator/coupons/:id<int>/grant [post]
// Review join request
//
// @Router /t/:tenantCode/v1/creator/members/:id<int>/review [post]
// @Summary Review join request
// @Description Approve or reject a tenant join request
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param id path int64 true "Join request ID"
// @Param form body dto.TenantJoinReviewForm true "Review form"
// @Success 200 {string} string "Reviewed"
// @Bind user local key(__ctx_user)
// @Bind id path
// @Bind form body
func (c *Creator) ReviewMember(ctx fiber.Ctx, user *models.User, id int64, form *dto.TenantJoinReviewForm) error {
tenantID := getTenantID(ctx)
return services.Tenant.ReviewJoin(ctx, tenantID, user.ID, id, form)
}
// Create member invite
//
// @Router /t/:tenantCode/v1/creator/members/invite [post]
// @Summary Create member invite
// @Description Create an invite for tenant members
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param form body dto.TenantInviteCreateForm true "Invite form"
// @Success 200 {object} dto.TenantInviteItem
// @Bind user local key(__ctx_user)
// @Bind form body
func (c *Creator) CreateMemberInvite(
ctx fiber.Ctx,
user *models.User,
form *dto.TenantInviteCreateForm,
) (*dto.TenantInviteItem, error) {
tenantID := getTenantID(ctx)
return services.Tenant.CreateInvite(ctx, tenantID, user.ID, form)
}
// List tenant members
//
// @Router /t/:tenantCode/v1/creator/members [get]
// @Summary List tenant members
// @Description List tenant members with filters
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param filter query dto.TenantMemberListFilter false "Member list filter"
// @Success 200 {object} requests.Pager{items=[]dto.TenantMemberItem}
// @Bind user local key(__ctx_user)
// @Bind filter query
func (c *Creator) ListMembers(ctx fiber.Ctx, user *models.User, filter *dto.TenantMemberListFilter) (*requests.Pager, error) {
tenantID := getTenantID(ctx)
return services.Tenant.ListMembers(ctx, tenantID, user.ID, filter)
}
// List member invites
//
// @Router /t/:tenantCode/v1/creator/members/invites [get]
// @Summary List member invites
// @Description List member invites with filters
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param filter query dto.TenantInviteListFilter false "Invite list filter"
// @Success 200 {object} requests.Pager{items=[]dto.TenantInviteListItem}
// @Bind user local key(__ctx_user)
// @Bind filter query
func (c *Creator) ListMemberInvites(ctx fiber.Ctx, user *models.User, filter *dto.TenantInviteListFilter) (*requests.Pager, error) {
tenantID := getTenantID(ctx)
return services.Tenant.ListInvites(ctx, tenantID, user.ID, filter)
}
// Disable member invite
//
// @Router /t/:tenantCode/v1/creator/members/invites/:id<int> [delete]
// @Summary Disable member invite
// @Description Disable a member invite by ID
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param id path int64 true "Invite ID"
// @Success 200 {string} string "Disabled"
// @Bind user local key(__ctx_user)
// @Bind id path
func (c *Creator) DisableMemberInvite(ctx fiber.Ctx, user *models.User, id int64) error {
tenantID := getTenantID(ctx)
return services.Tenant.DisableInvite(ctx, tenantID, user.ID, id)
}
// List member join requests
//
// @Router /t/:tenantCode/v1/creator/members/join-requests [get]
// @Summary List member join requests
// @Description List tenant join requests
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param filter query dto.TenantJoinRequestListFilter false "Join request list filter"
// @Success 200 {object} requests.Pager{items=[]dto.TenantJoinRequestItem}
// @Bind user local key(__ctx_user)
// @Bind filter query
func (c *Creator) ListMemberJoinRequests(ctx fiber.Ctx, user *models.User, filter *dto.TenantJoinRequestListFilter) (*requests.Pager, error) {
tenantID := getTenantID(ctx)
return services.Tenant.ListJoinRequests(ctx, tenantID, user.ID, filter)
}
// Remove tenant member
//
// @Router /t/:tenantCode/v1/creator/members/:id<int> [delete]
// @Summary Remove tenant member
// @Description Remove a tenant member by relation ID
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param id path int64 true "Member ID"
// @Success 200 {string} string "Removed"
// @Bind user local key(__ctx_user)
// @Bind id path
func (c *Creator) RemoveMember(ctx fiber.Ctx, user *models.User, id int64) error {
tenantID := getTenantID(ctx)
return services.Tenant.RemoveMember(ctx, tenantID, user.ID, id)
}
// Get report overview
//
// @Router /t/:tenantCode/v1/creator/reports/overview [get]
// @Summary Report overview
// @Description Get creator report overview
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param start_at query string false "Start time (RFC3339)"
// @Param end_at query string false "End time (RFC3339)"
// @Param granularity query string false "Granularity (day)"
// @Success 200 {object} dto.ReportOverviewResponse
// @Bind user local key(__ctx_user)
// @Bind filter query
func (c *Creator) ReportOverview(
ctx fiber.Ctx,
user *models.User,
filter *dto.ReportOverviewFilter,
) (*dto.ReportOverviewResponse, error) {
tenantID := getTenantID(ctx)
return services.Creator.ReportOverview(ctx, tenantID, user.ID, filter)
}
// Export report overview
//
// @Router /t/:tenantCode/v1/creator/reports/export [post]
// @Summary Export report overview
// @Description Export creator report overview
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param form body dto.ReportExportForm true "Export form"
// @Success 200 {object} dto.ReportExportResponse
// @Bind user local key(__ctx_user)
// @Bind form body
func (c *Creator) ExportReport(
ctx fiber.Ctx,
user *models.User,
form *dto.ReportExportForm,
) (*dto.ReportExportResponse, error) {
tenantID := getTenantID(ctx)
return services.Creator.ExportReport(ctx, tenantID, user.ID, form)
}
// Get creator dashboard stats
//
// @Router /t/:tenantCode/v1/creator/dashboard [get]
// @Summary Dashboard stats
// @Description Get creator dashboard stats
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Success 200 {object} dto.DashboardStats
// @Bind user local key(__ctx_user)
func (c *Creator) Dashboard(ctx fiber.Ctx, user *models.User) (*dto.DashboardStats, error) {
tenantID := getTenantID(ctx)
return services.Creator.Dashboard(ctx, tenantID, user.ID)
}
// Get content details for edit
//
// @Router /t/:tenantCode/v1/creator/contents/:id<int> [get]
// @Summary Get content
// @Description Get content details for edit
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param id path int64 true "Content ID"
// @Success 200 {object} dto.ContentEditDTO
// @Bind user local key(__ctx_user)
// @Bind id path
func (c *Creator) GetContent(ctx fiber.Ctx, user *models.User, id int64) (*dto.ContentEditDTO, error) {
tenantID := getTenantID(ctx)
return services.Creator.GetContent(ctx, tenantID, user.ID, id)
}
// List creator contents
//
// @Router /t/:tenantCode/v1/creator/contents [get]
// @Summary List contents
// @Description List creator contents
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param status query string false "Status"
// @Param genre query string false "Genre"
// @Param keyword query string false "Keyword"
// @Success 200 {array} dto.ContentItem
// @Bind user local key(__ctx_user)
// @Bind filter query
func (c *Creator) ListContents(
ctx fiber.Ctx,
user *models.User,
filter *dto.CreatorContentListFilter,
) (*requests.Pager, error) {
tenantID := getTenantID(ctx)
return services.Creator.ListContents(ctx, tenantID, user.ID, filter)
}
// Create/Publish content
//
// @Router /t/:tenantCode/v1/creator/contents [post]
// @Summary Create content
// @Description Create/Publish content
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param form body dto.ContentCreateForm true "Content form"
// @Success 200 {string} string "Created"
// @Bind user local key(__ctx_user)
// @Bind form body
func (c *Creator) CreateContent(ctx fiber.Ctx, user *models.User, form *dto.ContentCreateForm) error {
tenantID := getTenantID(ctx)
return services.Creator.CreateContent(ctx, tenantID, user.ID, form)
}
// Update content
//
// @Router /t/:tenantCode/v1/creator/contents/:id<int> [put]
// @Summary Update content
// @Description Update content
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param id path int64 true "Content ID"
// @Param form body dto.ContentUpdateForm true "Update form"
// @Success 200 {string} string "Updated"
// @Bind user local key(__ctx_user)
// @Bind id path
// @Bind form body
func (c *Creator) UpdateContent(ctx fiber.Ctx, user *models.User, id int64, form *dto.ContentUpdateForm) error {
tenantID := getTenantID(ctx)
return services.Creator.UpdateContent(ctx, tenantID, user.ID, id, form)
}
// Delete content
//
// @Router /t/:tenantCode/v1/creator/contents/:id<int> [delete]
// @Summary Delete content
// @Description Delete content
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param id path int64 true "Content ID"
// @Success 200 {string} string "Deleted"
// @Bind user local key(__ctx_user)
// @Bind id path
func (c *Creator) DeleteContent(ctx fiber.Ctx, user *models.User, id int64) error {
tenantID := getTenantID(ctx)
return services.Creator.DeleteContent(ctx, tenantID, user.ID, id)
}
// List sales orders
//
// @Router /t/:tenantCode/v1/creator/orders [get]
// @Summary List sales orders
// @Description List sales orders
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param status query string false "Status"
// @Param keyword query string false "Keyword"
// @Success 200 {array} dto.Order
// @Bind user local key(__ctx_user)
// @Bind filter query
func (c *Creator) ListOrders(
ctx fiber.Ctx,
user *models.User,
filter *dto.CreatorOrderListFilter,
) ([]dto.Order, error) {
tenantID := getTenantID(ctx)
return services.Creator.ListOrders(ctx, tenantID, user.ID, filter)
}
// Process refund
//
// @Router /t/:tenantCode/v1/creator/orders/:id<int>/refund [post]
// @Summary Process refund
// @Description Process refund
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param id path int64 true "Order ID"
// @Param form body dto.RefundForm true "Refund form"
// @Success 200 {string} string "Processed"
// @Bind user local key(__ctx_user)
// @Bind id path
// @Bind form body
func (c *Creator) Refund(ctx fiber.Ctx, user *models.User, id int64, form *dto.RefundForm) error {
tenantID := getTenantID(ctx)
return services.Creator.ProcessRefund(ctx, tenantID, user.ID, id, form)
}
// Get channel settings
//
// @Router /t/:tenantCode/v1/creator/settings [get]
// @Summary Get settings
// @Description Get channel settings
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Success 200 {object} dto.Settings
// @Bind user local key(__ctx_user)
func (c *Creator) GetSettings(ctx fiber.Ctx, user *models.User) (*dto.Settings, error) {
tenantID := getTenantID(ctx)
return services.Creator.GetSettings(ctx, tenantID, user.ID)
}
// Update channel settings
//
// @Router /t/:tenantCode/v1/creator/settings [put]
// @Summary Update settings
// @Description Update channel settings
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param form body dto.Settings true "Settings form"
// @Success 200 {string} string "Updated"
// @Bind user local key(__ctx_user)
// @Bind form body
func (c *Creator) UpdateSettings(ctx fiber.Ctx, user *models.User, form *dto.Settings) error {
tenantID := getTenantID(ctx)
return services.Creator.UpdateSettings(ctx, tenantID, user.ID, form)
}
// List payout accounts
//
// @Router /t/:tenantCode/v1/creator/payout-accounts [get]
// @Summary List payout accounts
// @Description List payout accounts
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Success 200 {array} dto.PayoutAccount
// @Bind user local key(__ctx_user)
func (c *Creator) ListPayoutAccounts(ctx fiber.Ctx, user *models.User) ([]dto.PayoutAccount, error) {
tenantID := getTenantID(ctx)
return services.Creator.ListPayoutAccounts(ctx, tenantID, user.ID)
}
// Add payout account
//
// @Router /t/:tenantCode/v1/creator/payout-accounts [post]
// @Summary Add payout account
// @Description Add payout account
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param form body dto.PayoutAccount true "Account form"
// @Success 200 {string} string "Added"
// @Bind user local key(__ctx_user)
// @Bind form body
func (c *Creator) AddPayoutAccount(ctx fiber.Ctx, user *models.User, form *dto.PayoutAccount) error {
tenantID := getTenantID(ctx)
return services.Creator.AddPayoutAccount(ctx, tenantID, user.ID, form)
}
// Remove payout account
//
// @Router /t/:tenantCode/v1/creator/payout-accounts [delete]
// @Summary Remove payout account
// @Description Remove payout account
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param id query int64 true "Account ID"
// @Success 200 {string} string "Removed"
// @Bind user local key(__ctx_user)
// @Bind id query
func (c *Creator) RemovePayoutAccount(ctx fiber.Ctx, user *models.User, id int64) error {
tenantID := getTenantID(ctx)
return services.Creator.RemovePayoutAccount(ctx, tenantID, user.ID, id)
}
// Request withdrawal
//
// @Router /t/:tenantCode/v1/creator/withdraw [post]
// @Summary Request withdrawal
// @Description Request withdrawal
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param form body dto.WithdrawForm true "Withdraw form"
// @Success 200 {string} string "Withdrawal requested"
// @Bind user local key(__ctx_user)
// @Bind form body
func (c *Creator) Withdraw(ctx fiber.Ctx, user *models.User, form *dto.WithdrawForm) error {
tenantID := getTenantID(ctx)
return services.Creator.Withdraw(ctx, tenantID, user.ID, form)
}
// Create coupon
//
// @Router /t/:tenantCode/v1/creator/coupons [post]
// @Summary Create coupon
// @Description Create coupon template
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param form body dto.CouponCreateForm true "Coupon form"
// @Success 200 {object} dto.CouponItem
// @Bind user local key(__ctx_user)
// @Bind form body
func (c *Creator) CreateCoupon(ctx fiber.Ctx, user *models.User, form *dto.CouponCreateForm) (*dto.CouponItem, error) {
tenantID := getTenantID(ctx)
return services.Coupon.Create(ctx, tenantID, user.ID, form)
}
// List coupons
//
// @Router /t/:tenantCode/v1/creator/coupons [get]
// @Summary List coupons
// @Description List coupon templates
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param page query int false "Page"
// @Param limit query int false "Limit"
// @Param type query string false "Type (fix_amount/discount)"
// @Param status query string false "Status (active/expired)"
// @Param keyword query string false "Keyword"
// @Success 200 {object} requests.Pager
// @Bind user local key(__ctx_user)
// @Bind filter query
func (c *Creator) ListCoupons(ctx fiber.Ctx, user *models.User, filter *dto.CouponListFilter) (*requests.Pager, error) {
tenantID := getTenantID(ctx)
return services.Coupon.List(ctx, tenantID, filter)
}
// Get coupon
//
// @Router /t/:tenantCode/v1/creator/coupons/:id<int> [get]
// @Summary Get coupon
// @Description Get coupon template detail
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param id path int64 true "Coupon ID"
// @Success 200 {object} dto.CouponItem
// @Bind user local key(__ctx_user)
// @Bind id path
func (c *Creator) GetCoupon(ctx fiber.Ctx, user *models.User, id int64) (*dto.CouponItem, error) {
tenantID := getTenantID(ctx)
return services.Coupon.Get(ctx, tenantID, id)
}
// Update coupon
//
// @Router /t/:tenantCode/v1/creator/coupons/:id<int> [put]
// @Summary Update coupon
// @Description Update coupon template
// @Tags CreatorCenter
// @Accept json
// @Produce json
// @Param id path int64 true "Coupon ID"
// @Param form body dto.CouponUpdateForm true "Coupon form"
// @Success 200 {object} dto.CouponItem
// @Bind user local key(__ctx_user)
// @Bind id path
// @Bind form body
func (c *Creator) UpdateCoupon(ctx fiber.Ctx, user *models.User, id int64, form *dto.CouponUpdateForm) (*dto.CouponItem, error) {
tenantID := getTenantID(ctx)
return services.Coupon.Update(ctx, tenantID, user.ID, id, form)
}
// Grant coupon
//
// @Router /t/:tenantCode/v1/creator/coupons/:id<int>/grant [post]
// @Summary Grant coupon
// @Description Grant coupon to users
// @Tags CreatorCenter

View File

@@ -32,3 +32,11 @@ type OrderStatusResponse struct {
// Status 订单状态unpaid/paid/completed 等)。
Status string `json:"status"`
}
// PaymentWebhookForm 支付回调参数。
type PaymentWebhookForm struct {
// OrderID 订单ID。
OrderID int64 `json:"order_id"`
// ExternalID 第三方支付流水号。
ExternalID string `json:"external_id"`
}

View File

@@ -50,485 +50,236 @@ func (r *Routes) Name() string {
// Each route is registered with its corresponding controller action and parameter bindings.
func (r *Routes) Register(router fiber.Router) {
// Register routes for controller: Common
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/media-assets/:id<int> -> common.DeleteMediaAsset")
router.Delete("/t/:tenantCode/v1/media-assets/:id<int>"[len(r.Path()):], Func2(
r.log.Debugf("Registering route: Delete /v1/t/:tenantCode/media-assets/:id<int> -> common.DeleteMediaAsset")
router.Delete("/v1/t/:tenantCode/media-assets/:id<int>"[len(r.Path()):], Func2(
r.common.DeleteMediaAsset,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/upload/:uploadId -> common.AbortUpload")
router.Delete("/t/:tenantCode/v1/upload/:uploadId"[len(r.Path()):], Func2(
r.log.Debugf("Registering route: Delete /v1/t/:tenantCode/upload/:uploadId -> common.AbortUpload")
router.Delete("/v1/t/:tenantCode/upload/:uploadId"[len(r.Path()):], Func2(
r.common.AbortUpload,
Local[*models.User]("__ctx_user"),
PathParam[string]("uploadId"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/common/options -> common.GetOptions")
router.Get("/t/:tenantCode/v1/common/options"[len(r.Path()):], DataFunc0(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/common/options -> common.GetOptions")
router.Get("/v1/t/:tenantCode/common/options"[len(r.Path()):], DataFunc0(
r.common.GetOptions,
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/upload/check -> common.CheckHash")
router.Get("/t/:tenantCode/v1/upload/check"[len(r.Path()):], DataFunc2(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/upload/check -> common.CheckHash")
router.Get("/v1/t/:tenantCode/upload/check"[len(r.Path()):], DataFunc2(
r.common.CheckHash,
Local[*models.User]("__ctx_user"),
QueryParam[string]("hash"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/upload -> common.Upload")
router.Post("/t/:tenantCode/v1/upload"[len(r.Path()):], DataFunc3(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/upload -> common.Upload")
router.Post("/v1/t/:tenantCode/upload"[len(r.Path()):], DataFunc3(
r.common.Upload,
Local[*models.User]("__ctx_user"),
File[multipart.FileHeader]("file"),
Body[dto.UploadForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/upload/complete -> common.CompleteUpload")
router.Post("/t/:tenantCode/v1/upload/complete"[len(r.Path()):], DataFunc2(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/upload/complete -> common.CompleteUpload")
router.Post("/v1/t/:tenantCode/upload/complete"[len(r.Path()):], DataFunc2(
r.common.CompleteUpload,
Local[*models.User]("__ctx_user"),
Body[dto.UploadCompleteForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/upload/init -> common.InitUpload")
router.Post("/t/:tenantCode/v1/upload/init"[len(r.Path()):], DataFunc2(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/upload/init -> common.InitUpload")
router.Post("/v1/t/:tenantCode/upload/init"[len(r.Path()):], DataFunc2(
r.common.InitUpload,
Local[*models.User]("__ctx_user"),
Body[dto.UploadInitForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/upload/part -> common.UploadPart")
router.Post("/t/:tenantCode/v1/upload/part"[len(r.Path()):], Func3(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/upload/part -> common.UploadPart")
router.Post("/v1/t/:tenantCode/upload/part"[len(r.Path()):], Func3(
r.common.UploadPart,
Local[*models.User]("__ctx_user"),
File[multipart.FileHeader]("file"),
Body[dto.UploadPartForm]("form"),
))
// Register routes for controller: Content
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/contents/:id<int>/favorite -> content.RemoveFavorite")
router.Delete("/t/:tenantCode/v1/contents/:id<int>/favorite"[len(r.Path()):], Func1(
r.log.Debugf("Registering route: Delete /v1/t/:tenantCode/contents/:id<int>/favorite -> content.RemoveFavorite")
router.Delete("/v1/t/:tenantCode/contents/:id<int>/favorite"[len(r.Path()):], Func1(
r.content.RemoveFavorite,
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/contents/:id<int>/like -> content.RemoveLike")
router.Delete("/t/:tenantCode/v1/contents/:id<int>/like"[len(r.Path()):], Func1(
r.log.Debugf("Registering route: Delete /v1/t/:tenantCode/contents/:id<int>/like -> content.RemoveLike")
router.Delete("/v1/t/:tenantCode/contents/:id<int>/like"[len(r.Path()):], Func1(
r.content.RemoveLike,
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/contents -> content.List")
router.Get("/t/:tenantCode/v1/contents"[len(r.Path()):], DataFunc1(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/contents -> content.List")
router.Get("/v1/t/:tenantCode/contents"[len(r.Path()):], DataFunc1(
r.content.List,
Query[dto.ContentListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/contents/:id<int> -> content.Get")
router.Get("/t/:tenantCode/v1/contents/:id<int>"[len(r.Path()):], DataFunc1(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/contents/:id<int> -> content.Get")
router.Get("/v1/t/:tenantCode/contents/:id<int>"[len(r.Path()):], DataFunc1(
r.content.Get,
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/contents/:id<int>/comments -> content.ListComments")
router.Get("/t/:tenantCode/v1/contents/:id<int>/comments"[len(r.Path()):], DataFunc2(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/contents/:id<int>/comments -> content.ListComments")
router.Get("/v1/t/:tenantCode/contents/:id<int>/comments"[len(r.Path()):], DataFunc2(
r.content.ListComments,
PathParam[int64]("id"),
QueryParam[int]("page"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/topics -> content.ListTopics")
router.Get("/t/:tenantCode/v1/topics"[len(r.Path()):], DataFunc0(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/topics -> content.ListTopics")
router.Get("/v1/t/:tenantCode/topics"[len(r.Path()):], DataFunc0(
r.content.ListTopics,
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/comments/:id<int>/like -> content.LikeComment")
router.Post("/t/:tenantCode/v1/comments/:id<int>/like"[len(r.Path()):], Func1(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/comments/:id<int>/like -> content.LikeComment")
router.Post("/v1/t/:tenantCode/comments/:id<int>/like"[len(r.Path()):], Func1(
r.content.LikeComment,
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/contents/:id<int>/comments -> content.CreateComment")
router.Post("/t/:tenantCode/v1/contents/:id<int>/comments"[len(r.Path()):], Func2(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/contents/:id<int>/comments -> content.CreateComment")
router.Post("/v1/t/:tenantCode/contents/:id<int>/comments"[len(r.Path()):], Func2(
r.content.CreateComment,
PathParam[int64]("id"),
Body[dto.CommentCreateForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/contents/:id<int>/favorite -> content.AddFavorite")
router.Post("/t/:tenantCode/v1/contents/:id<int>/favorite"[len(r.Path()):], Func1(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/contents/:id<int>/favorite -> content.AddFavorite")
router.Post("/v1/t/:tenantCode/contents/:id<int>/favorite"[len(r.Path()):], Func1(
r.content.AddFavorite,
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/contents/:id<int>/like -> content.AddLike")
router.Post("/t/:tenantCode/v1/contents/:id<int>/like"[len(r.Path()):], Func1(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/contents/:id<int>/like -> content.AddLike")
router.Post("/v1/t/:tenantCode/contents/:id<int>/like"[len(r.Path()):], Func1(
r.content.AddLike,
PathParam[int64]("id"),
))
// Register routes for controller: Creator
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/creator/contents/:id<int> -> creator.DeleteContent")
router.Delete("/t/:tenantCode/v1/creator/contents/:id<int>"[len(r.Path()):], Func2(
r.creator.DeleteContent,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/creator/members/:id<int> -> creator.RemoveMember")
router.Delete("/t/:tenantCode/v1/creator/members/:id<int>"[len(r.Path()):], Func2(
r.creator.RemoveMember,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/creator/members/invites/:id<int> -> creator.DisableMemberInvite")
router.Delete("/t/:tenantCode/v1/creator/members/invites/:id<int>"[len(r.Path()):], Func2(
r.creator.DisableMemberInvite,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/creator/payout-accounts -> creator.RemovePayoutAccount")
router.Delete("/t/:tenantCode/v1/creator/payout-accounts"[len(r.Path()):], Func2(
r.creator.RemovePayoutAccount,
Local[*models.User]("__ctx_user"),
QueryParam[int64]("id"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/contents -> creator.ListContents")
router.Get("/t/:tenantCode/v1/creator/contents"[len(r.Path()):], DataFunc2(
r.creator.ListContents,
Local[*models.User]("__ctx_user"),
Query[dto.CreatorContentListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/contents/:id<int> -> creator.GetContent")
router.Get("/t/:tenantCode/v1/creator/contents/:id<int>"[len(r.Path()):], DataFunc2(
r.creator.GetContent,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/coupons -> creator.ListCoupons")
router.Get("/t/:tenantCode/v1/creator/coupons"[len(r.Path()):], DataFunc2(
r.creator.ListCoupons,
Local[*models.User]("__ctx_user"),
Query[dto.CouponListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/coupons/:id<int> -> creator.GetCoupon")
router.Get("/t/:tenantCode/v1/creator/coupons/:id<int>"[len(r.Path()):], DataFunc2(
r.creator.GetCoupon,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/dashboard -> creator.Dashboard")
router.Get("/t/:tenantCode/v1/creator/dashboard"[len(r.Path()):], DataFunc1(
r.creator.Dashboard,
Local[*models.User]("__ctx_user"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/members -> creator.ListMembers")
router.Get("/t/:tenantCode/v1/creator/members"[len(r.Path()):], DataFunc2(
r.creator.ListMembers,
Local[*models.User]("__ctx_user"),
Query[dto.TenantMemberListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/members/invites -> creator.ListMemberInvites")
router.Get("/t/:tenantCode/v1/creator/members/invites"[len(r.Path()):], DataFunc2(
r.creator.ListMemberInvites,
Local[*models.User]("__ctx_user"),
Query[dto.TenantInviteListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/members/join-requests -> creator.ListMemberJoinRequests")
router.Get("/t/:tenantCode/v1/creator/members/join-requests"[len(r.Path()):], DataFunc2(
r.creator.ListMemberJoinRequests,
Local[*models.User]("__ctx_user"),
Query[dto.TenantJoinRequestListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/orders -> creator.ListOrders")
router.Get("/t/:tenantCode/v1/creator/orders"[len(r.Path()):], DataFunc2(
r.creator.ListOrders,
Local[*models.User]("__ctx_user"),
Query[dto.CreatorOrderListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/payout-accounts -> creator.ListPayoutAccounts")
router.Get("/t/:tenantCode/v1/creator/payout-accounts"[len(r.Path()):], DataFunc1(
r.creator.ListPayoutAccounts,
Local[*models.User]("__ctx_user"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/reports/overview -> creator.ReportOverview")
router.Get("/t/:tenantCode/v1/creator/reports/overview"[len(r.Path()):], DataFunc2(
r.creator.ReportOverview,
Local[*models.User]("__ctx_user"),
Query[dto.ReportOverviewFilter]("filter"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creator/settings -> creator.GetSettings")
router.Get("/t/:tenantCode/v1/creator/settings"[len(r.Path()):], DataFunc1(
r.creator.GetSettings,
Local[*models.User]("__ctx_user"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/creator/apply -> creator.Apply")
router.Post("/t/:tenantCode/v1/creator/apply"[len(r.Path()):], Func2(
r.creator.Apply,
Local[*models.User]("__ctx_user"),
Body[dto.ApplyForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/creator/contents -> creator.CreateContent")
router.Post("/t/:tenantCode/v1/creator/contents"[len(r.Path()):], Func2(
r.creator.CreateContent,
Local[*models.User]("__ctx_user"),
Body[dto.ContentCreateForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/creator/coupons -> creator.CreateCoupon")
router.Post("/t/:tenantCode/v1/creator/coupons"[len(r.Path()):], DataFunc2(
r.creator.CreateCoupon,
Local[*models.User]("__ctx_user"),
Body[dto.CouponCreateForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/creator/coupons/:id<int>/grant -> creator.GrantCoupon")
router.Post("/t/:tenantCode/v1/creator/coupons/:id<int>/grant"[len(r.Path()):], DataFunc3(
r.creator.GrantCoupon,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
Body[dto.CouponGrantForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/creator/members/:id<int>/review -> creator.ReviewMember")
router.Post("/t/:tenantCode/v1/creator/members/:id<int>/review"[len(r.Path()):], Func3(
r.creator.ReviewMember,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
Body[dto.TenantJoinReviewForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/creator/members/invite -> creator.CreateMemberInvite")
router.Post("/t/:tenantCode/v1/creator/members/invite"[len(r.Path()):], DataFunc2(
r.creator.CreateMemberInvite,
Local[*models.User]("__ctx_user"),
Body[dto.TenantInviteCreateForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/creator/orders/:id<int>/refund -> creator.Refund")
router.Post("/t/:tenantCode/v1/creator/orders/:id<int>/refund"[len(r.Path()):], Func3(
r.creator.Refund,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
Body[dto.RefundForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/creator/payout-accounts -> creator.AddPayoutAccount")
router.Post("/t/:tenantCode/v1/creator/payout-accounts"[len(r.Path()):], Func2(
r.creator.AddPayoutAccount,
Local[*models.User]("__ctx_user"),
Body[dto.PayoutAccount]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/creator/reports/export -> creator.ExportReport")
router.Post("/t/:tenantCode/v1/creator/reports/export"[len(r.Path()):], DataFunc2(
r.creator.ExportReport,
Local[*models.User]("__ctx_user"),
Body[dto.ReportExportForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/creator/withdraw -> creator.Withdraw")
router.Post("/t/:tenantCode/v1/creator/withdraw"[len(r.Path()):], Func2(
r.creator.Withdraw,
Local[*models.User]("__ctx_user"),
Body[dto.WithdrawForm]("form"),
))
r.log.Debugf("Registering route: Put /t/:tenantCode/v1/creator/contents/:id<int> -> creator.UpdateContent")
router.Put("/t/:tenantCode/v1/creator/contents/:id<int>"[len(r.Path()):], Func3(
r.creator.UpdateContent,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
Body[dto.ContentUpdateForm]("form"),
))
r.log.Debugf("Registering route: Put /t/:tenantCode/v1/creator/coupons/:id<int> -> creator.UpdateCoupon")
router.Put("/t/:tenantCode/v1/creator/coupons/:id<int>"[len(r.Path()):], DataFunc3(
r.creator.UpdateCoupon,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
Body[dto.CouponUpdateForm]("form"),
))
r.log.Debugf("Registering route: Put /t/:tenantCode/v1/creator/settings -> creator.UpdateSettings")
router.Put("/t/:tenantCode/v1/creator/settings"[len(r.Path()):], Func2(
r.creator.UpdateSettings,
Local[*models.User]("__ctx_user"),
Body[dto.Settings]("form"),
))
// Register routes for controller: Storage
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/storage/* -> storage.Download")
router.Get("/t/:tenantCode/v1/storage/*"[len(r.Path()):], Func2(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/storage/* -> storage.Download")
router.Get("/v1/t/:tenantCode/storage/*"[len(r.Path()):], Func2(
r.storage.Download,
QueryParam[string]("expires"),
QueryParam[string]("sign"),
))
r.log.Debugf("Registering route: Put /t/:tenantCode/v1/storage/* -> storage.Upload")
router.Put("/t/:tenantCode/v1/storage/*"[len(r.Path()):], DataFunc2(
r.log.Debugf("Registering route: Put /v1/t/:tenantCode/storage/* -> storage.Upload")
router.Put("/v1/t/:tenantCode/storage/*"[len(r.Path()):], DataFunc2(
r.storage.Upload,
QueryParam[string]("expires"),
QueryParam[string]("sign"),
))
// Register routes for controller: Tenant
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/tenants/:id<int>/follow -> tenant.Unfollow")
router.Delete("/t/:tenantCode/v1/tenants/:id<int>/follow"[len(r.Path()):], Func2(
r.tenant.Unfollow,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/tenants/:id<int>/join -> tenant.CancelJoin")
router.Delete("/t/:tenantCode/v1/tenants/:id<int>/join"[len(r.Path()):], Func1(
r.tenant.CancelJoin,
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creators/:id<int>/contents -> tenant.ListContents")
router.Get("/t/:tenantCode/v1/creators/:id<int>/contents"[len(r.Path()):], DataFunc2(
r.tenant.ListContents,
PathParam[int64]("id"),
Query[dto.ContentListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/tenants -> tenant.List")
router.Get("/t/:tenantCode/v1/tenants"[len(r.Path()):], DataFunc1(
r.tenant.List,
Query[dto.TenantListFilter]("filter"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/tenants/:id<int> -> tenant.Get")
router.Get("/t/:tenantCode/v1/tenants/:id<int>"[len(r.Path()):], DataFunc2(
r.tenant.Get,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/tenants/:id<int>/follow -> tenant.Follow")
router.Post("/t/:tenantCode/v1/tenants/:id<int>/follow"[len(r.Path()):], Func2(
r.tenant.Follow,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/tenants/:id<int>/invites/accept -> tenant.AcceptInvite")
router.Post("/t/:tenantCode/v1/tenants/:id<int>/invites/accept"[len(r.Path()):], Func2(
r.tenant.AcceptInvite,
PathParam[int64]("id"),
Body[dto.TenantInviteAcceptForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/tenants/:id<int>/join -> tenant.ApplyJoin")
router.Post("/t/:tenantCode/v1/tenants/:id<int>/join"[len(r.Path()):], Func2(
r.tenant.ApplyJoin,
PathParam[int64]("id"),
Body[dto.TenantJoinApplyForm]("form"),
))
// Register routes for controller: Transaction
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/orders/:id<int>/status -> transaction.Status")
router.Get("/t/:tenantCode/v1/orders/:id<int>/status"[len(r.Path()):], DataFunc2(
r.transaction.Status,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/orders -> transaction.Create")
router.Post("/t/:tenantCode/v1/orders"[len(r.Path()):], DataFunc2(
r.transaction.Create,
Local[*models.User]("__ctx_user"),
Body[dto.OrderCreateForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/orders/:id<int>/pay -> transaction.Pay")
router.Post("/t/:tenantCode/v1/orders/:id<int>/pay"[len(r.Path()):], DataFunc3(
r.transaction.Pay,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
Body[dto.OrderPayForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/webhook/payment/notify -> transaction.Webhook")
router.Post("/t/:tenantCode/v1/webhook/payment/notify"[len(r.Path()):], DataFunc1(
r.transaction.Webhook,
Body[WebhookForm]("form"),
))
// Register routes for controller: User
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/me/favorites/:contentId<int> -> user.RemoveFavorite")
router.Delete("/t/:tenantCode/v1/me/favorites/:contentId<int>"[len(r.Path()):], Func2(
r.log.Debugf("Registering route: Delete /v1/t/:tenantCode/me/favorites/:contentId<int> -> user.RemoveFavorite")
router.Delete("/v1/t/:tenantCode/me/favorites/:contentId<int>"[len(r.Path()):], Func2(
r.user.RemoveFavorite,
Local[*models.User]("__ctx_user"),
PathParam[int64]("contentId"),
))
r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/me/likes/:contentId<int> -> user.RemoveLike")
router.Delete("/t/:tenantCode/v1/me/likes/:contentId<int>"[len(r.Path()):], Func2(
r.log.Debugf("Registering route: Delete /v1/t/:tenantCode/me/likes/:contentId<int> -> user.RemoveLike")
router.Delete("/v1/t/:tenantCode/me/likes/:contentId<int>"[len(r.Path()):], Func2(
r.user.RemoveLike,
Local[*models.User]("__ctx_user"),
PathParam[int64]("contentId"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/me -> user.Me")
router.Get("/t/:tenantCode/v1/me"[len(r.Path()):], DataFunc1(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me -> user.Me")
router.Get("/v1/t/:tenantCode/me"[len(r.Path()):], DataFunc1(
r.user.Me,
Local[*models.User]("__ctx_user"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/me/coupons -> user.MyCoupons")
router.Get("/t/:tenantCode/v1/me/coupons"[len(r.Path()):], DataFunc2(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me/coupons -> user.MyCoupons")
router.Get("/v1/t/:tenantCode/me/coupons"[len(r.Path()):], DataFunc2(
r.user.MyCoupons,
Local[*models.User]("__ctx_user"),
QueryParam[string]("status"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/me/coupons/available -> user.AvailableCoupons")
router.Get("/t/:tenantCode/v1/me/coupons/available"[len(r.Path()):], DataFunc2(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me/coupons/available -> user.AvailableCoupons")
router.Get("/v1/t/:tenantCode/me/coupons/available"[len(r.Path()):], DataFunc2(
r.user.AvailableCoupons,
Local[*models.User]("__ctx_user"),
QueryParam[int64]("amount"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/me/favorites -> user.Favorites")
router.Get("/t/:tenantCode/v1/me/favorites"[len(r.Path()):], DataFunc1(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me/favorites -> user.Favorites")
router.Get("/v1/t/:tenantCode/me/favorites"[len(r.Path()):], DataFunc1(
r.user.Favorites,
Local[*models.User]("__ctx_user"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/me/following -> user.Following")
router.Get("/t/:tenantCode/v1/me/following"[len(r.Path()):], DataFunc1(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me/following -> user.Following")
router.Get("/v1/t/:tenantCode/me/following"[len(r.Path()):], DataFunc1(
r.user.Following,
Local[*models.User]("__ctx_user"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/me/library -> user.Library")
router.Get("/t/:tenantCode/v1/me/library"[len(r.Path()):], DataFunc1(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me/library -> user.Library")
router.Get("/v1/t/:tenantCode/me/library"[len(r.Path()):], DataFunc1(
r.user.Library,
Local[*models.User]("__ctx_user"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/me/likes -> user.Likes")
router.Get("/t/:tenantCode/v1/me/likes"[len(r.Path()):], DataFunc1(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me/likes -> user.Likes")
router.Get("/v1/t/:tenantCode/me/likes"[len(r.Path()):], DataFunc1(
r.user.Likes,
Local[*models.User]("__ctx_user"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/me/notifications -> user.Notifications")
router.Get("/t/:tenantCode/v1/me/notifications"[len(r.Path()):], DataFunc3(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me/notifications -> user.Notifications")
router.Get("/v1/t/:tenantCode/me/notifications"[len(r.Path()):], DataFunc3(
r.user.Notifications,
Local[*models.User]("__ctx_user"),
QueryParam[string]("type"),
QueryParam[int]("page"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/me/orders -> user.ListOrders")
router.Get("/t/:tenantCode/v1/me/orders"[len(r.Path()):], DataFunc2(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me/orders -> user.ListOrders")
router.Get("/v1/t/:tenantCode/me/orders"[len(r.Path()):], DataFunc2(
r.user.ListOrders,
Local[*models.User]("__ctx_user"),
QueryParam[string]("status"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/me/orders/:id<int> -> user.GetOrder")
router.Get("/t/:tenantCode/v1/me/orders/:id<int>"[len(r.Path()):], DataFunc2(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me/orders/:id<int> -> user.GetOrder")
router.Get("/v1/t/:tenantCode/me/orders/:id<int>"[len(r.Path()):], DataFunc2(
r.user.GetOrder,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/me/wallet -> user.Wallet")
router.Get("/t/:tenantCode/v1/me/wallet"[len(r.Path()):], DataFunc1(
r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me/wallet -> user.Wallet")
router.Get("/v1/t/:tenantCode/me/wallet"[len(r.Path()):], DataFunc1(
r.user.Wallet,
Local[*models.User]("__ctx_user"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/me/coupons/receive -> user.ReceiveCoupon")
router.Post("/t/:tenantCode/v1/me/coupons/receive"[len(r.Path()):], DataFunc2(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/me/coupons/receive -> user.ReceiveCoupon")
router.Post("/v1/t/:tenantCode/me/coupons/receive"[len(r.Path()):], DataFunc2(
r.user.ReceiveCoupon,
Local[*models.User]("__ctx_user"),
Body[dto.CouponReceiveForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/me/favorites -> user.AddFavorite")
router.Post("/t/:tenantCode/v1/me/favorites"[len(r.Path()):], Func2(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/me/favorites -> user.AddFavorite")
router.Post("/v1/t/:tenantCode/me/favorites"[len(r.Path()):], Func2(
r.user.AddFavorite,
Local[*models.User]("__ctx_user"),
QueryParam[int64]("content_id"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/me/likes -> user.AddLike")
router.Post("/t/:tenantCode/v1/me/likes"[len(r.Path()):], Func2(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/me/likes -> user.AddLike")
router.Post("/v1/t/:tenantCode/me/likes"[len(r.Path()):], Func2(
r.user.AddLike,
Local[*models.User]("__ctx_user"),
QueryParam[int64]("content_id"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/me/notifications/:id<int>/read -> user.MarkNotificationRead")
router.Post("/t/:tenantCode/v1/me/notifications/:id<int>/read"[len(r.Path()):], Func2(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/me/notifications/:id<int>/read -> user.MarkNotificationRead")
router.Post("/v1/t/:tenantCode/me/notifications/:id<int>/read"[len(r.Path()):], Func2(
r.user.MarkNotificationRead,
Local[*models.User]("__ctx_user"),
PathParam[int64]("id"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/me/notifications/read-all -> user.MarkAllNotificationsRead")
router.Post("/t/:tenantCode/v1/me/notifications/read-all"[len(r.Path()):], Func1(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/me/notifications/read-all -> user.MarkAllNotificationsRead")
router.Post("/v1/t/:tenantCode/me/notifications/read-all"[len(r.Path()):], Func1(
r.user.MarkAllNotificationsRead,
Local[*models.User]("__ctx_user"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/me/realname -> user.RealName")
router.Post("/t/:tenantCode/v1/me/realname"[len(r.Path()):], Func2(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/me/realname -> user.RealName")
router.Post("/v1/t/:tenantCode/me/realname"[len(r.Path()):], Func2(
r.user.RealName,
Local[*models.User]("__ctx_user"),
Body[dto.RealNameForm]("form"),
))
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/me/wallet/recharge -> user.Recharge")
router.Post("/t/:tenantCode/v1/me/wallet/recharge"[len(r.Path()):], DataFunc2(
r.log.Debugf("Registering route: Post /v1/t/:tenantCode/me/wallet/recharge -> user.Recharge")
router.Post("/v1/t/:tenantCode/me/wallet/recharge"[len(r.Path()):], DataFunc2(
r.user.Recharge,
Local[*models.User]("__ctx_user"),
Body[dto.RechargeForm]("form"),
))
r.log.Debugf("Registering route: Put /t/:tenantCode/v1/me -> user.Update")
router.Put("/t/:tenantCode/v1/me"[len(r.Path()):], Func2(
r.log.Debugf("Registering route: Put /v1/t/:tenantCode/me -> user.Update")
router.Put("/v1/t/:tenantCode/me"[len(r.Path()):], Func2(
r.user.Update,
Local[*models.User]("__ctx_user"),
Body[dto.UserUpdate]("form"),

View File

@@ -1,7 +1,7 @@
package v1
func (r *Routes) Path() string {
return "/t/:tenantCode/v1"
return "/v1/t/:tenantCode"
}
func (r *Routes) Middlewares() []any {

View File

@@ -17,7 +17,7 @@ type Storage struct {
// Upload file
//
// @Router /t/:tenantCode/v1/storage/* [put]
// @Router /v1/t/:tenantCode/storage/* [put]
// @Summary Upload file
// @Tags Storage
// @Accept octet-stream
@@ -58,7 +58,7 @@ func (s *Storage) Upload(ctx fiber.Ctx, expires, sign string) (string, error) {
// Download file
//
// @Router /t/:tenantCode/v1/storage/* [get]
// @Router /v1/t/:tenantCode/storage/* [get]
// @Summary Download file
// @Tags Storage
// @Accept json

View File

@@ -3,9 +3,7 @@ package v1
import (
"quyun/v2/app/errorx"
"quyun/v2/app/http/v1/dto"
"quyun/v2/app/requests"
"quyun/v2/app/services"
"quyun/v2/database/models"
"github.com/gofiber/fiber/v3"
)
@@ -15,157 +13,15 @@ type Tenant struct{}
// List creator contents
//
// @Router /t/:tenantCode/v1/creators/:id<int>/contents [get]
// @Summary List creator contents
// @Description List contents of a specific creator
// @Tags TenantPublic
// @Accept json
// @Produce json
// @Param id path int64 true "Creator User ID"
// @Param page query int false "Page"
// @Param limit query int false "Limit"
// @Success 200 {object} requests.Pager
// @Bind id path
// @Bind filter query
func (t *Tenant) ListContents(ctx fiber.Ctx, id int64, filter *dto.ContentListFilter) (*requests.Pager, error) {
tenantID := getTenantID(ctx)
if filter == nil {
filter = &dto.ContentListFilter{}
}
if tenantID > 0 {
filter.TenantID = &tenantID
}
filter.AuthorID = &id
return services.Content.List(ctx, tenantID, filter)
}
// @Router /v1/t/:tenantCode/creators/:id<int>/contents [get]
// @Router /v1/t/:tenantCode/tenants [get]
// @Router /v1/t/:tenantCode/tenants/:id<int> [get]
// @Router /v1/t/:tenantCode/tenants/:id<int>/follow [post]
// @Router /v1/t/:tenantCode/tenants/:id<int>/follow [delete]
// @Router /v1/t/:tenantCode/tenants/:id<int>/join [post]
// @Router /v1/t/:tenantCode/tenants/:id<int>/join [delete]
// @Router /v1/t/:tenantCode/tenants/:id<int>/invites/accept [post]
// List tenants (search)
//
// @Router /t/:tenantCode/v1/tenants [get]
// @Summary List tenants
// @Description Search tenants
// @Tags TenantPublic
// @Accept json
// @Produce json
// @Param keyword query string false "Keyword"
// @Param page query int false "Page"
// @Param limit query int false "Limit"
// @Success 200 {object} requests.Pager
// @Bind filter query
func (t *Tenant) List(ctx fiber.Ctx, filter *dto.TenantListFilter) (*requests.Pager, error) {
tenantID := getTenantID(ctx)
return services.Tenant.List(ctx, tenantID, filter)
}
// Get tenant public profile
//
// @Router /t/:tenantCode/v1/tenants/:id<int> [get]
// @Summary Get tenant profile
// @Description Get tenant public profile
// @Tags TenantPublic
// @Accept json
// @Produce json
// @Param id path int64 true "Tenant ID"
// @Success 200 {object} dto.TenantProfile
// @Bind user local key(__ctx_user)
// @Bind id path
func (t *Tenant) Get(ctx fiber.Ctx, user *models.User, id int64) (*dto.TenantProfile, error) {
uid := int64(0)
if user != nil {
uid = user.ID
}
tenantID := getTenantID(ctx)
if tenantID > 0 && id != tenantID {
return nil, errorx.ErrForbidden.WithMsg("租户不匹配")
}
return services.Tenant.GetPublicProfile(ctx, id, uid)
}
// Follow a tenant
//
// @Router /t/:tenantCode/v1/tenants/:id<int>/follow [post]
// @Summary Follow tenant
// @Description Follow a tenant
// @Tags TenantPublic
// @Accept json
// @Produce json
// @Param id path int64 true "Tenant ID"
// @Success 200 {string} string "Followed"
// @Bind user local key(__ctx_user)
// @Bind id path
func (t *Tenant) Follow(ctx fiber.Ctx, user *models.User, id int64) error {
tenantID := getTenantID(ctx)
if tenantID > 0 && id != tenantID {
return errorx.ErrForbidden.WithMsg("租户不匹配")
}
return services.Tenant.Follow(ctx, tenantID, user.ID)
}
// Unfollow a tenant
//
// @Router /t/:tenantCode/v1/tenants/:id<int>/follow [delete]
// @Summary Unfollow tenant
// @Description Unfollow a tenant
// @Tags TenantPublic
// @Accept json
// @Produce json
// @Param id path int64 true "Tenant ID"
// @Success 200 {string} string "Unfollowed"
// @Bind user local key(__ctx_user)
// @Bind id path
func (t *Tenant) Unfollow(ctx fiber.Ctx, user *models.User, id int64) error {
tenantID := getTenantID(ctx)
if tenantID > 0 && id != tenantID {
return errorx.ErrForbidden.WithMsg("租户不匹配")
}
return services.Tenant.Unfollow(ctx, tenantID, user.ID)
}
// Apply to join a tenant
//
// @Router /t/:tenantCode/v1/tenants/:id<int>/join [post]
// @Summary Apply to join tenant
// @Description Submit join request for a tenant
// @Tags TenantPublic
// @Accept json
// @Produce json
// @Param id path int64 true "Tenant ID"
// @Param form body dto.TenantJoinApplyForm true "Join form"
// @Success 200 {string} string "Applied"
// @Bind id path
// @Bind form body
func (t *Tenant) ApplyJoin(ctx fiber.Ctx, id int64, form *dto.TenantJoinApplyForm) error {
tenantID := getTenantID(ctx)
if tenantID > 0 && id != tenantID {
return errorx.ErrForbidden.WithMsg("租户不匹配")
}
userID := getUserID(ctx)
return services.Tenant.ApplyJoin(ctx, id, userID, form)
}
// Cancel join request
//
// @Router /t/:tenantCode/v1/tenants/:id<int>/join [delete]
// @Summary Cancel join request
// @Description Cancel join request for a tenant
// @Tags TenantPublic
// @Accept json
// @Produce json
// @Param id path int64 true "Tenant ID"
// @Success 200 {string} string "Canceled"
// @Bind id path
func (t *Tenant) CancelJoin(ctx fiber.Ctx, id int64) error {
tenantID := getTenantID(ctx)
if tenantID > 0 && id != tenantID {
return errorx.ErrForbidden.WithMsg("租户不匹配")
}
userID := getUserID(ctx)
return services.Tenant.CancelJoin(ctx, id, userID)
}
// Accept tenant invite
//
// @Router /t/:tenantCode/v1/tenants/:id<int>/invites/accept [post]
// @Summary Accept tenant invite
// @Description Accept a tenant invite by code
// @Tags TenantPublic

View File

@@ -3,7 +3,6 @@ package v1
import (
"quyun/v2/app/http/v1/dto"
"quyun/v2/app/services"
"quyun/v2/database/models"
"github.com/gofiber/fiber/v3"
)
@@ -13,83 +12,20 @@ type Transaction struct{}
// Create Order
//
// @Router /t/:tenantCode/v1/orders [post]
// @Summary Create Order
// @Description Create Order
// @Tags Transaction
// @Accept json
// @Produce json
// @Param form body dto.OrderCreateForm true "Create form"
// @Success 200 {object} dto.OrderCreateResponse
// @Bind user local key(__ctx_user)
// @Bind form body
func (t *Transaction) Create(
ctx fiber.Ctx,
user *models.User,
form *dto.OrderCreateForm,
) (*dto.OrderCreateResponse, error) {
tenantID := getTenantID(ctx)
return services.Order.Create(ctx, tenantID, user.ID, form)
}
// @Router /v1/t/:tenantCode/orders [post]
// @Router /v1/t/:tenantCode/orders/:id<int>/pay [post]
// @Router /v1/t/:tenantCode/orders/:id<int>/status [get]
// @Router /v1/t/:tenantCode/webhook/payment/notify [post]
// Pay for order
//
// @Router /t/:tenantCode/v1/orders/:id<int>/pay [post]
// @Summary Pay for order
// @Description Pay for order
// @Tags Transaction
// @Accept json
// @Produce json
// @Param id path int64 true "Order ID"
// @Param form body dto.OrderPayForm true "Pay form"
// @Success 200 {object} dto.OrderPayResponse
// @Bind user local key(__ctx_user)
// @Bind id path
// @Bind form body
func (t *Transaction) Pay(
ctx fiber.Ctx,
user *models.User,
id int64,
form *dto.OrderPayForm,
) (*dto.OrderPayResponse, error) {
tenantID := getTenantID(ctx)
return services.Order.Pay(ctx, tenantID, user.ID, id, form)
}
// Check order payment status
//
// @Router /t/:tenantCode/v1/orders/:id<int>/status [get]
// @Summary Check order status
// @Description Check order payment status
// @Tags Transaction
// @Accept json
// @Produce json
// @Param id path int64 true "Order ID"
// @Success 200 {object} dto.OrderStatusResponse
// @Bind user local key(__ctx_user)
// @Bind id path
func (t *Transaction) Status(ctx fiber.Ctx, user *models.User, id int64) (*dto.OrderStatusResponse, error) {
tenantID := getTenantID(ctx)
return services.Order.Status(ctx, tenantID, user.ID, id)
}
type WebhookForm struct {
OrderID int64 `json:"order_id"`
ExternalID string `json:"external_id"`
}
// Payment Webhook
//
// @Router /t/:tenantCode/v1/webhook/payment/notify [post]
// @Summary Payment Webhook
// @Description Payment Webhook
// @Tags Transaction
// @Accept json
// @Produce json
// @Param form body WebhookForm true "Webhook Data"
// @Param form body dto.PaymentWebhookForm true "Webhook Data"
// @Success 200 {string} string "success"
// @Bind form body
func (t *Transaction) Webhook(ctx fiber.Ctx, form *WebhookForm) (string, error) {
func (t *Transaction) Webhook(ctx fiber.Ctx, form *dto.PaymentWebhookForm) (string, error) {
tenantID := getTenantID(ctx)
err := services.Order.ProcessExternalPayment(ctx, tenantID, form.OrderID, form.ExternalID)
if err != nil {

View File

@@ -16,7 +16,7 @@ type User struct{}
// Get current user profile
//
// @Router /t/:tenantCode/v1/me [get]
// @Router /v1/t/:tenantCode/me [get]
// @Summary Get user profile
// @Description Get current user profile
// @Tags UserCenter
@@ -25,13 +25,12 @@ type User struct{}
// @Success 200 {object} auth_dto.User
// @Bind user local key(__ctx_user)
func (u *User) Me(ctx fiber.Ctx, user *models.User) (*auth_dto.User, error) {
// uid := cast.ToInt64(ctx.Locals(consts.CtxKeyUser))
return services.User.ToAuthUserDTO(user), nil
}
// Update user profile
//
// @Router /t/:tenantCode/v1/me [put]
// @Router /v1/t/:tenantCode/me [put]
// @Summary Update user profile
// @Description Update user profile
// @Tags UserCenter
@@ -47,7 +46,7 @@ func (u *User) Update(ctx fiber.Ctx, user *models.User, form *dto.UserUpdate) er
// Submit real-name authentication
//
// @Router /t/:tenantCode/v1/me/realname [post]
// @Router /v1/t/:tenantCode/me/realname [post]
// @Summary Realname auth
// @Description Submit real-name authentication
// @Tags UserCenter
@@ -63,7 +62,7 @@ func (u *User) RealName(ctx fiber.Ctx, user *models.User, form *dto.RealNameForm
// Get wallet balance and transactions
//
// @Router /t/:tenantCode/v1/me/wallet [get]
// @Router /v1/t/:tenantCode/me/wallet [get]
// @Summary Get wallet
// @Description Get wallet balance and transactions
// @Tags UserCenter
@@ -78,7 +77,7 @@ func (u *User) Wallet(ctx fiber.Ctx, user *models.User) (*dto.WalletResponse, er
// Recharge wallet
//
// @Router /t/:tenantCode/v1/me/wallet/recharge [post]
// @Router /v1/t/:tenantCode/me/wallet/recharge [post]
// @Summary Recharge wallet
// @Description Recharge wallet
// @Tags UserCenter
@@ -95,7 +94,7 @@ func (u *User) Recharge(ctx fiber.Ctx, user *models.User, form *dto.RechargeForm
// List user orders
//
// @Router /t/:tenantCode/v1/me/orders [get]
// @Router /v1/t/:tenantCode/me/orders [get]
// @Summary List orders
// @Description List user orders
// @Tags UserCenter
@@ -112,7 +111,7 @@ func (u *User) ListOrders(ctx fiber.Ctx, user *models.User, status string) ([]dt
// Get user order detail
//
// @Router /t/:tenantCode/v1/me/orders/:id<int> [get]
// @Router /v1/t/:tenantCode/me/orders/:id<int> [get]
// @Summary Get order detail
// @Description Get user order detail
// @Tags UserCenter
@@ -129,7 +128,7 @@ func (u *User) GetOrder(ctx fiber.Ctx, user *models.User, id int64) (*dto.Order,
// Get purchased content
//
// @Router /t/:tenantCode/v1/me/library [get]
// @Router /v1/t/:tenantCode/me/library [get]
// @Summary Get library
// @Description Get purchased content
// @Tags UserCenter
@@ -144,7 +143,7 @@ func (u *User) Library(ctx fiber.Ctx, user *models.User) ([]dto.ContentItem, err
// Get favorites
//
// @Router /t/:tenantCode/v1/me/favorites [get]
// @Router /v1/t/:tenantCode/me/favorites [get]
// @Summary Get favorites
// @Description Get favorites
// @Tags UserCenter
@@ -159,7 +158,7 @@ func (u *User) Favorites(ctx fiber.Ctx, user *models.User) ([]dto.ContentItem, e
// Add to favorites
//
// @Router /t/:tenantCode/v1/me/favorites [post]
// @Router /v1/t/:tenantCode/me/favorites [post]
// @Summary Add favorite
// @Description Add to favorites
// @Tags UserCenter
@@ -176,7 +175,7 @@ func (u *User) AddFavorite(ctx fiber.Ctx, user *models.User, contentId int64) er
// Remove from favorites
//
// @Router /t/:tenantCode/v1/me/favorites/:contentId<int> [delete]
// @Router /v1/t/:tenantCode/me/favorites/:contentId<int> [delete]
// @Summary Remove favorite
// @Description Remove from favorites
// @Tags UserCenter
@@ -193,7 +192,7 @@ func (u *User) RemoveFavorite(ctx fiber.Ctx, user *models.User, contentId int64)
// Get liked contents
//
// @Router /t/:tenantCode/v1/me/likes [get]
// @Router /v1/t/:tenantCode/me/likes [get]
// @Summary Get likes
// @Description Get liked contents
// @Tags UserCenter
@@ -208,7 +207,7 @@ func (u *User) Likes(ctx fiber.Ctx, user *models.User) ([]dto.ContentItem, error
// Like content
//
// @Router /t/:tenantCode/v1/me/likes [post]
// @Router /v1/t/:tenantCode/me/likes [post]
// @Summary Like content
// @Description Like content
// @Tags UserCenter
@@ -225,7 +224,7 @@ func (u *User) AddLike(ctx fiber.Ctx, user *models.User, contentId int64) error
// Unlike content
//
// @Router /t/:tenantCode/v1/me/likes/:contentId<int> [delete]
// @Router /v1/t/:tenantCode/me/likes/:contentId<int> [delete]
// @Summary Unlike content
// @Description Unlike content
// @Tags UserCenter
@@ -242,7 +241,7 @@ func (u *User) RemoveLike(ctx fiber.Ctx, user *models.User, contentId int64) err
// Get following tenants
//
// @Router /t/:tenantCode/v1/me/following [get]
// @Router /v1/t/:tenantCode/me/following [get]
// @Summary Get following
// @Description Get following tenants
// @Tags UserCenter
@@ -257,7 +256,7 @@ func (u *User) Following(ctx fiber.Ctx, user *models.User) ([]dto.TenantProfile,
// Get notifications
//
// @Router /t/:tenantCode/v1/me/notifications [get]
// @Router /v1/t/:tenantCode/me/notifications [get]
// @Summary Get notifications
// @Description Get notifications
// @Tags UserCenter
@@ -276,7 +275,7 @@ func (u *User) Notifications(ctx fiber.Ctx, user *models.User, typeArg string, p
// Mark notification as read
//
// @Router /t/:tenantCode/v1/me/notifications/:id<int>/read [post]
// @Router /v1/t/:tenantCode/me/notifications/:id<int>/read [post]
// @Summary Mark as read
// @Tags UserCenter
// @Accept json
@@ -292,7 +291,7 @@ func (u *User) MarkNotificationRead(ctx fiber.Ctx, user *models.User, id int64)
// Mark all notifications as read
//
// @Router /t/:tenantCode/v1/me/notifications/read-all [post]
// @Router /v1/t/:tenantCode/me/notifications/read-all [post]
// @Summary Mark all as read
// @Tags UserCenter
// @Accept json
@@ -306,7 +305,7 @@ func (u *User) MarkAllNotificationsRead(ctx fiber.Ctx, user *models.User) error
// List my coupons
//
// @Router /t/:tenantCode/v1/me/coupons [get]
// @Router /v1/t/:tenantCode/me/coupons [get]
// @Summary List coupons
// @Description List my coupons
// @Tags UserCenter
@@ -323,7 +322,7 @@ func (u *User) MyCoupons(ctx fiber.Ctx, user *models.User, status string) ([]dto
// List available coupons for order amount
//
// @Router /t/:tenantCode/v1/me/coupons/available [get]
// @Router /v1/t/:tenantCode/me/coupons/available [get]
// @Summary List available coupons
// @Description List coupons available for the given order amount
// @Tags UserCenter
@@ -340,7 +339,7 @@ func (u *User) AvailableCoupons(ctx fiber.Ctx, user *models.User, amount int64)
// Receive coupon
//
// @Router /t/:tenantCode/v1/me/coupons/receive [post]
// @Router /v1/t/:tenantCode/me/coupons/receive [post]
// @Summary Receive coupon
// @Description Receive a coupon by coupon_id
// @Tags UserCenter

View File

@@ -164,17 +164,10 @@ func isPublicRoute(ctx fiber.Ctx) bool {
}
}
if method == fiber.MethodPost && path == "/v1/webhook/payment/notify" {
if method == fiber.MethodPost && (path == "/v1/webhook/payment/notify" || path == "/v1/auth/otp" || path == "/v1/auth/login") {
return true
}
if method == fiber.MethodPost {
switch path {
case "/v1/auth/otp", "/v1/auth/login":
return true
}
}
if method == fiber.MethodPut && strings.HasPrefix(path, "/v1/storage/") {
return true
}
@@ -186,17 +179,23 @@ func isSuperPublicRoute(ctx fiber.Ctx) bool {
path := ctx.Path()
method := ctx.Method()
if method == fiber.MethodPost && path == "/super/v1/auth/login" {
if method == fiber.MethodPost {
if path == "/super/v1/auth/login" || path == "/auth/login" || path == "/login" {
return true
}
}
if method == fiber.MethodGet {
if path == "/super/v1/auth/token" || path == "/auth/token" || path == "/token" {
return true
}
}
return false
}
func normalizeTenantPath(path string) string {
if !strings.HasPrefix(path, "/t/") {
return path
}
if strings.HasPrefix(path, "/t/") {
rest := strings.TrimPrefix(path, "/t/")
slash := strings.Index(rest, "/")
if slash == -1 {
@@ -208,3 +207,13 @@ func normalizeTenantPath(path string) string {
}
return path
}
if strings.HasPrefix(path, "/v1/t/") {
rest := strings.TrimPrefix(path, "/v1/t/")
slash := strings.Index(rest, "/")
if slash == -1 {
return path
}
return "/v1" + rest[slash:]
}
return path
}

View File

@@ -64,10 +64,7 @@ func (s *user) LoginWithOTP(ctx context.Context, tenantID int64, phone, otp stri
if u.Status == consts.UserStatusBanned {
return nil, errorx.ErrAccountDisabled
}
// 4. 校验租户成员关系租户上下文下仅允许成员登录)
if err := s.ensureTenantMember(ctx, tenantID, u.ID); err != nil {
return nil, err
}
// 4. 登录不校验租户成员关系租户资源访问时再校验权限
// 5. 生成 Token
token, err := s.jwt.CreateToken(s.jwt.CreateClaims(jwt.BaseClaims{

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,101 @@
# Implementation Plan: Global Auth + Tenant Route Prefix
**Branch**: `main` | **Date**: 2026-01-26 | **Spec**: N/A
**Input**: 改为全局登录;租户资源访问再鉴权;后端路由从 `/t/:tenantCode/v1` 改为 `/v1/t/:tenantCode`,同步前端路由。
**Note**: 本计划遵循 `docs/templates/plan-template.md`
## Summary
实施全局登录(不要求 tenantCode并将多租户路由前缀统一为 `/v1/t/:tenantCode`。同步更新后端路由与前端路由/请求封装,确保登录与资源鉴权一致。
## Technical Context
**Language/Version**: Go 1.22 + Vue 3 (Vite)
**Primary Dependencies**: Fiber, GORM-Gen, Vue Router
**Storage**: PostgreSQL
**Testing**: go test / 前端 build+l int如需
**Target Platform**: local/staging
**Project Type**: Web application
**Performance Goals**: N/A
**Constraints**: 不改生成文件,需通过 `atomctl` 重新生成路由/Swagger
**Scale/Scope**: auth + 路由前缀调整
## Constitution Check
- 遵循 `backend/llm.txt`(控制器薄、服务层处理、生成文件不手改)
- 变更后需跑 `atomctl gen route/provider/swag`
## Project Structure
### Backend
```text
backend/app/http/v1/
backend/app/services/
backend/app/http/**/routes.*.go
```
### Frontend
```text
frontend/portal/src/router/index.js
frontend/portal/src/utils/request.js
frontend/portal/src/api/*
frontend/superadmin/src/router/index.js
```
### Docs
```text
docs/plan.md
```
## Plan Phases
### Phase 1: Backend route + auth changes
- 调整 `/t/:tenantCode/v1``/v1/t/:tenantCode`
- 登录逻辑改为全局登录(租户鉴权移到资源访问)
### Phase 2: Frontend route + request changes
- 更新 portal 路由基座与 request baseUrl
- 更新所有前端 API 路径对应新前缀
### Phase 3: Regenerate + sanity check
- 生成 routes/providers/swagger
- 关键页面手动/自动冒烟校验
## Tasks
**Format**: `[ID] [P?] [Story] Description`
### Phase 1
- [ ] T001 [US0] 修改后端路由前缀与路由注册
- [ ] T002 [US0] 调整登录逻辑为全局登录
### Phase 2
- [ ] T010 [US1] 修改 portal 路由与 request baseUrl
- [ ] T011 [US1] 更新 portal API 路径
### Phase 3
- [ ] T020 [US2] 重新生成路由与 swagger
- [x] T021 [US2] 冒烟验证核心路径
## Dependencies
- Phase 1 → Phase 2 → Phase 3
## Acceptance Criteria
- 登录不依赖 tenantCode。
- 租户路由统一为 `/v1/t/:tenantCode`
- Portal 页面正常加载并可登录。
## Risks
- 大量前端 API 路径需要同步改动。
- 老路径可能被外部依赖使用。
## Complexity Tracking
无。

View File

@@ -3,8 +3,8 @@
## Service Startup (Local)
- Backend: `go run ./backend/main.go serve` (default `http://localhost:8080`).
- Portal frontend: `npm -C frontend/portal install` then `npm -C frontend/portal run dev` (default `http://localhost:5174`).
- Superadmin frontend: `npm -C frontend/superadmin install` then `npm -C frontend/superadmin run dev` (default `http://localhost:5173`).
- Portal frontend: `npm -C frontend/portal install` then `npm -C frontend/portal run dev` (default `http://localhost:5174`, remote use `http://10.1.1.104:5174`).
- Superadmin frontend: `npm -C frontend/superadmin install` then `npm -C frontend/superadmin run dev` (default `http://localhost:5173`, remote use `http://10.1.1.104:5173`).
- Portal dev server proxies `/v1` to `http://localhost:8080` (`frontend/portal/vite.config.js`).
- Superadmin dev server proxies `/super/v1` and `/v1` to `http://localhost:8080` (`frontend/superadmin/vite.config.mjs`).
@@ -29,7 +29,7 @@
### MCP Example Flow (AI Guidance)
1. **Open Portal Home**
- `chrome-devtools_new_page``http://localhost:5174/t/:tenantCode`
- `chrome-devtools_new_page``http://10.1.1.104:5174/t/:tenantCode`
- `chrome-devtools_wait_for` text: `探索戏曲` or page title
2. **Portal Login**
@@ -50,7 +50,7 @@
- Assert detail page loads and cover/media is visible
5. **Superadmin Login**
- `chrome-devtools_new_page``http://localhost:5173/super/auth/login`
- `chrome-devtools_new_page``http://10.1.1.104:5173/super/auth/login`
- Fill `#username` with `superadmin` and `#password1` with `superadmin123`
- Click `Sign In`
- Wait for dashboard cards or `Dashboard` text
@@ -80,7 +80,7 @@
## Portal Smoke (Use tenantCode)
- Tenant code: query DB (`SELECT code FROM tenants ORDER BY id DESC LIMIT 1;`), then open `http://localhost:5174/t/:tenantCode`.- Login: OTP is fixed to `1234` (see `backend/app/services/user.go`).
- Tenant code: query DB (`SELECT code FROM tenants ORDER BY id DESC LIMIT 1;`), then open `http://10.1.1.104:5174/t/:tenantCode`.- Login: OTP is fixed to `1234` (see `backend/app/services/user.go`).
- Home: `/t/:tenantCode` lists content.
- Detail: open any content detail; cover and main asset render.
- Login: sign in as `test` (phone `13800138000`, OTP `1234`) and open `/t/:tenantCode/me`.

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted, watch } from "vue";
import { useRouter, useRoute } from "vue-router";
import { ref, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import { tenantPath } from "../utils/tenant";
const isLoggedIn = ref(false);
@@ -28,13 +28,6 @@ onMounted(() => {
checkAuth();
});
// Watch route changes to refresh auth state (e.g. after login redirect)
watch(
() => route.path,
() => {
checkAuth();
},
);
const logout = () => {
localStorage.removeItem("token");
@@ -174,7 +167,7 @@ const logout = () => {
<template v-else>
<router-link
:to="tenantRoute('/auth/login')"
:to="'/auth/login'"
class="bg-primary-600 text-white px-6 py-2 rounded-full font-medium hover:bg-primary-700 transition-all shadow-sm shadow-primary-100 active:scale-95"
>登录 / 注册</router-link
>

View File

@@ -21,11 +21,6 @@ const router = createRouter({
name: "content-detail",
component: () => import("../views/content/DetailView.vue"),
},
{
path: "t/:id",
name: "tenant-home",
component: () => import("../views/tenant/HomeView.vue"),
},
{
path: "explore",
name: "explore",
@@ -49,7 +44,7 @@ const router = createRouter({
],
},
{
path: "/t/:tenantCode/auth",
path: "/auth",
component: LayoutAuth,
children: [
{

View File

@@ -3,10 +3,12 @@ import { getTenantCode } from "./tenant";
export async function request(endpoint, options = {}) {
const tenantCode = getTenantCode();
if (!tenantCode) {
const isAuthRequest = endpoint.startsWith("/auth/");
const baseUrl = isAuthRequest ? "/v1" : tenantCode ? `/v1/t/${tenantCode}` : "/v1";
if (!tenantCode && !isAuthRequest && !endpoint.startsWith("/tenants")) {
throw new Error("Tenant code missing in URL");
}
const baseUrl = `/t/${tenantCode}/v1`;
const token = localStorage.getItem("token");
const headers = {
@@ -45,7 +47,7 @@ export async function request(endpoint, options = {}) {
if (res.status === 401) {
localStorage.removeItem("token");
localStorage.removeItem("user");
const loginPath = `/t/${tenantCode}/auth/login`;
const loginPath = tenantCode ? `/t/${tenantCode}/auth/login` : "/auth/login";
// Redirect to login if not already there
if (!window.location.pathname.includes("/auth/login")) {
window.location.href = loginPath;

View File

@@ -248,7 +248,7 @@ onMounted(fetchData);
v-for="creator in matchedCreators"
:key="creator.id"
class="flex items-center gap-3 p-3 rounded-xl hover:bg-slate-50 transition-colors cursor-pointer border border-transparent hover:border-slate-200"
@click="$router.push(tenantRoute(`/t/${creator.id}`))"
@click="$router.push(`/t/${creator.id}`)"
>
<img
:src="
@@ -397,12 +397,12 @@ onMounted(fetchData);
`https://api.dicebear.com/7.x/avataaars/svg?seed=${creator.id}`
"
class="w-10 h-10 rounded-full cursor-pointer"
@click="$router.push(tenantRoute(`/t/${creator.id}`))"
@click="$router.push(`/t/${creator.id}`)"
/>
<div class="flex-1 min-w-0">
<div
class="font-bold text-slate-900 text-sm truncate hover:text-primary-600 cursor-pointer"
@click="$router.push(tenantRoute(`/t/${creator.id}`))"
@click="$router.push(`/t/${creator.id}`)"
>
{{ creator.name }}
</div>

View File

@@ -47,7 +47,9 @@ const login = async () => {
life: 1000,
});
setTimeout(() => {
router.push(tenantPath("/", route));
const fallback = tenantPath("/", route);
const target = route.query?.redirect || fallback;
router.push(target);
}, 1000);
} catch (e) {
toast.add({