feat: add tenant admin invite management, ledger overview, order details, and order management features

- Implemented Invite management with creation, searching, and disabling functionalities.
- Added Ledger overview for financial transactions with filtering options.
- Developed Order Detail view for individual order insights and refund capabilities.
- Created Orders management page with search, reset, and pagination features.
- Enhanced user experience with toast notifications for actions and error handling.
This commit is contained in:
2025-12-24 19:41:44 +08:00
parent 1d0b38bbb7
commit 87f569cc6a
27 changed files with 3652 additions and 151 deletions

View File

@@ -5,6 +5,7 @@ import (
"quyun/v2/app/errorx"
"quyun/v2/app/http/tenant/dto"
"quyun/v2/app/requests"
"quyun/v2/app/services"
"quyun/v2/database/models"
"quyun/v2/pkg/consts"
@@ -28,6 +29,45 @@ func requireTenantAdmin(tenantUser *models.TenantUser) error {
return nil
}
// list
//
// @Summary 内容列表(租户管理)
// @Tags Tenant
// @Accept json
// @Produce json
// @Param tenantCode path string true "Tenant Code"
// @Param filter query dto.AdminContentListFilter true "Filter"
// @Success 200 {object} requests.Pager{items=dto.AdminContentItem}
//
// @Router /t/:tenantCode/v1/admin/contents [get]
// @Bind tenant local key(tenant)
// @Bind tenantUser local key(tenant_user)
// @Bind filter query
func (*contentAdmin) list(ctx fiber.Ctx, tenant *models.Tenant, tenantUser *models.TenantUser, filter *dto.AdminContentListFilter) (*requests.Pager, error) {
if err := requireTenantAdmin(tenantUser); err != nil {
return nil, err
}
if filter == nil {
filter = &dto.AdminContentListFilter{}
}
filter.Pagination.Format()
log.WithFields(log.Fields{
"tenant_id": tenant.ID,
"user_id": tenantUser.UserID,
"query_user_id": filter.UserID,
"keyword": filter.KeywordTrimmed(),
"status": filter.Status,
"visibility": filter.Visibility,
"published_at_from": filter.PublishedAtFrom,
"published_at_to": filter.PublishedAtTo,
"created_at_from": filter.CreatedAtFrom,
"created_at_to": filter.CreatedAtTo,
}).Info("tenant.admin.contents.list")
return services.Content.AdminContentPage(ctx.Context(), tenant.ID, filter)
}
// create
//
// @Summary 创建内容(草稿)

View File

@@ -0,0 +1,56 @@
package dto
import (
"strings"
"time"
"quyun/v2/app/requests"
"quyun/v2/database/models"
"quyun/v2/pkg/consts"
"go.ipao.vip/gen/types"
)
// AdminContentListFilter 租户管理员查询“内容列表(含草稿/已发布/已下架等)”的过滤条件。
type AdminContentListFilter struct {
requests.Pagination `json:",inline" query:",inline"`
requests.SortQueryFilter `json:",inline" query:",inline"`
ID *int64 `json:"id,omitempty" query:"id"`
UserID *int64 `json:"user_id,omitempty" query:"user_id"`
Keyword *string `json:"keyword,omitempty" query:"keyword"`
Status *consts.ContentStatus `json:"status,omitempty" query:"status"`
Visibility *consts.ContentVisibility `json:"visibility,omitempty" query:"visibility"`
PublishedAtFrom *time.Time `json:"published_at_from,omitempty" query:"published_at_from"`
PublishedAtTo *time.Time `json:"published_at_to,omitempty" query:"published_at_to"`
CreatedAtFrom *time.Time `json:"created_at_from,omitempty" query:"created_at_from"`
CreatedAtTo *time.Time `json:"created_at_to,omitempty" query:"created_at_to"`
}
func (f *AdminContentListFilter) KeywordTrimmed() string {
if f == nil || f.Keyword == nil {
return ""
}
return strings.TrimSpace(*f.Keyword)
}
type AdminContentOwnerLite struct {
ID int64 `json:"id"`
Username string `json:"username"`
Status consts.UserStatus `json:"status"`
Roles types.Array[consts.Role] `json:"roles"`
}
type AdminContentItem struct {
Content *models.Content `json:"content,omitempty"`
Price *models.ContentPrice `json:"price,omitempty"`
Owner *AdminContentOwnerLite `json:"owner,omitempty"`
StatusDescription string `json:"status_description,omitempty"`
VisibilityDescription string `json:"visibility_description,omitempty"`
}

View File

@@ -82,6 +82,13 @@ func (r *Routes) Register(router fiber.Router) {
PathParam[int64]("contentID"),
))
// Register routes for controller: contentAdmin
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/admin/contents -> contentAdmin.list")
router.Get("/t/:tenantCode/v1/admin/contents"[len(r.Path()):], DataFunc3(
r.contentAdmin.list,
Local[*models.Tenant]("tenant"),
Local[*models.TenantUser]("tenant_user"),
Query[dto.AdminContentListFilter]("filter"),
))
r.log.Debugf("Registering route: Patch /t/:tenantCode/v1/admin/contents/:contentID -> contentAdmin.update")
router.Patch("/t/:tenantCode/v1/admin/contents/:contentID"[len(r.Path()):], DataFunc4(
r.contentAdmin.update,