diff --git a/README.md b/README.md
index a369adc..8c74d0e 100644
--- a/README.md
+++ b/README.md
@@ -85,3 +85,4 @@ npm run dev
- [数据库设计](docs/database.md)
- [接口规划](docs/api.md)
- [业务规则](docs/business-rules.md)
+- [分销网管理后台设计方案](docs/分销网管理后台设计方案.md)
diff --git a/backend/internal/model/distribution.go b/backend/internal/model/distribution.go
new file mode 100644
index 0000000..c449808
--- /dev/null
+++ b/backend/internal/model/distribution.go
@@ -0,0 +1,164 @@
+package model
+
+import "time"
+
+// DistributionSite 表示一个独立分销网站主体。
+type DistributionSite struct {
+ ID uint64 `gorm:"primaryKey" json:"id"`
+ SiteCode string `gorm:"size:64;not null;uniqueIndex" json:"site_code"`
+ SiteName string `gorm:"size:128;not null" json:"site_name"`
+ Domain string `gorm:"size:255;not null;uniqueIndex" json:"domain"`
+ Status string `gorm:"size:16;not null;default:'active';index" json:"status"`
+ DisplayAccountEnabled bool `gorm:"not null;default:true" json:"display_account_enabled"`
+ DefaultSupportGroupID *uint64 `gorm:"index" json:"default_support_group_id,omitempty"`
+ TaxEnabled bool `gorm:"not null;default:false" json:"tax_enabled"`
+ TaxRateBPS int64 `gorm:"not null;default:100" json:"tax_rate_bps"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+func (DistributionSite) TableName() string { return "distribution_sites" }
+
+// DistributionSiteMember 将现有后台管理员映射到分销网站。
+type DistributionSiteMember struct {
+ ID uint64 `gorm:"primaryKey" json:"id"`
+ SiteID uint64 `gorm:"not null;uniqueIndex:uk_distribution_site_member;index" json:"site_id"`
+ AdminUserID uint64 `gorm:"not null;uniqueIndex:uk_distribution_site_member;index" json:"admin_user_id"`
+ MemberType string `gorm:"size:32;not null" json:"member_type"`
+ Status string `gorm:"size:16;not null;default:'active';index" json:"status"`
+ DataScope string `gorm:"size:32;not null;default:'site'" json:"data_scope"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+func (DistributionSiteMember) TableName() string { return "distribution_site_members" }
+
+// DistributionSiteAccount 控制账号在分销网站的展示状态。
+type DistributionSiteAccount struct {
+ ID uint64 `gorm:"primaryKey" json:"id"`
+ SiteID uint64 `gorm:"not null;uniqueIndex:uk_distribution_site_account;index" json:"site_id"`
+ AccountID uint64 `gorm:"not null;uniqueIndex:uk_distribution_site_account;index" json:"account_id"`
+ ListingID uint64 `gorm:"not null;index" json:"listing_id"`
+ DisplayStatus string `gorm:"size:16;not null;default:'enabled';index" json:"display_status"`
+ DisplayStartedAt *time.Time `json:"display_started_at,omitempty"`
+ DisplayEndedAt *time.Time `json:"display_ended_at,omitempty"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+func (DistributionSiteAccount) TableName() string { return "distribution_site_accounts" }
+
+// DistributionRuleVersion 是新订单使用的分账规则快照来源。
+type DistributionRuleVersion struct {
+ ID uint64 `gorm:"primaryKey" json:"id"`
+ SiteID *uint64 `gorm:"index" json:"site_id,omitempty"`
+ Mode string `gorm:"size:32;not null;index" json:"mode"`
+ Version int `gorm:"not null" json:"version"`
+ Status string `gorm:"size:16;not null;default:'draft';index" json:"status"`
+ PlatformReceiveRatio float64 `gorm:"type:decimal(10,2);not null;default:0" json:"platform_receive_ratio"`
+ PlatformSellRatio float64 `gorm:"type:decimal(10,2);not null;default:0" json:"platform_sell_ratio"`
+ DistributorReceiveRatio float64 `gorm:"type:decimal(10,2);not null;default:0" json:"distributor_receive_ratio"`
+ DistributorSellRatio float64 `gorm:"type:decimal(10,2);not null;default:0" json:"distributor_sell_ratio"`
+ DistributorARatio float64 `gorm:"type:decimal(10,2);not null;default:0" json:"distributor_a_ratio"`
+ DistributorBRatio float64 `gorm:"type:decimal(10,2);not null;default:0" json:"distributor_b_ratio"`
+ ServiceFeeValue float64 `gorm:"type:decimal(12,4);not null;default:0" json:"service_fee_value"`
+ ServiceFeeUnit string `gorm:"size:24;not null;default:'percent'" json:"service_fee_unit"`
+ PaymentFeeBPS int64 `gorm:"not null;default:50" json:"payment_fee_bps"`
+ TaxEnabled bool `gorm:"not null;default:false" json:"tax_enabled"`
+ TaxRateBPS int64 `gorm:"not null;default:100" json:"tax_rate_bps"`
+ EffectiveFrom time.Time `gorm:"index" json:"effective_from"`
+ EffectiveTo *time.Time `gorm:"index" json:"effective_to,omitempty"`
+ CreatedBy *uint64 `json:"created_by,omitempty"`
+ CreatedAt time.Time `json:"created_at"`
+}
+
+func (DistributionRuleVersion) TableName() string { return "distribution_rule_versions" }
+
+// DistributionOrderFinancial 保存正常订单的分销财务快照。
+type DistributionOrderFinancial struct {
+ ID uint64 `gorm:"primaryKey" json:"id"`
+ OrderID uint64 `gorm:"not null;uniqueIndex" json:"order_id"`
+ SiteID uint64 `gorm:"not null;index" json:"site_id"`
+ RuleVersionID uint64 `gorm:"not null" json:"rule_version_id"`
+ GrossPaidAmountCent int64 `gorm:"not null;default:0" json:"gross_paid_amount_cent"`
+ RefundAmountCent int64 `gorm:"not null;default:0" json:"refund_amount_cent"`
+ NetPaidAmountCent int64 `gorm:"not null;default:0" json:"net_paid_amount_cent"`
+ PaymentFeeBPS int64 `gorm:"not null;default:50" json:"payment_fee_bps"`
+ PaymentFeeCent int64 `gorm:"not null;default:0" json:"payment_fee_cent"`
+ ServiceFeeValue float64 `gorm:"type:decimal(12,4);not null;default:0" json:"service_fee_value"`
+ ServiceFeeUnit string `gorm:"size:24;not null;default:'percent'" json:"service_fee_unit"`
+ ServiceFeeCent int64 `gorm:"not null;default:0" json:"service_fee_cent"`
+ MarkupAmountCent int64 `gorm:"not null;default:0" json:"markup_amount_cent"`
+ TaxEnabled bool `gorm:"not null;default:false" json:"tax_enabled"`
+ TaxRateBPS int64 `gorm:"not null;default:100" json:"tax_rate_bps"`
+ TaxAmountCent int64 `gorm:"not null;default:0" json:"tax_amount_cent"`
+ SplitBaseAmountCent int64 `gorm:"not null;default:0" json:"split_base_amount_cent"`
+ RoundingAdjustmentCent int64 `gorm:"not null;default:0" json:"rounding_adjustment_cent"`
+ SettlementStatus string `gorm:"size:24;not null;default:'pending';index" json:"settlement_status"`
+ SettledAt *time.Time `json:"settled_at,omitempty"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+func (DistributionOrderFinancial) TableName() string { return "distribution_order_financials" }
+
+// DistributionOrderParty 保存订单创建时的参与方和金额快照,避免规则或站点变更影响历史订单。
+type DistributionOrderParty struct {
+ ID uint64 `gorm:"primaryKey" json:"id"`
+ OrderID uint64 `gorm:"not null;index" json:"order_id"`
+ SiteID uint64 `gorm:"not null;index" json:"site_id"`
+ PartyRole string `gorm:"size:32;not null;index" json:"party_role"`
+ PartyType string `gorm:"size:16;not null" json:"party_type"`
+ PartyID uint64 `gorm:"not null" json:"party_id"`
+ Ratio float64 `gorm:"type:decimal(10,2);not null;default:0" json:"ratio"`
+ IncomeAmountCent int64 `gorm:"not null;default:0" json:"income_amount_cent"`
+ CostAmountCent int64 `gorm:"not null;default:0" json:"cost_amount_cent"`
+ ProfitAmountCent int64 `gorm:"not null;default:0" json:"profit_amount_cent"`
+ TaxAmountCent int64 `gorm:"not null;default:0" json:"tax_amount_cent"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+func (DistributionOrderParty) TableName() string { return "distribution_order_parties" }
+
+// DistributionSettlementBatch 是按站点生成的结算批次。
+type DistributionSettlementBatch struct {
+ ID uint64 `gorm:"primaryKey" json:"id"`
+ SiteID uint64 `gorm:"not null;index" json:"site_id"`
+ BatchNo string `gorm:"size:64;not null;uniqueIndex" json:"batch_no"`
+ PeriodStart time.Time `gorm:"not null" json:"period_start"`
+ PeriodEnd time.Time `gorm:"not null" json:"period_end"`
+ TotalAmountCent int64 `gorm:"not null;default:0" json:"total_amount_cent"`
+ Status string `gorm:"size:16;not null;default:'draft';index" json:"status"`
+ CreatedBy *uint64 `json:"created_by,omitempty"`
+ SettledAt *time.Time `json:"settled_at,omitempty"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+func (DistributionSettlementBatch) TableName() string { return "distribution_settlement_batches" }
+
+type DistributionSettlementItem struct {
+ ID uint64 `gorm:"primaryKey" json:"id"`
+ BatchID uint64 `gorm:"not null;index" json:"batch_id"`
+ OrderID uint64 `gorm:"not null;index" json:"order_id"`
+ PartyID uint64 `gorm:"not null;index" json:"party_id"`
+ AmountCent int64 `gorm:"not null;default:0" json:"amount_cent"`
+ Status string `gorm:"size:16;not null;default:'pending'" json:"status"`
+ CreatedAt time.Time `json:"created_at"`
+}
+
+func (DistributionSettlementItem) TableName() string { return "distribution_settlement_items" }
+
+// DistributionSitePaymentChannel 将一个或多个现有支付商户配置绑定到分销站点。
+type DistributionSitePaymentChannel struct {
+ ID uint64 `gorm:"primaryKey" json:"id"`
+ SiteID uint64 `gorm:"not null;uniqueIndex:uk_distribution_site_payment;index" json:"site_id"`
+ ConfigID uint64 `gorm:"not null;uniqueIndex:uk_distribution_site_payment;index" json:"config_id"`
+ Priority int `gorm:"not null;default:0" json:"priority"`
+ Status string `gorm:"size:16;not null;default:'active';index" json:"status"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+func (DistributionSitePaymentChannel) TableName() string { return "distribution_site_payment_channels" }
diff --git a/backend/internal/model/order.go b/backend/internal/model/order.go
index a49ec76..50ff007 100644
--- a/backend/internal/model/order.go
+++ b/backend/internal/model/order.go
@@ -41,30 +41,36 @@ type RentalOrder struct {
GrowthPointsAwarded int64 `gorm:"not null;default:0" json:"growth_points_awarded"`
GrowthPointsAwardedAt *time.Time `json:"growth_points_awarded_at"`
AccountSnapshot datatypes.JSON `json:"account_snapshot"`
- Status string `gorm:"size:32;not null;default:'pending_payment'" json:"status"`
- HandoffStatus string `gorm:"size:32;not null;default:'none'" json:"handoff_status"`
- HandoffMode string `gorm:"size:16;not null;default:'owner';index" json:"handoff_mode"`
- SettlementMode string `gorm:"size:32;not null;default:'owner_wallet'" json:"settlement_mode"`
- ManagedAdminID *uint64 `gorm:"index" json:"managed_admin_id"`
- SettlementStatus string `gorm:"size:32;not null;default:'unsettled'" json:"settlement_status"`
- OfflineSettlementStatus string `gorm:"size:16;not null;default:'none';index" json:"offline_settlement_status"`
- OfflineSettlementAmountCent int64 `gorm:"not null;default:0" json:"offline_settlement_amount_cent"`
- OfflineSettlementRemark string `gorm:"size:255;not null;default:''" json:"offline_settlement_remark"`
- OfflineSettledBy *uint64 `json:"offline_settled_by"`
- OfflineSettledAt *time.Time `json:"offline_settled_at"`
- RefundStatus string `gorm:"size:32;not null;default:'none';index" json:"refund_status"`
- RefundAmountCent int64 `gorm:"not null;default:0" json:"refund_amount_cent"`
- RefundedAt *time.Time `json:"refunded_at"`
- DepositHoldStatus string `gorm:"size:16;not null;default:'none';index" json:"deposit_hold_status"`
- DepositHoldAmountCent int64 `gorm:"not null;default:0" json:"deposit_hold_amount_cent"`
- DepositHoldReason string `gorm:"size:255;not null;default:''" json:"deposit_hold_reason"`
- DepositHeldBy *uint64 `json:"deposit_held_by"`
- DepositHeldAt *time.Time `json:"deposit_held_at"`
- DepositHoldReleasedAt *time.Time `json:"deposit_hold_released_at"`
- OwnerSettledAt *time.Time `json:"owner_settled_at"`
- SettledAt *time.Time `json:"settled_at"`
- CreatedAt time.Time `json:"created_at"`
- UpdatedAt time.Time `json:"updated_at"`
+ AccountSource string `gorm:"size:32;not null;default:'internal';index" json:"account_source"`
+ SourceChannel string `gorm:"size:32;not null;default:''" json:"source_channel"`
+ // 分销正常订单字段:仅在卖家和租客都通过分销网站完成交易时填写。
+ DistributionSiteID *uint64 `gorm:"index" json:"distribution_site_id,omitempty"`
+ DistributionMode string `gorm:"size:32;not null;default:'';index" json:"distribution_mode"`
+ DistributionRuleVersionID *uint64 `gorm:"index" json:"distribution_rule_version_id,omitempty"`
+ Status string `gorm:"size:32;not null;default:'pending_payment'" json:"status"`
+ HandoffStatus string `gorm:"size:32;not null;default:'none'" json:"handoff_status"`
+ HandoffMode string `gorm:"size:16;not null;default:'owner';index" json:"handoff_mode"`
+ SettlementMode string `gorm:"size:32;not null;default:'owner_wallet'" json:"settlement_mode"`
+ ManagedAdminID *uint64 `gorm:"index" json:"managed_admin_id"`
+ SettlementStatus string `gorm:"size:32;not null;default:'unsettled'" json:"settlement_status"`
+ OfflineSettlementStatus string `gorm:"size:16;not null;default:'none';index" json:"offline_settlement_status"`
+ OfflineSettlementAmountCent int64 `gorm:"not null;default:0" json:"offline_settlement_amount_cent"`
+ OfflineSettlementRemark string `gorm:"size:255;not null;default:''" json:"offline_settlement_remark"`
+ OfflineSettledBy *uint64 `json:"offline_settled_by"`
+ OfflineSettledAt *time.Time `json:"offline_settled_at"`
+ RefundStatus string `gorm:"size:32;not null;default:'none';index" json:"refund_status"`
+ RefundAmountCent int64 `gorm:"not null;default:0" json:"refund_amount_cent"`
+ RefundedAt *time.Time `json:"refunded_at"`
+ DepositHoldStatus string `gorm:"size:16;not null;default:'none';index" json:"deposit_hold_status"`
+ DepositHoldAmountCent int64 `gorm:"not null;default:0" json:"deposit_hold_amount_cent"`
+ DepositHoldReason string `gorm:"size:255;not null;default:''" json:"deposit_hold_reason"`
+ DepositHeldBy *uint64 `json:"deposit_held_by"`
+ DepositHeldAt *time.Time `json:"deposit_held_at"`
+ DepositHoldReleasedAt *time.Time `json:"deposit_hold_released_at"`
+ OwnerSettledAt *time.Time `json:"owner_settled_at"`
+ SettledAt *time.Time `json:"settled_at"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
}
func (RentalOrder) TableName() string {
diff --git a/backend/internal/modules/distribution/dto.go b/backend/internal/modules/distribution/dto.go
new file mode 100644
index 0000000..c8fab62
--- /dev/null
+++ b/backend/internal/modules/distribution/dto.go
@@ -0,0 +1,179 @@
+package distribution
+
+import "time"
+
+type SiteDTO struct {
+ ID uint64 `json:"id"`
+ SiteCode string `json:"site_code"`
+ SiteName string `json:"site_name"`
+ Domain string `json:"domain"`
+ Status string `json:"status"`
+ DisplayAccountEnabled bool `json:"display_account_enabled"`
+ DefaultSupportGroupID *uint64 `json:"default_support_group_id,omitempty"`
+ TaxEnabled bool `json:"tax_enabled"`
+ TaxRateBPS int64 `json:"tax_rate_bps"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+type CreateSiteRequest struct {
+ SiteCode string `json:"site_code" binding:"required,max=64"`
+ SiteName string `json:"site_name" binding:"required,max=128"`
+ Domain string `json:"domain" binding:"required,max=255"`
+ DisplayAccountEnabled *bool `json:"display_account_enabled"`
+ DefaultSupportGroupID *uint64 `json:"default_support_group_id"`
+ TaxEnabled *bool `json:"tax_enabled"`
+ TaxRateBPS *int64 `json:"tax_rate_bps"`
+}
+
+type UpdateSiteRequest struct {
+ SiteName *string `json:"site_name"`
+ Domain *string `json:"domain"`
+ Status *string `json:"status"`
+ DisplayAccountEnabled *bool `json:"display_account_enabled"`
+ DefaultSupportGroupID *uint64 `json:"default_support_group_id"`
+ TaxEnabled *bool `json:"tax_enabled"`
+ TaxRateBPS *int64 `json:"tax_rate_bps"`
+}
+
+type RuleDTO struct {
+ ID uint64 `json:"id"`
+ SiteID *uint64 `json:"site_id,omitempty"`
+ Mode string `json:"mode"`
+ Version int `json:"version"`
+ Status string `json:"status"`
+ PlatformReceiveRatio float64 `json:"platform_receive_ratio"`
+ PlatformSellRatio float64 `json:"platform_sell_ratio"`
+ DistributorReceiveRatio float64 `json:"distributor_receive_ratio"`
+ DistributorSellRatio float64 `json:"distributor_sell_ratio"`
+ DistributorARatio float64 `json:"distributor_a_ratio"`
+ DistributorBRatio float64 `json:"distributor_b_ratio"`
+ ServiceFeeValue float64 `json:"service_fee_value"`
+ ServiceFeeUnit string `json:"service_fee_unit"`
+ PaymentFeeBPS int64 `json:"payment_fee_bps"`
+ TaxEnabled bool `json:"tax_enabled"`
+ TaxRateBPS int64 `json:"tax_rate_bps"`
+ EffectiveFrom time.Time `json:"effective_from"`
+ EffectiveTo *time.Time `json:"effective_to,omitempty"`
+ CreatedBy *uint64 `json:"created_by,omitempty"`
+ CreatedAt time.Time `json:"created_at"`
+}
+
+type CreateRuleRequest struct {
+ SiteID *uint64 `json:"site_id"`
+ Mode string `json:"mode" binding:"required"`
+ PlatformReceiveRatio float64 `json:"platform_receive_ratio"`
+ PlatformSellRatio float64 `json:"platform_sell_ratio"`
+ DistributorReceiveRatio float64 `json:"distributor_receive_ratio"`
+ DistributorSellRatio float64 `json:"distributor_sell_ratio"`
+ DistributorARatio float64 `json:"distributor_a_ratio"`
+ DistributorBRatio float64 `json:"distributor_b_ratio"`
+ ServiceFeeValue float64 `json:"service_fee_value"`
+ ServiceFeeUnit string `json:"service_fee_unit" binding:"required"`
+ PaymentFeeBPS int64 `json:"payment_fee_bps"`
+ TaxEnabled bool `json:"tax_enabled"`
+ TaxRateBPS int64 `json:"tax_rate_bps"`
+ EffectiveFrom time.Time `json:"effective_from"`
+}
+
+type UpdateRuleStatusRequest struct {
+ Status string `json:"status" binding:"required,oneof=draft active disabled"`
+}
+
+type DistributionOrderDTO struct {
+ OrderID uint64 `json:"order_id"`
+ OrderNo string `json:"order_no"`
+ SiteID uint64 `json:"site_id"`
+ ListingID uint64 `json:"listing_id"`
+ AccountID uint64 `json:"account_id"`
+ OwnerID uint64 `json:"owner_id"`
+ RenterID uint64 `json:"renter_id"`
+ Status string `json:"status"`
+ HandoffStatus string `json:"handoff_status"`
+ RefundStatus string `json:"refund_status"`
+ SettlementStatus string `json:"settlement_status"`
+ DistributionMode string `json:"distribution_mode"`
+ GrossPaidAmountCent int64 `json:"gross_paid_amount_cent"`
+ RefundAmountCent int64 `json:"refund_amount_cent"`
+ NetPaidAmountCent int64 `json:"net_paid_amount_cent"`
+ PaymentFeeCent int64 `json:"payment_fee_cent"`
+ ServiceFeeCent int64 `json:"service_fee_cent"`
+ MarkupAmountCent int64 `json:"markup_amount_cent"`
+ TaxAmountCent int64 `json:"tax_amount_cent"`
+ SplitBaseAmountCent int64 `json:"split_base_amount_cent"`
+ CreatedAt time.Time `json:"created_at"`
+}
+
+type DistributionOrderListQuery struct {
+ SiteID uint64
+ Kind string
+ Status string
+ Page int
+ Size int
+}
+
+type DistributionOrderPage struct {
+ Items []DistributionOrderDTO `json:"items"`
+ Total int64 `json:"total"`
+ Page int `json:"page"`
+ PageSize int `json:"page_size"`
+}
+
+type AccountDisplayDTO struct {
+ ID uint64 `json:"id"`
+ SiteID uint64 `json:"site_id"`
+ AccountID uint64 `json:"account_id"`
+ ListingID uint64 `json:"listing_id"`
+ DisplayStatus string `json:"display_status"`
+}
+
+type UpdateAccountDisplayRequest struct {
+ DisplayStatus string `json:"display_status" binding:"required,oneof=enabled disabled sold offline"`
+}
+
+type PaymentChannelDTO struct {
+ ID uint64 `json:"id"`
+ SiteID uint64 `json:"site_id"`
+ ConfigID uint64 `json:"config_id"`
+ Priority int `json:"priority"`
+ Status string `json:"status"`
+}
+
+type BindPaymentChannelRequest struct {
+ ConfigID uint64 `json:"config_id" binding:"required"`
+ Priority int `json:"priority"`
+}
+
+type SettlementBatchDTO struct {
+ ID uint64 `json:"id"`
+ SiteID uint64 `json:"site_id"`
+ BatchNo string `json:"batch_no"`
+ PeriodStart time.Time `json:"period_start"`
+ PeriodEnd time.Time `json:"period_end"`
+ TotalAmountCent int64 `json:"total_amount_cent"`
+ Status string `json:"status"`
+}
+
+type CreateSettlementBatchRequest struct {
+ SiteID uint64 `json:"site_id" binding:"required"`
+ PeriodStart time.Time `json:"period_start" binding:"required"`
+ PeriodEnd time.Time `json:"period_end" binding:"required"`
+}
+
+type SnapshotPartyRequest struct {
+ PartyRole string `json:"party_role" binding:"required"`
+ PartyType string `json:"party_type" binding:"required"`
+ PartyID uint64 `json:"party_id"`
+ Ratio float64 `json:"ratio"`
+ IncomeAmountCent int64 `json:"income_amount_cent"`
+ CostAmountCent int64 `json:"cost_amount_cent"`
+}
+
+type CreateFinancialSnapshotRequest struct {
+ SiteID uint64 `json:"site_id" binding:"required"`
+ RuleVersionID uint64 `json:"rule_version_id" binding:"required"`
+ GrossPaidAmountCent int64 `json:"gross_paid_amount_cent"`
+ RefundAmountCent int64 `json:"refund_amount_cent"`
+ MarkupAmountCent int64 `json:"markup_amount_cent"`
+ Parties []SnapshotPartyRequest `json:"parties"`
+}
diff --git a/backend/internal/modules/distribution/handler.go b/backend/internal/modules/distribution/handler.go
new file mode 100644
index 0000000..c0313ca
--- /dev/null
+++ b/backend/internal/modules/distribution/handler.go
@@ -0,0 +1,343 @@
+package distribution
+
+import (
+ "errors"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "hfb_sys/backend/internal/middleware"
+ "hfb_sys/backend/pkg/response"
+
+ "github.com/gin-gonic/gin"
+)
+
+type Handler struct{ service *Service }
+
+func NewHandler(service *Service) *Handler { return &Handler{service: service} }
+
+func (h *Handler) ListSites(c *gin.Context) {
+ items, err := h.service.ListSites(c.Request.Context())
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, gin.H{"items": items})
+}
+
+func (h *Handler) GetSite(c *gin.Context) {
+ id, ok := parseID(c)
+ if !ok {
+ return
+ }
+ item, err := h.service.FindSite(c.Request.Context(), id)
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, item)
+}
+
+func (h *Handler) CreateSite(c *gin.Context) {
+ var req CreateSiteRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "分销网站参数不正确")
+ return
+ }
+ item, err := h.service.CreateSite(c.Request.Context(), req)
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.Created(c, item)
+}
+
+func (h *Handler) UpdateSite(c *gin.Context) {
+ id, ok := parseID(c)
+ if !ok {
+ return
+ }
+ var req UpdateSiteRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "分销网站参数不正确")
+ return
+ }
+ item, err := h.service.UpdateSite(c.Request.Context(), id, req)
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, item)
+}
+
+func (h *Handler) ListRules(c *gin.Context) {
+ var siteID *uint64
+ if raw := strings.TrimSpace(c.Query("site_id")); raw != "" {
+ id, err := strconv.ParseUint(raw, 10, 64)
+ if err != nil || id == 0 {
+ response.BadRequest(c, "site_id 不正确")
+ return
+ }
+ siteID = &id
+ }
+ items, err := h.service.ListRules(c.Request.Context(), siteID, c.Query("mode"))
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, gin.H{"items": items})
+}
+
+func (h *Handler) CreateRule(c *gin.Context) {
+ adminID, ok := currentAdminID(c)
+ if !ok {
+ response.Unauthorized(c, "缺少管理员上下文")
+ return
+ }
+ var req CreateRuleRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "分销规则参数不正确")
+ return
+ }
+ item, err := h.service.CreateRule(c.Request.Context(), adminID, req)
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.Created(c, item)
+}
+
+func (h *Handler) UpdateRuleStatus(c *gin.Context) {
+ id, ok := parseID(c)
+ if !ok {
+ return
+ }
+ var req UpdateRuleStatusRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "规则状态不正确")
+ return
+ }
+ item, err := h.service.UpdateRuleStatus(c.Request.Context(), id, req.Status)
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, item)
+}
+
+func (h *Handler) ListOrders(c *gin.Context) {
+ siteID := parseQueryUint(c, "site_id")
+ if siteID < 0 {
+ return
+ }
+ page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
+ size, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
+ result, err := h.service.ListOrders(c.Request.Context(), DistributionOrderListQuery{SiteID: uint64(siteID), Kind: c.Query("kind"), Status: c.Query("status"), Page: page, Size: size})
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, result)
+}
+
+func (h *Handler) FindOrder(c *gin.Context) {
+ id, ok := parseID(c)
+ if !ok {
+ return
+ }
+ item, err := h.service.FindOrder(c.Request.Context(), id)
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, item)
+}
+
+func (h *Handler) CreateFinancialSnapshot(c *gin.Context) {
+ id, ok := parseID(c)
+ if !ok {
+ return
+ }
+ var req CreateFinancialSnapshotRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "分销财务快照参数不正确")
+ return
+ }
+ item, err := h.service.CreateFinancialSnapshot(c.Request.Context(), id, req)
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.Created(c, item)
+}
+
+func (h *Handler) ListAccountDisplays(c *gin.Context) {
+ siteID := parseQueryUint(c, "site_id")
+ if siteID <= 0 {
+ if siteID == 0 {
+ response.BadRequest(c, "site_id 不正确")
+ }
+ return
+ }
+ items, err := h.service.ListAccountDisplays(c.Request.Context(), uint64(siteID), c.Query("status"))
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, gin.H{"items": items})
+}
+
+func (h *Handler) UpdateAccountDisplay(c *gin.Context) {
+ id, ok := parseID(c)
+ if !ok {
+ return
+ }
+ var req UpdateAccountDisplayRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "账号展示状态不正确")
+ return
+ }
+ item, err := h.service.UpdateAccountDisplay(c.Request.Context(), id, req.DisplayStatus)
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, item)
+}
+
+func (h *Handler) ListPaymentChannels(c *gin.Context) {
+ siteID := parseQueryUint(c, "site_id")
+ if siteID <= 0 {
+ response.BadRequest(c, "site_id 不正确")
+ return
+ }
+ items, err := h.service.ListPaymentChannels(c.Request.Context(), uint64(siteID))
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, gin.H{"items": items})
+}
+
+func (h *Handler) BindPaymentChannel(c *gin.Context) {
+ siteID := parseQueryUint(c, "site_id")
+ if siteID <= 0 {
+ response.BadRequest(c, "site_id 不正确")
+ return
+ }
+ var req BindPaymentChannelRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "支付通道参数不正确")
+ return
+ }
+ item, err := h.service.BindPaymentChannel(c.Request.Context(), uint64(siteID), req)
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.Created(c, item)
+}
+
+func (h *Handler) UnbindPaymentChannel(c *gin.Context) {
+ id, ok := parseID(c)
+ if !ok {
+ return
+ }
+ if err := h.service.UnbindPaymentChannel(c.Request.Context(), id); err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, gin.H{"deleted": true})
+}
+
+func (h *Handler) ListSettlementBatches(c *gin.Context) {
+ siteID := parseQueryUint(c, "site_id")
+ if siteID <= 0 {
+ response.BadRequest(c, "site_id 不正确")
+ return
+ }
+ items, err := h.service.ListSettlementBatches(c.Request.Context(), uint64(siteID))
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, gin.H{"items": items})
+}
+
+func (h *Handler) CreateSettlementBatch(c *gin.Context) {
+ adminID, ok := currentAdminID(c)
+ if !ok {
+ response.Unauthorized(c, "缺少管理员上下文")
+ return
+ }
+ var req CreateSettlementBatchRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "结算周期参数不正确")
+ return
+ }
+ item, err := h.service.CreateSettlementBatch(c.Request.Context(), adminID, req)
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.Created(c, item)
+}
+
+func (h *Handler) MarkSettlementBatch(c *gin.Context) {
+ id, ok := parseID(c)
+ if !ok {
+ return
+ }
+ item, err := h.service.MarkSettlementBatch(c.Request.Context(), id)
+ if err != nil {
+ writeError(c, err)
+ return
+ }
+ response.OK(c, item)
+}
+
+// parseQueryUint 返回 -1 表示格式错误,0 表示未传入。
+func parseQueryUint(c *gin.Context, key string) int64 {
+ raw := strings.TrimSpace(c.Query(key))
+ if raw == "" {
+ return 0
+ }
+ id, err := strconv.ParseUint(raw, 10, 64)
+ if err != nil || id == 0 {
+ response.BadRequest(c, key+" 不正确")
+ return -1
+ }
+ return int64(id)
+}
+
+func currentAdminID(c *gin.Context) (uint64, bool) {
+ value, ok := c.Get(middleware.ContextAdminID)
+ if !ok {
+ return 0, false
+ }
+ id, ok := value.(uint64)
+ return id, ok && id > 0
+}
+
+func parseID(c *gin.Context) (uint64, bool) {
+ id, err := strconv.ParseUint(c.Param("id"), 10, 64)
+ if err != nil || id == 0 {
+ response.BadRequest(c, "ID 不正确")
+ return 0, false
+ }
+ return id, true
+}
+
+func writeError(c *gin.Context, err error) {
+ switch {
+ case errors.Is(err, ErrSiteNotFound), errors.Is(err, ErrRuleNotFound), errors.Is(err, ErrOrderNotFound), errors.Is(err, ErrAccountDisplayNotFound), errors.Is(err, ErrPaymentChannelNotFound), errors.Is(err, ErrSettlementBatchNotFound):
+ response.NotFound(c, "分销资源不存在")
+ case errors.Is(err, ErrInvalidAmount), errors.Is(err, ErrInvalidRate), errors.Is(err, ErrInvalidServiceUnit), errors.Is(err, ErrUnsupportedMode), errors.Is(err, ErrInvalidOrderKind), errors.Is(err, ErrInvalidPeriod), errors.Is(err, ErrBatchAlreadySettled):
+ response.BadRequest(c, "分销规则参数不正确")
+ case errors.Is(err, ErrDependencyUnavailable):
+ response.ServiceUnavailable(c, "分销服务暂时不可用")
+ default:
+ response.RecordError(c, err)
+ response.Error(c, http.StatusInternalServerError, "distribution_error", "分销服务暂时不可用")
+ }
+}
diff --git a/backend/internal/modules/distribution/pricing.go b/backend/internal/modules/distribution/pricing.go
new file mode 100644
index 0000000..9c1ed3e
--- /dev/null
+++ b/backend/internal/modules/distribution/pricing.go
@@ -0,0 +1,162 @@
+package distribution
+
+import (
+ "errors"
+ "math"
+)
+
+const (
+ ModeTwoPartyDistributorReceive = "two_party_distributor_receive"
+ ModeTwoPartyPlatformReceive = "two_party_platform_receive"
+ ModeThreeParty = "three_party"
+
+ ServiceFeeUnitPercent = "percent"
+ ServiceFeeUnitFixedAmount = "fixed_amount"
+ ServiceFeeUnitRatio = "ratio"
+)
+
+var (
+ ErrInvalidAmount = errors.New("invalid distribution amount")
+ ErrInvalidRate = errors.New("invalid distribution rate")
+ ErrInvalidServiceUnit = errors.New("invalid service fee unit")
+ ErrUnsupportedMode = errors.New("unsupported distribution mode")
+)
+
+// Rule 是后台可编辑的分账规则。比例字段按参与方角色命名,避免方向歧义。
+type Rule struct {
+ Mode string
+ PlatformReceiveRatio float64
+ PlatformSellRatio float64
+ DistributorReceiveRatio float64
+ DistributorSellRatio float64
+ DistributorARatio float64
+ DistributorBRatio float64
+ ServiceFeeValue float64
+ ServiceFeeUnit string
+ PaymentFeeBPS int64
+ TaxEnabled bool
+ TaxRateBPS int64
+}
+
+// SettlementInput 使用支付单和退款单的整数分快照计算分销费用。
+type SettlementInput struct {
+ GrossPaidAmountCent int64
+ RefundAmountCent int64
+ MarkupAmountCent int64
+ GrossProfitByParty []PartyProfitInput
+}
+
+type PartyProfitInput struct {
+ PartyRole string
+ IncomeAmountCent int64
+ CostAmountCent int64
+}
+
+type PartyTaxResult struct {
+ PartyRole string `json:"party_role"`
+ GrossProfitCent int64 `json:"gross_profit_cent"`
+ TaxAmountCent int64 `json:"tax_amount_cent"`
+}
+
+type SettlementResult struct {
+ GrossPaidAmountCent int64
+ RefundAmountCent int64
+ NetPaidAmountCent int64
+ PaymentFeeCent int64
+ ServiceFeeCent int64
+ MarkupAmountCent int64
+ SplitBaseAmountCent int64
+ TaxAmountCent int64
+ TaxByParty []PartyTaxResult
+}
+
+// DefaultRule 返回文档中约定的第一版默认值;调用方应将其写入规则表而非写死到业务流程。
+func DefaultRule(mode string) (Rule, error) {
+ base := Rule{
+ Mode: mode, ServiceFeeValue: 2.5, ServiceFeeUnit: ServiceFeeUnitRatio,
+ PaymentFeeBPS: 50, TaxEnabled: false, TaxRateBPS: 100,
+ }
+ switch mode {
+ case ModeTwoPartyDistributorReceive:
+ base.DistributorReceiveRatio = 3.5
+ base.PlatformSellRatio = 4
+ case ModeTwoPartyPlatformReceive:
+ base.PlatformReceiveRatio = 3.5
+ base.DistributorSellRatio = 4
+ case ModeThreeParty:
+ base.DistributorARatio = 3.5
+ base.DistributorBRatio = 4
+ default:
+ return Rule{}, ErrUnsupportedMode
+ }
+ return base, nil
+}
+
+// CalculateSettlement 先扣支付手续费,再计算服务费和分成基数;退款金额不能超过支付金额。
+func CalculateSettlement(rule Rule, input SettlementInput) (SettlementResult, error) {
+ if input.GrossPaidAmountCent < 0 || input.RefundAmountCent < 0 || input.MarkupAmountCent < 0 || input.RefundAmountCent > input.GrossPaidAmountCent {
+ return SettlementResult{}, ErrInvalidAmount
+ }
+ if rule.PaymentFeeBPS < 0 || rule.PaymentFeeBPS > 10000 || rule.TaxRateBPS < 0 || rule.TaxRateBPS > 10000 || rule.ServiceFeeValue < 0 {
+ return SettlementResult{}, ErrInvalidRate
+ }
+ if rule.ServiceFeeUnit != ServiceFeeUnitPercent && rule.ServiceFeeUnit != ServiceFeeUnitFixedAmount && rule.ServiceFeeUnit != ServiceFeeUnitRatio {
+ return SettlementResult{}, ErrInvalidServiceUnit
+ }
+ netPaid := input.GrossPaidAmountCent - input.RefundAmountCent
+ paymentFee := roundHalfUp(float64(netPaid) * float64(rule.PaymentFeeBPS) / 10000)
+ serviceFee, err := serviceFeeCent(rule, netPaid)
+ if err != nil {
+ return SettlementResult{}, err
+ }
+ splitBase := netPaid - paymentFee - serviceFee
+ if splitBase < 0 {
+ splitBase = 0
+ }
+ result := SettlementResult{
+ GrossPaidAmountCent: input.GrossPaidAmountCent,
+ RefundAmountCent: input.RefundAmountCent,
+ NetPaidAmountCent: netPaid,
+ PaymentFeeCent: paymentFee,
+ ServiceFeeCent: serviceFee,
+ MarkupAmountCent: input.MarkupAmountCent,
+ SplitBaseAmountCent: splitBase,
+ }
+ if rule.TaxEnabled {
+ result.TaxByParty = make([]PartyTaxResult, 0, len(input.GrossProfitByParty))
+ for _, party := range input.GrossProfitByParty {
+ profit := party.IncomeAmountCent - party.CostAmountCent
+ tax := roundHalfUp(float64(max(profit, 0)) * float64(rule.TaxRateBPS) / 10000)
+ result.TaxAmountCent += tax
+ result.TaxByParty = append(result.TaxByParty, PartyTaxResult{PartyRole: party.PartyRole, GrossProfitCent: profit, TaxAmountCent: tax})
+ }
+ }
+ return result, nil
+}
+
+func serviceFeeCent(rule Rule, netPaid int64) (int64, error) {
+ switch rule.ServiceFeeUnit {
+ case ServiceFeeUnitPercent:
+ return roundHalfUp(float64(netPaid) * rule.ServiceFeeValue / 100), nil
+ case ServiceFeeUnitFixedAmount:
+ return roundHalfUp(rule.ServiceFeeValue * 100), nil
+ case ServiceFeeUnitRatio:
+ if rule.ServiceFeeValue <= 0 {
+ return 0, nil
+ }
+ return roundHalfUp(float64(netPaid) / rule.ServiceFeeValue), nil
+ default:
+ return 0, ErrInvalidServiceUnit
+ }
+}
+
+func roundHalfUp(value float64) int64 {
+ return int64(math.Floor(value + 0.5))
+}
+
+func max(a, b int64) int64 {
+ if a > b {
+ return a
+ }
+ return b
+}
diff --git a/backend/internal/modules/distribution/pricing_test.go b/backend/internal/modules/distribution/pricing_test.go
new file mode 100644
index 0000000..7abd1dd
--- /dev/null
+++ b/backend/internal/modules/distribution/pricing_test.go
@@ -0,0 +1,55 @@
+package distribution
+
+import "testing"
+
+func TestDefaultRules(t *testing.T) {
+ cases := []struct {
+ mode string
+ check func(Rule) bool
+ }{
+ {ModeTwoPartyDistributorReceive, func(r Rule) bool { return r.DistributorReceiveRatio == 3.5 && r.PlatformSellRatio == 4 }},
+ {ModeTwoPartyPlatformReceive, func(r Rule) bool { return r.PlatformReceiveRatio == 3.5 && r.DistributorSellRatio == 4 }},
+ {ModeThreeParty, func(r Rule) bool { return r.DistributorARatio == 3.5 && r.DistributorBRatio == 4 }},
+ }
+ for _, item := range cases {
+ rule, err := DefaultRule(item.mode)
+ if err != nil || !item.check(rule) || rule.ServiceFeeValue != 2.5 || rule.PaymentFeeBPS != 50 {
+ t.Fatalf("default rule %s = %#v, err=%v", item.mode, rule, err)
+ }
+ }
+}
+
+func TestCalculateSettlementDeductsFeeBeforeSplit(t *testing.T) {
+ rule := Rule{ServiceFeeValue: 0, ServiceFeeUnit: ServiceFeeUnitFixedAmount, PaymentFeeBPS: 50}
+ got, err := CalculateSettlement(rule, SettlementInput{GrossPaidAmountCent: 10001, RefundAmountCent: 1000, MarkupAmountCent: 300})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got.NetPaidAmountCent != 9001 || got.PaymentFeeCent != 45 || got.SplitBaseAmountCent != 8956 {
+ t.Fatalf("unexpected settlement: %#v", got)
+ }
+}
+
+func TestCalculateSettlementTaxesEachPositivePartyProfit(t *testing.T) {
+ rule := Rule{ServiceFeeUnit: ServiceFeeUnitFixedAmount, PaymentFeeBPS: 0, TaxEnabled: true, TaxRateBPS: 100}
+ got, err := CalculateSettlement(rule, SettlementInput{
+ GrossPaidAmountCent: 10000,
+ GrossProfitByParty: []PartyProfitInput{
+ {PartyRole: "distributor_a", IncomeAmountCent: 1000, CostAmountCent: 400},
+ {PartyRole: "distributor_b", IncomeAmountCent: 500, CostAmountCent: 700},
+ },
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got.TaxAmountCent != 6 || len(got.TaxByParty) != 2 || got.TaxByParty[0].TaxAmountCent != 6 || got.TaxByParty[1].TaxAmountCent != 0 {
+ t.Fatalf("unexpected tax result: %#v", got)
+ }
+}
+
+func TestCalculateSettlementRejectsRefundOverPayment(t *testing.T) {
+ _, err := CalculateSettlement(Rule{ServiceFeeUnit: ServiceFeeUnitFixedAmount}, SettlementInput{GrossPaidAmountCent: 1, RefundAmountCent: 2})
+ if err != ErrInvalidAmount {
+ t.Fatalf("err = %v", err)
+ }
+}
diff --git a/backend/internal/modules/distribution/repository.go b/backend/internal/modules/distribution/repository.go
new file mode 100644
index 0000000..ed5251f
--- /dev/null
+++ b/backend/internal/modules/distribution/repository.go
@@ -0,0 +1,399 @@
+package distribution
+
+import (
+ "context"
+ "errors"
+ "strings"
+ "time"
+
+ "hfb_sys/backend/internal/model"
+
+ "gorm.io/gorm"
+)
+
+var (
+ ErrSiteNotFound = errors.New("distribution site not found")
+ ErrRuleNotFound = errors.New("distribution rule not found")
+ ErrOrderNotFound = errors.New("distribution order not found")
+ ErrInvalidOrderKind = errors.New("invalid distribution order kind")
+ ErrAccountDisplayNotFound = errors.New("distribution account display not found")
+ ErrPaymentChannelNotFound = errors.New("distribution payment channel not found")
+ ErrInvalidPeriod = errors.New("invalid distribution settlement period")
+ ErrSettlementBatchNotFound = errors.New("distribution settlement batch not found")
+ ErrBatchAlreadySettled = errors.New("distribution settlement batch already settled")
+)
+
+type Repository struct{ db *gorm.DB }
+
+func NewRepository(db *gorm.DB) *Repository { return &Repository{db: db} }
+
+func (r *Repository) ListSites(ctx context.Context) ([]SiteDTO, error) {
+ var rows []model.DistributionSite
+ if err := r.db.WithContext(ctx).Order("id ASC").Find(&rows).Error; err != nil {
+ return nil, err
+ }
+ items := make([]SiteDTO, 0, len(rows))
+ for _, row := range rows {
+ items = append(items, siteDTO(row))
+ }
+ return items, nil
+}
+
+func (r *Repository) FindSite(ctx context.Context, id uint64) (*SiteDTO, error) {
+ var row model.DistributionSite
+ if err := r.db.WithContext(ctx).First(&row, id).Error; err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, ErrSiteNotFound
+ }
+ return nil, err
+ }
+ item := siteDTO(row)
+ return &item, nil
+}
+
+func (r *Repository) CreateSite(ctx context.Context, req CreateSiteRequest) (*SiteDTO, error) {
+ row := model.DistributionSite{
+ SiteCode: strings.TrimSpace(req.SiteCode), SiteName: strings.TrimSpace(req.SiteName), Domain: strings.TrimSpace(req.Domain),
+ Status: "active", DisplayAccountEnabled: true, TaxRateBPS: 100,
+ }
+ if req.DisplayAccountEnabled != nil {
+ row.DisplayAccountEnabled = *req.DisplayAccountEnabled
+ }
+ if req.DefaultSupportGroupID != nil {
+ row.DefaultSupportGroupID = req.DefaultSupportGroupID
+ }
+ if req.TaxEnabled != nil {
+ row.TaxEnabled = *req.TaxEnabled
+ }
+ if req.TaxRateBPS != nil {
+ row.TaxRateBPS = *req.TaxRateBPS
+ }
+ if err := r.db.WithContext(ctx).Create(&row).Error; err != nil {
+ return nil, err
+ }
+ item := siteDTO(row)
+ return &item, nil
+}
+
+func (r *Repository) UpdateSite(ctx context.Context, id uint64, req UpdateSiteRequest) (*SiteDTO, error) {
+ var row model.DistributionSite
+ if err := r.db.WithContext(ctx).First(&row, id).Error; err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, ErrSiteNotFound
+ }
+ return nil, err
+ }
+ if req.SiteName != nil {
+ row.SiteName = strings.TrimSpace(*req.SiteName)
+ }
+ if req.Domain != nil {
+ row.Domain = strings.TrimSpace(*req.Domain)
+ }
+ if req.Status != nil {
+ row.Status = strings.TrimSpace(*req.Status)
+ }
+ if req.DisplayAccountEnabled != nil {
+ row.DisplayAccountEnabled = *req.DisplayAccountEnabled
+ }
+ if req.DefaultSupportGroupID != nil {
+ row.DefaultSupportGroupID = req.DefaultSupportGroupID
+ }
+ if req.TaxEnabled != nil {
+ row.TaxEnabled = *req.TaxEnabled
+ }
+ if req.TaxRateBPS != nil {
+ row.TaxRateBPS = *req.TaxRateBPS
+ }
+ if err := r.db.WithContext(ctx).Save(&row).Error; err != nil {
+ return nil, err
+ }
+ item := siteDTO(row)
+ return &item, nil
+}
+
+func (r *Repository) ListRules(ctx context.Context, siteID *uint64, mode string) ([]RuleDTO, error) {
+ db := r.db.WithContext(ctx).Model(&model.DistributionRuleVersion{}).Order("effective_from DESC, id DESC")
+ if siteID == nil {
+ db = db.Where("site_id IS NULL")
+ } else {
+ db = db.Where("site_id = ?", *siteID)
+ }
+ if mode = strings.TrimSpace(mode); mode != "" {
+ db = db.Where("mode = ?", mode)
+ }
+ var rows []model.DistributionRuleVersion
+ if err := db.Find(&rows).Error; err != nil {
+ return nil, err
+ }
+ items := make([]RuleDTO, 0, len(rows))
+ for _, row := range rows {
+ items = append(items, ruleDTO(row))
+ }
+ return items, nil
+}
+
+func (r *Repository) CreateRule(ctx context.Context, adminID uint64, req CreateRuleRequest) (*RuleDTO, error) {
+ version := 1
+ var maxVersion int
+ db := r.db.WithContext(ctx).Model(&model.DistributionRuleVersion{}).Where("mode = ?", req.Mode)
+ if req.SiteID == nil {
+ db = db.Where("site_id IS NULL")
+ } else {
+ db = db.Where("site_id = ?", *req.SiteID)
+ }
+ if err := db.Select("COALESCE(MAX(version), 0)").Scan(&maxVersion).Error; err != nil {
+ return nil, err
+ }
+ version = maxVersion + 1
+ effectiveFrom := req.EffectiveFrom
+ if effectiveFrom.IsZero() {
+ effectiveFrom = time.Now()
+ }
+ row := model.DistributionRuleVersion{
+ SiteID: req.SiteID, Mode: strings.TrimSpace(req.Mode), Version: version, Status: "draft",
+ PlatformReceiveRatio: req.PlatformReceiveRatio, PlatformSellRatio: req.PlatformSellRatio,
+ DistributorReceiveRatio: req.DistributorReceiveRatio, DistributorSellRatio: req.DistributorSellRatio,
+ DistributorARatio: req.DistributorARatio, DistributorBRatio: req.DistributorBRatio,
+ ServiceFeeValue: req.ServiceFeeValue, ServiceFeeUnit: strings.TrimSpace(req.ServiceFeeUnit),
+ PaymentFeeBPS: req.PaymentFeeBPS, TaxEnabled: req.TaxEnabled, TaxRateBPS: req.TaxRateBPS,
+ EffectiveFrom: effectiveFrom, CreatedBy: &adminID,
+ }
+ if row.PaymentFeeBPS == 0 {
+ row.PaymentFeeBPS = 50
+ }
+ if row.TaxRateBPS == 0 {
+ row.TaxRateBPS = 100
+ }
+ if err := r.db.WithContext(ctx).Create(&row).Error; err != nil {
+ return nil, err
+ }
+ item := ruleDTO(row)
+ return &item, nil
+}
+
+func (r *Repository) UpdateRuleStatus(ctx context.Context, id uint64, status string) (*RuleDTO, error) {
+ var row model.DistributionRuleVersion
+ if err := r.db.WithContext(ctx).First(&row, id).Error; err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, ErrRuleNotFound
+ }
+ return nil, err
+ }
+ row.Status = strings.TrimSpace(status)
+ if err := r.db.WithContext(ctx).Save(&row).Error; err != nil {
+ return nil, err
+ }
+ item := ruleDTO(row)
+ return &item, nil
+}
+
+func siteDTO(row model.DistributionSite) SiteDTO {
+ return SiteDTO{ID: row.ID, SiteCode: row.SiteCode, SiteName: row.SiteName, Domain: row.Domain, Status: row.Status, DisplayAccountEnabled: row.DisplayAccountEnabled, DefaultSupportGroupID: row.DefaultSupportGroupID, TaxEnabled: row.TaxEnabled, TaxRateBPS: row.TaxRateBPS, CreatedAt: row.CreatedAt, UpdatedAt: row.UpdatedAt}
+}
+
+func ruleDTO(row model.DistributionRuleVersion) RuleDTO {
+ return RuleDTO{ID: row.ID, SiteID: row.SiteID, Mode: row.Mode, Version: row.Version, Status: row.Status, PlatformReceiveRatio: row.PlatformReceiveRatio, PlatformSellRatio: row.PlatformSellRatio, DistributorReceiveRatio: row.DistributorReceiveRatio, DistributorSellRatio: row.DistributorSellRatio, DistributorARatio: row.DistributorARatio, DistributorBRatio: row.DistributorBRatio, ServiceFeeValue: row.ServiceFeeValue, ServiceFeeUnit: row.ServiceFeeUnit, PaymentFeeBPS: row.PaymentFeeBPS, TaxEnabled: row.TaxEnabled, TaxRateBPS: row.TaxRateBPS, EffectiveFrom: row.EffectiveFrom, EffectiveTo: row.EffectiveTo, CreatedBy: row.CreatedBy, CreatedAt: row.CreatedAt}
+}
+
+func (r *Repository) ListOrders(ctx context.Context, query DistributionOrderListQuery) (*DistributionOrderPage, error) {
+ page, size := query.Page, query.Size
+ if page < 1 {
+ page = 1
+ }
+ if size < 1 {
+ size = 20
+ }
+ if size > 100 {
+ size = 100
+ }
+ base := r.db.WithContext(ctx).Table("rental_orders ro").Joins("JOIN distribution_order_financials f ON f.order_id = ro.id")
+ if query.SiteID > 0 {
+ base = base.Where("ro.distribution_site_id = ?", query.SiteID)
+ }
+ if query.Status != "" {
+ base = base.Where("ro.status = ?", query.Status)
+ }
+ switch strings.TrimSpace(query.Kind) {
+ case "sold":
+ base = base.Where("EXISTS (SELECT 1 FROM distribution_order_parties p WHERE p.order_id = ro.id AND p.party_role IN ('distributor_sell','distributor_b'))")
+ case "receive":
+ base = base.Where("EXISTS (SELECT 1 FROM distribution_order_parties p WHERE p.order_id = ro.id AND p.party_role IN ('distributor_receive','distributor_a'))")
+ case "", "all":
+ default:
+ return nil, ErrInvalidOrderKind
+ }
+ var total int64
+ if err := base.Session(&gorm.Session{}).Select("ro.id").Count(&total).Error; err != nil {
+ return nil, err
+ }
+ items := make([]DistributionOrderDTO, 0)
+ selectSQL := "ro.id AS order_id, ro.order_no, ro.distribution_site_id AS site_id, ro.listing_id, ro.account_id, ro.owner_id, ro.renter_id, ro.status, ro.handoff_status, ro.refund_status, ro.settlement_status, ro.distribution_mode, f.gross_paid_amount_cent, f.refund_amount_cent, f.net_paid_amount_cent, f.payment_fee_cent, f.service_fee_cent, f.markup_amount_cent, f.tax_amount_cent, f.split_base_amount_cent, ro.created_at"
+ if err := base.Select(selectSQL).Order("ro.id DESC").Offset((page - 1) * size).Limit(size).Scan(&items).Error; err != nil {
+ return nil, err
+ }
+ return &DistributionOrderPage{Items: items, Total: total, Page: page, PageSize: size}, nil
+}
+
+func (r *Repository) FindOrder(ctx context.Context, id uint64) (*DistributionOrderDTO, error) {
+ var item DistributionOrderDTO
+ err := r.db.WithContext(ctx).Table("rental_orders ro").Joins("JOIN distribution_order_financials f ON f.order_id = ro.id").Select("ro.id AS order_id, ro.order_no, ro.distribution_site_id AS site_id, ro.listing_id, ro.account_id, ro.owner_id, ro.renter_id, ro.status, ro.handoff_status, ro.refund_status, ro.settlement_status, ro.distribution_mode, f.gross_paid_amount_cent, f.refund_amount_cent, f.net_paid_amount_cent, f.payment_fee_cent, f.service_fee_cent, f.markup_amount_cent, f.tax_amount_cent, f.split_base_amount_cent, ro.created_at").Where("ro.id = ?", id).Scan(&item).Error
+ if err != nil {
+ return nil, err
+ }
+ if item.OrderID == 0 {
+ return nil, ErrOrderNotFound
+ }
+ return &item, nil
+}
+
+func (r *Repository) ListAccountDisplays(ctx context.Context, siteID uint64, status string) ([]AccountDisplayDTO, error) {
+ var rows []model.DistributionSiteAccount
+ db := r.db.WithContext(ctx).Where("site_id = ?", siteID).Order("id DESC")
+ if status != "" {
+ db = db.Where("display_status = ?", status)
+ }
+ if err := db.Find(&rows).Error; err != nil {
+ return nil, err
+ }
+ items := make([]AccountDisplayDTO, 0, len(rows))
+ for _, row := range rows {
+ items = append(items, AccountDisplayDTO{ID: row.ID, SiteID: row.SiteID, AccountID: row.AccountID, ListingID: row.ListingID, DisplayStatus: row.DisplayStatus})
+ }
+ return items, nil
+}
+
+func (r *Repository) UpdateAccountDisplay(ctx context.Context, id uint64, status string) (*AccountDisplayDTO, error) {
+ var row model.DistributionSiteAccount
+ if err := r.db.WithContext(ctx).First(&row, id).Error; err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, ErrAccountDisplayNotFound
+ }
+ return nil, err
+ }
+ row.DisplayStatus = strings.TrimSpace(status)
+ if err := r.db.WithContext(ctx).Save(&row).Error; err != nil {
+ return nil, err
+ }
+ item := &AccountDisplayDTO{ID: row.ID, SiteID: row.SiteID, AccountID: row.AccountID, ListingID: row.ListingID, DisplayStatus: row.DisplayStatus}
+ return item, nil
+}
+
+func (r *Repository) ListPaymentChannels(ctx context.Context, siteID uint64) ([]PaymentChannelDTO, error) {
+ var rows []model.DistributionSitePaymentChannel
+ if err := r.db.WithContext(ctx).Where("site_id = ?", siteID).Order("priority ASC, id ASC").Find(&rows).Error; err != nil {
+ return nil, err
+ }
+ items := make([]PaymentChannelDTO, 0, len(rows))
+ for _, row := range rows {
+ items = append(items, PaymentChannelDTO{ID: row.ID, SiteID: row.SiteID, ConfigID: row.ConfigID, Priority: row.Priority, Status: row.Status})
+ }
+ return items, nil
+}
+
+func (r *Repository) BindPaymentChannel(ctx context.Context, siteID uint64, req BindPaymentChannelRequest) (*PaymentChannelDTO, error) {
+ row := model.DistributionSitePaymentChannel{SiteID: siteID, ConfigID: req.ConfigID, Priority: req.Priority, Status: "active"}
+ if err := r.db.WithContext(ctx).Where("site_id = ? AND config_id = ?", siteID, req.ConfigID).FirstOrCreate(&row).Error; err != nil {
+ return nil, err
+ }
+ return &PaymentChannelDTO{ID: row.ID, SiteID: row.SiteID, ConfigID: row.ConfigID, Priority: row.Priority, Status: row.Status}, nil
+}
+
+func (r *Repository) UnbindPaymentChannel(ctx context.Context, id uint64) error {
+ result := r.db.WithContext(ctx).Delete(&model.DistributionSitePaymentChannel{}, id)
+ if result.Error != nil {
+ return result.Error
+ }
+ if result.RowsAffected == 0 {
+ return ErrPaymentChannelNotFound
+ }
+ return nil
+}
+
+func (r *Repository) ListSettlementBatches(ctx context.Context, siteID uint64) ([]SettlementBatchDTO, error) {
+ var rows []model.DistributionSettlementBatch
+ if err := r.db.WithContext(ctx).Where("site_id = ?", siteID).Order("id DESC").Find(&rows).Error; err != nil {
+ return nil, err
+ }
+ items := make([]SettlementBatchDTO, 0, len(rows))
+ for _, row := range rows {
+ items = append(items, SettlementBatchDTO{ID: row.ID, SiteID: row.SiteID, BatchNo: row.BatchNo, PeriodStart: row.PeriodStart, PeriodEnd: row.PeriodEnd, TotalAmountCent: row.TotalAmountCent, Status: row.Status})
+ }
+ return items, nil
+}
+
+func (r *Repository) CreateSettlementBatch(ctx context.Context, adminID uint64, req CreateSettlementBatchRequest) (*SettlementBatchDTO, error) {
+ if req.PeriodEnd.Before(req.PeriodStart) {
+ return nil, ErrInvalidPeriod
+ }
+ row := model.DistributionSettlementBatch{SiteID: req.SiteID, BatchNo: "DS" + time.Now().Format("20060102150405.000000000"), PeriodStart: req.PeriodStart, PeriodEnd: req.PeriodEnd, Status: "draft", CreatedBy: &adminID}
+ if err := r.db.WithContext(ctx).Create(&row).Error; err != nil {
+ return nil, err
+ }
+ return &SettlementBatchDTO{ID: row.ID, SiteID: row.SiteID, BatchNo: row.BatchNo, PeriodStart: row.PeriodStart, PeriodEnd: row.PeriodEnd, TotalAmountCent: row.TotalAmountCent, Status: row.Status}, nil
+}
+
+func (r *Repository) MarkSettlementBatch(ctx context.Context, id uint64) (*SettlementBatchDTO, error) {
+ var row model.DistributionSettlementBatch
+ if err := r.db.WithContext(ctx).First(&row, id).Error; err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, ErrSettlementBatchNotFound
+ }
+ return nil, err
+ }
+ if row.Status == "settled" {
+ return nil, ErrBatchAlreadySettled
+ }
+ now := time.Now()
+ row.Status = "settled"
+ row.SettledAt = &now
+ if err := r.db.WithContext(ctx).Save(&row).Error; err != nil {
+ return nil, err
+ }
+ return &SettlementBatchDTO{ID: row.ID, SiteID: row.SiteID, BatchNo: row.BatchNo, PeriodStart: row.PeriodStart, PeriodEnd: row.PeriodEnd, TotalAmountCent: row.TotalAmountCent, Status: row.Status}, nil
+}
+
+func (r *Repository) CreateFinancialSnapshot(ctx context.Context, orderID uint64, req CreateFinancialSnapshotRequest) (*DistributionOrderDTO, error) {
+ var rule model.DistributionRuleVersion
+ if err := r.db.WithContext(ctx).First(&rule, req.RuleVersionID).Error; err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, ErrRuleNotFound
+ }
+ return nil, err
+ }
+ if req.SiteID == 0 || orderID == 0 {
+ return nil, ErrInvalidAmount
+ }
+ partyInputs := make([]PartyProfitInput, 0, len(req.Parties))
+ for _, party := range req.Parties {
+ partyInputs = append(partyInputs, PartyProfitInput{PartyRole: party.PartyRole, IncomeAmountCent: party.IncomeAmountCent, CostAmountCent: party.CostAmountCent})
+ }
+ result, err := CalculateSettlement(Rule{ServiceFeeValue: rule.ServiceFeeValue, ServiceFeeUnit: rule.ServiceFeeUnit, PaymentFeeBPS: rule.PaymentFeeBPS, TaxEnabled: rule.TaxEnabled, TaxRateBPS: rule.TaxRateBPS}, SettlementInput{GrossPaidAmountCent: req.GrossPaidAmountCent, RefundAmountCent: req.RefundAmountCent, MarkupAmountCent: req.MarkupAmountCent, GrossProfitByParty: partyInputs})
+ if err != nil {
+ return nil, err
+ }
+ err = r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
+ financial := model.DistributionOrderFinancial{OrderID: orderID, SiteID: req.SiteID, RuleVersionID: req.RuleVersionID, GrossPaidAmountCent: result.GrossPaidAmountCent, RefundAmountCent: result.RefundAmountCent, NetPaidAmountCent: result.NetPaidAmountCent, PaymentFeeBPS: rule.PaymentFeeBPS, PaymentFeeCent: result.PaymentFeeCent, ServiceFeeValue: rule.ServiceFeeValue, ServiceFeeUnit: rule.ServiceFeeUnit, ServiceFeeCent: result.ServiceFeeCent, MarkupAmountCent: result.MarkupAmountCent, TaxEnabled: rule.TaxEnabled, TaxRateBPS: rule.TaxRateBPS, TaxAmountCent: result.TaxAmountCent, SplitBaseAmountCent: result.SplitBaseAmountCent, SettlementStatus: "pending"}
+ if err := tx.Where("order_id = ?", orderID).Assign(financial).FirstOrCreate(&financial).Error; err != nil {
+ return err
+ }
+ if err := tx.Where("order_id = ?", orderID).Delete(&model.DistributionOrderParty{}).Error; err != nil {
+ return err
+ }
+ for idx, party := range req.Parties {
+ tax := int64(0)
+ if idx < len(result.TaxByParty) {
+ tax = result.TaxByParty[idx].TaxAmountCent
+ }
+ profit := party.IncomeAmountCent - party.CostAmountCent
+ row := model.DistributionOrderParty{OrderID: orderID, SiteID: req.SiteID, PartyRole: party.PartyRole, PartyType: party.PartyType, PartyID: party.PartyID, Ratio: party.Ratio, IncomeAmountCent: party.IncomeAmountCent, CostAmountCent: party.CostAmountCent, ProfitAmountCent: profit, TaxAmountCent: tax}
+ if err := tx.Create(&row).Error; err != nil {
+ return err
+ }
+ }
+ return tx.Model(&model.RentalOrder{}).Where("id = ?", orderID).Updates(map[string]any{"distribution_site_id": req.SiteID, "distribution_rule_version_id": req.RuleVersionID}).Error
+ })
+ if err != nil {
+ return nil, err
+ }
+ return r.FindOrder(ctx, orderID)
+}
diff --git a/backend/internal/modules/distribution/repository_test.go b/backend/internal/modules/distribution/repository_test.go
new file mode 100644
index 0000000..bf98979
--- /dev/null
+++ b/backend/internal/modules/distribution/repository_test.go
@@ -0,0 +1,87 @@
+package distribution
+
+import (
+ "testing"
+ "time"
+
+ "hfb_sys/backend/internal/model"
+
+ "gorm.io/driver/sqlite"
+ "gorm.io/gorm"
+)
+
+func distributionTestDB(t *testing.T) *gorm.DB {
+ t.Helper()
+ db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := db.AutoMigrate(&model.ChatConversation{}, &model.RentalOrder{}, &model.DistributionSite{}, &model.DistributionRuleVersion{}, &model.DistributionOrderFinancial{}, &model.DistributionOrderParty{}, &model.DistributionSiteAccount{}, &model.DistributionSitePaymentChannel{}, &model.DistributionSettlementBatch{}, &model.DistributionSettlementItem{}); err != nil {
+ t.Fatal(err)
+ }
+ return db
+}
+
+func TestDistributionOrderSnapshotAndQueries(t *testing.T) {
+ db := distributionTestDB(t)
+ repo := NewRepository(db)
+ site := model.DistributionSite{SiteCode: "demo", SiteName: "演示站", Domain: "distribution.example.com", DisplayAccountEnabled: true, TaxRateBPS: 100}
+ if err := db.Create(&site).Error; err != nil {
+ t.Fatal(err)
+ }
+ now := time.Now()
+ rule := model.DistributionRuleVersion{SiteID: &site.ID, Mode: ModeTwoPartyDistributorReceive, Version: 1, Status: "active", ServiceFeeValue: 0, ServiceFeeUnit: ServiceFeeUnitFixedAmount, PaymentFeeBPS: 50, TaxRateBPS: 100, EffectiveFrom: now}
+ if err := db.Create(&rule).Error; err != nil {
+ t.Fatal(err)
+ }
+ order := model.RentalOrder{OrderNo: "D-1", ListingID: 1, AccountID: 2, OwnerID: 3, RenterID: 4, Status: "completed"}
+ if err := db.Create(&order).Error; err != nil {
+ t.Fatal(err)
+ }
+ _, err := repo.CreateFinancialSnapshot(t.Context(), order.ID, CreateFinancialSnapshotRequest{SiteID: site.ID, RuleVersionID: rule.ID, GrossPaidAmountCent: 10001, RefundAmountCent: 1000, MarkupAmountCent: 300, Parties: []SnapshotPartyRequest{{PartyRole: "distributor_receive", PartyType: "site", PartyID: site.ID, IncomeAmountCent: 1000, CostAmountCent: 400}}})
+ if err != nil {
+ t.Fatal(err)
+ }
+ page, err := repo.ListOrders(t.Context(), DistributionOrderListQuery{SiteID: site.ID, Kind: "receive", Page: 1, Size: 20})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if page.Total != 1 || len(page.Items) != 1 || page.Items[0].PaymentFeeCent != 45 || page.Items[0].NetPaidAmountCent != 9001 {
+ t.Fatalf("unexpected page: %#v", page)
+ }
+ if _, err := repo.FindOrder(t.Context(), order.ID); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestDistributionPaymentChannelAndSettlementBatch(t *testing.T) {
+ db := distributionTestDB(t)
+ repo := NewRepository(db)
+ site := model.DistributionSite{SiteCode: "demo", SiteName: "演示站", Domain: "distribution.example.com", TaxRateBPS: 100}
+ if err := db.Create(&site).Error; err != nil {
+ t.Fatal(err)
+ }
+ channel, err := repo.BindPaymentChannel(t.Context(), site.ID, BindPaymentChannelRequest{ConfigID: 9, Priority: 1})
+ if err != nil {
+ t.Fatal(err)
+ }
+ items, err := repo.ListPaymentChannels(t.Context(), site.ID)
+ if err != nil || len(items) != 1 || items[0].ConfigID != 9 {
+ t.Fatalf("channels = %#v, err=%v", items, err)
+ }
+ if err := repo.UnbindPaymentChannel(t.Context(), channel.ID); err != nil {
+ t.Fatal(err)
+ }
+ start := time.Now().Add(-time.Hour)
+ batch, err := repo.CreateSettlementBatch(t.Context(), 1, CreateSettlementBatchRequest{SiteID: site.ID, PeriodStart: start, PeriodEnd: time.Now()})
+ if err != nil {
+ t.Fatal(err)
+ }
+ settled, err := repo.MarkSettlementBatch(t.Context(), batch.ID)
+ if err != nil || settled.Status != "settled" {
+ t.Fatalf("settled = %#v, err=%v", settled, err)
+ }
+ if _, err := repo.MarkSettlementBatch(t.Context(), batch.ID); err != ErrBatchAlreadySettled {
+ t.Fatalf("second settle err = %v", err)
+ }
+}
diff --git a/backend/internal/modules/distribution/service.go b/backend/internal/modules/distribution/service.go
new file mode 100644
index 0000000..881aa7b
--- /dev/null
+++ b/backend/internal/modules/distribution/service.go
@@ -0,0 +1,129 @@
+package distribution
+
+import (
+ "context"
+ "errors"
+)
+
+var ErrDependencyUnavailable = errors.New("distribution dependency unavailable")
+
+type Service struct{ repo *Repository }
+
+func NewService(repo *Repository) *Service { return &Service{repo: repo} }
+
+func (s *Service) available() error {
+ if s == nil || s.repo == nil {
+ return ErrDependencyUnavailable
+ }
+ return nil
+}
+func (s *Service) ListSites(ctx context.Context) ([]SiteDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.ListSites(ctx)
+}
+func (s *Service) FindSite(ctx context.Context, id uint64) (*SiteDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.FindSite(ctx, id)
+}
+func (s *Service) CreateSite(ctx context.Context, req CreateSiteRequest) (*SiteDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.CreateSite(ctx, req)
+}
+func (s *Service) UpdateSite(ctx context.Context, id uint64, req UpdateSiteRequest) (*SiteDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.UpdateSite(ctx, id, req)
+}
+func (s *Service) ListRules(ctx context.Context, siteID *uint64, mode string) ([]RuleDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.ListRules(ctx, siteID, mode)
+}
+func (s *Service) CreateRule(ctx context.Context, adminID uint64, req CreateRuleRequest) (*RuleDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.CreateRule(ctx, adminID, req)
+}
+func (s *Service) UpdateRuleStatus(ctx context.Context, id uint64, status string) (*RuleDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.UpdateRuleStatus(ctx, id, status)
+}
+
+func (s *Service) ListOrders(ctx context.Context, query DistributionOrderListQuery) (*DistributionOrderPage, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.ListOrders(ctx, query)
+}
+func (s *Service) FindOrder(ctx context.Context, id uint64) (*DistributionOrderDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.FindOrder(ctx, id)
+}
+func (s *Service) ListAccountDisplays(ctx context.Context, siteID uint64, status string) ([]AccountDisplayDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.ListAccountDisplays(ctx, siteID, status)
+}
+func (s *Service) UpdateAccountDisplay(ctx context.Context, id uint64, status string) (*AccountDisplayDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.UpdateAccountDisplay(ctx, id, status)
+}
+func (s *Service) ListPaymentChannels(ctx context.Context, siteID uint64) ([]PaymentChannelDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.ListPaymentChannels(ctx, siteID)
+}
+func (s *Service) BindPaymentChannel(ctx context.Context, siteID uint64, req BindPaymentChannelRequest) (*PaymentChannelDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.BindPaymentChannel(ctx, siteID, req)
+}
+func (s *Service) UnbindPaymentChannel(ctx context.Context, id uint64) error {
+ if err := s.available(); err != nil {
+ return err
+ }
+ return s.repo.UnbindPaymentChannel(ctx, id)
+}
+func (s *Service) ListSettlementBatches(ctx context.Context, siteID uint64) ([]SettlementBatchDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.ListSettlementBatches(ctx, siteID)
+}
+func (s *Service) CreateSettlementBatch(ctx context.Context, adminID uint64, req CreateSettlementBatchRequest) (*SettlementBatchDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.CreateSettlementBatch(ctx, adminID, req)
+}
+func (s *Service) MarkSettlementBatch(ctx context.Context, id uint64) (*SettlementBatchDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.MarkSettlementBatch(ctx, id)
+}
+
+func (s *Service) CreateFinancialSnapshot(ctx context.Context, orderID uint64, req CreateFinancialSnapshotRequest) (*DistributionOrderDTO, error) {
+ if err := s.available(); err != nil {
+ return nil, err
+ }
+ return s.repo.CreateFinancialSnapshot(ctx, orderID, req)
+}
diff --git a/backend/internal/router/router.go b/backend/internal/router/router.go
index 615d855..06fc017 100644
--- a/backend/internal/router/router.go
+++ b/backend/internal/router/router.go
@@ -22,6 +22,7 @@ import (
"hfb_sys/backend/internal/modules/chat"
"hfb_sys/backend/internal/modules/chathub"
"hfb_sys/backend/internal/modules/dispute"
+ "hfb_sys/backend/internal/modules/distribution"
filemodule "hfb_sys/backend/internal/modules/file"
"hfb_sys/backend/internal/modules/listing"
"hfb_sys/backend/internal/modules/mohong"
@@ -308,6 +309,13 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
}
systemConfigService := systemconfig.NewService(systemConfigRepo)
systemConfigHandler := systemconfig.NewHandler(systemConfigService)
+ var distributionRepo *distribution.Repository
+ if deps.DB != nil {
+ distributionRepo = distribution.NewRepository(deps.DB)
+ }
+ distributionService := distribution.NewService(distributionRepo)
+ distributionHandler := distribution.NewHandler(distributionService)
+ backupMonitorHandler := backupmonitor.NewHandler(cfg.BackupStatusFile)
var adminRoleRepo *adminrole.Repository
if deps.DB != nil {
adminRoleRepo = adminrole.NewRepository(deps.DB, deps.Redis)
@@ -588,6 +596,24 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
adminRoutes.POST("/auth/logout", adminAuthHandler.Logout)
adminRoutes.POST("/files/upload", fileHandler.Upload)
adminRoutes.GET("/dashboard", requirePerm("dashboard:view"), adminDashboardHandler.Summary)
+ adminRoutes.GET("/distribution/sites", requirePerm("distribution:site_view"), distributionHandler.ListSites)
+ adminRoutes.GET("/distribution/sites/:id", requirePerm("distribution:site_view"), distributionHandler.GetSite)
+ adminRoutes.POST("/distribution/sites", requirePerm("distribution:site_manage"), distributionHandler.CreateSite)
+ adminRoutes.PUT("/distribution/sites/:id", requirePerm("distribution:site_manage"), distributionHandler.UpdateSite)
+ adminRoutes.GET("/distribution/rules", requirePerm("distribution:rule_view"), distributionHandler.ListRules)
+ adminRoutes.POST("/distribution/rules", requirePerm("distribution:rule_manage"), distributionHandler.CreateRule)
+ adminRoutes.PUT("/distribution/rules/:id/status", requirePerm("distribution:rule_manage"), distributionHandler.UpdateRuleStatus)
+ adminRoutes.GET("/distribution/orders", requirePerm("distribution:order_view"), distributionHandler.ListOrders)
+ adminRoutes.GET("/distribution/orders/:id", requirePerm("distribution:order_view"), distributionHandler.FindOrder)
+ adminRoutes.POST("/distribution/orders/:id/financial-snapshot", requirePerm("distribution:settlement_manage"), distributionHandler.CreateFinancialSnapshot)
+ adminRoutes.GET("/distribution/accounts", requirePerm("distribution:account_view"), distributionHandler.ListAccountDisplays)
+ adminRoutes.PUT("/distribution/accounts/:id/display", requirePerm("distribution:account_display"), distributionHandler.UpdateAccountDisplay)
+ adminRoutes.GET("/distribution/payment-channels", requirePerm("distribution:payment_view"), distributionHandler.ListPaymentChannels)
+ adminRoutes.POST("/distribution/payment-channels", requirePerm("distribution:payment_manage"), distributionHandler.BindPaymentChannel)
+ adminRoutes.DELETE("/distribution/payment-channels/:id", requirePerm("distribution:payment_manage"), distributionHandler.UnbindPaymentChannel)
+ adminRoutes.GET("/distribution/settlement-batches", requirePerm("distribution:settlement_view"), distributionHandler.ListSettlementBatches)
+ adminRoutes.POST("/distribution/settlement-batches", requirePerm("distribution:settlement_manage"), distributionHandler.CreateSettlementBatch)
+ adminRoutes.POST("/distribution/settlement-batches/:id/settle", requirePerm("distribution:settlement_manage"), distributionHandler.MarkSettlementBatch)
adminRoutes.GET("/files/object", fileHandler.Object)
adminRoutes.GET("/users", requirePerm("user:view"), adminUserHandler.List)
adminRoutes.POST("/users/:id/freeze", requirePerm("user:freeze"), adminUserHandler.Freeze)
diff --git a/backend/migrations/000062_distribution_init.sql b/backend/migrations/000062_distribution_init.sql
new file mode 100644
index 0000000..b371a7e
--- /dev/null
+++ b/backend/migrations/000062_distribution_init.sql
@@ -0,0 +1,259 @@
+-- +goose Up
+
+CREATE TABLE distribution_sites (
+ id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+ site_code VARCHAR(64) NOT NULL,
+ site_name VARCHAR(128) NOT NULL,
+ domain VARCHAR(255) NOT NULL,
+ status VARCHAR(16) NOT NULL DEFAULT 'active',
+ display_account_enabled TINYINT(1) NOT NULL DEFAULT 1,
+ default_support_group_id BIGINT UNSIGNED NULL,
+ tax_enabled TINYINT(1) NOT NULL DEFAULT 0,
+ tax_rate_bps BIGINT NOT NULL DEFAULT 100,
+ created_at DATETIME NOT NULL,
+ updated_at DATETIME NOT NULL,
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_distribution_sites_code (site_code),
+ UNIQUE KEY uk_distribution_sites_domain (domain),
+ KEY idx_distribution_sites_status (status)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销网站主体';
+
+CREATE TABLE distribution_site_members (
+ id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+ site_id BIGINT UNSIGNED NOT NULL,
+ admin_user_id BIGINT UNSIGNED NOT NULL,
+ member_type VARCHAR(32) NOT NULL,
+ status VARCHAR(16) NOT NULL DEFAULT 'active',
+ data_scope VARCHAR(32) NOT NULL DEFAULT 'site',
+ created_at DATETIME NOT NULL,
+ updated_at DATETIME NOT NULL,
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_distribution_site_member (site_id, admin_user_id),
+ KEY idx_distribution_site_members_admin (admin_user_id, status),
+ CONSTRAINT fk_distribution_site_members_site FOREIGN KEY (site_id) REFERENCES distribution_sites (id),
+ CONSTRAINT fk_distribution_site_members_admin FOREIGN KEY (admin_user_id) REFERENCES admin_users (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销网站后台成员';
+
+CREATE TABLE distribution_site_accounts (
+ id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+ site_id BIGINT UNSIGNED NOT NULL,
+ account_id BIGINT UNSIGNED NOT NULL,
+ listing_id BIGINT UNSIGNED NOT NULL,
+ display_status VARCHAR(16) NOT NULL DEFAULT 'enabled',
+ display_started_at DATETIME NULL,
+ display_ended_at DATETIME NULL,
+ created_at DATETIME NOT NULL,
+ updated_at DATETIME NOT NULL,
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_distribution_site_account (site_id, account_id),
+ KEY idx_distribution_site_accounts_listing (listing_id),
+ KEY idx_distribution_site_accounts_display (site_id, display_status),
+ CONSTRAINT fk_distribution_site_accounts_site FOREIGN KEY (site_id) REFERENCES distribution_sites (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销网站账号展示关系';
+
+CREATE TABLE distribution_rule_versions (
+ id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+ site_id BIGINT UNSIGNED NULL,
+ mode VARCHAR(32) NOT NULL,
+ version INT NOT NULL,
+ status VARCHAR(16) NOT NULL DEFAULT 'draft',
+ platform_receive_ratio DECIMAL(10,2) NOT NULL DEFAULT 0,
+ platform_sell_ratio DECIMAL(10,2) NOT NULL DEFAULT 0,
+ distributor_receive_ratio DECIMAL(10,2) NOT NULL DEFAULT 0,
+ distributor_sell_ratio DECIMAL(10,2) NOT NULL DEFAULT 0,
+ distributor_a_ratio DECIMAL(10,2) NOT NULL DEFAULT 0,
+ distributor_b_ratio DECIMAL(10,2) NOT NULL DEFAULT 0,
+ service_fee_value DECIMAL(12,4) NOT NULL DEFAULT 0,
+ service_fee_unit VARCHAR(24) NOT NULL DEFAULT 'ratio',
+ payment_fee_bps BIGINT NOT NULL DEFAULT 50,
+ tax_enabled TINYINT(1) NOT NULL DEFAULT 0,
+ tax_rate_bps BIGINT NOT NULL DEFAULT 100,
+ effective_from DATETIME NOT NULL,
+ effective_to DATETIME NULL,
+ created_by BIGINT UNSIGNED NULL,
+ created_at DATETIME NOT NULL,
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_distribution_rule_version (site_id, mode, version),
+ KEY idx_distribution_rule_effective (site_id, mode, status, effective_from),
+ CONSTRAINT fk_distribution_rule_site FOREIGN KEY (site_id) REFERENCES distribution_sites (id),
+ CONSTRAINT fk_distribution_rule_admin FOREIGN KEY (created_by) REFERENCES admin_users (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销分账规则版本';
+
+CREATE TABLE distribution_order_financials (
+ id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+ order_id BIGINT UNSIGNED NOT NULL,
+ site_id BIGINT UNSIGNED NOT NULL,
+ rule_version_id BIGINT UNSIGNED NOT NULL,
+ gross_paid_amount_cent BIGINT NOT NULL DEFAULT 0,
+ refund_amount_cent BIGINT NOT NULL DEFAULT 0,
+ net_paid_amount_cent BIGINT NOT NULL DEFAULT 0,
+ payment_fee_bps BIGINT NOT NULL DEFAULT 50,
+ payment_fee_cent BIGINT NOT NULL DEFAULT 0,
+ service_fee_value DECIMAL(12,4) NOT NULL DEFAULT 0,
+ service_fee_unit VARCHAR(24) NOT NULL DEFAULT 'ratio',
+ service_fee_cent BIGINT NOT NULL DEFAULT 0,
+ markup_amount_cent BIGINT NOT NULL DEFAULT 0,
+ tax_enabled TINYINT(1) NOT NULL DEFAULT 0,
+ tax_rate_bps BIGINT NOT NULL DEFAULT 100,
+ tax_amount_cent BIGINT NOT NULL DEFAULT 0,
+ split_base_amount_cent BIGINT NOT NULL DEFAULT 0,
+ rounding_adjustment_cent BIGINT NOT NULL DEFAULT 0,
+ settlement_status VARCHAR(24) NOT NULL DEFAULT 'pending',
+ settled_at DATETIME NULL,
+ created_at DATETIME NOT NULL,
+ updated_at DATETIME NOT NULL,
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_distribution_order_financial_order (order_id),
+ KEY idx_distribution_order_financial_site_status (site_id, settlement_status),
+ CONSTRAINT fk_distribution_order_financial_order FOREIGN KEY (order_id) REFERENCES rental_orders (id),
+ CONSTRAINT fk_distribution_order_financial_site FOREIGN KEY (site_id) REFERENCES distribution_sites (id),
+ CONSTRAINT fk_distribution_order_financial_rule FOREIGN KEY (rule_version_id) REFERENCES distribution_rule_versions (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销正常订单财务快照';
+
+CREATE TABLE distribution_order_parties (
+ id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+ order_id BIGINT UNSIGNED NOT NULL,
+ site_id BIGINT UNSIGNED NOT NULL,
+ party_role VARCHAR(32) NOT NULL,
+ party_type VARCHAR(16) NOT NULL,
+ party_id BIGINT UNSIGNED NOT NULL,
+ ratio DECIMAL(10,2) NOT NULL DEFAULT 0,
+ income_amount_cent BIGINT NOT NULL DEFAULT 0,
+ cost_amount_cent BIGINT NOT NULL DEFAULT 0,
+ profit_amount_cent BIGINT NOT NULL DEFAULT 0,
+ tax_amount_cent BIGINT NOT NULL DEFAULT 0,
+ created_at DATETIME NOT NULL,
+ updated_at DATETIME NOT NULL,
+ PRIMARY KEY (id),
+ KEY idx_distribution_order_parties_order (order_id),
+ KEY idx_distribution_order_parties_site_role (site_id, party_role),
+ CONSTRAINT fk_distribution_order_parties_order FOREIGN KEY (order_id) REFERENCES rental_orders (id),
+ CONSTRAINT fk_distribution_order_parties_site FOREIGN KEY (site_id) REFERENCES distribution_sites (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销订单参与方快照';
+
+CREATE TABLE distribution_settlement_batches (
+ id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+ site_id BIGINT UNSIGNED NOT NULL,
+ batch_no VARCHAR(64) NOT NULL,
+ period_start DATETIME NOT NULL,
+ period_end DATETIME NOT NULL,
+ total_amount_cent BIGINT NOT NULL DEFAULT 0,
+ status VARCHAR(16) NOT NULL DEFAULT 'draft',
+ created_by BIGINT UNSIGNED NULL,
+ settled_at DATETIME NULL,
+ created_at DATETIME NOT NULL,
+ updated_at DATETIME NOT NULL,
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_distribution_settlement_batch_no (batch_no),
+ KEY idx_distribution_settlement_batches_site_status (site_id, status),
+ CONSTRAINT fk_distribution_settlement_batches_site FOREIGN KEY (site_id) REFERENCES distribution_sites (id),
+ CONSTRAINT fk_distribution_settlement_batches_admin FOREIGN KEY (created_by) REFERENCES admin_users (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销结算批次';
+
+CREATE TABLE distribution_settlement_items (
+ id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+ batch_id BIGINT UNSIGNED NOT NULL,
+ order_id BIGINT UNSIGNED NOT NULL,
+ party_id BIGINT UNSIGNED NOT NULL,
+ amount_cent BIGINT NOT NULL DEFAULT 0,
+ status VARCHAR(16) NOT NULL DEFAULT 'pending',
+ created_at DATETIME NOT NULL,
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_distribution_settlement_item (batch_id, order_id, party_id),
+ KEY idx_distribution_settlement_items_order (order_id),
+ CONSTRAINT fk_distribution_settlement_items_batch FOREIGN KEY (batch_id) REFERENCES distribution_settlement_batches (id),
+ CONSTRAINT fk_distribution_settlement_items_order FOREIGN KEY (order_id) REFERENCES rental_orders (id),
+ CONSTRAINT fk_distribution_settlement_items_party FOREIGN KEY (party_id) REFERENCES distribution_order_parties (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销结算明细';
+
+CREATE TABLE distribution_site_payment_channels (
+ id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+ site_id BIGINT UNSIGNED NOT NULL,
+ config_id BIGINT UNSIGNED NOT NULL,
+ priority INT NOT NULL DEFAULT 0,
+ status VARCHAR(16) NOT NULL DEFAULT 'active',
+ created_at DATETIME NOT NULL,
+ updated_at DATETIME NOT NULL,
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_distribution_site_payment (site_id, config_id),
+ KEY idx_distribution_site_payment_status (site_id, status, priority),
+ CONSTRAINT fk_distribution_site_payment_site FOREIGN KEY (site_id) REFERENCES distribution_sites (id),
+ CONSTRAINT fk_distribution_site_payment_config FOREIGN KEY (config_id) REFERENCES payment_merchant_configs (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销网站收款通道绑定';
+
+ALTER TABLE rental_orders
+ ADD COLUMN distribution_site_id BIGINT UNSIGNED NULL AFTER source_channel,
+ ADD COLUMN distribution_mode VARCHAR(32) NOT NULL DEFAULT '' AFTER distribution_site_id,
+ ADD COLUMN distribution_rule_version_id BIGINT UNSIGNED NULL AFTER distribution_mode,
+ ADD KEY idx_rental_orders_distribution_site (distribution_site_id, distribution_mode),
+ ADD CONSTRAINT fk_rental_orders_distribution_site FOREIGN KEY (distribution_site_id) REFERENCES distribution_sites (id),
+ ADD CONSTRAINT fk_rental_orders_distribution_rule FOREIGN KEY (distribution_rule_version_id) REFERENCES distribution_rule_versions (id);
+
+INSERT INTO permissions (code, name, resource, action) VALUES
+('distribution:site_view', '查看分销网站', 'distribution', 'site_view'),
+('distribution:site_manage', '管理分销网站', 'distribution', 'site_manage'),
+('distribution:order_view', '查看分销订单', 'distribution', 'order_view'),
+('distribution:process_view', '查看分销订单过程', 'distribution', 'process_view'),
+('distribution:finance_view', '查看分销财务', 'distribution', 'finance_view'),
+('distribution:settlement_view', '查看分销结算', 'distribution', 'settlement_view'),
+('distribution:settlement_manage', '管理分销结算', 'distribution', 'settlement_manage'),
+('distribution:rule_view', '查看分销规则', 'distribution', 'rule_view'),
+('distribution:rule_manage', '管理分销规则', 'distribution', 'rule_manage'),
+('distribution:account_view', '查看分销账号', 'distribution', 'account_view'),
+('distribution:account_display', '管理分销账号展示', 'distribution', 'account_display'),
+('distribution:payment_view', '查看分销支付通道', 'distribution', 'payment_view'),
+('distribution:payment_manage', '管理分销支付通道', 'distribution', 'payment_manage'),
+('distribution:chat_view', '查看分销客服消息', 'distribution', 'chat_view'),
+('distribution:chat_send', '发送分销客服消息', 'distribution', 'chat_send')
+ON DUPLICATE KEY UPDATE name = VALUES(name), resource = VALUES(resource), action = VALUES(action);
+
+INSERT IGNORE INTO role_permissions (role_id, permission_id)
+SELECT r.id, p.id FROM roles r CROSS JOIN permissions p
+WHERE r.code = 'super_admin' AND p.code LIKE 'distribution:%';
+
+INSERT INTO distribution_rule_versions (
+ site_id, mode, version, status,
+ platform_receive_ratio, platform_sell_ratio,
+ distributor_receive_ratio, distributor_sell_ratio,
+ distributor_a_ratio, distributor_b_ratio,
+ service_fee_value, service_fee_unit, payment_fee_bps,
+ tax_enabled, tax_rate_bps, effective_from, created_at
+) VALUES
+ (NULL, 'two_party_distributor_receive', 1, 'active', 0, 4, 3.5, 0, 0, 0, 2.5, 'ratio', 50, 0, 100, NOW(), NOW()),
+ (NULL, 'two_party_platform_receive', 1, 'active', 3.5, 0, 0, 4, 0, 0, 2.5, 'ratio', 50, 0, 100, NOW(), NOW()),
+ (NULL, 'three_party', 1, 'active', 0, 0, 0, 0, 3.5, 4, 2.5, 'ratio', 50, 0, 100, NOW(), NOW())
+ON DUPLICATE KEY UPDATE
+ status = VALUES(status),
+ service_fee_value = VALUES(service_fee_value),
+ service_fee_unit = VALUES(service_fee_unit),
+ payment_fee_bps = VALUES(payment_fee_bps);
+
+-- +goose Down
+
+DROP TABLE IF EXISTS distribution_order_financials;
+DROP TABLE IF EXISTS distribution_site_payment_channels;
+DROP TABLE IF EXISTS distribution_settlement_items;
+DROP TABLE IF EXISTS distribution_settlement_batches;
+DROP TABLE IF EXISTS distribution_order_parties;
+ALTER TABLE rental_orders
+ DROP FOREIGN KEY fk_rental_orders_distribution_rule,
+ DROP FOREIGN KEY fk_rental_orders_distribution_site,
+ DROP KEY idx_rental_orders_distribution_site,
+ DROP COLUMN distribution_rule_version_id,
+ DROP COLUMN distribution_mode,
+ DROP COLUMN distribution_site_id;
+DROP TABLE IF EXISTS distribution_rule_versions;
+DROP TABLE IF EXISTS distribution_site_accounts;
+DROP TABLE IF EXISTS distribution_site_members;
+DROP TABLE IF EXISTS distribution_sites;
+
+DELETE rp FROM role_permissions rp
+JOIN permissions p ON p.id = rp.permission_id
+WHERE p.code LIKE 'distribution:%';
+
+DELETE FROM permissions WHERE code IN (
+ 'distribution:site_view', 'distribution:site_manage', 'distribution:order_view', 'distribution:process_view',
+ 'distribution:finance_view', 'distribution:settlement_view', 'distribution:settlement_manage',
+ 'distribution:rule_view', 'distribution:rule_manage', 'distribution:account_view', 'distribution:account_display',
+ 'distribution:payment_view', 'distribution:payment_manage', 'distribution:chat_view', 'distribution:chat_send'
+);
diff --git a/deploy/caddy/Caddyfile b/deploy/caddy/Caddyfile
index 9bc5cc0..3c513db 100644
--- a/deploy/caddy/Caddyfile
+++ b/deploy/caddy/Caddyfile
@@ -68,6 +68,17 @@
}
}
+# 分销管理后台使用独立 React + Ant Design 静态资源,API 与主站复用 backend。
+distribution.{$CADDY_DOMAIN} {
+ encode zstd gzip
+ import public_api
+ handle {
+ root * /srv/distribution-admin
+ try_files {path} /index.html
+ file_server
+ }
+}
+
# 选号网(hfb_show):仅当 CADDY_SHOW_DOMAIN 有值时由部署脚本启用 conf.d/show.caddy。
# 未配置时 conf.d 为空,不影响主站。
import /etc/caddy/conf.d/*.caddy
@@ -86,3 +97,8 @@ monitor.{$CADDY_DOMAIN} {
}
}
}
+
+# gitea 自托管代码库
+gitea.hao858.com {
+ reverse_proxy gitea:3000
+}
diff --git a/deploy/caddy/Dockerfile b/deploy/caddy/Dockerfile
index b058afb..a8dd135 100644
--- a/deploy/caddy/Dockerfile
+++ b/deploy/caddy/Dockerfile
@@ -10,11 +10,21 @@ RUN npm install
COPY frontend/ .
RUN npm run build
-# 第二阶段:Caddy 作为公网入口,托管主站 + 选号网静态资源
+# 第二套独立分销管理后台(React + Ant Design)
+FROM node:24-alpine AS distribution-build
+WORKDIR /src
+COPY distribution-admin/package*.json ./
+RUN --mount=type=cache,target=/root/.npm \
+ npm ci --prefer-offline --no-audit --fund=false
+COPY distribution-admin/ .
+RUN npm run build
+
+# 第三阶段:Caddy 作为公网入口,托管主站、分销后台和选号网静态资源
FROM caddy:2.10-alpine
COPY --from=build /src/dist /srv/dist
# show-dist 由 deploy-prod.sh 在 build 前准备(hfb_show 构建产物或占位页)
COPY deploy/caddy/show-dist /srv/show-dist
+COPY --from=distribution-build /src/dist /srv/distribution-admin
EXPOSE 80 443
diff --git a/distribution-admin/Dockerfile b/distribution-admin/Dockerfile
new file mode 100644
index 0000000..72bdc90
--- /dev/null
+++ b/distribution-admin/Dockerfile
@@ -0,0 +1,10 @@
+FROM node:22-alpine AS build
+WORKDIR /app
+COPY package*.json ./
+RUN npm install
+COPY . .
+RUN npm run build
+
+FROM nginx:1.27-alpine
+COPY --from=build /app/dist /usr/share/nginx/html
+COPY nginx.conf /etc/nginx/conf.d/default.conf
diff --git a/distribution-admin/index.html b/distribution-admin/index.html
new file mode 100644
index 0000000..5da73cf
--- /dev/null
+++ b/distribution-admin/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ 分销网管理后台
+
+
+
+
+
+
diff --git a/distribution-admin/nginx.conf b/distribution-admin/nginx.conf
new file mode 100644
index 0000000..a60520b
--- /dev/null
+++ b/distribution-admin/nginx.conf
@@ -0,0 +1,7 @@
+server {
+ listen 80;
+ root /usr/share/nginx/html;
+ index index.html;
+ location /api/ { proxy_pass http://backend:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
+ location / { try_files $uri /index.html; }
+}
diff --git a/distribution-admin/package-lock.json b/distribution-admin/package-lock.json
new file mode 100644
index 0000000..96b3c81
--- /dev/null
+++ b/distribution-admin/package-lock.json
@@ -0,0 +1,3353 @@
+{
+ "name": "hfb-distribution-admin",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "hfb-distribution-admin",
+ "version": "0.1.0",
+ "dependencies": {
+ "@ant-design/icons": "^6.0.0",
+ "antd": "^5.27.6",
+ "axios": "^1.13.2",
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ "react-router-dom": "^7.9.4"
+ },
+ "devDependencies": {
+ "@types/react": "^19.1.13",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react": "^5.0.4",
+ "typescript": "~5.9.3",
+ "vite": "^7.2.4"
+ }
+ },
+ "node_modules/@ant-design/colors": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-8.0.1.tgz",
+ "integrity": "sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/fast-color": "^3.0.0"
+ }
+ },
+ "node_modules/@ant-design/cssinjs": {
+ "version": "1.24.0",
+ "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz",
+ "integrity": "sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "@emotion/hash": "^0.8.0",
+ "@emotion/unitless": "^0.7.5",
+ "classnames": "^2.3.1",
+ "csstype": "^3.1.3",
+ "rc-util": "^5.35.0",
+ "stylis": "^4.3.4"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@ant-design/cssinjs-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz",
+ "integrity": "sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/cssinjs": "^1.21.0",
+ "@babel/runtime": "^7.23.2",
+ "rc-util": "^5.38.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@ant-design/fast-color": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.1.tgz",
+ "integrity": "sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/@ant-design/icons": {
+ "version": "6.3.4",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.3.4.tgz",
+ "integrity": "sha512-kDoUlppczVyCUTFthF6cHmPzMBYqNZQhu9j6EyIX/YIdM9j9CZNZoJUjzVw7/1v1VO8WwVMmPp15r/uwIAS0AQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/colors": "^8.0.1",
+ "@ant-design/icons-svg": "^4.6.0",
+ "@rc-component/util": "^1.11.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@ant-design/icons-svg": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.6.0.tgz",
+ "integrity": "sha512-PRomU725ABMf/lnQp5HiB7my1kjEbFY0D10N4lXYxK6TIB1gKjIVD5MRThpDaezLgw1D774J8eOeeDB0M6wHrQ==",
+ "license": "MIT"
+ },
+ "node_modules/@ant-design/react-slick": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz",
+ "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.4",
+ "classnames": "^2.2.5",
+ "json2mq": "^0.2.0",
+ "resize-observer-polyfill": "^1.5.1",
+ "throttle-debounce": "^5.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz",
+ "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.8",
+ "@babel/types": "^7.29.8",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
+ "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.8"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
+ "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
+ "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
+ "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz",
+ "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.8",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.8",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.8",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
+ "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
+ "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
+ "license": "MIT"
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
+ "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
+ "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
+ "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
+ "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
+ "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
+ "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
+ "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
+ "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
+ "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
+ "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
+ "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
+ "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
+ "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
+ "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
+ "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
+ "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
+ "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
+ "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
+ "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
+ "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
+ "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
+ "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@napi-rs/lzma-linux-x64-gnu": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
+ "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^22.20 || ^24.12 || >=25"
+ }
+ },
+ "node_modules/@rc-component/async-validator": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.1.2.tgz",
+ "integrity": "sha512-WYbrZSjzznU1ekD0qFq2qRxt309VoS61MTG5npnFQlKYcoy9IzU8T+ZCIhq5bGAXRbXysABFWTspicMfmWFwow==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.4"
+ },
+ "engines": {
+ "node": ">=14.x"
+ }
+ },
+ "node_modules/@rc-component/color-picker": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz",
+ "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/fast-color": "^2.0.6",
+ "@babel/runtime": "^7.23.6",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.38.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/color-picker/node_modules/@ant-design/fast-color": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz",
+ "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/@rc-component/context": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz",
+ "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/mini-decimal": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.4.tgz",
+ "integrity": "sha512-xiuXcaCwyOWpD8a8scdExFl+bntNphAW8XeenL1ig2en0AAZY0Pcp4pC0dI22qJ+NvxKn9RoNIoRdqYU3BLH4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/@rc-component/mutate-observer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz",
+ "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/portal": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz",
+ "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/qrcode": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.1.3.tgz",
+ "integrity": "sha512-aGv6alnn4HbDEsURzKP+jv13rbi1VxmAYfBNZr5GKF1iohMNWy5tAVoJ1E3cOvzMB1kbUPvCXchM6zSFlRGPhA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/tour": {
+ "version": "1.15.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.1.tgz",
+ "integrity": "sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "@rc-component/portal": "^1.0.0-9",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/trigger": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.3.1.tgz",
+ "integrity": "sha512-ORENF39PeXTzM+gQEshuk460Z8N4+6DkjpxlpE7Q3gYy1iBpLrx0FOJz3h62ryrJZ/3zCAUIkT1Pb/8hHWpb3A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2",
+ "@rc-component/portal": "^1.1.0",
+ "classnames": "^2.3.2",
+ "rc-motion": "^2.0.0",
+ "rc-resize-observer": "^1.3.1",
+ "rc-util": "^5.44.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/util": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.13.0.tgz",
+ "integrity": "sha512-IXf2zBfZrbQGT+G8LxnoICDBGnMHIPIiq5HCRn3EDfKUx28x1rnbZBj46ULJf4sM8ImTG1SepqfNBdxZziqQ4w==",
+ "license": "MIT",
+ "dependencies": {
+ "is-mobile": "^5.0.0",
+ "react-is": "^19.2.7"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz",
+ "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz",
+ "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz",
+ "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz",
+ "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz",
+ "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz",
+ "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz",
+ "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz",
+ "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz",
+ "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz",
+ "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz",
+ "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz",
+ "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz",
+ "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz",
+ "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz",
+ "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz",
+ "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz",
+ "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz",
+ "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz",
+ "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz",
+ "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz",
+ "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz",
+ "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz",
+ "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz",
+ "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz",
+ "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz",
+ "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.18",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz",
+ "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.7.tgz",
+ "integrity": "sha512-I8bPpDLcHBv1qiIiXDCy71Rt8eQDKJP0sMSWJphDdAcdqiJ1sGpZamavoEIRZmYzjia9LuEb2HlYdDpmoENpvQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz",
+ "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.29.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-rc.3",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.18.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/antd": {
+ "version": "5.29.3",
+ "resolved": "https://registry.npmjs.org/antd/-/antd-5.29.3.tgz",
+ "integrity": "sha512-3DdbGCa9tWAJGcCJ6rzR8EJFsv2CtyEbkVabZE14pfgUHfCicWCj0/QzQVLDYg8CPfQk9BH7fHCoTXHTy7MP/A==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/colors": "^7.2.1",
+ "@ant-design/cssinjs": "^1.23.0",
+ "@ant-design/cssinjs-utils": "^1.1.3",
+ "@ant-design/fast-color": "^2.0.6",
+ "@ant-design/icons": "^5.6.1",
+ "@ant-design/react-slick": "~1.1.2",
+ "@babel/runtime": "^7.26.0",
+ "@rc-component/color-picker": "~2.0.1",
+ "@rc-component/mutate-observer": "^1.1.0",
+ "@rc-component/qrcode": "~1.1.0",
+ "@rc-component/tour": "~1.15.1",
+ "@rc-component/trigger": "^2.3.0",
+ "classnames": "^2.5.1",
+ "copy-to-clipboard": "^3.3.3",
+ "dayjs": "^1.11.11",
+ "rc-cascader": "~3.34.0",
+ "rc-checkbox": "~3.5.0",
+ "rc-collapse": "~3.9.0",
+ "rc-dialog": "~9.6.0",
+ "rc-drawer": "~7.3.0",
+ "rc-dropdown": "~4.2.1",
+ "rc-field-form": "~2.7.1",
+ "rc-image": "~7.12.0",
+ "rc-input": "~1.8.0",
+ "rc-input-number": "~9.5.0",
+ "rc-mentions": "~2.20.0",
+ "rc-menu": "~9.16.1",
+ "rc-motion": "^2.9.5",
+ "rc-notification": "~5.6.4",
+ "rc-pagination": "~5.1.0",
+ "rc-picker": "~4.11.3",
+ "rc-progress": "~4.0.0",
+ "rc-rate": "~2.13.1",
+ "rc-resize-observer": "^1.4.3",
+ "rc-segmented": "~2.7.0",
+ "rc-select": "~14.16.8",
+ "rc-slider": "~11.1.9",
+ "rc-steps": "~6.0.1",
+ "rc-switch": "~4.1.0",
+ "rc-table": "~7.54.0",
+ "rc-tabs": "~15.7.0",
+ "rc-textarea": "~1.10.2",
+ "rc-tooltip": "~6.4.0",
+ "rc-tree": "~5.13.1",
+ "rc-tree-select": "~5.27.0",
+ "rc-upload": "~4.11.0",
+ "rc-util": "^5.44.4",
+ "scroll-into-view-if-needed": "^3.1.0",
+ "throttle-debounce": "^5.0.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ant-design"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/antd/node_modules/@ant-design/colors": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.1.tgz",
+ "integrity": "sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/fast-color": "^2.0.6"
+ }
+ },
+ "node_modules/antd/node_modules/@ant-design/fast-color": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz",
+ "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/antd/node_modules/@ant-design/icons": {
+ "version": "5.6.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.6.1.tgz",
+ "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/colors": "^7.0.0",
+ "@ant-design/icons-svg": "^4.4.0",
+ "@babel/runtime": "^7.24.8",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.31.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/axios": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz",
+ "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.16.0",
+ "form-data": "^4.0.6",
+ "https-proxy-agent": "^5.0.1",
+ "proxy-from-env": "^2.1.0"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.11.21",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz",
+ "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.8",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
+ "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.11.12",
+ "caniuse-lite": "^1.0.30001809",
+ "electron-to-chromium": "^1.5.402",
+ "node-releases": "^2.0.53",
+ "update-browserslist-db": "^1.3.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001810",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
+ "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/classnames": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
+ "license": "MIT"
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/compute-scroll-into-view": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz",
+ "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==",
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/copy-to-clipboard": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+ "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+ "license": "MIT",
+ "dependencies": {
+ "toggle-selection": "^1.0.6"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.23",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz",
+ "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.422",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz",
+ "integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
+ "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.28.2",
+ "@esbuild/android-arm": "0.28.2",
+ "@esbuild/android-arm64": "0.28.2",
+ "@esbuild/android-x64": "0.28.2",
+ "@esbuild/darwin-arm64": "0.28.2",
+ "@esbuild/darwin-x64": "0.28.2",
+ "@esbuild/freebsd-arm64": "0.28.2",
+ "@esbuild/freebsd-x64": "0.28.2",
+ "@esbuild/linux-arm": "0.28.2",
+ "@esbuild/linux-arm64": "0.28.2",
+ "@esbuild/linux-ia32": "0.28.2",
+ "@esbuild/linux-loong64": "0.28.2",
+ "@esbuild/linux-mips64el": "0.28.2",
+ "@esbuild/linux-ppc64": "0.28.2",
+ "@esbuild/linux-riscv64": "0.28.2",
+ "@esbuild/linux-s390x": "0.28.2",
+ "@esbuild/linux-x64": "0.28.2",
+ "@esbuild/netbsd-arm64": "0.28.2",
+ "@esbuild/netbsd-x64": "0.28.2",
+ "@esbuild/openbsd-arm64": "0.28.2",
+ "@esbuild/openbsd-x64": "0.28.2",
+ "@esbuild/openharmony-arm64": "0.28.2",
+ "@esbuild/sunos-x64": "0.28.2",
+ "@esbuild/win32-arm64": "0.28.2",
+ "@esbuild/win32-ia32": "0.28.2",
+ "@esbuild/win32-x64": "0.28.2"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+ "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.4",
+ "mime-types": "^2.1.35"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/is-mobile": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-5.0.0.tgz",
+ "integrity": "sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==",
+ "license": "MIT"
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json2mq": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
+ "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "string-convert": "^0.2.0"
+ }
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.18",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
+ "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.54",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz",
+ "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
+ "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.28",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
+ "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.18",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/rc-cascader": {
+ "version": "3.34.0",
+ "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.34.0.tgz",
+ "integrity": "sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.25.7",
+ "classnames": "^2.3.1",
+ "rc-select": "~14.16.2",
+ "rc-tree": "~5.13.0",
+ "rc-util": "^5.43.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-checkbox": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.5.0.tgz",
+ "integrity": "sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.25.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-collapse": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.9.0.tgz",
+ "integrity": "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.3.4",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-dialog": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz",
+ "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/portal": "^1.0.0-8",
+ "classnames": "^2.2.6",
+ "rc-motion": "^2.3.0",
+ "rc-util": "^5.21.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-drawer": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.3.0.tgz",
+ "integrity": "sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@rc-component/portal": "^1.1.1",
+ "classnames": "^2.2.6",
+ "rc-motion": "^2.6.1",
+ "rc-util": "^5.38.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-dropdown": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.1.tgz",
+ "integrity": "sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.44.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.11.0",
+ "react-dom": ">=16.11.0"
+ }
+ },
+ "node_modules/rc-field-form": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.7.1.tgz",
+ "integrity": "sha512-vKeSifSJ6HoLaAB+B8aq/Qgm8a3dyxROzCtKNCsBQgiverpc4kWDQihoUwzUj+zNWJOykwSY4dNX3QrGwtVb9A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "@rc-component/async-validator": "^5.0.3",
+ "rc-util": "^5.32.2"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-image": {
+ "version": "7.12.0",
+ "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.12.0.tgz",
+ "integrity": "sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.2",
+ "@rc-component/portal": "^1.0.2",
+ "classnames": "^2.2.6",
+ "rc-dialog": "~9.6.0",
+ "rc-motion": "^2.6.2",
+ "rc-util": "^5.34.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-input": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.8.0.tgz",
+ "integrity": "sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.18.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/rc-input-number": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.5.0.tgz",
+ "integrity": "sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/mini-decimal": "^1.0.1",
+ "classnames": "^2.2.5",
+ "rc-input": "~1.8.0",
+ "rc-util": "^5.40.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-mentions": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.20.0.tgz",
+ "integrity": "sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.22.5",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.2.6",
+ "rc-input": "~1.8.0",
+ "rc-menu": "~9.16.0",
+ "rc-textarea": "~1.10.0",
+ "rc-util": "^5.34.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-menu": {
+ "version": "9.16.1",
+ "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.1.tgz",
+ "integrity": "sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "2.x",
+ "rc-motion": "^2.4.3",
+ "rc-overflow": "^1.3.1",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-motion": {
+ "version": "2.9.5",
+ "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz",
+ "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.44.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-notification": {
+ "version": "5.6.4",
+ "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.4.tgz",
+ "integrity": "sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.9.0",
+ "rc-util": "^5.20.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-overflow": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.5.0.tgz",
+ "integrity": "sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.37.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-pagination": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-5.1.0.tgz",
+ "integrity": "sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.38.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-picker": {
+ "version": "4.11.3",
+ "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.11.3.tgz",
+ "integrity": "sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.2.1",
+ "rc-overflow": "^1.3.2",
+ "rc-resize-observer": "^1.4.0",
+ "rc-util": "^5.43.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "date-fns": ">= 2.x",
+ "dayjs": ">= 1.x",
+ "luxon": ">= 3.x",
+ "moment": ">= 2.x",
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ },
+ "peerDependenciesMeta": {
+ "date-fns": {
+ "optional": true
+ },
+ "dayjs": {
+ "optional": true
+ },
+ "luxon": {
+ "optional": true
+ },
+ "moment": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/rc-progress": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz",
+ "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.16.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-rate": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.1.tgz",
+ "integrity": "sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-resize-observer": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz",
+ "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.7",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.44.1",
+ "resize-observer-polyfill": "^1.5.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-segmented": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.7.1.tgz",
+ "integrity": "sha512-izj1Nw/Dw2Vb7EVr+D/E9lUTkBe+kKC+SAFSU9zqr7WV2W5Ktaa9Gc7cB2jTqgk8GROJayltaec+DBlYKc6d+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-motion": "^2.4.4",
+ "rc-util": "^5.17.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/rc-select": {
+ "version": "14.16.8",
+ "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.8.tgz",
+ "integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/trigger": "^2.1.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.0.1",
+ "rc-overflow": "^1.3.1",
+ "rc-util": "^5.16.1",
+ "rc-virtual-list": "^3.5.2"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/rc-slider": {
+ "version": "11.1.9",
+ "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.9.tgz",
+ "integrity": "sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.36.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-steps": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz",
+ "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.16.7",
+ "classnames": "^2.2.3",
+ "rc-util": "^5.16.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-switch": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz",
+ "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.30.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-table": {
+ "version": "7.54.0",
+ "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.54.0.tgz",
+ "integrity": "sha512-/wDTkki6wBTjwylwAGjpLKYklKo9YgjZwAU77+7ME5mBoS32Q4nAwoqhA2lSge6fobLW3Tap6uc5xfwaL2p0Sw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/context": "^1.4.0",
+ "classnames": "^2.2.5",
+ "rc-resize-observer": "^1.1.0",
+ "rc-util": "^5.44.3",
+ "rc-virtual-list": "^3.14.2"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-tabs": {
+ "version": "15.7.0",
+ "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.7.0.tgz",
+ "integrity": "sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.2",
+ "classnames": "2.x",
+ "rc-dropdown": "~4.2.0",
+ "rc-menu": "~9.16.0",
+ "rc-motion": "^2.6.2",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.34.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-textarea": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.10.2.tgz",
+ "integrity": "sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.1",
+ "rc-input": "~1.8.0",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-tooltip": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.4.0.tgz",
+ "integrity": "sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.2",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.3.1",
+ "rc-util": "^5.44.3"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-tree": {
+ "version": "5.13.1",
+ "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.13.1.tgz",
+ "integrity": "sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.0.1",
+ "rc-util": "^5.16.1",
+ "rc-virtual-list": "^3.5.1"
+ },
+ "engines": {
+ "node": ">=10.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/rc-tree-select": {
+ "version": "5.27.0",
+ "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.27.0.tgz",
+ "integrity": "sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.25.7",
+ "classnames": "2.x",
+ "rc-select": "~14.16.2",
+ "rc-tree": "~5.13.0",
+ "rc-util": "^5.43.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/rc-upload": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.11.0.tgz",
+ "integrity": "sha512-ZUyT//2JAehfHzjWowqROcwYJKnZkIUGWaTE/VogVrepSl7AFNbQf4+zGfX4zl9Vrj/Jm8scLO0R6UlPDKK4wA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-util": {
+ "version": "5.44.4",
+ "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz",
+ "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "react-is": "^18.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-util/node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "license": "MIT"
+ },
+ "node_modules/rc-virtual-list": {
+ "version": "3.19.2",
+ "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz",
+ "integrity": "sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.0",
+ "classnames": "^2.2.6",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.36.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.8",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz",
+ "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.8",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz",
+ "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.8"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "19.2.8",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz",
+ "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==",
+ "license": "MIT"
+ },
+ "node_modules/react-refresh": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
+ "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.3.tgz",
+ "integrity": "sha512-gyXgtdr5uACJ5b1Q4udzjVV+tb/rlHIMJKuJ0e89R4Kzgz47z/rgP0dIKxktqIEUhDHluGTPJJH/wRha7CyqsA==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.3.tgz",
+ "integrity": "sha512-ytVbyBBM7vMfRCam25r0WMhSVSom909A8p+8m0/f1w853dz/xfFu6etAT2SEbVoSnI+ZoPRDqIsQXVT89gp7kg==",
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.18.3"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/resize-observer-polyfill": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+ "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
+ "license": "MIT"
+ },
+ "node_modules/rollup": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.1.tgz",
+ "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.9"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@napi-rs/lzma-linux-x64-gnu": "1.5.1",
+ "@rollup/rollup-android-arm-eabi": "4.63.1",
+ "@rollup/rollup-android-arm64": "4.63.1",
+ "@rollup/rollup-darwin-arm64": "4.63.1",
+ "@rollup/rollup-darwin-x64": "4.63.1",
+ "@rollup/rollup-freebsd-arm64": "4.63.1",
+ "@rollup/rollup-freebsd-x64": "4.63.1",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.63.1",
+ "@rollup/rollup-linux-arm-musleabihf": "4.63.1",
+ "@rollup/rollup-linux-arm64-gnu": "4.63.1",
+ "@rollup/rollup-linux-arm64-musl": "4.63.1",
+ "@rollup/rollup-linux-loong64-gnu": "4.63.1",
+ "@rollup/rollup-linux-loong64-musl": "4.63.1",
+ "@rollup/rollup-linux-ppc64-gnu": "4.63.1",
+ "@rollup/rollup-linux-ppc64-musl": "4.63.1",
+ "@rollup/rollup-linux-riscv64-gnu": "4.63.1",
+ "@rollup/rollup-linux-riscv64-musl": "4.63.1",
+ "@rollup/rollup-linux-s390x-gnu": "4.63.1",
+ "@rollup/rollup-linux-x64-gnu": "4.63.1",
+ "@rollup/rollup-linux-x64-musl": "4.63.1",
+ "@rollup/rollup-openbsd-x64": "4.63.1",
+ "@rollup/rollup-openharmony-arm64": "4.63.1",
+ "@rollup/rollup-win32-arm64-msvc": "4.63.1",
+ "@rollup/rollup-win32-ia32-msvc": "4.63.1",
+ "@rollup/rollup-win32-x64-gnu": "4.63.1",
+ "@rollup/rollup-win32-x64-msvc": "4.63.1",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/scroll-into-view-if-needed": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz",
+ "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "compute-scroll-into-view": "^3.0.2"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "license": "MIT"
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string-convert": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
+ "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==",
+ "license": "MIT"
+ },
+ "node_modules/stylis": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz",
+ "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==",
+ "license": "MIT"
+ },
+ "node_modules/throttle-debounce": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
+ "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.22"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/toggle-selection": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+ "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==",
+ "license": "MIT"
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz",
+ "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.3.6",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz",
+ "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ }
+ }
+}
diff --git a/distribution-admin/package.json b/distribution-admin/package.json
new file mode 100644
index 0000000..d5ed216
--- /dev/null
+++ b/distribution-admin/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "hfb-distribution-admin",
+ "private": true,
+ "version": "0.1.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite --host 0.0.0.0",
+ "build": "tsc --noEmit && vite build",
+ "preview": "vite preview --host 0.0.0.0",
+ "test": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@ant-design/icons": "^6.0.0",
+ "antd": "^5.27.6",
+ "axios": "^1.13.2",
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ "react-router-dom": "^7.9.4"
+ },
+ "devDependencies": {
+ "@types/react": "^19.1.13",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react": "^5.0.4",
+ "typescript": "~5.9.3",
+ "vite": "^7.2.4"
+ }
+}
diff --git a/distribution-admin/src/main.tsx b/distribution-admin/src/main.tsx
new file mode 100644
index 0000000..ff7fdc7
--- /dev/null
+++ b/distribution-admin/src/main.tsx
@@ -0,0 +1,40 @@
+import React, { useEffect, useState } from 'react'
+import { createRoot } from 'react-dom/client'
+import { App, Card, ConfigProvider, Layout, Menu, Space, Statistic, Table, Tag, Typography, message } from 'antd'
+import { AccountBookOutlined, ApiOutlined, DashboardOutlined, FileSearchOutlined, SettingOutlined, ShopOutlined } from '@ant-design/icons'
+import axios from 'axios'
+import './style.css'
+
+const api = axios.create({ baseURL: '/api' })
+type Site = { id: number; site_name: string; site_code: string; domain: string; status: string; display_account_enabled: boolean; tax_enabled: boolean; tax_rate_bps: number }
+type Order = { order_id: number; order_no: string; site_id: number; status: string; settlement_status: string; net_paid_amount_cent: number; payment_fee_cent: number; service_fee_cent: number; tax_amount_cent: number; created_at: string }
+
+function Dashboard() {
+ const [sites, setSites] = useState([])
+ const [orders, setOrders] = useState([])
+ useEffect(() => { Promise.all([api.get('/admin/distribution/sites'), api.get('/admin/distribution/orders?kind=all')]).then(([a, b]) => { setSites(a.data.data.items ?? []); setOrders(b.data.data.items ?? []) }).catch(() => message.error('数据加载失败')) }, [])
+ const total = orders.reduce((sum, item) => sum + item.net_paid_amount_cent, 0)
+ return 分销运营概览
{v} }, { title: '净支付', dataIndex: 'net_paid_amount_cent', render: (v) => `¥${(v / 100).toFixed(2)}` }, { title: '创建时间', dataIndex: 'created_at' }]} />
+}
+
+function Sites() {
+ const [items, setItems] = useState([])
+ useEffect(() => { api.get('/admin/distribution/sites').then((r) => setItems(r.data.data.items ?? [])).catch(() => message.error('站点加载失败')) }, [])
+ return 分销网站 v ? 开启 : 关闭 }, { title: '税费', render: (_, r) => r.tax_enabled ? `${r.tax_rate_bps / 100}%` : '关闭' }]} />
+}
+
+function Orders({ kind }: { kind: 'sold' | 'receive' }) {
+ const [items, setItems] = useState([])
+ useEffect(() => { api.get(`/admin/distribution/orders?kind=${kind}`).then((r) => setItems(r.data.data.items ?? [])).catch(() => message.error('订单加载失败')) }, [kind])
+ return {kind === 'sold' ? '售出账号订单' : '分销收号订单'} `¥${(v / 100).toFixed(2)}` }, { title: '手续费', dataIndex: 'payment_fee_cent', render: (v) => `¥${(v / 100).toFixed(2)}` }, { title: '服务费', dataIndex: 'service_fee_cent', render: (v) => `¥${(v / 100).toFixed(2)}` }, { title: '结算', dataIndex: 'settlement_status' }]} />
+}
+
+function Placeholder({ title }: { title: string }) { return {title}该模块已预留接口入口,可在后台继续配置。 }
+
+function Shell() {
+ const [key, setKey] = useState('dashboard')
+ const content = key === 'dashboard' ? : key === 'sites' ? : key === 'sold' ? : key === 'receive' ? : )[key] ?? '分销后台'} />
+ return HFB 分销后台
主站 hao858.com · 分销子域名 distribution.hao858.com{content}
+}
+
+createRoot(document.getElementById('root')!).render()
diff --git a/distribution-admin/src/style.css b/distribution-admin/src/style.css
new file mode 100644
index 0000000..8a8d8f0
--- /dev/null
+++ b/distribution-admin/src/style.css
@@ -0,0 +1,9 @@
+:root { font-family: Inter, "PingFang SC", sans-serif; color: #1f2937; background: #f5f7fa; }
+* { box-sizing: border-box; }
+body { margin: 0; }
+.app { min-height: 100vh; }
+.brand { height: 64px; padding: 20px 18px; color: #fff; font-weight: 600; letter-spacing: 0; }
+.header { display: flex; align-items: center; background: #fff; padding: 0 24px; border-bottom: 1px solid #edf0f5; }
+.page { width: 100%; padding: 24px; }
+.stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; width: 100%; }
+@media (max-width: 720px) { .page { padding: 16px; } .stats { grid-template-columns: 1fr; } }
diff --git a/distribution-admin/tsconfig.json b/distribution-admin/tsconfig.json
new file mode 100644
index 0000000..e935337
--- /dev/null
+++ b/distribution-admin/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": ["src"]
+}
diff --git a/distribution-admin/vite.config.ts b/distribution-admin/vite.config.ts
new file mode 100644
index 0000000..42431f1
--- /dev/null
+++ b/distribution-admin/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+ plugins: [react()],
+ server: { port: 5190, proxy: { '/api': 'http://localhost:8080' } },
+})
diff --git a/docs/分销网管理后台设计方案.md b/docs/分销网管理后台设计方案.md
new file mode 100644
index 0000000..03e3f84
--- /dev/null
+++ b/docs/分销网管理后台设计方案.md
@@ -0,0 +1,776 @@
+# 分销网管理后台设计方案
+
+> 状态:草案 v0.2
+> 更新时间:2026-09-04
+> 适用范围:分销网站正常租赁订单管理、订单分账、分销客服与支付配置
+
+## 1. 文档目的
+
+本文档记录分销网管理后台的产品计划、领域模型、分账口径、接口边界、部署方案和实施顺序。
+
+本文档是持续维护的设计基线。业务规则尚未确认的部分使用“待确认”标记,进入开发前必须补齐;已创建的订单必须使用规则和金额快照,不能因后续规则变更而重算历史账目。
+
+## 2. 现有系统基础
+
+当前 HFB Sys 是 Go + Gin + GORM + MySQL + Redis 后端,Vue 3 + Element Plus 用户端/运营后台前端,生产通过 Caddy 托管静态文件并反代单个 backend 服务。
+
+可复用的现有领域:
+
+- 用户:`users`、实名认证、用户状态。
+- 商品和账号:`rental_listings`、`game_accounts`、账号资产快照。
+- 正常订单:`rental_orders`、交接、归还、结账、退款和订单过程事件。
+- 订单来源:`rental_orders` 的正常租赁交易;分销网只处理卖家和租客都在网站内完成的正常订单。
+- 资金:`wallet_accounts`、`wallet_ledger`、`withdrawal_requests`。
+- 支付:`payment_merchant_configs`、支付单、退款单和支付配置使用日志。
+- 客服:`chat_conversations`、`chat_messages`、客服组和二维码池。
+- 管理权限:`admin_users`、`roles`、`permissions`、角色权限关联和审计日志。
+
+现有系统的价格快照已包含 `seller_ratio`、`buyer_ratio`、`seller_total_price`、`buyer_total_price` 和 `platform_markup_amount`。但现有默认语义是平台加价归平台,分销计划要求销售加价归卖方,因此分销必须使用独立的分账快照,不能直接改变旧字段含义。
+
+## 3. 总体架构
+
+### 3.1 独立前端、复用后端
+
+分销网管理后台独立为新的前端工程,推荐 React + TypeScript + Vite + Ant Design。现有 Vue + Element Plus 用户端和运营后台保持不变。
+
+```text
+主站:https://hao858.com/
+现有运营后台:https://hao858.com/manage-xxx
+分销后台:暂定使用主域名下的独立子域名,例如 `distribution.hao858.com`
+```
+
+分销后台通过独立域名提供静态资源,`/api/*` 反代到现有 Go backend:
+
+```text
+distribution.hao858.com(暂定)
+ ├── / 独立 Ant Design 静态资源
+ └── /api/* reverse_proxy backend:8080
+```
+
+这样可以避免在同一个 Vue 应用中同时引入 Element Plus 和 Ant Design,也避免跨域 Cookie 和 CORS 配置。
+
+### 3.2 后端暂不拆服务
+
+第一阶段不新建独立分销后端服务。分销模块继续使用现有 MySQL、Redis、钱包、订单、支付、客服和权限体系,以便在同一事务中处理支付、退款、分成、税费和钱包流水。
+
+建议新增后端目录:
+
+```text
+backend/internal/modules/distribution/
+```
+
+建议文件:
+
+```text
+dto.go
+handler.go
+service.go
+repository.go
+query.go
+mutation.go
+pricing.go
+settlement.go
+payment.go
+qrcode.go
+chat.go
+presenter.go
+audit.go
+```
+
+## 4. 业务目标与后台功能
+
+### 4.1 分销后台功能
+
+第一版需要覆盖(仅限正常租赁订单):
+
+1. 显示正常订单中已售出账号的详情。
+2. 显示分销网站自己通过正常订单收购/接收的账号详情(卖家在分销网站上架,租客完成交易)。
+3. 配置分销网站在正常订单中的收号比例;主站卖出比例统一或按规则版本管理。
+4. 分销网站使用指定客服组和指定二维码池的拉群码。
+5. 查看手续费、服务费、加价、税费、分成和结算金额。
+6. 配置分销网站是否展示账号,并可单独关闭某个账号。
+7. 配置分销网站可使用的一个或多个收款支付渠道。
+8. 分销客服只读订单信息和过程记录,并可按权限处理客服消息。
+9. 主站查看所有分销网站的收售订单、收款金额和结算数据。
+
+### 4.2 建议页面
+
+```text
+仪表盘
+分销网站管理
+正常订单售出账号
+分销收售订单
+佣金/分成明细
+结算管理
+比例与税费规则
+支付通道
+客服组与二维码
+账号展示管理
+报表
+操作日志
+```
+
+## 5. 业务主体和权限
+
+### 5.1 分销网站
+
+建议使用独立主体表 `distribution_sites`。分销网站不是普通用户,也不直接等同于平台管理员。
+
+分销网站至少需要保存:
+
+- 编码、名称、域名和状态。
+- 联系人信息。
+- 是否允许自动展示账号。
+- 默认客服组和专属二维码池。
+- 税费开关和税率。
+- 创建、停用和更新时间。
+
+### 5.2 分销网站成员
+
+建议使用 `distribution_site_members` 将现有 `admin_users` 映射到一个或多个分销网站。
+
+成员类型建议包括:
+
+- `operator`:查看业务和账号展示。
+- `finance`:查看分成、结算和收款金额。
+- `customer_service`:查看订单、过程记录和客服消息。
+
+分销网站成员只能看到所属站点的数据。前端传入的 `site_id` 不能扩大数据范围,后端必须从当前管理员成员关系中推导并校验。
+
+### 5.3 权限码
+
+建议新增:
+
+```text
+distribution:site_view
+distribution:site_manage
+distribution:account_view
+distribution:account_display
+distribution:order_view
+distribution:process_view
+distribution:finance_view
+distribution:settlement_view
+distribution:settlement_manage
+distribution:rule_view
+distribution:rule_manage
+distribution:payment_view
+distribution:payment_manage
+distribution:qrcode_view
+distribution:qrcode_manage
+distribution:chat_view
+distribution:chat_send
+distribution:all_view
+```
+
+分销客服默认只拥有:
+
+```text
+distribution:order_view
+distribution:process_view
+distribution:chat_view
+distribution:chat_send
+```
+
+客服禁止修改比例、支付配置、展示状态、退款、订单状态和结算结果。
+
+## 6. 订单和账号展示
+
+### 6.1 售出账号详情
+
+分销后台需要显示:
+
+- 订单号、商品编号、账号基本信息和账号快照。
+- 分销网站、收号方、售出方和租客。
+- 收号比例、销售比例、销售基础价、销售加价和实际支付金额。
+- 退款、手续费、服务费、税费、各方收入和结算状态。
+- 交接、租用、归还、结账、退款、仲裁等过程事件。
+
+账号密码、实名信息等敏感字段必须按照现有后台脱敏和权限规则返回。是否允许分销成员查看完整登录信息:待确认。
+
+### 6.2 分销网站正常订单收号详情
+
+分销网的收号不是线下提号记录,而是正常租赁订单中的一方角色:卖家在分销网站上架账号,租客在分销网站完成支付、交接、租用和归还。该类记录应从 `rental_orders` 及分销订单参与方快照查询,不能使用 `admin_pickups` 作为数据来源。
+
+正常订单需要保存分销站点和参与方快照,例如:
+
+```text
+distribution_site_id
+seller_party_id
+renter_party_id
+receive_ratio
+receive_amount_cent
+rule_version
+```
+
+不新增独立的线下收号流程;如需展示“分销网收号记录”,通过正常订单中的 `distributor_receive` 角色和订单状态筛选实现。
+
+### 6.3 账号展示开关
+
+网站级配置:
+
+```text
+distribution_sites.display_account_enabled
+```
+
+账号级配置:
+
+```text
+distribution_site_accounts.display_status
+```
+
+账号状态建议为:
+
+```text
+enabled
+disabled
+sold
+offline
+```
+
+关闭展示不删除账号、不影响历史订单,只阻止新展示或新订单,并写入审计日志。
+
+一个账号是否允许同时挂载多个分销网站:待确认。第一版建议一个账号在同一时刻只归属一个可售分销网站,避免重复出售和归因冲突。
+
+### 6.4 不纳入分销网的业务
+
+以下业务不属于分销网正常订单,不进入分销订单分账:
+
+- 平台或管理员线下提号(`admin_pickups`)。
+- 平台代管账号及线下交接、线下结算。
+- 撞车商品及撞车订单(`mohong`/`crash`)。
+- 分销网站之外的外部导入、私下交易或人工补录订单。
+
+分销网订单必须同时满足:订单来源站点是分销网站、卖家和租客均属于该正常订单链路、支付和订单状态在平台系统内可追踪。
+
+## 7. 比例、费用和分账口径
+
+### 7.1 初步默认规则(可后台调整)
+
+第一版先按以下规则建立默认配置,后续由平台后台调整。默认配置不是写死在代码中的常量,必须保存为可启用/停用、可版本化的后台规则:
+
+```text
+两方:分销网收 3.5,主站卖出 4,服务费 2.5
+两方:分销网卖 4,主站收 3.5,服务费 2.5
+三方:主站服务费 2.5,分销 A 上架 3.5,分销 B 卖出 4
+```
+
+初步配置含义:
+
+| 模式 | 参与方 | 初始比例/费用 |
+| --- | --- | --- |
+| 两方-分销网收号 | 分销网收号、主站卖出 | 分销网 `3.5`,主站 `4`,服务费 `2.5` |
+| 两方-主站收号 | 分销网卖出、主站收号 | 分销网 `4`,主站 `3.5`,服务费 `2.5` |
+| 三方 | 主站服务费、分销 A 上架、分销 B 卖出 | 主站服务费 `2.5`,分销 A `3.5`,分销 B `4` |
+
+以上是当前业务的初始值。后台需要允许平台管理员按分销网站、交易模式和生效时间修改比例及费用;新订单读取当前生效规则并写入快照,历史订单不跟随后台修改。
+
+现有系统的比例语义是“哈夫币数量 / 比例 = 价格”,比例越大,价格越低。3.5 和 4 在分销规则中先分别作为“收号/上架方比例”和“卖出方比例”保存;如果后续确认比例含义需要调整,只影响规则解释和新订单,已结算订单仍以金额快照为准。
+
+### 7.2 必须确认的口径
+
+以下项目在 v0.2 中分为“已有初始值”和“仍待确认”:
+
+| 项目 | 待确认内容 |
+| --- | --- |
+| 两方/三方初始比例 | 已按 7.1 建立默认值,后台可调整 |
+| 分销网站收号比例 | 已确定可按站点调整;是否设置上下限待确认 |
+| 主站统一卖出比例 | 初步按规则版本配置;是否全站固定待确认 |
+| 三方分销 | 已确定保留 A 上架、B 卖出角色;结算顺序和亏损承担待确认 |
+| 3.5 和 4 的最终业务含义 | 初步按收号/上架方与卖出方比例保存,最终文字口径待确认 |
+| 服务费 2.5 | 百分比、金额,还是比例值 |
+| 销售加价 | 是否计入服务费和税费基数 |
+| 税费税基 | 毛利、收入,还是分成后金额 |
+| 退款处理 | 重算剩余分成,还是生成反向冲正流水 |
+| 四舍五入尾差 | 统一归主站还是按最大分成方承担 |
+
+### 7.3 后台调整要求
+
+后台调整规则时必须:
+
+1. 只允许具备 `distribution:rule_manage` 权限的管理员操作。
+2. 新增规则版本,不直接覆盖已生效版本的历史内容。
+3. 支持按分销网站、交易模式配置收号比例、卖出比例、服务费、手续费和税费开关。
+4. 支持设置生效时间;同一站点同一模式同一时刻只能有一个生效版本。
+5. 修改前展示影响范围和新旧值,确认后写入审计日志。
+6. 已创建、已支付或已结算订单继续使用其规则快照。
+7. 比例、费率和金额必须由后端校验范围,前端输入不能绕过校验。
+
+### 7.4 推荐的金额模型
+
+比例只负责生成金额,最终结算全部以整数分金额为准。每笔订单保存:
+
+```text
+ratio_snapshot
+amount_snapshot
+rule_version
+```
+
+建议所有金额字段使用 `*_cent BIGINT`,所有费率使用基点 `*_bps INT`:
+
+```text
+0.5% = 50 bps
+1% = 100 bps
+```
+
+### 7.5 手续费
+
+计划要求手续费在分成前扣除,按退款后的实际支付金额计算,费率为 0.5%,结果四舍五入。
+
+推荐公式:
+
+```text
+实际支付金额 = 支付金额 - 成功退款金额
+手续费 = round_half_up(实际支付金额 × 50 / 10000)
+分成基数 = 实际支付金额 - 手续费
+```
+
+“服务费 2.5”是否属于同一手续费层级:待确认。实现时必须将 `payment_fee`、`service_fee` 分成独立字段,不能合并为一个不可解释的费用。
+
+### 7.6 销售加价
+
+计划要求商家加价销售时,加价全部归卖方。
+
+建议保存:
+
+```text
+markup_amount_cent
+markup_beneficiary_type
+markup_beneficiary_id
+```
+
+建议第一版按以下方式实现:
+
+```text
+销售总价 - 销售基础价 = 销售加价
+销售加价全部计入卖方收入
+```
+
+加价是否计入服务费和税费基数:待确认。无明确规则前,结算引擎必须分别展示基础分成和加价,避免隐含扣费。
+
+### 7.7 税费
+
+计划要求对分销所得利益按毛利的 1% 计税,并支持开关和可调税率;每个主体承担自己的分成税费。
+
+推荐配置:
+
+```text
+tax_enabled
+tax_rate_bps
+```
+
+推荐公式:
+
+```text
+毛利 = 该主体收入 - 该主体收号成本 - 该主体承担的费用
+税额 = round_half_up(毛利 × tax_rate_bps / 10000)
+```
+
+每个主体分别计算税费,主站、分销 A、分销 B 不合并税基。毛利为负时税额暂按 0 处理,亏损如何结转:待确认。
+
+### 7.8 账务不变量
+
+每笔分销订单的结算必须满足:
+
+```text
+实际支付金额
+= 手续费
+ + 平台服务费
+ + 各参与方收入
+ + 税费
+ + 固定规则下的舍入尾差
+```
+
+退款、拒付、仲裁和结算撤销必须生成反向明细,不能直接覆盖原结算记录。
+
+## 8. 建议数据模型
+
+### 8.1 分销网站
+
+```text
+distribution_sites
+```
+
+建议字段:
+
+```text
+id, site_code, site_name, domain, status
+display_account_enabled
+default_support_group_id
+tax_enabled, tax_rate_bps
+created_at, updated_at
+```
+
+### 8.2 成员关系
+
+```text
+distribution_site_members
+```
+
+```text
+id, site_id, admin_user_id, member_type, status, data_scope
+created_at, updated_at
+```
+
+### 8.3 账号展示关系
+
+```text
+distribution_site_accounts
+```
+
+```text
+id, site_id, account_id, listing_id, display_status
+display_started_at, display_ended_at
+created_at, updated_at
+```
+
+### 8.4 规则版本
+
+```text
+distribution_rules
+distribution_rule_versions
+```
+
+规则需要保存:
+
+```text
+site_id
+mode: two_party / three_party
+platform_receive_ratio
+platform_sell_ratio
+distributor_receive_ratio
+distributor_sell_ratio
+distributor_a_ratio
+distributor_b_ratio
+service_fee_value
+service_fee_unit: percent / fixed_amount / ratio
+payment_fee_bps
+tax_enabled
+tax_rate_bps
+effective_from, effective_to
+version, status
+```
+
+首批默认规则按第 7.1 节初始化:
+
+```text
+two_party_distributor_receive: distributor_receive_ratio=3.5, platform_sell_ratio=4, service_fee_value=2.5
+two_party_platform_receive: platform_receive_ratio=3.5, distributor_sell_ratio=4, service_fee_value=2.5
+three_party: distributor_a_ratio=3.5, distributor_b_ratio=4, service_fee_value=2.5
+```
+
+其中 `two_party_platform_receive` 的参与方方向为分销网卖出、主站收号;比例初始值为分销网 `4`、主站 `3.5`。规则字段使用参与方角色命名,避免只写 `receive_ratio`/`sell_ratio` 造成方向歧义。上面的服务费 `2.5` 先作为规则配置值保存,具体单位由后台规则字段和后续口径确认决定。默认规则必须通过迁移或初始化数据写入,不写死在前端或结算代码中。
+
+### 8.5 订单参与方
+
+```text
+distribution_order_parties
+```
+
+```text
+order_id, site_id, party_role, user_id
+ratio, base_amount_cent, markup_amount_cent
+gross_amount_cent, fee_amount_cent, tax_amount_cent
+net_amount_cent, status
+```
+
+`party_role` 建议支持:
+
+```text
+platform
+distributor_a
+distributor_b
+distributor_receive
+seller
+buyer
+```
+
+### 8.6 订单财务快照
+
+```text
+distribution_order_financials
+```
+
+```text
+order_id UNIQUE
+gross_paid_amount_cent
+refund_amount_cent
+net_paid_amount_cent
+payment_fee_bps, payment_fee_cent
+service_fee_bps, service_fee_cent
+markup_amount_cent
+tax_enabled, tax_rate_bps, tax_amount_cent
+split_base_amount_cent
+settlement_status
+rule_version
+created_at, settled_at
+```
+
+### 8.7 结算批次和明细
+
+```text
+distribution_settlements
+distribution_settlement_items
+```
+
+结算批次保存周期、状态、审核人、付款时间和失败原因;明细保存每个站点、参与方、收入、成本、毛利、手续费、税费和钱包流水关联。
+
+### 8.8 支付配置绑定
+
+```text
+distribution_site_payment_configs
+```
+
+一个分销网站可以绑定一个或多个现有 `payment_merchant_configs`。支付下单时保存 `site_id` 和 `payment_config_id` 快照,回调不重新依赖当前配置。
+
+### 8.9 二维码和客服组
+
+建议给二维码池或关联表增加:
+
+```text
+distribution_site_id
+support_group_id
+```
+
+取码优先匹配“分销网站 + 客服组”的专属二维码池。站点已配置专属池时,不回退到其他站点二维码。
+
+## 9. API 规划
+
+### 9.1 平台管理员
+
+```text
+GET /api/admin/distribution/sites
+POST /api/admin/distribution/sites
+GET /api/admin/distribution/sites/:id
+PUT /api/admin/distribution/sites/:id
+POST /api/admin/distribution/sites/:id/enable
+POST /api/admin/distribution/sites/:id/disable
+
+GET /api/admin/distribution/sold-orders
+GET /api/admin/distribution/sold-orders/:id
+GET /api/admin/distribution/receive-orders
+GET /api/admin/distribution/receive-orders/:id
+
+GET /api/admin/distribution/rules
+POST /api/admin/distribution/rules
+PUT /api/admin/distribution/rules/:id
+
+GET /api/admin/distribution/settlements
+GET /api/admin/distribution/settlements/:id
+POST /api/admin/distribution/settlements/:id/approve
+POST /api/admin/distribution/settlements/:id/reject
+
+GET /api/admin/distribution/reports/summary
+GET /api/admin/distribution/reports/orders
+GET /api/admin/distribution/reports/finance
+```
+
+### 9.2 分销网站成员
+
+```text
+GET /api/admin/distribution/me
+GET /api/admin/distribution/orders
+GET /api/admin/distribution/orders/:id
+GET /api/admin/distribution/receive-orders
+GET /api/admin/distribution/chats
+GET /api/admin/distribution/chats/:id/messages
+POST /api/admin/distribution/chats/:id/messages
+```
+
+同一套后端接口通过成员关系限制 `site_id`。平台管理员可以显式选择站点或查看全部;分销成员不能自行传入其他站点 ID。
+
+## 10. 支付和域名上下文
+
+生产环境建议通过请求 Host 解析分销网站:
+
+```text
+请求域名 -> 服务端解析 site_id -> 校验站点状态 -> 选择绑定支付配置
+```
+
+开发环境可以临时使用 `site_code`,但生产支付下单不能完全信任前端提交的站点参数。
+
+支付配置选择规则:
+
+1. 只读取当前站点启用的支付配置。
+2. 支持一个或多个配置,按优先级或健康状态选择。
+3. 支付单保存站点和支付配置快照。
+4. 回调、查询和退款使用支付单快照,不因后台配置变更而切换通道。
+
+## 11. 独立前端工程
+
+建议目录:
+
+```text
+distribution-admin/
+├── package.json
+├── vite.config.ts
+├── tsconfig.json
+├── Dockerfile
+└── src/
+ ├── main.tsx
+ ├── router/
+ ├── layouts/
+ ├── stores/
+ ├── services/
+ ├── types/
+ └── features/
+ ├── dashboard/
+ ├── sites/
+ ├── sold-orders/
+ ├── receive-orders/
+ ├── settlements/
+ ├── payment-configs/
+ ├── qrcodes/
+ ├── chats/
+ └── reports/
+```
+
+建议继续复用现有 API 响应格式:
+
+```json
+{
+ "code": "ok",
+ "message": "ok",
+ "data": {}
+}
+```
+
+独立后台第一阶段可复用现有管理员登录接口和 HttpOnly Cookie,但不与现有前端共享 localStorage。是否建设分销员独立登录:待确认;如果分销员不是平台管理员,应新增独立主体和认证体系。
+
+## 12. 部署方案
+
+新增独立前端构建单元:
+
+```text
+distribution-admin/Dockerfile
+```
+
+Caddy 增加独立子域名站点。子域名名称暂定,最终以 DNS、证书和运营规划确认的名称为准:
+
+```text
+distribution.hao858.com {
+ handle /api/* {
+ reverse_proxy backend:8080
+ }
+
+ handle {
+ root * /srv/distribution-admin
+ try_files {path} /index.html
+ file_server
+ }
+}
+```
+
+主站、现有运营后台和分销后台分别构建、分别发布。数据库迁移仍由现有生产发布流程执行,后端和前端发布需要保持兼容窗口。
+
+## 13. 实施阶段
+
+### 阶段 0:确认业务口径
+
+- 确认 3.5、4、2.5 的单位和方向。
+- 确认两方、三方参与方及亏损承担。
+- 确认加价是否计入服务费和税费。
+- 确认税费毛利计算口径。
+- 确认退款采用重算还是冲正。
+- 确认分销员和分销客服的身份模型。
+- 确认账号是否允许多站点展示。
+
+### 阶段 1:数据库和计算引擎
+
+新增 Goose 迁移,建议从 `000062_distribution_init.sql` 开始,包含站点、成员、展示关系、默认规则版本、订单参与方、财务快照、结算、支付绑定、二维码绑定和权限初始化。默认分账值先按第 7.1 节写入,后续通过后台规则页面调整。
+
+先完成纯函数计算和单元测试,不接真实订单。
+
+### 阶段 2:订单生命周期接入
+
+接入:
+
+```text
+支付成功
+退款成功
+订单完成
+订单仲裁
+订单取消
+```
+
+要求支付回调、退款回调、订单完成和结算操作幂等;规则修改不影响已有订单;钱包流水和分销结算明细金额一致。
+
+### 阶段 3:后台 API
+
+完成站点、售出订单、正常订单收号记录、比例规则、结算、支付通道、二维码客服和账号展示接口,并补齐站点数据隔离测试。
+
+### 阶段 4:Ant Design 前端
+
+开发顺序:登录和权限、仪表盘、售出订单、正常订单收号记录、站点配置、规则、结算、支付、客服二维码、报表。
+
+### 阶段 5:独立生产部署
+
+接入独立子域名、DNS、Caddy 配置、前端镜像和发布脚本,完成健康检查、登录、订单查询、支付配置和客服消息冒烟测试。
+
+## 14. 测试和验收标准
+
+### 权限隔离
+
+- 分销网站 A 不能查询分销网站 B。
+- 分销客服不能修改比例、支付配置、订单状态和结算。
+- 主站财务可查看全部站点资金数据。
+- 超级管理员可管理全部站点。
+
+### 账务正确性
+
+- 手续费按退款后的实际支付金额计算。
+- 手续费先扣除,再进行分成。
+- 0.5% 使用四舍五入并以分保存。
+- 销售加价归指定卖方。
+- 各主体独立计算税费。
+- 重复支付、退款和结算请求不会重复入账。
+- 分销结算明细与钱包流水一致。
+
+### 业务正确性
+
+- 售出订单和正常订单收号记录均可查询。
+- 线下提号、平台代管和撞车订单不会进入分销订单列表或分账。
+- 账号关闭展示后不影响历史订单。
+- 规则修改只影响新订单。
+- 支付只使用当前分销网站绑定的通道。
+- 分销网站使用指定客服组和二维码池。
+- 客服消息按站点隔离。
+
+### 发布标准
+
+- `./scripts/check.sh` 通过。
+- 新迁移具备 Up/Down 并通过 SQL 门禁。
+- 金额、退款、分账、税费和状态机变更包含回归测试。
+- 生产环境完成独立域名、后台登录、订单查询和支付配置冒烟验证。
+
+## 15. 待确认清单
+
+以下问题确认后,更新本文档版本并进入详细表结构和 API 契约设计:
+
+1. 分销网站是平台合作方,还是普通用户下的分销身份?
+2. 分销客服是否使用现有 `admin_users`?
+3. 3.5、4、2.5 的确切单位和计算方向是什么?
+4. 服务费和支付手续费是否都按实际支付净额计算?
+5. 加价是否计入服务费、税费和退款基数?
+6. 税费是否只对正毛利计提?亏损是否结转?
+7. 退款后佣金是重算剩余金额,还是追加负向冲正?
+8. 一个账号能否同时展示在多个分销网站?
+9. 分销后台子域名最终使用哪个名称(当前暂定 `distribution.hao858.com`)?
+10. 分销网站是否需要独立登录入口和独立账号体系?
+11. 分销网站是否能查看完整账号登录资料?
+
+---
+
+后续每次确认业务规则后,优先更新第 7、15 节,再同步更新第 8、9、13、14 节,确保规则、数据、接口、实施和验收标准保持一致。
+
+## 16. 当前实现基线(v0.3)
+
+本分支已落地第一批可运行功能,主站域名为 `https://hao858.com/`,独立后台工程位于 `distribution-admin/`,子域名暂定 `https://distribution.hao858.com/`:
+
+- Go 后端新增 `distribution_sites`、成员、账号展示、规则版本、订单财务快照、订单参与方、结算批次/明细、支付通道绑定表。
+- `rental_orders` 增加分销站点、模式和规则版本字段;只有正常租赁订单才允许写入这些字段。
+- 分账计算引擎已实现退款后净支付、0.5% 基点手续费先扣、服务费三种单位、加价单独展示、各参与方毛利税费和四舍五入。
+- 管理 API 已覆盖站点、规则、售出/收号订单查询、财务快照、账号展示开关、支付通道绑定和结算批次。
+- 后台权限使用 `distribution:*` 权限码;后续接入成员站点数据范围校验时,不能使用请求参数扩大范围。
+- `distribution-admin` 使用 React + TypeScript + Vite + Ant Design,已提供仪表盘、站点、售出订单、收号订单及结算/支付/账号/规则/客服入口和 Docker 镜像。
+
+当前仍需在下一迭代接入真实支付回调、退款/订单完成结算、钱包流水、客服二维码池和成员级站点隔离;在这些链路接通前,财务快照接口只作为后台受控的幂等写入入口。