From cde4fb8594b17951a2f53945cc3be473c1d3a0d7 Mon Sep 17 00:00:00 2001 From: Rogee Date: Mon, 26 Jan 2026 18:04:05 +0800 Subject: [PATCH] feat: switch to global auth and tenant route prefix --- backend/app/http/v1/auth/auth.go | 16 +- backend/app/http/v1/auth/routes.gen.go | 8 +- backend/app/http/v1/auth/routes.manual.go | 6 +- backend/app/http/v1/common.go | 16 +- backend/app/http/v1/content.go | 20 +- backend/app/http/v1/creator.go | 537 +---- backend/app/http/v1/dto/order.go | 8 + backend/app/http/v1/routes.gen.go | 413 +--- backend/app/http/v1/routes.manual.go | 2 +- backend/app/http/v1/storage.go | 4 +- backend/app/http/v1/tenant.go | 160 +- backend/app/http/v1/transaction.go | 76 +- backend/app/http/v1/user.go | 43 +- backend/app/middlewares/middlewares.go | 47 +- backend/app/services/user.go | 5 +- backend/docs/docs.go | 2282 +----------------- backend/docs/swagger.json | 2282 +----------------- backend/docs/swagger.yaml | 1557 +----------- docs/plan.md | 101 + docs/seed_verification.md | 10 +- frontend/portal/src/components/TopNavbar.vue | 57 +- frontend/portal/src/router/index.js | 7 +- frontend/portal/src/utils/request.js | 8 +- frontend/portal/src/views/HomeView.vue | 6 +- frontend/portal/src/views/auth/LoginView.vue | 4 +- 25 files changed, 479 insertions(+), 7196 deletions(-) diff --git a/backend/app/http/v1/auth/auth.go b/backend/app/http/v1/auth/auth.go index c142817..2d8cf44 100644 --- a/backend/app/http/v1/auth/auth.go +++ b/backend/app/http/v1/auth/auth.go @@ -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) } diff --git a/backend/app/http/v1/auth/routes.gen.go b/backend/app/http/v1/auth/routes.gen.go index 9e9b8fa..f163611 100644 --- a/backend/app/http/v1/auth/routes.gen.go +++ b/backend/app/http/v1/auth/routes.gen.go @@ -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"), )) diff --git a/backend/app/http/v1/auth/routes.manual.go b/backend/app/http/v1/auth/routes.manual.go index 75ccfac..2117250 100644 --- a/backend/app/http/v1/auth/routes.manual.go +++ b/backend/app/http/v1/auth/routes.manual.go @@ -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{} } diff --git a/backend/app/http/v1/common.go b/backend/app/http/v1/common.go index c915728..566f694 100644 --- a/backend/app/http/v1/common.go +++ b/backend/app/http/v1/common.go @@ -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 [delete] +// @Router /v1/t/:tenantCode/media-assets/:id [delete] // @Summary Delete media asset // @Description Delete media asset // @Tags Common diff --git a/backend/app/http/v1/content.go b/backend/app/http/v1/content.go index 49735ae..78d6edb 100644 --- a/backend/app/http/v1/content.go +++ b/backend/app/http/v1/content.go @@ -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 [get] +// @Router /v1/t/:tenantCode/contents/:id [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/comments [get] +// @Router /v1/t/:tenantCode/contents/:id/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/comments [post] +// @Router /v1/t/:tenantCode/contents/:id/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/like [post] +// @Router /v1/t/:tenantCode/comments/:id/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/like [post] +// @Router /v1/t/:tenantCode/contents/:id/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/like [delete] +// @Router /v1/t/:tenantCode/contents/:id/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/favorite [post] +// @Router /v1/t/:tenantCode/contents/:id/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/favorite [delete] +// @Router /v1/t/:tenantCode/contents/:id/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 diff --git a/backend/app/http/v1/creator.go b/backend/app/http/v1/creator.go index 5a8322e..517c1e8 100644 --- a/backend/app/http/v1/creator.go +++ b/backend/app/http/v1/creator.go @@ -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/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 [delete] +// @Router /v1/t/:tenantCode/creator/members/join-requests [get] +// @Router /v1/t/:tenantCode/creator/members/:id [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 [get] +// @Router /v1/t/:tenantCode/creator/contents [get] +// @Router /v1/t/:tenantCode/creator/contents [post] +// @Router /v1/t/:tenantCode/creator/contents/:id [put] +// @Router /v1/t/:tenantCode/creator/contents/:id [delete] +// @Router /v1/t/:tenantCode/creator/orders [get] +// @Router /v1/t/:tenantCode/creator/orders/:id/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 [get] +// @Router /v1/t/:tenantCode/creator/coupons/:id [put] +// @Router /v1/t/:tenantCode/creator/coupons/:id/grant [post] -// Review join request -// -// @Router /t/:tenantCode/v1/creator/members/:id/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 [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 [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 [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 [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 [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/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 [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 [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/grant [post] // @Summary Grant coupon // @Description Grant coupon to users // @Tags CreatorCenter diff --git a/backend/app/http/v1/dto/order.go b/backend/app/http/v1/dto/order.go index ef94822..f48ec6b 100644 --- a/backend/app/http/v1/dto/order.go +++ b/backend/app/http/v1/dto/order.go @@ -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"` +} diff --git a/backend/app/http/v1/routes.gen.go b/backend/app/http/v1/routes.gen.go index 649d1d7..d3ce598 100644 --- a/backend/app/http/v1/routes.gen.go +++ b/backend/app/http/v1/routes.gen.go @@ -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 -> common.DeleteMediaAsset") - router.Delete("/t/:tenantCode/v1/media-assets/:id"[len(r.Path()):], Func2( + r.log.Debugf("Registering route: Delete /v1/t/:tenantCode/media-assets/:id -> common.DeleteMediaAsset") + router.Delete("/v1/t/:tenantCode/media-assets/:id"[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/favorite -> content.RemoveFavorite") - router.Delete("/t/:tenantCode/v1/contents/:id/favorite"[len(r.Path()):], Func1( + r.log.Debugf("Registering route: Delete /v1/t/:tenantCode/contents/:id/favorite -> content.RemoveFavorite") + router.Delete("/v1/t/:tenantCode/contents/:id/favorite"[len(r.Path()):], Func1( r.content.RemoveFavorite, PathParam[int64]("id"), )) - r.log.Debugf("Registering route: Delete /t/:tenantCode/v1/contents/:id/like -> content.RemoveLike") - router.Delete("/t/:tenantCode/v1/contents/:id/like"[len(r.Path()):], Func1( + r.log.Debugf("Registering route: Delete /v1/t/:tenantCode/contents/:id/like -> content.RemoveLike") + router.Delete("/v1/t/:tenantCode/contents/:id/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 -> content.Get") - router.Get("/t/:tenantCode/v1/contents/:id"[len(r.Path()):], DataFunc1( + r.log.Debugf("Registering route: Get /v1/t/:tenantCode/contents/:id -> content.Get") + router.Get("/v1/t/:tenantCode/contents/:id"[len(r.Path()):], DataFunc1( r.content.Get, PathParam[int64]("id"), )) - r.log.Debugf("Registering route: Get /t/:tenantCode/v1/contents/:id/comments -> content.ListComments") - router.Get("/t/:tenantCode/v1/contents/:id/comments"[len(r.Path()):], DataFunc2( + r.log.Debugf("Registering route: Get /v1/t/:tenantCode/contents/:id/comments -> content.ListComments") + router.Get("/v1/t/:tenantCode/contents/:id/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/like -> content.LikeComment") - router.Post("/t/:tenantCode/v1/comments/:id/like"[len(r.Path()):], Func1( + r.log.Debugf("Registering route: Post /v1/t/:tenantCode/comments/:id/like -> content.LikeComment") + router.Post("/v1/t/:tenantCode/comments/:id/like"[len(r.Path()):], Func1( r.content.LikeComment, PathParam[int64]("id"), )) - r.log.Debugf("Registering route: Post /t/:tenantCode/v1/contents/:id/comments -> content.CreateComment") - router.Post("/t/:tenantCode/v1/contents/:id/comments"[len(r.Path()):], Func2( + r.log.Debugf("Registering route: Post /v1/t/:tenantCode/contents/:id/comments -> content.CreateComment") + router.Post("/v1/t/:tenantCode/contents/:id/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/favorite -> content.AddFavorite") - router.Post("/t/:tenantCode/v1/contents/:id/favorite"[len(r.Path()):], Func1( + r.log.Debugf("Registering route: Post /v1/t/:tenantCode/contents/:id/favorite -> content.AddFavorite") + router.Post("/v1/t/:tenantCode/contents/:id/favorite"[len(r.Path()):], Func1( r.content.AddFavorite, PathParam[int64]("id"), )) - r.log.Debugf("Registering route: Post /t/:tenantCode/v1/contents/:id/like -> content.AddLike") - router.Post("/t/:tenantCode/v1/contents/:id/like"[len(r.Path()):], Func1( + r.log.Debugf("Registering route: Post /v1/t/:tenantCode/contents/:id/like -> content.AddLike") + router.Post("/v1/t/:tenantCode/contents/:id/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 -> creator.DeleteContent") - router.Delete("/t/:tenantCode/v1/creator/contents/:id"[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 -> creator.RemoveMember") - router.Delete("/t/:tenantCode/v1/creator/members/:id"[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 -> creator.DisableMemberInvite") - router.Delete("/t/:tenantCode/v1/creator/members/invites/:id"[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 -> creator.GetContent") - router.Get("/t/:tenantCode/v1/creator/contents/:id"[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 -> creator.GetCoupon") - router.Get("/t/:tenantCode/v1/creator/coupons/:id"[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/grant -> creator.GrantCoupon") - router.Post("/t/:tenantCode/v1/creator/coupons/:id/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/review -> creator.ReviewMember") - router.Post("/t/:tenantCode/v1/creator/members/:id/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/refund -> creator.Refund") - router.Post("/t/:tenantCode/v1/creator/orders/:id/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 -> creator.UpdateContent") - router.Put("/t/:tenantCode/v1/creator/contents/:id"[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 -> creator.UpdateCoupon") - router.Put("/t/:tenantCode/v1/creator/coupons/:id"[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/follow -> tenant.Unfollow") - router.Delete("/t/:tenantCode/v1/tenants/:id/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/join -> tenant.CancelJoin") - router.Delete("/t/:tenantCode/v1/tenants/:id/join"[len(r.Path()):], Func1( - r.tenant.CancelJoin, - PathParam[int64]("id"), - )) - r.log.Debugf("Registering route: Get /t/:tenantCode/v1/creators/:id/contents -> tenant.ListContents") - router.Get("/t/:tenantCode/v1/creators/:id/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 -> tenant.Get") - router.Get("/t/:tenantCode/v1/tenants/:id"[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/follow -> tenant.Follow") - router.Post("/t/:tenantCode/v1/tenants/:id/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/invites/accept -> tenant.AcceptInvite") - router.Post("/t/:tenantCode/v1/tenants/:id/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/join -> tenant.ApplyJoin") - router.Post("/t/:tenantCode/v1/tenants/:id/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/status -> transaction.Status") - router.Get("/t/:tenantCode/v1/orders/:id/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/pay -> transaction.Pay") - router.Post("/t/:tenantCode/v1/orders/:id/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 -> user.RemoveFavorite") - router.Delete("/t/:tenantCode/v1/me/favorites/:contentId"[len(r.Path()):], Func2( + r.log.Debugf("Registering route: Delete /v1/t/:tenantCode/me/favorites/:contentId -> user.RemoveFavorite") + router.Delete("/v1/t/:tenantCode/me/favorites/:contentId"[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 -> user.RemoveLike") - router.Delete("/t/:tenantCode/v1/me/likes/:contentId"[len(r.Path()):], Func2( + r.log.Debugf("Registering route: Delete /v1/t/:tenantCode/me/likes/:contentId -> user.RemoveLike") + router.Delete("/v1/t/:tenantCode/me/likes/:contentId"[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 -> user.GetOrder") - router.Get("/t/:tenantCode/v1/me/orders/:id"[len(r.Path()):], DataFunc2( + r.log.Debugf("Registering route: Get /v1/t/:tenantCode/me/orders/:id -> user.GetOrder") + router.Get("/v1/t/:tenantCode/me/orders/:id"[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/read -> user.MarkNotificationRead") - router.Post("/t/:tenantCode/v1/me/notifications/:id/read"[len(r.Path()):], Func2( + r.log.Debugf("Registering route: Post /v1/t/:tenantCode/me/notifications/:id/read -> user.MarkNotificationRead") + router.Post("/v1/t/:tenantCode/me/notifications/:id/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"), diff --git a/backend/app/http/v1/routes.manual.go b/backend/app/http/v1/routes.manual.go index a01bcb9..5a145d2 100644 --- a/backend/app/http/v1/routes.manual.go +++ b/backend/app/http/v1/routes.manual.go @@ -1,7 +1,7 @@ package v1 func (r *Routes) Path() string { - return "/t/:tenantCode/v1" + return "/v1/t/:tenantCode" } func (r *Routes) Middlewares() []any { diff --git a/backend/app/http/v1/storage.go b/backend/app/http/v1/storage.go index 646d340..117e116 100644 --- a/backend/app/http/v1/storage.go +++ b/backend/app/http/v1/storage.go @@ -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 diff --git a/backend/app/http/v1/tenant.go b/backend/app/http/v1/tenant.go index e0b58f9..1ce4f1a 100644 --- a/backend/app/http/v1/tenant.go +++ b/backend/app/http/v1/tenant.go @@ -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/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/contents [get] +// @Router /v1/t/:tenantCode/tenants [get] +// @Router /v1/t/:tenantCode/tenants/:id [get] +// @Router /v1/t/:tenantCode/tenants/:id/follow [post] +// @Router /v1/t/:tenantCode/tenants/:id/follow [delete] +// @Router /v1/t/:tenantCode/tenants/:id/join [post] +// @Router /v1/t/:tenantCode/tenants/:id/join [delete] +// @Router /v1/t/:tenantCode/tenants/:id/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 [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/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/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/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/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/invites/accept [post] // @Summary Accept tenant invite // @Description Accept a tenant invite by code // @Tags TenantPublic diff --git a/backend/app/http/v1/transaction.go b/backend/app/http/v1/transaction.go index b220d94..55ab4f5 100644 --- a/backend/app/http/v1/transaction.go +++ b/backend/app/http/v1/transaction.go @@ -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/pay [post] +// @Router /v1/t/:tenantCode/orders/:id/status [get] +// @Router /v1/t/:tenantCode/webhook/payment/notify [post] -// Pay for order -// -// @Router /t/:tenantCode/v1/orders/:id/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/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 { diff --git a/backend/app/http/v1/user.go b/backend/app/http/v1/user.go index 0a80927..1cd3e17 100644 --- a/backend/app/http/v1/user.go +++ b/backend/app/http/v1/user.go @@ -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 [get] +// @Router /v1/t/:tenantCode/me/orders/:id [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 [delete] +// @Router /v1/t/:tenantCode/me/favorites/:contentId [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 [delete] +// @Router /v1/t/:tenantCode/me/likes/:contentId [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/read [post] +// @Router /v1/t/:tenantCode/me/notifications/:id/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 diff --git a/backend/app/middlewares/middlewares.go b/backend/app/middlewares/middlewares.go index 78b243e..6e90d52 100644 --- a/backend/app/middlewares/middlewares.go +++ b/backend/app/middlewares/middlewares.go @@ -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,25 +179,41 @@ func isSuperPublicRoute(ctx fiber.Ctx) bool { path := ctx.Path() method := ctx.Method() - if method == fiber.MethodPost && path == "/super/v1/auth/login" { - return true + 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/") { + if strings.HasPrefix(path, "/t/") { + rest := strings.TrimPrefix(path, "/t/") + slash := strings.Index(rest, "/") + if slash == -1 { + return path + } + rest = rest[slash:] + if strings.HasPrefix(rest, "/v1") { + return rest + } return path } - rest := strings.TrimPrefix(path, "/t/") - slash := strings.Index(rest, "/") - if slash == -1 { - return path - } - rest = rest[slash:] - if strings.HasPrefix(rest, "/v1") { - return rest + 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 } diff --git a/backend/app/services/user.go b/backend/app/services/user.go index 787c985..156cbae 100644 --- a/backend/app/services/user.go +++ b/backend/app/services/user.go @@ -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{ diff --git a/backend/docs/docs.go b/backend/docs/docs.go index af5f4d3..b8c003f 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -3749,7 +3749,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/auth/login": { + "/v1/auth/login": { "post": { "description": "Login or register user using phone number and OTP", "consumes": [ @@ -3783,7 +3783,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/auth/otp": { + "/v1/auth/otp": { "post": { "description": "Send OTP to phone number", "consumes": [ @@ -3817,7 +3817,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/comments/{id}/like": { + "/v1/t/{tenantCode}/comments/{id}/like": { "post": { "description": "Like a comment", "consumes": [ @@ -3850,7 +3850,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/common/options": { + "/v1/t/{tenantCode}/common/options": { "get": { "description": "Get global options (enums)", "consumes": [ @@ -3873,7 +3873,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/contents": { + "/v1/t/{tenantCode}/contents": { "get": { "description": "List contents with filtering and pagination", "consumes": [ @@ -3956,7 +3956,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/contents/{id}": { + "/v1/t/{tenantCode}/contents/{id}": { "get": { "description": "Get content detail by ID", "consumes": [ @@ -3989,7 +3989,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/contents/{id}/comments": { + "/v1/t/{tenantCode}/contents/{id}/comments": { "get": { "description": "Get comments for a content", "consumes": [ @@ -4083,7 +4083,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/contents/{id}/favorite": { + "/v1/t/{tenantCode}/contents/{id}/favorite": { "post": { "tags": [ "Content" @@ -4133,7 +4133,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/contents/{id}/like": { + "/v1/t/{tenantCode}/contents/{id}/like": { "post": { "tags": [ "Content" @@ -4183,1206 +4183,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/creator/apply": { - "post": { - "description": "Apply to become a creator", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Apply creator", - "parameters": [ - { - "description": "Apply form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.ApplyForm" - } - } - ], - "responses": { - "200": { - "description": "Application submitted", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/contents": { - "get": { - "description": "List creator contents", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List contents", - "parameters": [ - { - "type": "string", - "description": "Status", - "name": "status", - "in": "query" - }, - { - "type": "string", - "description": "Genre", - "name": "genre", - "in": "query" - }, - { - "type": "string", - "description": "Keyword", - "name": "keyword", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.ContentItem" - } - } - } - } - }, - "post": { - "description": "Create/Publish content", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Create content", - "parameters": [ - { - "description": "Content form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.ContentCreateForm" - } - } - ], - "responses": { - "200": { - "description": "Created", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/contents/{id}": { - "get": { - "description": "Get content details for edit", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Get content", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Content ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.ContentEditDTO" - } - } - } - }, - "put": { - "description": "Update content", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Update content", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Content ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Update form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.ContentUpdateForm" - } - } - ], - "responses": { - "200": { - "description": "Updated", - "schema": { - "type": "string" - } - } - } - }, - "delete": { - "description": "Delete content", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Delete content", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Content ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Deleted", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/coupons": { - "get": { - "description": "List coupon templates", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List coupons", - "parameters": [ - { - "type": "integer", - "description": "Page", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "Type (fix_amount/discount)", - "name": "type", - "in": "query" - }, - { - "type": "string", - "description": "Status (active/expired)", - "name": "status", - "in": "query" - }, - { - "type": "string", - "description": "Keyword", - "name": "keyword", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/requests.Pager" - } - } - } - }, - "post": { - "description": "Create coupon template", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Create coupon", - "parameters": [ - { - "description": "Coupon form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.CouponCreateForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.CouponItem" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/coupons/{id}": { - "get": { - "description": "Get coupon template detail", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Get coupon", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Coupon ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.CouponItem" - } - } - } - }, - "put": { - "description": "Update coupon template", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Update coupon", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Coupon ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Coupon form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.CouponUpdateForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.CouponItem" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/coupons/{id}/grant": { - "post": { - "description": "Grant coupon to users", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Grant coupon", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Coupon ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Grant form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.CouponGrantForm" - } - } - ], - "responses": { - "200": { - "description": "Granted", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/dashboard": { - "get": { - "description": "Get creator dashboard stats", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Dashboard stats", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.DashboardStats" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members": { - "get": { - "description": "List tenant members with filters", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List tenant members", - "parameters": [ - { - "type": "string", - "description": "Keyword 关键词搜索(匹配用户名/昵称/手机号)。", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "Limit is page size; only values in {10,20,50,100} are accepted (otherwise defaults to 10).", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Page is 1-based page index; values \u003c= 0 are normalized to 1.", - "name": "page", - "in": "query" - }, - { - "enum": [ - "member", - "tenant_admin" - ], - "type": "string", - "x-enum-varnames": [ - "TenantUserRoleMember", - "TenantUserRoleTenantAdmin" - ], - "description": "Role 成员角色筛选(member/tenant_admin)。", - "name": "role", - "in": "query" - }, - { - "enum": [ - "active", - "inactive", - "pending_verify", - "verified", - "banned" - ], - "type": "string", - "x-enum-varnames": [ - "UserStatusActive", - "UserStatusInactive", - "UserStatusPendingVerify", - "UserStatusVerified", - "UserStatusBanned" - ], - "description": "Status 成员状态筛选(active/verified/banned 等)。", - "name": "status", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/requests.Pager" - }, - { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.TenantMemberItem" - } - } - } - } - ] - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/invite": { - "post": { - "description": "Create an invite for tenant members", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Create member invite", - "parameters": [ - { - "description": "Invite form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.TenantInviteCreateForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.TenantInviteItem" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/invites": { - "get": { - "description": "List member invites with filters", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List member invites", - "parameters": [ - { - "type": "integer", - "description": "Limit is page size; only values in {10,20,50,100} are accepted (otherwise defaults to 10).", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Page is 1-based page index; values \u003c= 0 are normalized to 1.", - "name": "page", - "in": "query" - }, - { - "enum": [ - "active", - "disabled", - "expired" - ], - "type": "string", - "x-enum-varnames": [ - "TenantInviteStatusActive", - "TenantInviteStatusDisabled", - "TenantInviteStatusExpired" - ], - "description": "Status 邀请状态筛选(active/disabled/expired)。", - "name": "status", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/requests.Pager" - }, - { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.TenantInviteListItem" - } - } - } - } - ] - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/invites/{id}": { - "delete": { - "description": "Disable a member invite by ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Disable member invite", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Invite ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Disabled", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/join-requests": { - "get": { - "description": "List tenant join requests", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List member join requests", - "parameters": [ - { - "type": "string", - "description": "Keyword 关键词搜索(匹配用户名/昵称/手机号)。", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "Limit is page size; only values in {10,20,50,100} are accepted (otherwise defaults to 10).", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Page is 1-based page index; values \u003c= 0 are normalized to 1.", - "name": "page", - "in": "query" - }, - { - "enum": [ - "pending", - "approved", - "rejected" - ], - "type": "string", - "x-enum-varnames": [ - "TenantJoinRequestStatusPending", - "TenantJoinRequestStatusApproved", - "TenantJoinRequestStatusRejected" - ], - "description": "Status 申请状态筛选(pending/approved/rejected)。", - "name": "status", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/requests.Pager" - }, - { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.TenantJoinRequestItem" - } - } - } - } - ] - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/{id}": { - "delete": { - "description": "Remove a tenant member by relation ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Remove tenant member", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Member ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Removed", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/{id}/review": { - "post": { - "description": "Approve or reject a tenant join request", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Review join request", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Join request ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Review form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.TenantJoinReviewForm" - } - } - ], - "responses": { - "200": { - "description": "Reviewed", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/orders": { - "get": { - "description": "List sales orders", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List sales orders", - "parameters": [ - { - "type": "string", - "description": "Status", - "name": "status", - "in": "query" - }, - { - "type": "string", - "description": "Keyword", - "name": "keyword", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.Order" - } - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/orders/{id}/refund": { - "post": { - "description": "Process refund", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Process refund", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Order ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Refund form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.RefundForm" - } - } - ], - "responses": { - "200": { - "description": "Processed", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/payout-accounts": { - "get": { - "description": "List payout accounts", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List payout accounts", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.PayoutAccount" - } - } - } - } - }, - "post": { - "description": "Add payout account", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Add payout account", - "parameters": [ - { - "description": "Account form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.PayoutAccount" - } - } - ], - "responses": { - "200": { - "description": "Added", - "schema": { - "type": "string" - } - } - } - }, - "delete": { - "description": "Remove payout account", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Remove payout account", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Account ID", - "name": "id", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "Removed", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/reports/export": { - "post": { - "description": "Export creator report overview", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Export report overview", - "parameters": [ - { - "description": "Export form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.ReportExportForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.ReportExportResponse" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/reports/overview": { - "get": { - "description": "Get creator report overview", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Report overview", - "parameters": [ - { - "type": "string", - "description": "Start time (RFC3339)", - "name": "start_at", - "in": "query" - }, - { - "type": "string", - "description": "End time (RFC3339)", - "name": "end_at", - "in": "query" - }, - { - "type": "string", - "description": "Granularity (day)", - "name": "granularity", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.ReportOverviewResponse" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/settings": { - "get": { - "description": "Get channel settings", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Get settings", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.Settings" - } - } - } - }, - "put": { - "description": "Update channel settings", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Update settings", - "parameters": [ - { - "description": "Settings form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.Settings" - } - } - ], - "responses": { - "200": { - "description": "Updated", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/withdraw": { - "post": { - "description": "Request withdrawal", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Request withdrawal", - "parameters": [ - { - "description": "Withdraw form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.WithdrawForm" - } - } - ], - "responses": { - "200": { - "description": "Withdrawal requested", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creators/{id}/contents": { - "get": { - "description": "List contents of a specific creator", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "List creator contents", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Creator User ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Page", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/requests.Pager" - } - } - } - } - }, - "/t/{tenantCode}/v1/me": { + "/v1/t/{tenantCode}/me": { "get": { "description": "Get current user profile", "consumes": [ @@ -5437,7 +4238,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/coupons": { + "/v1/t/{tenantCode}/me/coupons": { "get": { "description": "List my coupons", "consumes": [ @@ -5471,7 +4272,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/coupons/available": { + "/v1/t/{tenantCode}/me/coupons/available": { "get": { "description": "List coupons available for the given order amount", "consumes": [ @@ -5507,7 +4308,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/coupons/receive": { + "/v1/t/{tenantCode}/me/coupons/receive": { "post": { "description": "Receive a coupon by coupon_id", "consumes": [ @@ -5541,7 +4342,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/favorites": { + "/v1/t/{tenantCode}/me/favorites": { "get": { "description": "Get favorites", "consumes": [ @@ -5598,7 +4399,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/favorites/{contentId}": { + "/v1/t/{tenantCode}/me/favorites/{contentId}": { "delete": { "description": "Remove from favorites", "consumes": [ @@ -5631,7 +4432,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/following": { + "/v1/t/{tenantCode}/me/following": { "get": { "description": "Get following tenants", "consumes": [ @@ -5657,7 +4458,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/library": { + "/v1/t/{tenantCode}/me/library": { "get": { "description": "Get purchased content", "consumes": [ @@ -5683,7 +4484,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/likes": { + "/v1/t/{tenantCode}/me/likes": { "get": { "description": "Get liked contents", "consumes": [ @@ -5740,7 +4541,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/likes/{contentId}": { + "/v1/t/{tenantCode}/me/likes/{contentId}": { "delete": { "description": "Unlike content", "consumes": [ @@ -5773,7 +4574,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/notifications": { + "/v1/t/{tenantCode}/me/notifications": { "get": { "description": "Get notifications", "consumes": [ @@ -5825,7 +4626,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/notifications/read-all": { + "/v1/t/{tenantCode}/me/notifications/read-all": { "post": { "consumes": [ "application/json" @@ -5847,7 +4648,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/notifications/{id}/read": { + "/v1/t/{tenantCode}/me/notifications/{id}/read": { "post": { "consumes": [ "application/json" @@ -5879,7 +4680,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/orders": { + "/v1/t/{tenantCode}/me/orders": { "get": { "description": "List user orders", "consumes": [ @@ -5913,7 +4714,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/orders/{id}": { + "/v1/t/{tenantCode}/me/orders/{id}": { "get": { "description": "Get user order detail", "consumes": [ @@ -5946,7 +4747,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/realname": { + "/v1/t/{tenantCode}/me/realname": { "post": { "description": "Submit real-name authentication", "consumes": [ @@ -5980,7 +4781,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/wallet": { + "/v1/t/{tenantCode}/me/wallet": { "get": { "description": "Get wallet balance and transactions", "consumes": [ @@ -6003,7 +4804,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/me/wallet/recharge": { + "/v1/t/{tenantCode}/me/wallet/recharge": { "post": { "description": "Recharge wallet", "consumes": [ @@ -6037,7 +4838,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/media-assets/{id}": { + "/v1/t/{tenantCode}/media-assets/{id}": { "delete": { "description": "Delete media asset", "consumes": [ @@ -6070,116 +4871,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/orders": { - "post": { - "description": "Create Order", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Transaction" - ], - "summary": "Create Order", - "parameters": [ - { - "description": "Create form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.OrderCreateForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.OrderCreateResponse" - } - } - } - } - }, - "/t/{tenantCode}/v1/orders/{id}/pay": { - "post": { - "description": "Pay for order", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Transaction" - ], - "summary": "Pay for order", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Order ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Pay form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.OrderPayForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.OrderPayResponse" - } - } - } - } - }, - "/t/{tenantCode}/v1/orders/{id}/status": { - "get": { - "description": "Check order payment status", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Transaction" - ], - "summary": "Check order status", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Order ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.OrderStatusResponse" - } - } - } - } - }, - "/t/{tenantCode}/v1/storage/{any}": { + "/v1/t/{tenantCode}/storage/{any}": { "get": { "consumes": [ "application/json" @@ -6265,262 +4957,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/tenants": { - "get": { - "description": "Search tenants", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "List tenants", - "parameters": [ - { - "type": "string", - "description": "Keyword", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "Page", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/requests.Pager" - } - } - } - } - }, - "/t/{tenantCode}/v1/tenants/{id}": { - "get": { - "description": "Get tenant public profile", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Get tenant profile", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.TenantProfile" - } - } - } - } - }, - "/t/{tenantCode}/v1/tenants/{id}/follow": { - "post": { - "description": "Follow a tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Follow tenant", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Followed", - "schema": { - "type": "string" - } - } - } - }, - "delete": { - "description": "Unfollow a tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Unfollow tenant", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Unfollowed", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/tenants/{id}/invites/accept": { - "post": { - "description": "Accept a tenant invite by code", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Accept tenant invite", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Invite form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.TenantInviteAcceptForm" - } - } - ], - "responses": { - "200": { - "description": "Accepted", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/tenants/{id}/join": { - "post": { - "description": "Submit join request for a tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Apply to join tenant", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Join form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.TenantJoinApplyForm" - } - } - ], - "responses": { - "200": { - "description": "Applied", - "schema": { - "type": "string" - } - } - } - }, - "delete": { - "description": "Cancel join request for a tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Cancel join request", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Canceled", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/topics": { + "/v1/t/{tenantCode}/topics": { "get": { "description": "List curated topics", "consumes": [ @@ -6546,7 +4983,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/upload": { + "/v1/t/{tenantCode}/upload": { "post": { "description": "Upload file", "consumes": [ @@ -6584,7 +5021,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/upload/check": { + "/v1/t/{tenantCode}/upload/check": { "get": { "description": "Check if file hash exists", "consumes": [ @@ -6616,7 +5053,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/upload/complete": { + "/v1/t/{tenantCode}/upload/complete": { "post": { "description": "Complete multipart upload", "consumes": [ @@ -6650,7 +5087,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/upload/init": { + "/v1/t/{tenantCode}/upload/init": { "post": { "description": "Initialize multipart upload", "consumes": [ @@ -6684,7 +5121,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/upload/part": { + "/v1/t/{tenantCode}/upload/part": { "post": { "description": "Upload a part", "consumes": [ @@ -6728,7 +5165,7 @@ const docTemplate = `{ } } }, - "/t/{tenantCode}/v1/upload/{uploadId}": { + "/v1/t/{tenantCode}/upload/{uploadId}": { "delete": { "description": "Abort multipart upload", "consumes": [ @@ -6759,40 +5196,6 @@ const docTemplate = `{ } } } - }, - "/t/{tenantCode}/v1/webhook/payment/notify": { - "post": { - "description": "Payment Webhook", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Transaction" - ], - "summary": "Payment Webhook", - "parameters": [ - { - "description": "Webhook Data", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v1.WebhookForm" - } - } - ], - "responses": { - "200": { - "description": "success", - "schema": { - "type": "string" - } - } - } - } } }, "definitions": { @@ -6973,32 +5376,6 @@ const docTemplate = `{ "RoleCreator" ] }, - "consts.TenantInviteStatus": { - "type": "string", - "enum": [ - "active", - "disabled", - "expired" - ], - "x-enum-varnames": [ - "TenantInviteStatusActive", - "TenantInviteStatusDisabled", - "TenantInviteStatusExpired" - ] - }, - "consts.TenantJoinRequestStatus": { - "type": "string", - "enum": [ - "pending", - "approved", - "rejected" - ], - "x-enum-varnames": [ - "TenantJoinRequestStatusPending", - "TenantJoinRequestStatusApproved", - "TenantJoinRequestStatusRejected" - ] - }, "consts.TenantLedgerType": { "type": "string", "enum": [ @@ -7156,56 +5533,6 @@ const docTemplate = `{ } } }, - "dto.ApplyForm": { - "type": "object", - "properties": { - "avatar": { - "description": "Avatar 头像URL。", - "type": "string" - }, - "bio": { - "description": "Bio 频道简介。", - "type": "string" - }, - "name": { - "description": "Name 频道/创作者名称。", - "type": "string" - } - } - }, - "dto.AssetDTO": { - "type": "object", - "properties": { - "id": { - "description": "ID 资源ID。", - "type": "integer" - }, - "name": { - "description": "Name 文件名。", - "type": "string" - }, - "role": { - "description": "Role 资源角色(cover/media/preview)。", - "type": "string" - }, - "size": { - "description": "Size 文件大小描述。", - "type": "string" - }, - "sort": { - "description": "Sort 排序权重。", - "type": "integer" - }, - "type": { - "description": "Type 资源类型(image/audio/video)。", - "type": "string" - }, - "url": { - "description": "URL 资源访问地址。", - "type": "string" - } - } - }, "dto.Comment": { "type": "object", "properties": { @@ -7260,45 +5587,6 @@ const docTemplate = `{ } } }, - "dto.ContentCreateForm": { - "type": "object", - "properties": { - "cover_ids": { - "description": "CoverIDs 封面资源ID集合。", - "type": "array", - "items": { - "type": "integer" - } - }, - "genre": { - "description": "Genre 内容分类/风格。", - "type": "string" - }, - "key": { - "description": "Key 音乐调性或主音。", - "type": "string" - }, - "media_ids": { - "description": "MediaIDs 媒体资源ID集合(音频/视频/图片)。", - "type": "array", - "items": { - "type": "integer" - } - }, - "price": { - "description": "Price 价格(单位元)。", - "type": "number" - }, - "status": { - "description": "Status 内容状态(draft/published)。", - "type": "string" - }, - "title": { - "description": "Title 内容标题。", - "type": "string" - } - } - }, "dto.ContentDetail": { "type": "object", "properties": { @@ -7411,54 +5699,6 @@ const docTemplate = `{ } } }, - "dto.ContentEditDTO": { - "type": "object", - "properties": { - "assets": { - "description": "Assets 资源列表(封面/媒体)。", - "type": "array", - "items": { - "$ref": "#/definitions/dto.AssetDTO" - } - }, - "description": { - "description": "Description 内容简介。", - "type": "string" - }, - "enable_trial": { - "description": "EnableTrial 是否开启试读/试听。", - "type": "boolean" - }, - "genre": { - "description": "Genre 内容分类。", - "type": "string" - }, - "id": { - "description": "ID 内容ID。", - "type": "integer" - }, - "key": { - "description": "Key 音乐调性或主音。", - "type": "string" - }, - "preview_seconds": { - "description": "PreviewSeconds 试看/试听秒数。", - "type": "integer" - }, - "price": { - "description": "Price 价格(单位元)。", - "type": "number" - }, - "status": { - "description": "Status 内容状态。", - "type": "string" - }, - "title": { - "description": "Title 内容标题。", - "type": "string" - } - } - }, "dto.ContentItem": { "type": "object", "properties": { @@ -7569,49 +5809,6 @@ const docTemplate = `{ } } }, - "dto.ContentUpdateForm": { - "type": "object", - "properties": { - "cover_ids": { - "description": "CoverIDs 封面资源ID集合。", - "type": "array", - "items": { - "type": "integer" - } - }, - "genre": { - "description": "Genre 内容分类/风格。", - "type": "string" - }, - "is_pinned": { - "description": "IsPinned 是否置顶。", - "type": "boolean" - }, - "key": { - "description": "Key 音乐调性或主音。", - "type": "string" - }, - "media_ids": { - "description": "MediaIDs 媒体资源ID集合。", - "type": "array", - "items": { - "type": "integer" - } - }, - "price": { - "description": "Price 价格(单位元,nil 表示不修改)。", - "type": "number" - }, - "status": { - "description": "Status 内容状态(draft/published)。", - "type": "string" - }, - "title": { - "description": "Title 内容标题(为空表示不修改)。", - "type": "string" - } - } - }, "dto.CouponCreateForm": { "type": "object", "properties": { @@ -7772,61 +5969,6 @@ const docTemplate = `{ } } }, - "dto.DashboardStats": { - "type": "object", - "properties": { - "new_messages": { - "description": "NewMessages 新消息数量。", - "type": "integer" - }, - "pending_refunds": { - "description": "PendingRefunds 待处理退款数量。", - "type": "integer" - }, - "total_followers": { - "description": "TotalFollowers 粉丝总数统计。", - "allOf": [ - { - "$ref": "#/definitions/dto.IntStatItem" - } - ] - }, - "total_revenue": { - "description": "TotalRevenue 累计收入统计(单位元)。", - "allOf": [ - { - "$ref": "#/definitions/dto.FloatStatItem" - } - ] - } - } - }, - "dto.FloatStatItem": { - "type": "object", - "properties": { - "trend": { - "description": "Trend 环比/同比变化比例。", - "type": "number" - }, - "value": { - "description": "Value 统计数值(浮点)。", - "type": "number" - } - } - }, - "dto.IntStatItem": { - "type": "object", - "properties": { - "trend": { - "description": "Trend 环比/同比变化比例。", - "type": "number" - }, - "value": { - "description": "Value 统计数值。", - "type": "integer" - } - } - }, "dto.MediaURL": { "type": "object", "properties": { @@ -7998,58 +6140,6 @@ const docTemplate = `{ } } }, - "dto.OrderCreateForm": { - "type": "object", - "properties": { - "content_id": { - "description": "ContentID 内容ID。", - "type": "integer" - }, - "idempotency_key": { - "description": "IdempotencyKey 幂等键(同一业务请求需保持一致)。", - "type": "string" - }, - "quantity": { - "description": "Quantity 购买数量(默认 1)。", - "type": "integer" - }, - "sku": { - "description": "Sku 规格标识(可选)。", - "type": "string" - }, - "user_coupon_id": { - "description": "UserCouponID 用户券ID(可选)。", - "type": "integer" - } - } - }, - "dto.OrderCreateResponse": { - "type": "object", - "properties": { - "order_id": { - "description": "OrderID 创建成功的订单ID。", - "type": "integer" - } - } - }, - "dto.OrderPayForm": { - "type": "object", - "properties": { - "method": { - "description": "Method 支付方式(alipay/balance)。", - "type": "string" - } - } - }, - "dto.OrderPayResponse": { - "type": "object", - "properties": { - "pay_params": { - "description": "PayParams 支付参数(透传给前端)。", - "type": "string" - } - } - }, "dto.OrderStatisticsResponse": { "type": "object", "properties": { @@ -8095,15 +6185,6 @@ const docTemplate = `{ } } }, - "dto.OrderStatusResponse": { - "type": "object", - "properties": { - "status": { - "description": "Status 订单状态(unpaid/paid/completed 等)。", - "type": "string" - } - } - }, "dto.OrderTenantLite": { "type": "object", "properties": { @@ -8121,48 +6202,16 @@ const docTemplate = `{ } } }, - "dto.PayoutAccount": { + "dto.PaymentWebhookForm": { "type": "object", "properties": { - "account": { - "description": "Account 收款账号。", + "external_id": { + "description": "ExternalID 第三方支付流水号。", "type": "string" }, - "id": { - "description": "ID 收款账户ID。", + "order_id": { + "description": "OrderID 订单ID。", "type": "integer" - }, - "name": { - "description": "Name 账户名称/开户行。", - "type": "string" - }, - "realname": { - "description": "Realname 收款人姓名。", - "type": "string" - }, - "review_reason": { - "description": "ReviewReason 审核说明/驳回原因。", - "type": "string" - }, - "reviewed_at": { - "description": "ReviewedAt 审核时间(RFC3339)。", - "type": "string" - }, - "status": { - "description": "Status 审核状态(pending/approved/rejected)。", - "allOf": [ - { - "$ref": "#/definitions/consts.PayoutAccountStatus" - } - ] - }, - "status_description": { - "description": "StatusDescription 审核状态描述(用于展示)。", - "type": "string" - }, - "type": { - "description": "Type 账户类型(bank/alipay)。", - "type": "string" } } }, @@ -8205,40 +6254,6 @@ const docTemplate = `{ } } }, - "dto.RefundForm": { - "type": "object", - "properties": { - "action": { - "description": "Action 处理动作(accept/reject)。", - "type": "string" - }, - "reason": { - "description": "Reason 退款原因/备注。", - "type": "string" - } - } - }, - "dto.ReportExportForm": { - "type": "object", - "properties": { - "end_at": { - "description": "EndAt 统计结束时间(RFC3339,可选;默认当前时间)。", - "type": "string" - }, - "format": { - "description": "Format 导出格式(仅支持 csv)。", - "type": "string" - }, - "granularity": { - "description": "Granularity 统计粒度(day;目前仅支持 day)。", - "type": "string" - }, - "start_at": { - "description": "StartAt 统计开始时间(RFC3339,可选;默认当前时间往前 7 天)。", - "type": "string" - } - } - }, "dto.ReportExportResponse": { "type": "object", "properties": { @@ -10974,55 +8989,6 @@ const docTemplate = `{ } } }, - "dto.TenantInviteListItem": { - "type": "object", - "properties": { - "code": { - "description": "Code 邀请码。", - "type": "string" - }, - "created_at": { - "description": "CreatedAt 创建时间(RFC3339)。", - "type": "string" - }, - "creator": { - "description": "Creator 创建者信息(可选)。", - "allOf": [ - { - "$ref": "#/definitions/dto.TenantMemberUserLite" - } - ] - }, - "expires_at": { - "description": "ExpiresAt 过期时间(RFC3339,空字符串表示不限制)。", - "type": "string" - }, - "id": { - "description": "ID 邀请记录ID。", - "type": "integer" - }, - "max_uses": { - "description": "MaxUses 最大可使用次数。", - "type": "integer" - }, - "remark": { - "description": "Remark 备注说明。", - "type": "string" - }, - "status": { - "description": "Status 邀请状态(active/disabled/expired)。", - "type": "string" - }, - "status_description": { - "description": "StatusDescription 状态描述。", - "type": "string" - }, - "used_count": { - "description": "UsedCount 已使用次数。", - "type": "integer" - } - } - }, "dto.TenantItem": { "type": "object", "properties": { @@ -11109,64 +9075,6 @@ const docTemplate = `{ } } }, - "dto.TenantJoinApplyForm": { - "type": "object", - "properties": { - "reason": { - "description": "Reason 申请加入原因(可选,空值会使用默认文案)。", - "type": "string" - } - } - }, - "dto.TenantJoinRequestItem": { - "type": "object", - "properties": { - "created_at": { - "description": "CreatedAt 申请时间(RFC3339)。", - "type": "string" - }, - "decided_at": { - "description": "DecidedAt 审核时间(RFC3339)。", - "type": "string" - }, - "decided_operator_user_id": { - "description": "DecidedOperatorUserID 审核操作者ID。", - "type": "integer" - }, - "decided_reason": { - "description": "DecidedReason 审核备注/原因。", - "type": "string" - }, - "id": { - "description": "ID 申请记录ID。", - "type": "integer" - }, - "reason": { - "description": "Reason 申请说明。", - "type": "string" - }, - "status": { - "description": "Status 申请状态。", - "type": "string" - }, - "status_description": { - "description": "StatusDescription 状态描述。", - "type": "string" - }, - "updated_at": { - "description": "UpdatedAt 更新时间(RFC3339)。", - "type": "string" - }, - "user": { - "description": "User 申请用户信息。", - "allOf": [ - { - "$ref": "#/definitions/dto.TenantMemberUserLite" - } - ] - } - } - }, "dto.TenantJoinReviewForm": { "type": "object", "properties": { @@ -11180,86 +9088,6 @@ const docTemplate = `{ } } }, - "dto.TenantMemberItem": { - "type": "object", - "properties": { - "created_at": { - "description": "CreatedAt 加入时间(RFC3339)。", - "type": "string" - }, - "id": { - "description": "ID 成员关系记录ID。", - "type": "integer" - }, - "role": { - "description": "Role 成员角色列表。", - "type": "array", - "items": { - "$ref": "#/definitions/consts.TenantUserRole" - } - }, - "role_description": { - "description": "RoleDescription 角色描述列表。", - "type": "array", - "items": { - "type": "string" - } - }, - "status": { - "description": "Status 成员状态。", - "allOf": [ - { - "$ref": "#/definitions/consts.UserStatus" - } - ] - }, - "status_description": { - "description": "StatusDescription 成员状态描述。", - "type": "string" - }, - "tenant_id": { - "description": "TenantID 租户ID。", - "type": "integer" - }, - "updated_at": { - "description": "UpdatedAt 更新时间(RFC3339)。", - "type": "string" - }, - "user": { - "description": "User 成员用户信息。", - "allOf": [ - { - "$ref": "#/definitions/dto.TenantMemberUserLite" - } - ] - } - } - }, - "dto.TenantMemberUserLite": { - "type": "object", - "properties": { - "avatar": { - "description": "Avatar 头像URL。", - "type": "string" - }, - "id": { - "description": "ID 用户ID。", - "type": "integer" - }, - "nickname": { - "description": "Nickname 昵称。", - "type": "string" - }, - "phone": { - "description": "Phone 手机号。", - "type": "string" - }, - "username": { - "description": "Username 用户名。", - "type": "string" - } - } - }, "dto.TenantOwnerUserLite": { "type": "object", "properties": { @@ -11799,23 +9627,6 @@ const docTemplate = `{ } } }, - "dto.WithdrawForm": { - "type": "object", - "properties": { - "account_id": { - "description": "AccountID 收款账户ID。", - "type": "integer" - }, - "amount": { - "description": "Amount 提现金额(单位元)。", - "type": "number" - }, - "method": { - "description": "Method 提现方式(wallet/external)。", - "type": "string" - } - } - }, "quyun_v2_app_http_super_v1_dto.Location": { "type": "object", "properties": { @@ -12056,17 +9867,6 @@ const docTemplate = `{ "type": "integer" } } - }, - "v1.WebhookForm": { - "type": "object", - "properties": { - "external_id": { - "type": "string" - }, - "order_id": { - "type": "integer" - } - } } }, "securityDefinitions": { diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index cadc0d4..30a64d5 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -3743,7 +3743,7 @@ } } }, - "/t/{tenantCode}/v1/auth/login": { + "/v1/auth/login": { "post": { "description": "Login or register user using phone number and OTP", "consumes": [ @@ -3777,7 +3777,7 @@ } } }, - "/t/{tenantCode}/v1/auth/otp": { + "/v1/auth/otp": { "post": { "description": "Send OTP to phone number", "consumes": [ @@ -3811,7 +3811,7 @@ } } }, - "/t/{tenantCode}/v1/comments/{id}/like": { + "/v1/t/{tenantCode}/comments/{id}/like": { "post": { "description": "Like a comment", "consumes": [ @@ -3844,7 +3844,7 @@ } } }, - "/t/{tenantCode}/v1/common/options": { + "/v1/t/{tenantCode}/common/options": { "get": { "description": "Get global options (enums)", "consumes": [ @@ -3867,7 +3867,7 @@ } } }, - "/t/{tenantCode}/v1/contents": { + "/v1/t/{tenantCode}/contents": { "get": { "description": "List contents with filtering and pagination", "consumes": [ @@ -3950,7 +3950,7 @@ } } }, - "/t/{tenantCode}/v1/contents/{id}": { + "/v1/t/{tenantCode}/contents/{id}": { "get": { "description": "Get content detail by ID", "consumes": [ @@ -3983,7 +3983,7 @@ } } }, - "/t/{tenantCode}/v1/contents/{id}/comments": { + "/v1/t/{tenantCode}/contents/{id}/comments": { "get": { "description": "Get comments for a content", "consumes": [ @@ -4077,7 +4077,7 @@ } } }, - "/t/{tenantCode}/v1/contents/{id}/favorite": { + "/v1/t/{tenantCode}/contents/{id}/favorite": { "post": { "tags": [ "Content" @@ -4127,7 +4127,7 @@ } } }, - "/t/{tenantCode}/v1/contents/{id}/like": { + "/v1/t/{tenantCode}/contents/{id}/like": { "post": { "tags": [ "Content" @@ -4177,1206 +4177,7 @@ } } }, - "/t/{tenantCode}/v1/creator/apply": { - "post": { - "description": "Apply to become a creator", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Apply creator", - "parameters": [ - { - "description": "Apply form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.ApplyForm" - } - } - ], - "responses": { - "200": { - "description": "Application submitted", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/contents": { - "get": { - "description": "List creator contents", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List contents", - "parameters": [ - { - "type": "string", - "description": "Status", - "name": "status", - "in": "query" - }, - { - "type": "string", - "description": "Genre", - "name": "genre", - "in": "query" - }, - { - "type": "string", - "description": "Keyword", - "name": "keyword", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.ContentItem" - } - } - } - } - }, - "post": { - "description": "Create/Publish content", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Create content", - "parameters": [ - { - "description": "Content form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.ContentCreateForm" - } - } - ], - "responses": { - "200": { - "description": "Created", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/contents/{id}": { - "get": { - "description": "Get content details for edit", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Get content", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Content ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.ContentEditDTO" - } - } - } - }, - "put": { - "description": "Update content", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Update content", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Content ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Update form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.ContentUpdateForm" - } - } - ], - "responses": { - "200": { - "description": "Updated", - "schema": { - "type": "string" - } - } - } - }, - "delete": { - "description": "Delete content", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Delete content", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Content ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Deleted", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/coupons": { - "get": { - "description": "List coupon templates", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List coupons", - "parameters": [ - { - "type": "integer", - "description": "Page", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "Type (fix_amount/discount)", - "name": "type", - "in": "query" - }, - { - "type": "string", - "description": "Status (active/expired)", - "name": "status", - "in": "query" - }, - { - "type": "string", - "description": "Keyword", - "name": "keyword", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/requests.Pager" - } - } - } - }, - "post": { - "description": "Create coupon template", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Create coupon", - "parameters": [ - { - "description": "Coupon form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.CouponCreateForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.CouponItem" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/coupons/{id}": { - "get": { - "description": "Get coupon template detail", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Get coupon", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Coupon ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.CouponItem" - } - } - } - }, - "put": { - "description": "Update coupon template", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Update coupon", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Coupon ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Coupon form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.CouponUpdateForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.CouponItem" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/coupons/{id}/grant": { - "post": { - "description": "Grant coupon to users", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Grant coupon", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Coupon ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Grant form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.CouponGrantForm" - } - } - ], - "responses": { - "200": { - "description": "Granted", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/dashboard": { - "get": { - "description": "Get creator dashboard stats", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Dashboard stats", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.DashboardStats" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members": { - "get": { - "description": "List tenant members with filters", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List tenant members", - "parameters": [ - { - "type": "string", - "description": "Keyword 关键词搜索(匹配用户名/昵称/手机号)。", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "Limit is page size; only values in {10,20,50,100} are accepted (otherwise defaults to 10).", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Page is 1-based page index; values \u003c= 0 are normalized to 1.", - "name": "page", - "in": "query" - }, - { - "enum": [ - "member", - "tenant_admin" - ], - "type": "string", - "x-enum-varnames": [ - "TenantUserRoleMember", - "TenantUserRoleTenantAdmin" - ], - "description": "Role 成员角色筛选(member/tenant_admin)。", - "name": "role", - "in": "query" - }, - { - "enum": [ - "active", - "inactive", - "pending_verify", - "verified", - "banned" - ], - "type": "string", - "x-enum-varnames": [ - "UserStatusActive", - "UserStatusInactive", - "UserStatusPendingVerify", - "UserStatusVerified", - "UserStatusBanned" - ], - "description": "Status 成员状态筛选(active/verified/banned 等)。", - "name": "status", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/requests.Pager" - }, - { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.TenantMemberItem" - } - } - } - } - ] - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/invite": { - "post": { - "description": "Create an invite for tenant members", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Create member invite", - "parameters": [ - { - "description": "Invite form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.TenantInviteCreateForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.TenantInviteItem" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/invites": { - "get": { - "description": "List member invites with filters", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List member invites", - "parameters": [ - { - "type": "integer", - "description": "Limit is page size; only values in {10,20,50,100} are accepted (otherwise defaults to 10).", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Page is 1-based page index; values \u003c= 0 are normalized to 1.", - "name": "page", - "in": "query" - }, - { - "enum": [ - "active", - "disabled", - "expired" - ], - "type": "string", - "x-enum-varnames": [ - "TenantInviteStatusActive", - "TenantInviteStatusDisabled", - "TenantInviteStatusExpired" - ], - "description": "Status 邀请状态筛选(active/disabled/expired)。", - "name": "status", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/requests.Pager" - }, - { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.TenantInviteListItem" - } - } - } - } - ] - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/invites/{id}": { - "delete": { - "description": "Disable a member invite by ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Disable member invite", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Invite ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Disabled", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/join-requests": { - "get": { - "description": "List tenant join requests", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List member join requests", - "parameters": [ - { - "type": "string", - "description": "Keyword 关键词搜索(匹配用户名/昵称/手机号)。", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "Limit is page size; only values in {10,20,50,100} are accepted (otherwise defaults to 10).", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Page is 1-based page index; values \u003c= 0 are normalized to 1.", - "name": "page", - "in": "query" - }, - { - "enum": [ - "pending", - "approved", - "rejected" - ], - "type": "string", - "x-enum-varnames": [ - "TenantJoinRequestStatusPending", - "TenantJoinRequestStatusApproved", - "TenantJoinRequestStatusRejected" - ], - "description": "Status 申请状态筛选(pending/approved/rejected)。", - "name": "status", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/requests.Pager" - }, - { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.TenantJoinRequestItem" - } - } - } - } - ] - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/{id}": { - "delete": { - "description": "Remove a tenant member by relation ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Remove tenant member", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Member ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Removed", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/members/{id}/review": { - "post": { - "description": "Approve or reject a tenant join request", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Review join request", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Join request ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Review form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.TenantJoinReviewForm" - } - } - ], - "responses": { - "200": { - "description": "Reviewed", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/orders": { - "get": { - "description": "List sales orders", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List sales orders", - "parameters": [ - { - "type": "string", - "description": "Status", - "name": "status", - "in": "query" - }, - { - "type": "string", - "description": "Keyword", - "name": "keyword", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.Order" - } - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/orders/{id}/refund": { - "post": { - "description": "Process refund", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Process refund", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Order ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Refund form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.RefundForm" - } - } - ], - "responses": { - "200": { - "description": "Processed", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/payout-accounts": { - "get": { - "description": "List payout accounts", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "List payout accounts", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/dto.PayoutAccount" - } - } - } - } - }, - "post": { - "description": "Add payout account", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Add payout account", - "parameters": [ - { - "description": "Account form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.PayoutAccount" - } - } - ], - "responses": { - "200": { - "description": "Added", - "schema": { - "type": "string" - } - } - } - }, - "delete": { - "description": "Remove payout account", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Remove payout account", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Account ID", - "name": "id", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "Removed", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/reports/export": { - "post": { - "description": "Export creator report overview", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Export report overview", - "parameters": [ - { - "description": "Export form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.ReportExportForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.ReportExportResponse" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/reports/overview": { - "get": { - "description": "Get creator report overview", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Report overview", - "parameters": [ - { - "type": "string", - "description": "Start time (RFC3339)", - "name": "start_at", - "in": "query" - }, - { - "type": "string", - "description": "End time (RFC3339)", - "name": "end_at", - "in": "query" - }, - { - "type": "string", - "description": "Granularity (day)", - "name": "granularity", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.ReportOverviewResponse" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/settings": { - "get": { - "description": "Get channel settings", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Get settings", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.Settings" - } - } - } - }, - "put": { - "description": "Update channel settings", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Update settings", - "parameters": [ - { - "description": "Settings form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.Settings" - } - } - ], - "responses": { - "200": { - "description": "Updated", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creator/withdraw": { - "post": { - "description": "Request withdrawal", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "CreatorCenter" - ], - "summary": "Request withdrawal", - "parameters": [ - { - "description": "Withdraw form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.WithdrawForm" - } - } - ], - "responses": { - "200": { - "description": "Withdrawal requested", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/creators/{id}/contents": { - "get": { - "description": "List contents of a specific creator", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "List creator contents", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Creator User ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Page", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/requests.Pager" - } - } - } - } - }, - "/t/{tenantCode}/v1/me": { + "/v1/t/{tenantCode}/me": { "get": { "description": "Get current user profile", "consumes": [ @@ -5431,7 +4232,7 @@ } } }, - "/t/{tenantCode}/v1/me/coupons": { + "/v1/t/{tenantCode}/me/coupons": { "get": { "description": "List my coupons", "consumes": [ @@ -5465,7 +4266,7 @@ } } }, - "/t/{tenantCode}/v1/me/coupons/available": { + "/v1/t/{tenantCode}/me/coupons/available": { "get": { "description": "List coupons available for the given order amount", "consumes": [ @@ -5501,7 +4302,7 @@ } } }, - "/t/{tenantCode}/v1/me/coupons/receive": { + "/v1/t/{tenantCode}/me/coupons/receive": { "post": { "description": "Receive a coupon by coupon_id", "consumes": [ @@ -5535,7 +4336,7 @@ } } }, - "/t/{tenantCode}/v1/me/favorites": { + "/v1/t/{tenantCode}/me/favorites": { "get": { "description": "Get favorites", "consumes": [ @@ -5592,7 +4393,7 @@ } } }, - "/t/{tenantCode}/v1/me/favorites/{contentId}": { + "/v1/t/{tenantCode}/me/favorites/{contentId}": { "delete": { "description": "Remove from favorites", "consumes": [ @@ -5625,7 +4426,7 @@ } } }, - "/t/{tenantCode}/v1/me/following": { + "/v1/t/{tenantCode}/me/following": { "get": { "description": "Get following tenants", "consumes": [ @@ -5651,7 +4452,7 @@ } } }, - "/t/{tenantCode}/v1/me/library": { + "/v1/t/{tenantCode}/me/library": { "get": { "description": "Get purchased content", "consumes": [ @@ -5677,7 +4478,7 @@ } } }, - "/t/{tenantCode}/v1/me/likes": { + "/v1/t/{tenantCode}/me/likes": { "get": { "description": "Get liked contents", "consumes": [ @@ -5734,7 +4535,7 @@ } } }, - "/t/{tenantCode}/v1/me/likes/{contentId}": { + "/v1/t/{tenantCode}/me/likes/{contentId}": { "delete": { "description": "Unlike content", "consumes": [ @@ -5767,7 +4568,7 @@ } } }, - "/t/{tenantCode}/v1/me/notifications": { + "/v1/t/{tenantCode}/me/notifications": { "get": { "description": "Get notifications", "consumes": [ @@ -5819,7 +4620,7 @@ } } }, - "/t/{tenantCode}/v1/me/notifications/read-all": { + "/v1/t/{tenantCode}/me/notifications/read-all": { "post": { "consumes": [ "application/json" @@ -5841,7 +4642,7 @@ } } }, - "/t/{tenantCode}/v1/me/notifications/{id}/read": { + "/v1/t/{tenantCode}/me/notifications/{id}/read": { "post": { "consumes": [ "application/json" @@ -5873,7 +4674,7 @@ } } }, - "/t/{tenantCode}/v1/me/orders": { + "/v1/t/{tenantCode}/me/orders": { "get": { "description": "List user orders", "consumes": [ @@ -5907,7 +4708,7 @@ } } }, - "/t/{tenantCode}/v1/me/orders/{id}": { + "/v1/t/{tenantCode}/me/orders/{id}": { "get": { "description": "Get user order detail", "consumes": [ @@ -5940,7 +4741,7 @@ } } }, - "/t/{tenantCode}/v1/me/realname": { + "/v1/t/{tenantCode}/me/realname": { "post": { "description": "Submit real-name authentication", "consumes": [ @@ -5974,7 +4775,7 @@ } } }, - "/t/{tenantCode}/v1/me/wallet": { + "/v1/t/{tenantCode}/me/wallet": { "get": { "description": "Get wallet balance and transactions", "consumes": [ @@ -5997,7 +4798,7 @@ } } }, - "/t/{tenantCode}/v1/me/wallet/recharge": { + "/v1/t/{tenantCode}/me/wallet/recharge": { "post": { "description": "Recharge wallet", "consumes": [ @@ -6031,7 +4832,7 @@ } } }, - "/t/{tenantCode}/v1/media-assets/{id}": { + "/v1/t/{tenantCode}/media-assets/{id}": { "delete": { "description": "Delete media asset", "consumes": [ @@ -6064,116 +4865,7 @@ } } }, - "/t/{tenantCode}/v1/orders": { - "post": { - "description": "Create Order", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Transaction" - ], - "summary": "Create Order", - "parameters": [ - { - "description": "Create form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.OrderCreateForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.OrderCreateResponse" - } - } - } - } - }, - "/t/{tenantCode}/v1/orders/{id}/pay": { - "post": { - "description": "Pay for order", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Transaction" - ], - "summary": "Pay for order", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Order ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Pay form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.OrderPayForm" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.OrderPayResponse" - } - } - } - } - }, - "/t/{tenantCode}/v1/orders/{id}/status": { - "get": { - "description": "Check order payment status", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Transaction" - ], - "summary": "Check order status", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Order ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.OrderStatusResponse" - } - } - } - } - }, - "/t/{tenantCode}/v1/storage/{any}": { + "/v1/t/{tenantCode}/storage/{any}": { "get": { "consumes": [ "application/json" @@ -6259,262 +4951,7 @@ } } }, - "/t/{tenantCode}/v1/tenants": { - "get": { - "description": "Search tenants", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "List tenants", - "parameters": [ - { - "type": "string", - "description": "Keyword", - "name": "keyword", - "in": "query" - }, - { - "type": "integer", - "description": "Page", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/requests.Pager" - } - } - } - } - }, - "/t/{tenantCode}/v1/tenants/{id}": { - "get": { - "description": "Get tenant public profile", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Get tenant profile", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dto.TenantProfile" - } - } - } - } - }, - "/t/{tenantCode}/v1/tenants/{id}/follow": { - "post": { - "description": "Follow a tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Follow tenant", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Followed", - "schema": { - "type": "string" - } - } - } - }, - "delete": { - "description": "Unfollow a tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Unfollow tenant", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Unfollowed", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/tenants/{id}/invites/accept": { - "post": { - "description": "Accept a tenant invite by code", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Accept tenant invite", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Invite form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.TenantInviteAcceptForm" - } - } - ], - "responses": { - "200": { - "description": "Accepted", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/tenants/{id}/join": { - "post": { - "description": "Submit join request for a tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Apply to join tenant", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Join form", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/dto.TenantJoinApplyForm" - } - } - ], - "responses": { - "200": { - "description": "Applied", - "schema": { - "type": "string" - } - } - } - }, - "delete": { - "description": "Cancel join request for a tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TenantPublic" - ], - "summary": "Cancel join request", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Tenant ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Canceled", - "schema": { - "type": "string" - } - } - } - } - }, - "/t/{tenantCode}/v1/topics": { + "/v1/t/{tenantCode}/topics": { "get": { "description": "List curated topics", "consumes": [ @@ -6540,7 +4977,7 @@ } } }, - "/t/{tenantCode}/v1/upload": { + "/v1/t/{tenantCode}/upload": { "post": { "description": "Upload file", "consumes": [ @@ -6578,7 +5015,7 @@ } } }, - "/t/{tenantCode}/v1/upload/check": { + "/v1/t/{tenantCode}/upload/check": { "get": { "description": "Check if file hash exists", "consumes": [ @@ -6610,7 +5047,7 @@ } } }, - "/t/{tenantCode}/v1/upload/complete": { + "/v1/t/{tenantCode}/upload/complete": { "post": { "description": "Complete multipart upload", "consumes": [ @@ -6644,7 +5081,7 @@ } } }, - "/t/{tenantCode}/v1/upload/init": { + "/v1/t/{tenantCode}/upload/init": { "post": { "description": "Initialize multipart upload", "consumes": [ @@ -6678,7 +5115,7 @@ } } }, - "/t/{tenantCode}/v1/upload/part": { + "/v1/t/{tenantCode}/upload/part": { "post": { "description": "Upload a part", "consumes": [ @@ -6722,7 +5159,7 @@ } } }, - "/t/{tenantCode}/v1/upload/{uploadId}": { + "/v1/t/{tenantCode}/upload/{uploadId}": { "delete": { "description": "Abort multipart upload", "consumes": [ @@ -6753,40 +5190,6 @@ } } } - }, - "/t/{tenantCode}/v1/webhook/payment/notify": { - "post": { - "description": "Payment Webhook", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Transaction" - ], - "summary": "Payment Webhook", - "parameters": [ - { - "description": "Webhook Data", - "name": "form", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v1.WebhookForm" - } - } - ], - "responses": { - "200": { - "description": "success", - "schema": { - "type": "string" - } - } - } - } } }, "definitions": { @@ -6967,32 +5370,6 @@ "RoleCreator" ] }, - "consts.TenantInviteStatus": { - "type": "string", - "enum": [ - "active", - "disabled", - "expired" - ], - "x-enum-varnames": [ - "TenantInviteStatusActive", - "TenantInviteStatusDisabled", - "TenantInviteStatusExpired" - ] - }, - "consts.TenantJoinRequestStatus": { - "type": "string", - "enum": [ - "pending", - "approved", - "rejected" - ], - "x-enum-varnames": [ - "TenantJoinRequestStatusPending", - "TenantJoinRequestStatusApproved", - "TenantJoinRequestStatusRejected" - ] - }, "consts.TenantLedgerType": { "type": "string", "enum": [ @@ -7150,56 +5527,6 @@ } } }, - "dto.ApplyForm": { - "type": "object", - "properties": { - "avatar": { - "description": "Avatar 头像URL。", - "type": "string" - }, - "bio": { - "description": "Bio 频道简介。", - "type": "string" - }, - "name": { - "description": "Name 频道/创作者名称。", - "type": "string" - } - } - }, - "dto.AssetDTO": { - "type": "object", - "properties": { - "id": { - "description": "ID 资源ID。", - "type": "integer" - }, - "name": { - "description": "Name 文件名。", - "type": "string" - }, - "role": { - "description": "Role 资源角色(cover/media/preview)。", - "type": "string" - }, - "size": { - "description": "Size 文件大小描述。", - "type": "string" - }, - "sort": { - "description": "Sort 排序权重。", - "type": "integer" - }, - "type": { - "description": "Type 资源类型(image/audio/video)。", - "type": "string" - }, - "url": { - "description": "URL 资源访问地址。", - "type": "string" - } - } - }, "dto.Comment": { "type": "object", "properties": { @@ -7254,45 +5581,6 @@ } } }, - "dto.ContentCreateForm": { - "type": "object", - "properties": { - "cover_ids": { - "description": "CoverIDs 封面资源ID集合。", - "type": "array", - "items": { - "type": "integer" - } - }, - "genre": { - "description": "Genre 内容分类/风格。", - "type": "string" - }, - "key": { - "description": "Key 音乐调性或主音。", - "type": "string" - }, - "media_ids": { - "description": "MediaIDs 媒体资源ID集合(音频/视频/图片)。", - "type": "array", - "items": { - "type": "integer" - } - }, - "price": { - "description": "Price 价格(单位元)。", - "type": "number" - }, - "status": { - "description": "Status 内容状态(draft/published)。", - "type": "string" - }, - "title": { - "description": "Title 内容标题。", - "type": "string" - } - } - }, "dto.ContentDetail": { "type": "object", "properties": { @@ -7405,54 +5693,6 @@ } } }, - "dto.ContentEditDTO": { - "type": "object", - "properties": { - "assets": { - "description": "Assets 资源列表(封面/媒体)。", - "type": "array", - "items": { - "$ref": "#/definitions/dto.AssetDTO" - } - }, - "description": { - "description": "Description 内容简介。", - "type": "string" - }, - "enable_trial": { - "description": "EnableTrial 是否开启试读/试听。", - "type": "boolean" - }, - "genre": { - "description": "Genre 内容分类。", - "type": "string" - }, - "id": { - "description": "ID 内容ID。", - "type": "integer" - }, - "key": { - "description": "Key 音乐调性或主音。", - "type": "string" - }, - "preview_seconds": { - "description": "PreviewSeconds 试看/试听秒数。", - "type": "integer" - }, - "price": { - "description": "Price 价格(单位元)。", - "type": "number" - }, - "status": { - "description": "Status 内容状态。", - "type": "string" - }, - "title": { - "description": "Title 内容标题。", - "type": "string" - } - } - }, "dto.ContentItem": { "type": "object", "properties": { @@ -7563,49 +5803,6 @@ } } }, - "dto.ContentUpdateForm": { - "type": "object", - "properties": { - "cover_ids": { - "description": "CoverIDs 封面资源ID集合。", - "type": "array", - "items": { - "type": "integer" - } - }, - "genre": { - "description": "Genre 内容分类/风格。", - "type": "string" - }, - "is_pinned": { - "description": "IsPinned 是否置顶。", - "type": "boolean" - }, - "key": { - "description": "Key 音乐调性或主音。", - "type": "string" - }, - "media_ids": { - "description": "MediaIDs 媒体资源ID集合。", - "type": "array", - "items": { - "type": "integer" - } - }, - "price": { - "description": "Price 价格(单位元,nil 表示不修改)。", - "type": "number" - }, - "status": { - "description": "Status 内容状态(draft/published)。", - "type": "string" - }, - "title": { - "description": "Title 内容标题(为空表示不修改)。", - "type": "string" - } - } - }, "dto.CouponCreateForm": { "type": "object", "properties": { @@ -7766,61 +5963,6 @@ } } }, - "dto.DashboardStats": { - "type": "object", - "properties": { - "new_messages": { - "description": "NewMessages 新消息数量。", - "type": "integer" - }, - "pending_refunds": { - "description": "PendingRefunds 待处理退款数量。", - "type": "integer" - }, - "total_followers": { - "description": "TotalFollowers 粉丝总数统计。", - "allOf": [ - { - "$ref": "#/definitions/dto.IntStatItem" - } - ] - }, - "total_revenue": { - "description": "TotalRevenue 累计收入统计(单位元)。", - "allOf": [ - { - "$ref": "#/definitions/dto.FloatStatItem" - } - ] - } - } - }, - "dto.FloatStatItem": { - "type": "object", - "properties": { - "trend": { - "description": "Trend 环比/同比变化比例。", - "type": "number" - }, - "value": { - "description": "Value 统计数值(浮点)。", - "type": "number" - } - } - }, - "dto.IntStatItem": { - "type": "object", - "properties": { - "trend": { - "description": "Trend 环比/同比变化比例。", - "type": "number" - }, - "value": { - "description": "Value 统计数值。", - "type": "integer" - } - } - }, "dto.MediaURL": { "type": "object", "properties": { @@ -7992,58 +6134,6 @@ } } }, - "dto.OrderCreateForm": { - "type": "object", - "properties": { - "content_id": { - "description": "ContentID 内容ID。", - "type": "integer" - }, - "idempotency_key": { - "description": "IdempotencyKey 幂等键(同一业务请求需保持一致)。", - "type": "string" - }, - "quantity": { - "description": "Quantity 购买数量(默认 1)。", - "type": "integer" - }, - "sku": { - "description": "Sku 规格标识(可选)。", - "type": "string" - }, - "user_coupon_id": { - "description": "UserCouponID 用户券ID(可选)。", - "type": "integer" - } - } - }, - "dto.OrderCreateResponse": { - "type": "object", - "properties": { - "order_id": { - "description": "OrderID 创建成功的订单ID。", - "type": "integer" - } - } - }, - "dto.OrderPayForm": { - "type": "object", - "properties": { - "method": { - "description": "Method 支付方式(alipay/balance)。", - "type": "string" - } - } - }, - "dto.OrderPayResponse": { - "type": "object", - "properties": { - "pay_params": { - "description": "PayParams 支付参数(透传给前端)。", - "type": "string" - } - } - }, "dto.OrderStatisticsResponse": { "type": "object", "properties": { @@ -8089,15 +6179,6 @@ } } }, - "dto.OrderStatusResponse": { - "type": "object", - "properties": { - "status": { - "description": "Status 订单状态(unpaid/paid/completed 等)。", - "type": "string" - } - } - }, "dto.OrderTenantLite": { "type": "object", "properties": { @@ -8115,48 +6196,16 @@ } } }, - "dto.PayoutAccount": { + "dto.PaymentWebhookForm": { "type": "object", "properties": { - "account": { - "description": "Account 收款账号。", + "external_id": { + "description": "ExternalID 第三方支付流水号。", "type": "string" }, - "id": { - "description": "ID 收款账户ID。", + "order_id": { + "description": "OrderID 订单ID。", "type": "integer" - }, - "name": { - "description": "Name 账户名称/开户行。", - "type": "string" - }, - "realname": { - "description": "Realname 收款人姓名。", - "type": "string" - }, - "review_reason": { - "description": "ReviewReason 审核说明/驳回原因。", - "type": "string" - }, - "reviewed_at": { - "description": "ReviewedAt 审核时间(RFC3339)。", - "type": "string" - }, - "status": { - "description": "Status 审核状态(pending/approved/rejected)。", - "allOf": [ - { - "$ref": "#/definitions/consts.PayoutAccountStatus" - } - ] - }, - "status_description": { - "description": "StatusDescription 审核状态描述(用于展示)。", - "type": "string" - }, - "type": { - "description": "Type 账户类型(bank/alipay)。", - "type": "string" } } }, @@ -8199,40 +6248,6 @@ } } }, - "dto.RefundForm": { - "type": "object", - "properties": { - "action": { - "description": "Action 处理动作(accept/reject)。", - "type": "string" - }, - "reason": { - "description": "Reason 退款原因/备注。", - "type": "string" - } - } - }, - "dto.ReportExportForm": { - "type": "object", - "properties": { - "end_at": { - "description": "EndAt 统计结束时间(RFC3339,可选;默认当前时间)。", - "type": "string" - }, - "format": { - "description": "Format 导出格式(仅支持 csv)。", - "type": "string" - }, - "granularity": { - "description": "Granularity 统计粒度(day;目前仅支持 day)。", - "type": "string" - }, - "start_at": { - "description": "StartAt 统计开始时间(RFC3339,可选;默认当前时间往前 7 天)。", - "type": "string" - } - } - }, "dto.ReportExportResponse": { "type": "object", "properties": { @@ -10968,55 +8983,6 @@ } } }, - "dto.TenantInviteListItem": { - "type": "object", - "properties": { - "code": { - "description": "Code 邀请码。", - "type": "string" - }, - "created_at": { - "description": "CreatedAt 创建时间(RFC3339)。", - "type": "string" - }, - "creator": { - "description": "Creator 创建者信息(可选)。", - "allOf": [ - { - "$ref": "#/definitions/dto.TenantMemberUserLite" - } - ] - }, - "expires_at": { - "description": "ExpiresAt 过期时间(RFC3339,空字符串表示不限制)。", - "type": "string" - }, - "id": { - "description": "ID 邀请记录ID。", - "type": "integer" - }, - "max_uses": { - "description": "MaxUses 最大可使用次数。", - "type": "integer" - }, - "remark": { - "description": "Remark 备注说明。", - "type": "string" - }, - "status": { - "description": "Status 邀请状态(active/disabled/expired)。", - "type": "string" - }, - "status_description": { - "description": "StatusDescription 状态描述。", - "type": "string" - }, - "used_count": { - "description": "UsedCount 已使用次数。", - "type": "integer" - } - } - }, "dto.TenantItem": { "type": "object", "properties": { @@ -11103,64 +9069,6 @@ } } }, - "dto.TenantJoinApplyForm": { - "type": "object", - "properties": { - "reason": { - "description": "Reason 申请加入原因(可选,空值会使用默认文案)。", - "type": "string" - } - } - }, - "dto.TenantJoinRequestItem": { - "type": "object", - "properties": { - "created_at": { - "description": "CreatedAt 申请时间(RFC3339)。", - "type": "string" - }, - "decided_at": { - "description": "DecidedAt 审核时间(RFC3339)。", - "type": "string" - }, - "decided_operator_user_id": { - "description": "DecidedOperatorUserID 审核操作者ID。", - "type": "integer" - }, - "decided_reason": { - "description": "DecidedReason 审核备注/原因。", - "type": "string" - }, - "id": { - "description": "ID 申请记录ID。", - "type": "integer" - }, - "reason": { - "description": "Reason 申请说明。", - "type": "string" - }, - "status": { - "description": "Status 申请状态。", - "type": "string" - }, - "status_description": { - "description": "StatusDescription 状态描述。", - "type": "string" - }, - "updated_at": { - "description": "UpdatedAt 更新时间(RFC3339)。", - "type": "string" - }, - "user": { - "description": "User 申请用户信息。", - "allOf": [ - { - "$ref": "#/definitions/dto.TenantMemberUserLite" - } - ] - } - } - }, "dto.TenantJoinReviewForm": { "type": "object", "properties": { @@ -11174,86 +9082,6 @@ } } }, - "dto.TenantMemberItem": { - "type": "object", - "properties": { - "created_at": { - "description": "CreatedAt 加入时间(RFC3339)。", - "type": "string" - }, - "id": { - "description": "ID 成员关系记录ID。", - "type": "integer" - }, - "role": { - "description": "Role 成员角色列表。", - "type": "array", - "items": { - "$ref": "#/definitions/consts.TenantUserRole" - } - }, - "role_description": { - "description": "RoleDescription 角色描述列表。", - "type": "array", - "items": { - "type": "string" - } - }, - "status": { - "description": "Status 成员状态。", - "allOf": [ - { - "$ref": "#/definitions/consts.UserStatus" - } - ] - }, - "status_description": { - "description": "StatusDescription 成员状态描述。", - "type": "string" - }, - "tenant_id": { - "description": "TenantID 租户ID。", - "type": "integer" - }, - "updated_at": { - "description": "UpdatedAt 更新时间(RFC3339)。", - "type": "string" - }, - "user": { - "description": "User 成员用户信息。", - "allOf": [ - { - "$ref": "#/definitions/dto.TenantMemberUserLite" - } - ] - } - } - }, - "dto.TenantMemberUserLite": { - "type": "object", - "properties": { - "avatar": { - "description": "Avatar 头像URL。", - "type": "string" - }, - "id": { - "description": "ID 用户ID。", - "type": "integer" - }, - "nickname": { - "description": "Nickname 昵称。", - "type": "string" - }, - "phone": { - "description": "Phone 手机号。", - "type": "string" - }, - "username": { - "description": "Username 用户名。", - "type": "string" - } - } - }, "dto.TenantOwnerUserLite": { "type": "object", "properties": { @@ -11793,23 +9621,6 @@ } } }, - "dto.WithdrawForm": { - "type": "object", - "properties": { - "account_id": { - "description": "AccountID 收款账户ID。", - "type": "integer" - }, - "amount": { - "description": "Amount 提现金额(单位元)。", - "type": "number" - }, - "method": { - "description": "Method 提现方式(wallet/external)。", - "type": "string" - } - } - }, "quyun_v2_app_http_super_v1_dto.Location": { "type": "object", "properties": { @@ -12050,17 +9861,6 @@ "type": "integer" } } - }, - "v1.WebhookForm": { - "type": "object", - "properties": { - "external_id": { - "type": "string" - }, - "order_id": { - "type": "integer" - } - } } }, "securityDefinitions": { diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 965a3a0..e529688 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -138,26 +138,6 @@ definitions: - RoleUser - RoleSuperAdmin - RoleCreator - consts.TenantInviteStatus: - enum: - - active - - disabled - - expired - type: string - x-enum-varnames: - - TenantInviteStatusActive - - TenantInviteStatusDisabled - - TenantInviteStatusExpired - consts.TenantJoinRequestStatus: - enum: - - pending - - approved - - rejected - type: string - x-enum-varnames: - - TenantJoinRequestStatusPending - - TenantJoinRequestStatusApproved - - TenantJoinRequestStatusRejected consts.TenantLedgerType: enum: - debit_purchase @@ -267,42 +247,6 @@ definitions: description: Username 用户名。 type: string type: object - dto.ApplyForm: - properties: - avatar: - description: Avatar 头像URL。 - type: string - bio: - description: Bio 频道简介。 - type: string - name: - description: Name 频道/创作者名称。 - type: string - type: object - dto.AssetDTO: - properties: - id: - description: ID 资源ID。 - type: integer - name: - description: Name 文件名。 - type: string - role: - description: Role 资源角色(cover/media/preview)。 - type: string - size: - description: Size 文件大小描述。 - type: string - sort: - description: Sort 排序权重。 - type: integer - type: - description: Type 资源类型(image/audio/video)。 - type: string - url: - description: URL 资源访问地址。 - type: string - type: object dto.Comment: properties: content: @@ -342,34 +286,6 @@ definitions: description: ReplyTo 被回复评论ID(0 表示一级评论)。 type: integer type: object - dto.ContentCreateForm: - properties: - cover_ids: - description: CoverIDs 封面资源ID集合。 - items: - type: integer - type: array - genre: - description: Genre 内容分类/风格。 - type: string - key: - description: Key 音乐调性或主音。 - type: string - media_ids: - description: MediaIDs 媒体资源ID集合(音频/视频/图片)。 - items: - type: integer - type: array - price: - description: Price 价格(单位元)。 - type: number - status: - description: Status 内容状态(draft/published)。 - type: string - title: - description: Title 内容标题。 - type: string - type: object dto.ContentDetail: properties: author_avatar: @@ -451,41 +367,6 @@ definitions: description: Visibility 内容可见性(如 public/tenant_only/private)。 type: string type: object - dto.ContentEditDTO: - properties: - assets: - description: Assets 资源列表(封面/媒体)。 - items: - $ref: '#/definitions/dto.AssetDTO' - type: array - description: - description: Description 内容简介。 - type: string - enable_trial: - description: EnableTrial 是否开启试读/试听。 - type: boolean - genre: - description: Genre 内容分类。 - type: string - id: - description: ID 内容ID。 - type: integer - key: - description: Key 音乐调性或主音。 - type: string - preview_seconds: - description: PreviewSeconds 试看/试听秒数。 - type: integer - price: - description: Price 价格(单位元)。 - type: number - status: - description: Status 内容状态。 - type: string - title: - description: Title 内容标题。 - type: string - type: object dto.ContentItem: properties: author_avatar: @@ -567,37 +448,6 @@ definitions: description: PriceAmount 原价金额(单位元)。 type: number type: object - dto.ContentUpdateForm: - properties: - cover_ids: - description: CoverIDs 封面资源ID集合。 - items: - type: integer - type: array - genre: - description: Genre 内容分类/风格。 - type: string - is_pinned: - description: IsPinned 是否置顶。 - type: boolean - key: - description: Key 音乐调性或主音。 - type: string - media_ids: - description: MediaIDs 媒体资源ID集合。 - items: - type: integer - type: array - price: - description: Price 价格(单位元,nil 表示不修改)。 - type: number - status: - description: Status 内容状态(draft/published)。 - type: string - title: - description: Title 内容标题(为空表示不修改)。 - type: string - type: object dto.CouponCreateForm: properties: description: @@ -714,41 +564,6 @@ definitions: description: Value 优惠券面值(分/折扣百分比)。 type: integer type: object - dto.DashboardStats: - properties: - new_messages: - description: NewMessages 新消息数量。 - type: integer - pending_refunds: - description: PendingRefunds 待处理退款数量。 - type: integer - total_followers: - allOf: - - $ref: '#/definitions/dto.IntStatItem' - description: TotalFollowers 粉丝总数统计。 - total_revenue: - allOf: - - $ref: '#/definitions/dto.FloatStatItem' - description: TotalRevenue 累计收入统计(单位元)。 - type: object - dto.FloatStatItem: - properties: - trend: - description: Trend 环比/同比变化比例。 - type: number - value: - description: Value 统计数值(浮点)。 - type: number - type: object - dto.IntStatItem: - properties: - trend: - description: Trend 环比/同比变化比例。 - type: number - value: - description: Value 统计数值。 - type: integer - type: object dto.MediaURL: properties: duration: @@ -872,42 +687,6 @@ definitions: description: Username 买家用户名。 type: string type: object - dto.OrderCreateForm: - properties: - content_id: - description: ContentID 内容ID。 - type: integer - idempotency_key: - description: IdempotencyKey 幂等键(同一业务请求需保持一致)。 - type: string - quantity: - description: Quantity 购买数量(默认 1)。 - type: integer - sku: - description: Sku 规格标识(可选)。 - type: string - user_coupon_id: - description: UserCouponID 用户券ID(可选)。 - type: integer - type: object - dto.OrderCreateResponse: - properties: - order_id: - description: OrderID 创建成功的订单ID。 - type: integer - type: object - dto.OrderPayForm: - properties: - method: - description: Method 支付方式(alipay/balance)。 - type: string - type: object - dto.OrderPayResponse: - properties: - pay_params: - description: PayParams 支付参数(透传给前端)。 - type: string - type: object dto.OrderStatisticsResponse: properties: by_status: @@ -938,12 +717,6 @@ definitions: description: StatusDescription 状态描述(用于展示)。 type: string type: object - dto.OrderStatusResponse: - properties: - status: - description: Status 订单状态(unpaid/paid/completed 等)。 - type: string - type: object dto.OrderTenantLite: properties: code: @@ -956,36 +729,14 @@ definitions: description: Name 租户名称。 type: string type: object - dto.PayoutAccount: + dto.PaymentWebhookForm: properties: - account: - description: Account 收款账号。 + external_id: + description: ExternalID 第三方支付流水号。 type: string - id: - description: ID 收款账户ID。 + order_id: + description: OrderID 订单ID。 type: integer - name: - description: Name 账户名称/开户行。 - type: string - realname: - description: Realname 收款人姓名。 - type: string - review_reason: - description: ReviewReason 审核说明/驳回原因。 - type: string - reviewed_at: - description: ReviewedAt 审核时间(RFC3339)。 - type: string - status: - allOf: - - $ref: '#/definitions/consts.PayoutAccountStatus' - description: Status 审核状态(pending/approved/rejected)。 - status_description: - description: StatusDescription 审核状态描述(用于展示)。 - type: string - type: - description: Type 账户类型(bank/alipay)。 - type: string type: object dto.RealNameForm: properties: @@ -1014,30 +765,6 @@ definitions: description: PayParams 支付参数(透传给前端)。 type: string type: object - dto.RefundForm: - properties: - action: - description: Action 处理动作(accept/reject)。 - type: string - reason: - description: Reason 退款原因/备注。 - type: string - type: object - dto.ReportExportForm: - properties: - end_at: - description: EndAt 统计结束时间(RFC3339,可选;默认当前时间)。 - type: string - format: - description: Format 导出格式(仅支持 csv)。 - type: string - granularity: - description: Granularity 统计粒度(day;目前仅支持 day)。 - type: string - start_at: - description: StartAt 统计开始时间(RFC3339,可选;默认当前时间往前 7 天)。 - type: string - type: object dto.ReportExportResponse: properties: content: @@ -2949,40 +2676,6 @@ definitions: description: UsedCount 已使用次数。 type: integer type: object - dto.TenantInviteListItem: - properties: - code: - description: Code 邀请码。 - type: string - created_at: - description: CreatedAt 创建时间(RFC3339)。 - type: string - creator: - allOf: - - $ref: '#/definitions/dto.TenantMemberUserLite' - description: Creator 创建者信息(可选)。 - expires_at: - description: ExpiresAt 过期时间(RFC3339,空字符串表示不限制)。 - type: string - id: - description: ID 邀请记录ID。 - type: integer - max_uses: - description: MaxUses 最大可使用次数。 - type: integer - remark: - description: Remark 备注说明。 - type: string - status: - description: Status 邀请状态(active/disabled/expired)。 - type: string - status_description: - description: StatusDescription 状态描述。 - type: string - used_count: - description: UsedCount 已使用次数。 - type: integer - type: object dto.TenantItem: properties: admin_users: @@ -3042,46 +2735,6 @@ definitions: description: UUID 租户UUID。 type: string type: object - dto.TenantJoinApplyForm: - properties: - reason: - description: Reason 申请加入原因(可选,空值会使用默认文案)。 - type: string - type: object - dto.TenantJoinRequestItem: - properties: - created_at: - description: CreatedAt 申请时间(RFC3339)。 - type: string - decided_at: - description: DecidedAt 审核时间(RFC3339)。 - type: string - decided_operator_user_id: - description: DecidedOperatorUserID 审核操作者ID。 - type: integer - decided_reason: - description: DecidedReason 审核备注/原因。 - type: string - id: - description: ID 申请记录ID。 - type: integer - reason: - description: Reason 申请说明。 - type: string - status: - description: Status 申请状态。 - type: string - status_description: - description: StatusDescription 状态描述。 - type: string - updated_at: - description: UpdatedAt 更新时间(RFC3339)。 - type: string - user: - allOf: - - $ref: '#/definitions/dto.TenantMemberUserLite' - description: User 申请用户信息。 - type: object dto.TenantJoinReviewForm: properties: action: @@ -3091,60 +2744,6 @@ definitions: description: Reason 审核说明(可选,用于展示驳回原因或备注)。 type: string type: object - dto.TenantMemberItem: - properties: - created_at: - description: CreatedAt 加入时间(RFC3339)。 - type: string - id: - description: ID 成员关系记录ID。 - type: integer - role: - description: Role 成员角色列表。 - items: - $ref: '#/definitions/consts.TenantUserRole' - type: array - role_description: - description: RoleDescription 角色描述列表。 - items: - type: string - type: array - status: - allOf: - - $ref: '#/definitions/consts.UserStatus' - description: Status 成员状态。 - status_description: - description: StatusDescription 成员状态描述。 - type: string - tenant_id: - description: TenantID 租户ID。 - type: integer - updated_at: - description: UpdatedAt 更新时间(RFC3339)。 - type: string - user: - allOf: - - $ref: '#/definitions/dto.TenantMemberUserLite' - description: User 成员用户信息。 - type: object - dto.TenantMemberUserLite: - properties: - avatar: - description: Avatar 头像URL。 - type: string - id: - description: ID 用户ID。 - type: integer - nickname: - description: Nickname 昵称。 - type: string - phone: - description: Phone 手机号。 - type: string - username: - description: Username 用户名。 - type: string - type: object dto.TenantOwnerUserLite: properties: id: @@ -3510,18 +3109,6 @@ definitions: $ref: '#/definitions/dto.Transaction' type: array type: object - dto.WithdrawForm: - properties: - account_id: - description: AccountID 收款账户ID。 - type: integer - amount: - description: Amount 提现金额(单位元)。 - type: number - method: - description: Method 提现方式(wallet/external)。 - type: string - type: object quyun_v2_app_http_super_v1_dto.Location: properties: city: @@ -3684,13 +3271,6 @@ definitions: paging). type: integer type: object - v1.WebhookForm: - properties: - external_id: - type: string - order_id: - type: integer - type: object externalDocs: description: OpenAPI url: https://swagger.io/resources/open-api/ @@ -6076,7 +5656,7 @@ paths: summary: Reject withdrawal tags: - Finance - /t/{tenantCode}/v1/auth/login: + /v1/auth/login: post: consumes: - application/json @@ -6098,7 +5678,7 @@ paths: summary: Login or Register with OTP tags: - Auth - /t/{tenantCode}/v1/auth/otp: + /v1/auth/otp: post: consumes: - application/json @@ -6120,7 +5700,7 @@ paths: summary: Send OTP tags: - Auth - /t/{tenantCode}/v1/comments/{id}/like: + /v1/t/{tenantCode}/comments/{id}/like: post: consumes: - application/json @@ -6142,7 +5722,7 @@ paths: summary: Like comment tags: - Content - /t/{tenantCode}/v1/common/options: + /v1/t/{tenantCode}/common/options: get: consumes: - application/json @@ -6157,7 +5737,7 @@ paths: summary: Get options tags: - Common - /t/{tenantCode}/v1/contents: + /v1/t/{tenantCode}/contents: get: consumes: - application/json @@ -6210,7 +5790,7 @@ paths: summary: List contents tags: - Content - /t/{tenantCode}/v1/contents/{id}: + /v1/t/{tenantCode}/contents/{id}: get: consumes: - application/json @@ -6232,7 +5812,7 @@ paths: summary: Get content detail tags: - Content - /t/{tenantCode}/v1/contents/{id}/comments: + /v1/t/{tenantCode}/contents/{id}/comments: get: consumes: - application/json @@ -6292,7 +5872,7 @@ paths: summary: Post comment tags: - Content - /t/{tenantCode}/v1/contents/{id}/favorite: + /v1/t/{tenantCode}/contents/{id}/favorite: delete: parameters: - description: Content ID @@ -6325,7 +5905,7 @@ paths: summary: Add favorite tags: - Content - /t/{tenantCode}/v1/contents/{id}/like: + /v1/t/{tenantCode}/contents/{id}/like: delete: parameters: - description: Content ID @@ -6358,798 +5938,7 @@ paths: summary: Add like tags: - Content - /t/{tenantCode}/v1/creator/apply: - post: - consumes: - - application/json - description: Apply to become a creator - parameters: - - description: Apply form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.ApplyForm' - produces: - - application/json - responses: - "200": - description: Application submitted - schema: - type: string - summary: Apply creator - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/contents: - get: - consumes: - - application/json - description: List creator contents - parameters: - - description: Status - in: query - name: status - type: string - - description: Genre - in: query - name: genre - type: string - - description: Keyword - in: query - name: keyword - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/dto.ContentItem' - type: array - summary: List contents - tags: - - CreatorCenter - post: - consumes: - - application/json - description: Create/Publish content - parameters: - - description: Content form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.ContentCreateForm' - produces: - - application/json - responses: - "200": - description: Created - schema: - type: string - summary: Create content - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/contents/{id}: - delete: - consumes: - - application/json - description: Delete content - parameters: - - description: Content ID - format: int64 - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: Deleted - schema: - type: string - summary: Delete content - tags: - - CreatorCenter - get: - consumes: - - application/json - description: Get content details for edit - parameters: - - description: Content ID - format: int64 - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.ContentEditDTO' - summary: Get content - tags: - - CreatorCenter - put: - consumes: - - application/json - description: Update content - parameters: - - description: Content ID - format: int64 - in: path - name: id - required: true - type: integer - - description: Update form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.ContentUpdateForm' - produces: - - application/json - responses: - "200": - description: Updated - schema: - type: string - summary: Update content - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/coupons: - get: - consumes: - - application/json - description: List coupon templates - parameters: - - description: Page - in: query - name: page - type: integer - - description: Limit - in: query - name: limit - type: integer - - description: Type (fix_amount/discount) - in: query - name: type - type: string - - description: Status (active/expired) - in: query - name: status - type: string - - description: Keyword - in: query - name: keyword - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/requests.Pager' - summary: List coupons - tags: - - CreatorCenter - post: - consumes: - - application/json - description: Create coupon template - parameters: - - description: Coupon form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.CouponCreateForm' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.CouponItem' - summary: Create coupon - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/coupons/{id}: - get: - consumes: - - application/json - description: Get coupon template detail - parameters: - - description: Coupon ID - format: int64 - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.CouponItem' - summary: Get coupon - tags: - - CreatorCenter - put: - consumes: - - application/json - description: Update coupon template - parameters: - - description: Coupon ID - format: int64 - in: path - name: id - required: true - type: integer - - description: Coupon form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.CouponUpdateForm' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.CouponItem' - summary: Update coupon - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/coupons/{id}/grant: - post: - consumes: - - application/json - description: Grant coupon to users - parameters: - - description: Coupon ID - format: int64 - in: path - name: id - required: true - type: integer - - description: Grant form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.CouponGrantForm' - produces: - - application/json - responses: - "200": - description: Granted - schema: - type: string - summary: Grant coupon - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/dashboard: - get: - consumes: - - application/json - description: Get creator dashboard stats - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.DashboardStats' - summary: Dashboard stats - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/members: - get: - consumes: - - application/json - description: List tenant members with filters - parameters: - - description: Keyword 关键词搜索(匹配用户名/昵称/手机号)。 - in: query - name: keyword - type: string - - description: Limit is page size; only values in {10,20,50,100} are accepted - (otherwise defaults to 10). - in: query - name: limit - type: integer - - description: Page is 1-based page index; values <= 0 are normalized to 1. - in: query - name: page - type: integer - - description: Role 成员角色筛选(member/tenant_admin)。 - enum: - - member - - tenant_admin - in: query - name: role - type: string - x-enum-varnames: - - TenantUserRoleMember - - TenantUserRoleTenantAdmin - - description: Status 成员状态筛选(active/verified/banned 等)。 - enum: - - active - - inactive - - pending_verify - - verified - - banned - in: query - name: status - type: string - x-enum-varnames: - - UserStatusActive - - UserStatusInactive - - UserStatusPendingVerify - - UserStatusVerified - - UserStatusBanned - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/requests.Pager' - - properties: - items: - items: - $ref: '#/definitions/dto.TenantMemberItem' - type: array - type: object - summary: List tenant members - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/members/{id}: - delete: - consumes: - - application/json - description: Remove a tenant member by relation ID - parameters: - - description: Member ID - format: int64 - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: Removed - schema: - type: string - summary: Remove tenant member - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/members/{id}/review: - post: - consumes: - - application/json - description: Approve or reject a tenant join request - parameters: - - description: Join request ID - format: int64 - in: path - name: id - required: true - type: integer - - description: Review form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.TenantJoinReviewForm' - produces: - - application/json - responses: - "200": - description: Reviewed - schema: - type: string - summary: Review join request - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/members/invite: - post: - consumes: - - application/json - description: Create an invite for tenant members - parameters: - - description: Invite form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.TenantInviteCreateForm' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.TenantInviteItem' - summary: Create member invite - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/members/invites: - get: - consumes: - - application/json - description: List member invites with filters - parameters: - - description: Limit is page size; only values in {10,20,50,100} are accepted - (otherwise defaults to 10). - in: query - name: limit - type: integer - - description: Page is 1-based page index; values <= 0 are normalized to 1. - in: query - name: page - type: integer - - description: Status 邀请状态筛选(active/disabled/expired)。 - enum: - - active - - disabled - - expired - in: query - name: status - type: string - x-enum-varnames: - - TenantInviteStatusActive - - TenantInviteStatusDisabled - - TenantInviteStatusExpired - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/requests.Pager' - - properties: - items: - items: - $ref: '#/definitions/dto.TenantInviteListItem' - type: array - type: object - summary: List member invites - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/members/invites/{id}: - delete: - consumes: - - application/json - description: Disable a member invite by ID - parameters: - - description: Invite ID - format: int64 - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: Disabled - schema: - type: string - summary: Disable member invite - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/members/join-requests: - get: - consumes: - - application/json - description: List tenant join requests - parameters: - - description: Keyword 关键词搜索(匹配用户名/昵称/手机号)。 - in: query - name: keyword - type: string - - description: Limit is page size; only values in {10,20,50,100} are accepted - (otherwise defaults to 10). - in: query - name: limit - type: integer - - description: Page is 1-based page index; values <= 0 are normalized to 1. - in: query - name: page - type: integer - - description: Status 申请状态筛选(pending/approved/rejected)。 - enum: - - pending - - approved - - rejected - in: query - name: status - type: string - x-enum-varnames: - - TenantJoinRequestStatusPending - - TenantJoinRequestStatusApproved - - TenantJoinRequestStatusRejected - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/requests.Pager' - - properties: - items: - items: - $ref: '#/definitions/dto.TenantJoinRequestItem' - type: array - type: object - summary: List member join requests - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/orders: - get: - consumes: - - application/json - description: List sales orders - parameters: - - description: Status - in: query - name: status - type: string - - description: Keyword - in: query - name: keyword - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/dto.Order' - type: array - summary: List sales orders - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/orders/{id}/refund: - post: - consumes: - - application/json - description: Process refund - parameters: - - description: Order ID - format: int64 - in: path - name: id - required: true - type: integer - - description: Refund form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.RefundForm' - produces: - - application/json - responses: - "200": - description: Processed - schema: - type: string - summary: Process refund - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/payout-accounts: - delete: - consumes: - - application/json - description: Remove payout account - parameters: - - description: Account ID - format: int64 - in: query - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: Removed - schema: - type: string - summary: Remove payout account - tags: - - CreatorCenter - get: - consumes: - - application/json - description: List payout accounts - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/dto.PayoutAccount' - type: array - summary: List payout accounts - tags: - - CreatorCenter - post: - consumes: - - application/json - description: Add payout account - parameters: - - description: Account form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.PayoutAccount' - produces: - - application/json - responses: - "200": - description: Added - schema: - type: string - summary: Add payout account - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/reports/export: - post: - consumes: - - application/json - description: Export creator report overview - parameters: - - description: Export form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.ReportExportForm' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.ReportExportResponse' - summary: Export report overview - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/reports/overview: - get: - consumes: - - application/json - description: Get creator report overview - parameters: - - description: Start time (RFC3339) - in: query - name: start_at - type: string - - description: End time (RFC3339) - in: query - name: end_at - type: string - - description: Granularity (day) - in: query - name: granularity - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.ReportOverviewResponse' - summary: Report overview - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/settings: - get: - consumes: - - application/json - description: Get channel settings - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.Settings' - summary: Get settings - tags: - - CreatorCenter - put: - consumes: - - application/json - description: Update channel settings - parameters: - - description: Settings form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.Settings' - produces: - - application/json - responses: - "200": - description: Updated - schema: - type: string - summary: Update settings - tags: - - CreatorCenter - /t/{tenantCode}/v1/creator/withdraw: - post: - consumes: - - application/json - description: Request withdrawal - parameters: - - description: Withdraw form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.WithdrawForm' - produces: - - application/json - responses: - "200": - description: Withdrawal requested - schema: - type: string - summary: Request withdrawal - tags: - - CreatorCenter - /t/{tenantCode}/v1/creators/{id}/contents: - get: - consumes: - - application/json - description: List contents of a specific creator - parameters: - - description: Creator User ID - format: int64 - in: path - name: id - required: true - type: integer - - description: Page - in: query - name: page - type: integer - - description: Limit - in: query - name: limit - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/requests.Pager' - summary: List creator contents - tags: - - TenantPublic - /t/{tenantCode}/v1/me: + /v1/t/{tenantCode}/me: get: consumes: - application/json @@ -7185,7 +5974,7 @@ paths: summary: Update user profile tags: - UserCenter - /t/{tenantCode}/v1/me/coupons: + /v1/t/{tenantCode}/me/coupons: get: consumes: - application/json @@ -7207,7 +5996,7 @@ paths: summary: List coupons tags: - UserCenter - /t/{tenantCode}/v1/me/coupons/available: + /v1/t/{tenantCode}/me/coupons/available: get: consumes: - application/json @@ -7231,7 +6020,7 @@ paths: summary: List available coupons tags: - UserCenter - /t/{tenantCode}/v1/me/coupons/receive: + /v1/t/{tenantCode}/me/coupons/receive: post: consumes: - application/json @@ -7253,7 +6042,7 @@ paths: summary: Receive coupon tags: - UserCenter - /t/{tenantCode}/v1/me/favorites: + /v1/t/{tenantCode}/me/favorites: get: consumes: - application/json @@ -7291,7 +6080,7 @@ paths: summary: Add favorite tags: - UserCenter - /t/{tenantCode}/v1/me/favorites/{contentId}: + /v1/t/{tenantCode}/me/favorites/{contentId}: delete: consumes: - application/json @@ -7313,7 +6102,7 @@ paths: summary: Remove favorite tags: - UserCenter - /t/{tenantCode}/v1/me/following: + /v1/t/{tenantCode}/me/following: get: consumes: - application/json @@ -7330,7 +6119,7 @@ paths: summary: Get following tags: - UserCenter - /t/{tenantCode}/v1/me/library: + /v1/t/{tenantCode}/me/library: get: consumes: - application/json @@ -7347,7 +6136,7 @@ paths: summary: Get library tags: - UserCenter - /t/{tenantCode}/v1/me/likes: + /v1/t/{tenantCode}/me/likes: get: consumes: - application/json @@ -7385,7 +6174,7 @@ paths: summary: Like content tags: - UserCenter - /t/{tenantCode}/v1/me/likes/{contentId}: + /v1/t/{tenantCode}/me/likes/{contentId}: delete: consumes: - application/json @@ -7407,7 +6196,7 @@ paths: summary: Unlike content tags: - UserCenter - /t/{tenantCode}/v1/me/notifications: + /v1/t/{tenantCode}/me/notifications: get: consumes: - application/json @@ -7438,7 +6227,7 @@ paths: summary: Get notifications tags: - UserCenter - /t/{tenantCode}/v1/me/notifications/{id}/read: + /v1/t/{tenantCode}/me/notifications/{id}/read: post: consumes: - application/json @@ -7459,7 +6248,7 @@ paths: summary: Mark as read tags: - UserCenter - /t/{tenantCode}/v1/me/notifications/read-all: + /v1/t/{tenantCode}/me/notifications/read-all: post: consumes: - application/json @@ -7473,7 +6262,7 @@ paths: summary: Mark all as read tags: - UserCenter - /t/{tenantCode}/v1/me/orders: + /v1/t/{tenantCode}/me/orders: get: consumes: - application/json @@ -7495,7 +6284,7 @@ paths: summary: List orders tags: - UserCenter - /t/{tenantCode}/v1/me/orders/{id}: + /v1/t/{tenantCode}/me/orders/{id}: get: consumes: - application/json @@ -7517,7 +6306,7 @@ paths: summary: Get order detail tags: - UserCenter - /t/{tenantCode}/v1/me/realname: + /v1/t/{tenantCode}/me/realname: post: consumes: - application/json @@ -7539,7 +6328,7 @@ paths: summary: Realname auth tags: - UserCenter - /t/{tenantCode}/v1/me/wallet: + /v1/t/{tenantCode}/me/wallet: get: consumes: - application/json @@ -7554,7 +6343,7 @@ paths: summary: Get wallet tags: - UserCenter - /t/{tenantCode}/v1/me/wallet/recharge: + /v1/t/{tenantCode}/me/wallet/recharge: post: consumes: - application/json @@ -7576,7 +6365,7 @@ paths: summary: Recharge wallet tags: - UserCenter - /t/{tenantCode}/v1/media-assets/{id}: + /v1/t/{tenantCode}/media-assets/{id}: delete: consumes: - application/json @@ -7598,79 +6387,7 @@ paths: summary: Delete media asset tags: - Common - /t/{tenantCode}/v1/orders: - post: - consumes: - - application/json - description: Create Order - parameters: - - description: Create form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.OrderCreateForm' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.OrderCreateResponse' - summary: Create Order - tags: - - Transaction - /t/{tenantCode}/v1/orders/{id}/pay: - post: - consumes: - - application/json - description: Pay for order - parameters: - - description: Order ID - format: int64 - in: path - name: id - required: true - type: integer - - description: Pay form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.OrderPayForm' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.OrderPayResponse' - summary: Pay for order - tags: - - Transaction - /t/{tenantCode}/v1/orders/{id}/status: - get: - consumes: - - application/json - description: Check order payment status - parameters: - - description: Order ID - format: int64 - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.OrderStatusResponse' - summary: Check order status - tags: - - Transaction - /t/{tenantCode}/v1/storage/{any}: + /v1/t/{tenantCode}/storage/{any}: get: consumes: - application/json @@ -7727,177 +6444,7 @@ paths: summary: Upload file tags: - Storage - /t/{tenantCode}/v1/tenants: - get: - consumes: - - application/json - description: Search tenants - parameters: - - description: Keyword - in: query - name: keyword - type: string - - description: Page - in: query - name: page - type: integer - - description: Limit - in: query - name: limit - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/requests.Pager' - summary: List tenants - tags: - - TenantPublic - /t/{tenantCode}/v1/tenants/{id}: - get: - consumes: - - application/json - description: Get tenant public profile - parameters: - - description: Tenant ID - format: int64 - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dto.TenantProfile' - summary: Get tenant profile - tags: - - TenantPublic - /t/{tenantCode}/v1/tenants/{id}/follow: - delete: - consumes: - - application/json - description: Unfollow a tenant - parameters: - - description: Tenant ID - format: int64 - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: Unfollowed - schema: - type: string - summary: Unfollow tenant - tags: - - TenantPublic - post: - consumes: - - application/json - description: Follow a tenant - parameters: - - description: Tenant ID - format: int64 - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: Followed - schema: - type: string - summary: Follow tenant - tags: - - TenantPublic - /t/{tenantCode}/v1/tenants/{id}/invites/accept: - post: - consumes: - - application/json - description: Accept a tenant invite by code - parameters: - - description: Tenant ID - format: int64 - in: path - name: id - required: true - type: integer - - description: Invite form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.TenantInviteAcceptForm' - produces: - - application/json - responses: - "200": - description: Accepted - schema: - type: string - summary: Accept tenant invite - tags: - - TenantPublic - /t/{tenantCode}/v1/tenants/{id}/join: - delete: - consumes: - - application/json - description: Cancel join request for a tenant - parameters: - - description: Tenant ID - format: int64 - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: Canceled - schema: - type: string - summary: Cancel join request - tags: - - TenantPublic - post: - consumes: - - application/json - description: Submit join request for a tenant - parameters: - - description: Tenant ID - format: int64 - in: path - name: id - required: true - type: integer - - description: Join form - in: body - name: form - required: true - schema: - $ref: '#/definitions/dto.TenantJoinApplyForm' - produces: - - application/json - responses: - "200": - description: Applied - schema: - type: string - summary: Apply to join tenant - tags: - - TenantPublic - /t/{tenantCode}/v1/topics: + /v1/t/{tenantCode}/topics: get: consumes: - application/json @@ -7914,7 +6461,7 @@ paths: summary: List topics tags: - Content - /t/{tenantCode}/v1/upload: + /v1/t/{tenantCode}/upload: post: consumes: - multipart/form-data @@ -7939,7 +6486,7 @@ paths: summary: Upload file tags: - Common - /t/{tenantCode}/v1/upload/{uploadId}: + /v1/t/{tenantCode}/upload/{uploadId}: delete: consumes: - application/json @@ -7960,7 +6507,7 @@ paths: summary: Abort upload tags: - Common - /t/{tenantCode}/v1/upload/check: + /v1/t/{tenantCode}/upload/check: get: consumes: - application/json @@ -7981,7 +6528,7 @@ paths: summary: Check hash tags: - Common - /t/{tenantCode}/v1/upload/complete: + /v1/t/{tenantCode}/upload/complete: post: consumes: - application/json @@ -8003,7 +6550,7 @@ paths: summary: Complete upload tags: - Common - /t/{tenantCode}/v1/upload/init: + /v1/t/{tenantCode}/upload/init: post: consumes: - application/json @@ -8025,7 +6572,7 @@ paths: summary: Init multipart upload tags: - Common - /t/{tenantCode}/v1/upload/part: + /v1/t/{tenantCode}/upload/part: post: consumes: - multipart/form-data @@ -8054,28 +6601,6 @@ paths: summary: Upload part tags: - Common - /t/{tenantCode}/v1/webhook/payment/notify: - post: - consumes: - - application/json - description: Payment Webhook - parameters: - - description: Webhook Data - in: body - name: form - required: true - schema: - $ref: '#/definitions/v1.WebhookForm' - produces: - - application/json - responses: - "200": - description: success - schema: - type: string - summary: Payment Webhook - tags: - - Transaction securityDefinitions: BasicAuth: type: basic diff --git a/docs/plan.md b/docs/plan.md index e69de29..b4c2b1d 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -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 + +无。 diff --git a/docs/seed_verification.md b/docs/seed_verification.md index 1428a22..7642406 100644 --- a/docs/seed_verification.md +++ b/docs/seed_verification.md @@ -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`. diff --git a/frontend/portal/src/components/TopNavbar.vue b/frontend/portal/src/components/TopNavbar.vue index 57ab8aa..09ca3e6 100644 --- a/frontend/portal/src/components/TopNavbar.vue +++ b/frontend/portal/src/components/TopNavbar.vue @@ -1,6 +1,6 @@