chore(init): 初始化解忧商行项目并完成 Phase 0 基座

This commit is contained in:
yml2213
2026-10-30 11:46:10 +08:00
commit 8d9481c251
689 changed files with 138703 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
package model
import "time"
type AdminUser struct {
ID uint64 `gorm:"primaryKey" json:"id"`
Username string `gorm:"size:64;not null;uniqueIndex" json:"username"`
PasswordHash string `gorm:"size:255;not null" json:"-"`
Nickname string `gorm:"size:64;not null;default:''" json:"nickname"`
Status string `gorm:"size:32;not null;default:'active'" json:"status"`
SupportStatus string `gorm:"size:16;not null;default:'offline';index" json:"support_status"`
TokenVersion int64 `gorm:"not null;default:1" json:"-"`
PasswordMustChange bool `gorm:"not null;default:false" json:"password_must_change"`
LastLoginAt *time.Time `json:"last_login_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (AdminUser) TableName() string {
return "admin_users"
}
+35
View File
@@ -0,0 +1,35 @@
package model
import "time"
// 公告中心来源模式(distribution_sites.announcement_mode):
// - inherit:分站展示主站公告 + 本站公告(默认)
// - own_only:分站只展示本站公告
const (
AnnouncementModeInherit = "inherit"
AnnouncementModeOwnOnly = "own_only"
)
type Announcement struct {
ID uint64 `gorm:"primaryKey" json:"id"`
// DistributionSiteID 公告归属站点:0=主站,>0=对应分站(分站公告中心按站点隔离)。
DistributionSiteID uint64 `gorm:"not null;default:0;index" json:"distribution_site_id"`
Title string `gorm:"size:255;not null" json:"title"`
Content string `gorm:"type:text;not null" json:"content"`
Category string `gorm:"size:32;not null;default:'notice'" json:"category"`
Priority int `gorm:"not null;default:0" json:"priority"`
IsPinned bool `gorm:"not null;default:false" json:"is_pinned"`
IsImportant bool `gorm:"not null;default:false" json:"is_important"`
// IsPopup 标记弹窗公告:前台首页弹出且需用户手动关闭,多条生效时只弹优先级最高的一条。
IsPopup bool `gorm:"not null;default:false" json:"is_popup"`
ViewCount int `gorm:"not null;default:0" json:"view_count"`
Status string `gorm:"size:32;not null;default:'draft'" json:"status"`
PublishedAt *time.Time `json:"published_at"`
CreatedBy *uint64 `json:"created_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Announcement) TableName() string {
return "announcements"
}
+24
View File
@@ -0,0 +1,24 @@
package model
import (
"time"
"gorm.io/datatypes"
)
type AuditLog struct {
ID uint64 `gorm:"primaryKey" json:"id"`
ActorType string `gorm:"size:32;not null" json:"actor_type"`
ActorID uint64 `gorm:"not null;index" json:"actor_id"`
Action string `gorm:"size:64;not null" json:"action"`
BizType string `gorm:"size:32;not null" json:"biz_type"`
BizID *uint64 `json:"biz_id"`
IP string `gorm:"size:64;not null;default:''" json:"ip"`
UserAgent string `gorm:"size:512;not null;default:''" json:"user_agent"`
Detail datatypes.JSON `json:"detail"`
CreatedAt time.Time `json:"created_at"`
}
func (AuditLog) TableName() string {
return "audit_logs"
}
+168
View File
@@ -0,0 +1,168 @@
package model
import (
"time"
"gorm.io/datatypes"
"gorm.io/gorm"
)
type ChatConversation struct {
ID uint64 `gorm:"primaryKey" json:"id"`
OrderID *uint64 `gorm:"uniqueIndex" json:"order_id"`
ListingID *uint64 `gorm:"index" json:"listing_id"`
DistributionSiteID *uint64 `gorm:"index" json:"distribution_site_id,omitempty"`
// 发布群没有固定订单时,以下字段保存该商品最新订单的轻量快照,供客服列表筛选使用。
// 这样读取会话列表不必每次扫描全部订单再按商品分组。
LatestOrderID *uint64 `gorm:"index" json:"-"`
LatestOrderNo string `gorm:"size:64;not null;default:''" json:"-"`
LatestOrderStatus string `gorm:"size:32;not null;default:'';index" json:"-"`
LatestOrderHandoffStatus string `gorm:"size:32;not null;default:''" json:"-"`
LatestOrderRefundStatus string `gorm:"size:32;not null;default:''" json:"-"`
// 待回复判断只需比较两个游标,避免每个会话再扫描聊天消息表。
LastAttentionMessageID uint64 `gorm:"not null;default:0" json:"-"`
LastAdminMessageID uint64 `gorm:"not null;default:0" json:"-"`
Type string `gorm:"size:32;not null;default:'order_group'" json:"type"`
SupportScene string `gorm:"column:support_scene;size:32;not null;default:''" json:"support_scene"`
Title string `gorm:"size:128;not null" json:"title"`
Status string `gorm:"size:32;not null;default:'active'" json:"status"`
LastMessageID *uint64 `json:"last_message_id"`
LastMessagePreview string `gorm:"size:255;not null;default:''" json:"last_message_preview"`
LastMessageAt *time.Time `json:"last_message_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (ChatConversation) TableName() string {
return "chat_conversations"
}
type ChatParticipant struct {
ID uint64 `gorm:"primaryKey" json:"id"`
ConversationID uint64 `gorm:"not null;index" json:"conversation_id"`
ParticipantType string `gorm:"size:16;not null;index" json:"participant_type"`
ParticipantID uint64 `gorm:"not null;index" json:"participant_id"`
Role string `gorm:"size:32;not null" json:"role"`
Remark string `gorm:"size:128;not null;default:''" json:"remark"`
LastReadAt *time.Time `json:"last_read_at"`
JoinedAt time.Time `json:"joined_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (ChatParticipant) TableName() string {
return "chat_participants"
}
// ChatAdminConversationState stores per-admin state even when the admin is not
// an assigned participant of the conversation.
type ChatAdminConversationState struct {
ID uint64 `gorm:"primaryKey" json:"id"`
ConversationID uint64 `gorm:"not null;uniqueIndex:uk_chat_admin_conversation_state;index" json:"conversation_id"`
AdminUserID uint64 `gorm:"not null;uniqueIndex:uk_chat_admin_conversation_state;index" json:"admin_user_id"`
Remark string `gorm:"size:128;not null;default:''" json:"remark"`
LastReadMessageID uint64 `gorm:"not null;default:0" json:"last_read_message_id"`
LastReadAt *time.Time `json:"last_read_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (ChatAdminConversationState) TableName() string {
return "chat_admin_conversation_states"
}
type ChatMessage struct {
ID uint64 `gorm:"primaryKey" json:"id"`
ConversationID uint64 `gorm:"not null;index" json:"conversation_id"`
SenderType string `gorm:"size:16;not null" json:"sender_type"`
SenderID uint64 `gorm:"not null;default:0" json:"sender_id"`
SenderRole string `gorm:"size:32;not null;default:''" json:"sender_role"`
ContentType string `gorm:"size:32;not null;default:'text'" json:"content_type"`
Content string `json:"content"`
AttachmentURLS datatypes.JSON `gorm:"column:attachment_urls" json:"attachment_urls"`
AdminAttentionType string `gorm:"column:admin_attention_type;size:32;not null;default:'';index" json:"admin_attention_type"`
CreatedAt time.Time `json:"created_at"`
}
func (ChatMessage) TableName() string {
return "chat_messages"
}
// AfterCreate 维护客服列表所需的两个消息游标;列表页只比较游标,不再对每个会话聚合消息表。
func (m *ChatMessage) AfterCreate(tx *gorm.DB) error {
if m.ID == 0 || m.ConversationID == 0 {
return nil
}
updates := map[string]any{}
if m.AdminAttentionType != "" || m.SenderType == "user" {
updates["last_attention_message_id"] = m.ID
}
if m.SenderType == "admin" {
updates["last_admin_message_id"] = m.ID
}
if len(updates) == 0 {
return nil
}
return tx.Model(&ChatConversation{}).Where("id = ?", m.ConversationID).Updates(updates).Error
}
type ChatQrCode struct {
ID uint64 `gorm:"primaryKey" json:"id"`
ImageURL string `gorm:"size:512;not null" json:"image_url"`
GroupName string `gorm:"size:128;not null;default:''" json:"group_name"`
DistributionSiteID *uint64 `gorm:"index" json:"distribution_site_id,omitempty"`
Status string `gorm:"size:16;not null;default:'unused'" json:"status"`
ConversationID *uint64 `gorm:"index" json:"conversation_id"`
UsedAt *time.Time `json:"used_at"`
ExpiresAt *time.Time `json:"expires_at"`
CreatedBy uint64 `gorm:"not null" json:"created_by"`
Note string `gorm:"size:255;not null;default:''" json:"note"`
WecomRenamed bool `gorm:"not null;default:false" json:"wecom_renamed"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (ChatQrCode) TableName() string {
return "chat_qrcode_pool"
}
type ChatQrCodeDeliveryTask struct {
ID uint64 `gorm:"primaryKey" json:"id"`
ConversationID uint64 `gorm:"column:conversation_id;not null;uniqueIndex" json:"conversation_id"`
Status string `gorm:"size:16;not null;default:'pending';index" json:"status"`
QrCodeID *uint64 `gorm:"column:qrcode_id;index" json:"qrcode_id"`
ErrorMessage string `gorm:"column:error_message;size:255;not null;default:''" json:"error_message"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
SentAt *time.Time `gorm:"column:sent_at" json:"sent_at"`
}
func (ChatQrCodeDeliveryTask) TableName() string {
return "chat_qrcode_delivery_tasks"
}
type ChatSupportGroup struct {
ID uint64 `gorm:"primaryKey" json:"id"`
Code string `gorm:"size:64;not null;uniqueIndex" json:"code"`
Name string `gorm:"size:64;not null" json:"name"`
Description string `gorm:"size:255;not null;default:''" json:"description"`
Status string `gorm:"size:16;not null;default:'active';index" json:"status"`
SortOrder int `gorm:"not null;default:0" json:"sort_order"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (ChatSupportGroup) TableName() string {
return "chat_support_groups"
}
type ChatSupportGroupMember struct {
ID uint64 `gorm:"primaryKey" json:"id"`
GroupID uint64 `gorm:"not null;uniqueIndex:uk_support_group_member" json:"group_id"`
AdminID uint64 `gorm:"column:admin_user_id;not null;uniqueIndex:uk_support_group_member;index" json:"admin_id"`
CreatedAt time.Time `json:"created_at"`
}
func (ChatSupportGroupMember) TableName() string {
return "chat_support_group_members"
}
@@ -0,0 +1,17 @@
package model
import "time"
type ChatQuickReply struct {
ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"`
AdminUserID uint64 `gorm:"not null;default:0;index" json:"admin_user_id"` // 0=全局, >0=个人
Title string `gorm:"size:64;not null" json:"title"`
Content string `gorm:"type:text;not null" json:"content"`
SortOrder int `gorm:"not null;default:0" json:"sort_order"`
CreatedAt time.Time `gorm:"not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
}
func (ChatQuickReply) TableName() string {
return "chat_quick_replies"
}
+39
View File
@@ -0,0 +1,39 @@
package model
import "time"
// CSManualEntry 客服手动录单记录。
// 客服在后台手动登记一个编号(如 202610120013),该编号与其登录账号绑定,
// 记录平台、操作人、登记时间等,供后续查询与按客服统计今日录单量。
// platform / operator 存名称快照:后台删掉选项也不影响历史记录展示。
type CSManualEntry struct {
ID uint64 `gorm:"primaryKey" json:"id"`
EntryNo string `gorm:"size:64;not null;uniqueIndex" json:"entry_no"`
Platform string `gorm:"size:64;not null;default:'';index" json:"platform"`
Operator string `gorm:"size:64;not null;default:'';index" json:"operator"`
AdminID uint64 `gorm:"not null;index" json:"admin_id"`
AdminName string `gorm:"size:64;not null;default:''" json:"admin_name"`
AdminUsername string `gorm:"size:64;not null;default:''" json:"admin_username"`
Remark string `gorm:"size:255;not null;default:''" json:"remark"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (CSManualEntry) TableName() string {
return "cs_manual_entries"
}
// CSManualEntryOption 客服手动录单的可选项配置(平台 / 操作人)。
// 仅作为录单表单的下拉数据源,记录里保存的是名称快照。
type CSManualEntryOption struct {
ID uint64 `gorm:"primaryKey" json:"id"`
Type string `gorm:"size:16;not null;uniqueIndex:uk_cs_manual_entry_options_type_name,priority:1" json:"type"`
Name string `gorm:"size:64;not null;uniqueIndex:uk_cs_manual_entry_options_type_name,priority:2" json:"name"`
SortOrder int `gorm:"not null;default:0" json:"sort_order"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (CSManualEntryOption) TableName() string {
return "cs_manual_entry_options"
}
+35
View File
@@ -0,0 +1,35 @@
package model
import (
"time"
"gorm.io/datatypes"
)
type Dispute struct {
ID uint64 `gorm:"primaryKey" json:"id"`
OrderID uint64 `gorm:"not null;index" json:"order_id"`
InitiatorID uint64 `gorm:"not null" json:"initiator_id"`
InitiatorType string `gorm:"size:16;not null;default:'user';index" json:"initiator_type"`
InitiatorAdminID *uint64 `gorm:"index" json:"initiator_admin_id"`
TargetUserID uint64 `gorm:"not null" json:"target_user_id"`
Type string `gorm:"size:32;not null" json:"type"`
Status string `gorm:"size:32;not null;default:'open'" json:"status"`
Description string `json:"description"`
EvidenceURLS datatypes.JSON `gorm:"column:evidence_urls" json:"evidence_urls"`
PreviousOrderStatus string `gorm:"size:32;not null;default:''" json:"previous_order_status"`
PreviousHandoffStatus string `gorm:"size:32;not null;default:''" json:"previous_handoff_status"`
PreviousSettlementStatus string `gorm:"size:32;not null;default:''" json:"previous_settlement_status"`
CheckoutID *uint64 `json:"checkout_id"`
PreviousCheckoutStatus string `gorm:"size:32;not null;default:''" json:"previous_checkout_status"`
ArbitrationResult string `gorm:"size:32;not null;default:''" json:"arbitration_result"`
ArbitrationRemark string `json:"arbitration_remark"`
HandledBy *uint64 `json:"handled_by"`
HandledAt *time.Time `json:"handled_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Dispute) TableName() string {
return "disputes"
}
+187
View File
@@ -0,0 +1,187 @@
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"`
LogoURL string `gorm:"size:512;not null;default:''" json:"logo_url"`
FaviconURL string `gorm:"size:512;not null;default:''" json:"favicon_url"`
BannersJSON string `gorm:"type:text" json:"banners_json"`
AnnouncementsJSON string `gorm:"type:text" json:"announcements_json"`
FooterInfoJSON string `gorm:"type:text" json:"footer_info_json"`
AWRecycleJSON string `gorm:"type:text" json:"aw_recycle_json"`
CooperationFeedbackJSON string `gorm:"type:text" json:"cooperation_feedback_json"`
// StorefrontTextJSON 首页文案(tagline/hero_badge/hero_desc/card_sub),空字段继承前台默认。
StorefrontTextJSON string `gorm:"type:text" json:"storefront_text_json"`
// AnnouncementMode 公告中心来源:inherit=主站公告+本站公告(默认),own_only=仅本站公告。
AnnouncementMode string `gorm:"size:16;not null;default:'inherit'" json:"announcement_mode"`
DefaultSupportGroupID *uint64 `gorm:"index" json:"default_support_group_id,omitempty"`
ListingQrcodeURL string `gorm:"size:512;not null;default:''" json:"listing_qrcode_url"`
SMSConfigJSON string `gorm:"type:text" json:"-"`
ReceiveRatioDelta float64 `gorm:"type:decimal(10,2);not null;default:0" json:"receive_ratio_delta"`
ReceiveCostDelta float64 `gorm:"type:decimal(10,2);not null;default:0" json:"receive_cost_delta"`
TaxEnabled bool `gorm:"not null;default:false" json:"tax_enabled"`
TaxRateBPS int64 `gorm:"not null;default:100" json:"tax_rate_bps"`
SettlementHoldDays int `gorm:"not null;default:7" json:"settlement_hold_days"`
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"`
// DisplayDisabledBy 记录下架来源:operator=站长人工下架,system=系统自动下架,空=展示中或历史数据。
// 自动铺货只会恢复 system/空 来源的下架行,人工下架保持不动。
DisplayDisabledBy string `gorm:"size:16;not null;default:''" json:"display_disabled_by"`
MarkupAmountCent int64 `gorm:"not null;default:0" json:"markup_amount_cent"`
ShelfRatioDelta float64 `gorm:"type:decimal(10,2);not null;default:0" json:"shelf_ratio_delta"`
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"`
ReleaseAt *time.Time `gorm:"index:idx_distribution_financial_release,priority:2" json:"release_at,omitempty"`
ReleasedAt *time.Time `json:"released_at,omitempty"`
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" }
+15
View File
@@ -0,0 +1,15 @@
package model
import "time"
// FileUploadOwner 记录用户上传的私有文件归属,用于业务关联创建前的临时访问授权。
type FileUploadOwner struct {
ID uint64 `gorm:"primaryKey"`
UserID uint64 `gorm:"not null;index"`
ObjectKey string `gorm:"size:512;not null;uniqueIndex"`
CreatedAt time.Time `json:"created_at"`
}
func (FileUploadOwner) TableName() string {
return "file_upload_owners"
}
+24
View File
@@ -0,0 +1,24 @@
package model
import (
"time"
"gorm.io/datatypes"
)
type HandoffRecord struct {
ID uint64 `gorm:"primaryKey" json:"id"`
OrderID uint64 `gorm:"not null;index" json:"order_id"`
FromUserID uint64 `gorm:"not null" json:"from_user_id"`
ToUserID uint64 `gorm:"not null" json:"to_user_id"`
Type string `gorm:"size:32;not null" json:"type"`
Content string `json:"content"`
AttachmentURLS datatypes.JSON `gorm:"column:attachment_urls" json:"attachment_urls"`
ConfirmedByRenterAt *time.Time `json:"confirmed_by_renter_at"`
ConfirmedByOwnerAt *time.Time `json:"confirmed_by_owner_at"`
CreatedAt time.Time `json:"created_at"`
}
func (HandoffRecord) TableName() string {
return "handoff_records"
}
+63
View File
@@ -0,0 +1,63 @@
package model
import "time"
// UserInvitation 用户邀请绑定关系表
type UserInvitation struct {
ID uint64 `gorm:"primaryKey" json:"id"`
InviterID uint64 `gorm:"not null;index:idx_user_invitations_inviter" json:"inviter_id"`
InviteeID uint64 `gorm:"not null;uniqueIndex:uk_user_invitations_invitee" json:"invitee_id"`
DistributionSiteID uint64 `gorm:"not null;default:0" json:"distribution_site_id"`
InviteCode string `gorm:"size:32;not null;default:''" json:"invite_code"`
Status string `gorm:"size:32;not null;default:'pending';index:idx_user_invitations_inviter" json:"status"` // pending/active/invalid
VerifiedAt *time.Time `json:"verified_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (UserInvitation) TableName() string {
return "user_invitations"
}
// InvitationRebateConfig 邀请返利比例配置表(主站或分销站点)
type InvitationRebateConfig struct {
ID uint64 `gorm:"primaryKey" json:"id"`
DistributionSiteID uint64 `gorm:"not null;uniqueIndex:uk_invitation_rebate_configs_site" json:"distribution_site_id"` // 0=主站
FirstMonthRate float64 `gorm:"type:decimal(6,4);not null;default:0.6000" json:"first_month_rate"`
SecondMonthRate float64 `gorm:"type:decimal(6,4);not null;default:0.3000" json:"second_month_rate"`
SubsequentRate float64 `gorm:"type:decimal(6,4);not null;default:0.1500" json:"subsequent_rate"`
Enabled bool `gorm:"not null" json:"enabled"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (InvitationRebateConfig) TableName() string {
return "invitation_rebate_configs"
}
// InvitationRebateLedger 邀请返利结算记录明细表
type InvitationRebateLedger struct {
ID uint64 `gorm:"primaryKey" json:"id"`
OrderID uint64 `gorm:"not null;uniqueIndex:uk_invitation_rebate_source_role,priority:2" json:"order_id"`
OrderNo string `gorm:"size:64;not null" json:"order_no"`
Source string `gorm:"size:32;not null;default:'rental_order';uniqueIndex:uk_invitation_rebate_source_role,priority:1" json:"source"` // rental_order / pickup
DistributionSiteID uint64 `gorm:"not null;default:0;index:idx_invitation_rebate_site" json:"distribution_site_id"`
InviterID uint64 `gorm:"not null;index:idx_invitation_rebate_inviter" json:"inviter_id"`
InviteeID uint64 `gorm:"not null" json:"invitee_id"`
TradeRole string `gorm:"size:16;not null;uniqueIndex:uk_invitation_rebate_source_role,priority:3" json:"trade_role"` // buyer / seller
OrderProfitBasisCent int64 `gorm:"not null;default:0" json:"order_profit_basis_cent"`
SingleSideBasisCent int64 `gorm:"not null;default:0" json:"single_side_basis_cent"`
RateApplied float64 `gorm:"type:decimal(6,4);not null;default:0" json:"rate_applied"`
DaysSinceVerified int `gorm:"not null;default:0" json:"days_since_verified"`
CycleStage string `gorm:"size:32;not null;default:''" json:"cycle_stage"` // first_month / second_month / subsequent
RebateAmountCent int64 `gorm:"not null;default:0" json:"rebate_amount_cent"`
WalletLedgerNo string `gorm:"size:64;not null;default:''" json:"wallet_ledger_no"`
Status string `gorm:"size:32;not null;default:'settled'" json:"status"` // settled / cancelled
SettledAt time.Time `gorm:"index:idx_invitation_rebate_inviter;index:idx_invitation_rebate_site" json:"settled_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (InvitationRebateLedger) TableName() string {
return "invitation_rebate_ledgers"
}
+75
View File
@@ -0,0 +1,75 @@
package model
import (
"time"
"gorm.io/datatypes"
)
type GameAccount struct {
ID uint64 `gorm:"primaryKey" json:"id"`
OwnerID uint64 `gorm:"not null;index" json:"owner_id"`
GameName string `gorm:"size:64;not null;default:'delta_force'" json:"game_name"`
ServerRegion string `gorm:"size:64;not null" json:"server_region"`
LoginPlatform string `gorm:"size:64;not null" json:"login_platform"`
Title string `gorm:"size:128;not null" json:"title"`
Description string `json:"description"`
RankLevel string `gorm:"size:64;not null;default:''" json:"rank_level"`
HafCoinAmount int64 `gorm:"not null;default:0" json:"haf_coin_amount"`
AssetSummary datatypes.JSON `json:"asset_summary"`
SeasonTags datatypes.JSON `json:"season_tags"`
ScreenshotURLS datatypes.JSON `gorm:"column:screenshot_urls" json:"screenshot_urls"`
Status string `gorm:"size:32;not null;default:'draft'" json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (GameAccount) TableName() string {
return "game_accounts"
}
type RentalListing struct {
ID uint64 `gorm:"primaryKey" json:"id"`
ListingNo string `gorm:"column:listing_no;size:20;not null;uniqueIndex" json:"listing_no"`
AccountID uint64 `gorm:"not null;index" json:"account_id"`
OwnerID uint64 `gorm:"not null;index" json:"owner_id"`
PriceCent int64 `gorm:"not null;default:0" json:"-"`
DepositAmountCent int64 `gorm:"not null;default:0" json:"-"`
InTransaction bool `gorm:"not null;default:false" json:"in_transaction"`
Status string `gorm:"size:32;not null;default:'draft'" json:"status"`
ReviewStatus string `gorm:"size:32;not null;default:'none'" json:"review_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"`
DistributionSiteID *uint64 `gorm:"column:distribution_site_id;index" json:"distribution_site_id,omitempty"`
SourceChannel string `gorm:"size:64;not null;default:''" json:"-"`
ReviewReason string `gorm:"size:255;not null;default:''" json:"review_reason"`
PublishedAt *time.Time `json:"published_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (RentalListing) TableName() string {
return "rental_listings"
}
type ListingUpload struct {
ID uint64 `gorm:"primaryKey" json:"id"`
UploaderName string `gorm:"size:64;not null;index" json:"uploader_name"`
SourceChannel string `gorm:"size:32;not null;default:'';index" json:"source_channel"`
MatchedAdminID *uint64 `gorm:"index" json:"matched_admin_id"`
OwnerID *uint64 `gorm:"index" json:"owner_id"`
ClientUploadTime *time.Time `json:"client_upload_time"`
ClientIP string `gorm:"size:64;not null;default:''" json:"client_ip"`
RawPayload datatypes.JSON `json:"raw_payload"`
ParsedPayload datatypes.JSON `json:"parsed_payload"`
ListingID *uint64 `gorm:"index" json:"listing_id"`
Status string `gorm:"size:32;not null;default:'draft_created'" json:"status"`
ErrorMessage string `gorm:"size:255;not null;default:''" json:"error_message"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (ListingUpload) TableName() string {
return "listing_uploads"
}
@@ -0,0 +1,22 @@
package model
import "time"
// ListingStatusEvent 记录商品上架/主动下架/交易离架等状态变更,供后台日统计使用。
type ListingStatusEvent struct {
ID uint64 `gorm:"primaryKey" json:"id"`
ListingID uint64 `gorm:"not null;index" json:"listing_id"`
OwnerID uint64 `gorm:"not null;default:0" json:"owner_id"`
EventType string `gorm:"size:32;not null" json:"event_type"`
FromStatus string `gorm:"size:32;not null;default:''" json:"from_status"`
ToStatus string `gorm:"size:32;not null;default:''" json:"to_status"`
Source string `gorm:"size:32;not null;default:''" json:"source"`
ActorType string `gorm:"size:16;not null;default:''" json:"actor_type"`
ActorID uint64 `gorm:"not null;default:0" json:"actor_id"`
Remark string `gorm:"size:255;not null;default:''" json:"remark"`
CreatedAt time.Time `json:"created_at"`
}
func (ListingStatusEvent) TableName() string {
return "listing_status_events"
}
+99
View File
@@ -0,0 +1,99 @@
package model
import (
"time"
"gorm.io/datatypes"
)
// MohongCategory 撞车商品分类。
type MohongCategory struct {
ID uint64 `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:64;not null;uniqueIndex" json:"name"`
Code string `gorm:"size:64;not null;default:''" json:"code"`
SortOrder int `gorm:"column:sort_order;not null;default:0" json:"sort_order"`
Status string `gorm:"size:16;not null;default:'enabled'" json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (MohongCategory) TableName() string {
return "mohong_categories"
}
// MohongProductCategory 商品与分类多对多(同一商品可出现在大红/九格等)。
type MohongProductCategory struct {
ProductID uint64 `gorm:"column:product_id;primaryKey" json:"product_id"`
CategoryID uint64 `gorm:"column:category_id;primaryKey;index" json:"category_id"`
}
func (MohongProductCategory) TableName() string {
return "mohong_product_categories"
}
// MohongProduct 撞车商品。
type MohongProduct struct {
ID uint64 `gorm:"primaryKey" json:"id"`
CategoryID *uint64 `gorm:"column:category_id;index" json:"category_id"` // 主分类(展示用)
Title string `gorm:"size:128;not null" json:"title"`
CoverURL string `gorm:"column:cover_url;size:512;not null;default:''" json:"cover_url"`
ImageURLs datatypes.JSON `gorm:"column:image_urls" json:"image_urls"`
Description string `gorm:"type:text;not null" json:"description"`
PriceCent int64 `gorm:"column:price_cent;not null;default:0" json:"-"`
OriginalPriceCent int64 `gorm:"column:original_price_cent;not null;default:0" json:"-"`
Unit string `gorm:"size:32;not null;default:'份'" json:"unit"`
Stock int `gorm:"not null;default:-1" json:"stock"`
SortOrder int `gorm:"column:sort_order;not null;default:0" json:"sort_order"`
Status string `gorm:"size:16;not null;default:'draft'" json:"status"`
QrcodeImageURL string `gorm:"column:qrcode_image_url;size:512;not null;default:''" json:"qrcode_image_url"`
CreatedBy *uint64 `gorm:"column:created_by" json:"created_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (MohongProduct) TableName() string {
return "mohong_products"
}
// MohongOrder 撞车订单。
type MohongOrder struct {
ID uint64 `gorm:"primaryKey" json:"id"`
OrderNo string `gorm:"size:64;not null;uniqueIndex" json:"order_no"`
UserID uint64 `gorm:"not null;index" json:"user_id"`
ProductID uint64 `gorm:"not null;index" json:"product_id"`
Quantity int `gorm:"not null;default:1" json:"quantity"`
UnitPriceCent int64 `gorm:"column:unit_price_cent;not null;default:0" json:"-"`
AmountCent int64 `gorm:"column:amount_cent;not null;default:0" json:"-"`
Status string `gorm:"size:32;not null;default:'pending_payment';index" json:"status"`
ProductSnapshot datatypes.JSON `gorm:"column:product_snapshot" json:"product_snapshot"`
ItemsSnapshot datatypes.JSON `gorm:"column:items_snapshot" json:"items_snapshot"`
QrcodeURLSnapshot string `gorm:"column:qrcode_url_snapshot;size:512;not null;default:''" json:"qrcode_url_snapshot"`
CopyText string `gorm:"type:text;not null" json:"copy_text"`
PaidAt *time.Time `json:"paid_at"`
CompletedAt *time.Time `json:"completed_at"`
CancelledAt *time.Time `json:"cancelled_at"`
CancelReason string `gorm:"size:255;not null;default:''" json:"cancel_reason"`
AdminRemark string `gorm:"size:255;not null;default:''" json:"admin_remark"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (MohongOrder) TableName() string {
return "mohong_orders"
}
const (
MohongCategoryStatusEnabled = "enabled"
MohongCategoryStatusDisabled = "disabled"
MohongProductStatusDraft = "draft"
MohongProductStatusOnSale = "on_sale"
MohongProductStatusOffSale = "off_sale"
MohongOrderStatusPendingPayment = "pending_payment"
MohongOrderStatusPaid = "paid"
MohongOrderStatusReceiving = "receiving" // 接待中:已有人接待买家
MohongOrderStatusCompleted = "completed"
MohongOrderStatusCancelled = "cancelled"
MohongOrderStatusRefunded = "refunded"
)
+35
View File
@@ -0,0 +1,35 @@
package model
import "time"
type Notification struct {
ID uint64 `gorm:"primaryKey" json:"id"`
UserID uint64 `gorm:"not null;index" json:"user_id"`
Type string `gorm:"size:32;not null" json:"type"`
Title string `gorm:"size:128;not null" json:"title"`
Content string `json:"content"`
BizType string `gorm:"size:32;not null;default:''" json:"biz_type"`
BizID *uint64 `json:"biz_id"`
DistributionSiteID *uint64 `gorm:"index" json:"distribution_site_id"`
ReadAt *time.Time `json:"read_at"`
CreatedAt time.Time `json:"created_at"`
}
func (Notification) TableName() string {
return "notifications"
}
type AdminNotification struct {
ID uint64 `gorm:"primaryKey" json:"id"`
AdminUserID uint64 `gorm:"not null;index" json:"admin_user_id"`
Type string `gorm:"size:32;not null" json:"type"`
Title string `gorm:"size:128;not null" json:"title"`
Content string `json:"content"`
IsRead bool `gorm:"not null;default:false" json:"is_read"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (AdminNotification) TableName() string {
return "admin_notifications"
}
+148
View File
@@ -0,0 +1,148 @@
package model
import (
"time"
"gorm.io/datatypes"
"gorm.io/gorm"
)
type RentalOrder struct {
ID uint64 `gorm:"primaryKey" json:"id"`
OrderNo string `gorm:"size:64;not null;uniqueIndex" json:"order_no"`
ListingID uint64 `gorm:"not null;index" json:"listing_id"`
AccountID uint64 `gorm:"not null;index" json:"account_id"`
OwnerID uint64 `gorm:"not null;index" json:"owner_id"`
RenterID uint64 `gorm:"not null;index" json:"renter_id"`
RentedAt *time.Time `json:"rented_at"`
HandoffStartedAt *time.Time `json:"handoff_started_at"`
EstimatedDurationHours int `gorm:"not null;default:24" json:"estimated_duration_hours"`
RentAmountCent int64 `gorm:"not null;default:0" json:"-"`
OwnerRentAmountCent int64 `gorm:"not null;default:0" json:"-"`
DepositAmountCent int64 `gorm:"not null;default:0" json:"-"`
DepositOriginalAmountCent int64 `gorm:"not null;default:0" json:"-"`
DepositWaivedAmountCent int64 `gorm:"not null;default:0" json:"-"`
DepositFreeLevelQuotaCent int64 `gorm:"not null;default:0" json:"deposit_free_level_quota_cent"`
DepositFreeManualQuotaCent int64 `gorm:"not null;default:0" json:"deposit_free_manual_quota_cent"`
DepositFreeUsedBeforeCent int64 `gorm:"not null;default:0" json:"deposit_free_used_before_cent"`
PlatformFeeCent int64 `gorm:"not null;default:0" json:"-"`
BasePriceCent int64 `gorm:"not null;default:0" json:"-"`
ShelfMarkupCent int64 `gorm:"not null;default:0" json:"-"`
MarkupSiteID uint64 `gorm:"not null;default:0;index" json:"markup_site_id,omitempty"`
ShelfRatio float64 `gorm:"type:decimal(10,2);not null;default:0" json:"shelf_ratio,omitempty"`
ShelfRatioDelta float64 `gorm:"type:decimal(10,2);not null;default:0" json:"shelf_ratio_delta,omitempty"`
RentOriginalAmountCent int64 `gorm:"not null;default:0" json:"-"`
RentDiscountAmountCent int64 `gorm:"not null;default:0" json:"-"`
PureCoinOriginalAmountCent int64 `gorm:"not null;default:0" json:"-"`
ExtraItemOriginalAmountCent int64 `gorm:"not null;default:0" json:"-"`
ActualCoinConsumedM float64 `gorm:"type:decimal(12,2);not null;default:0" json:"actual_coin_consumed_m"`
ActualPureCoinAmountCent int64 `gorm:"not null;default:0" json:"-"`
ActualPureCoinDiscountCent int64 `gorm:"not null;default:0" json:"-"`
ActualExtraItemAmountCent int64 `gorm:"not null;default:0" json:"-"`
RenterGrowthLevel string `gorm:"size:32;not null;default:'normal'" json:"renter_growth_level"`
RenterGrowthLevelName string `gorm:"size:32;not null;default:'普通'" json:"renter_growth_level_name"`
RenterDiscountBps int `gorm:"not null;default:10000" json:"renter_discount_bps"`
GrowthPointsBasisCent int64 `gorm:"not null;default:0" json:"growth_points_basis_cent"`
GrowthPointsPerYuan int64 `gorm:"not null;default:0" json:"growth_points_per_yuan"`
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"`
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"`
// 推广归因:下单时由前端携带的短码,用于成交统计。
SourceTrackCode string `gorm:"size:64;index:idx_rental_orders_source_track_code" json:"source_track_code,omitempty"`
// 号主从自己的订单列表隐藏的时间。订单、款项、租客和后台记录都保留。
OwnerHiddenAt *time.Time `json:"-"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (RentalOrder) TableName() string {
return "rental_orders"
}
// AfterSave 将商品关联发布群的最新订单快照一并更新。
// 客服列表会高频按订单状态筛选,直接读这个快照可避免每次聚合全量 rental_orders。
func (o *RentalOrder) AfterSave(tx *gorm.DB) error {
if o.ID == 0 || o.ListingID == 0 {
return nil
}
return tx.Model(&ChatConversation{}).
Where("listing_id = ? AND order_id IS NULL", o.ListingID).
Where("latest_order_id IS NULL OR latest_order_id <= ?", o.ID).
Updates(map[string]any{
"latest_order_id": o.ID,
"latest_order_no": o.OrderNo,
"latest_order_status": o.Status,
"latest_order_handoff_status": o.HandoffStatus,
"latest_order_refund_status": o.RefundStatus,
}).Error
}
// 押金暂扣状态:客服可对进行中订单的押金退款进行暂扣,订单照常结算,
// 但本应原路退给租客的押金部分挂起不退,后续由客服手动归还。
const (
DepositHoldStatusNone = "none" // 未暂扣
DepositHoldStatusHeld = "held" // 已暂扣(押金退款被拦截/挂起)
DepositHoldStatusReleased = "released" // 已归还
)
// IsDepositHeld 判断订单当前是否处于押金暂扣状态。
func (o *RentalOrder) IsDepositHeld() bool {
return o.DepositHoldStatus == DepositHoldStatusHeld
}
// ApplyDepositHold 在任一结算退款路径中拦截押金退款。
// totalRefundCent 是本次本应原路退还给租客的总金额(含租金退款和押金退款),
// depositPortionCent 是其中属于押金退款的部分。
// 若订单已暂扣,则把押金部分(不超过本次退款总额)从退款中扣除,
// 并把尚未记录过的部分累加到暂扣金额(累计不超过实付押金),返回扣除后实际发起原路退款的金额;
// 未暂扣时原样返回 totalRefundCent。
// 注意:本函数只做“少退、挂起”,绝不多退,因此在任何路径下都不会造成租客损失或平台多付。
func (o *RentalOrder) ApplyDepositHold(totalRefundCent, depositPortionCent int64) int64 {
if !o.IsDepositHeld() {
return totalRefundCent
}
withheld := depositPortionCent
if withheld > totalRefundCent {
withheld = totalRefundCent
}
if withheld <= 0 {
return totalRefundCent
}
remainingDepositHoldCent := o.DepositAmountCent - o.DepositHoldAmountCent
if remainingDepositHoldCent > 0 {
newHold := withheld
if newHold > remainingDepositHoldCent {
newHold = remainingDepositHoldCent
}
o.DepositHoldAmountCent += newHold
}
return totalRefundCent - withheld
}
+44
View File
@@ -0,0 +1,44 @@
package model
import (
"time"
"gorm.io/datatypes"
)
type OrderCheckout struct {
ID uint64 `gorm:"primaryKey" json:"id"`
OrderID uint64 `gorm:"not null;index" json:"order_id"`
InitiatedBy uint64 `gorm:"not null" json:"initiated_by"`
Status string `gorm:"size:32;not null;default:'submitted'" json:"status"`
RoundCount int `gorm:"not null;default:1" json:"round_count"`
Turn string `gorm:"size:16;not null;default:'owner'" json:"turn"`
ProposedBy uint64 `gorm:"not null;default:0" json:"proposed_by"`
RentAmountCent int64 `gorm:"not null;default:0" json:"-"`
OwnerRentAmountCent int64 `gorm:"not null;default:0" json:"-"`
PlatformFeeCent int64 `gorm:"not null;default:0" json:"-"`
DepositAmountCent int64 `gorm:"not null;default:0" json:"-"`
PureCoinAmountCent int64 `gorm:"not null;default:0" json:"-"`
PureCoinDiscountCent int64 `gorm:"not null;default:0" json:"-"`
PureCoinPayableCent int64 `gorm:"not null;default:0" json:"-"`
ConsumableAmountCent int64 `gorm:"not null;default:0" json:"-"`
CoinConsumedM float64 `gorm:"type:decimal(12,2);not null;default:0" json:"coin_consumed_m"`
OtherAmountCent int64 `gorm:"not null;default:0" json:"-"`
DepositDeductAmountCent int64 `gorm:"not null;default:0" json:"-"`
RenterRefundAmountCent int64 `gorm:"not null;default:0" json:"-"`
OwnerIncomeAmountCent int64 `gorm:"not null;default:0" json:"-"`
ShortfallCent int64 `gorm:"not null;default:0" json:"-"`
OvershootAmountCent int64 `gorm:"not null;default:0" json:"-"`
Content string `json:"content"`
EvidenceURLS datatypes.JSON `gorm:"column:evidence_urls" json:"evidence_urls"`
OwnerAdjustmentReason string `json:"owner_adjustment_reason"`
OwnerAdjustedAt *time.Time `json:"owner_adjusted_at"`
RenterConfirmedAt *time.Time `json:"renter_confirmed_at"`
RenterRejectedAt *time.Time `json:"renter_rejected_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (OrderCheckout) TableName() string {
return "order_checkouts"
}
+49
View File
@@ -0,0 +1,49 @@
package model
import "testing"
func TestRentalOrderApplyDepositHold(t *testing.T) {
order := RentalOrder{
DepositAmountCent: 1000,
DepositHoldStatus: DepositHoldStatusHeld,
}
actualRefundCent := order.ApplyDepositHold(1500, 1000)
if actualRefundCent != 500 {
t.Fatalf("actual refund = %d, want 500", actualRefundCent)
}
if order.DepositHoldAmountCent != 1000 {
t.Fatalf("hold amount = %d, want 1000", order.DepositHoldAmountCent)
}
}
func TestRentalOrderApplyDepositHoldKeepsWithholdingAfterAmountRecorded(t *testing.T) {
order := RentalOrder{
DepositAmountCent: 1000,
DepositHoldStatus: DepositHoldStatusHeld,
DepositHoldAmountCent: 1000,
}
actualRefundCent := order.ApplyDepositHold(1500, 1000)
if actualRefundCent != 500 {
t.Fatalf("actual refund = %d, want 500", actualRefundCent)
}
if order.DepositHoldAmountCent != 1000 {
t.Fatalf("hold amount = %d, want 1000", order.DepositHoldAmountCent)
}
}
func TestRentalOrderApplyDepositHoldSkipsWhenNotHeld(t *testing.T) {
order := RentalOrder{
DepositAmountCent: 1000,
DepositHoldStatus: DepositHoldStatusNone,
}
actualRefundCent := order.ApplyDepositHold(1500, 1000)
if actualRefundCent != 1500 {
t.Fatalf("actual refund = %d, want 1500", actualRefundCent)
}
if order.DepositHoldAmountCent != 0 {
t.Fatalf("hold amount = %d, want 0", order.DepositHoldAmountCent)
}
}
+41
View File
@@ -0,0 +1,41 @@
package model
import (
"time"
"gorm.io/datatypes"
)
type PaymentOrder struct {
ID uint64 `gorm:"primaryKey" json:"id"`
PaymentNo string `gorm:"size:64;not null;uniqueIndex" json:"payment_no"`
OrderID uint64 `gorm:"not null;index" json:"order_id"`
OrderNo string `gorm:"size:64;not null;index" json:"order_no"`
UserID uint64 `gorm:"not null;index" json:"user_id"`
PaymentConfigID uint64 `gorm:"not null;default:0;index" json:"payment_config_id"`
Provider string `gorm:"size:32;not null" json:"provider"`
MerchantID string `gorm:"size:32;not null;default:''" json:"merchant_id"`
ThirdOrderID string `gorm:"size:64;not null;uniqueIndex" json:"third_order_id"`
ProviderOrderID string `gorm:"size:64;not null;default:'';index" json:"provider_order_id"`
PayWay string `gorm:"size:16;not null;default:''" json:"pay_way"`
JSPayFlag string `gorm:"column:jspay_flag;size:8;not null;default:''" json:"jspay_flag"`
AmountCent int64 `gorm:"not null;default:0" json:"amount_cent"`
BizType string `gorm:"size:32;not null;default:'order_pay';index" json:"biz_type"`
Status string `gorm:"size:32;not null;default:'created';index" json:"status"`
TDCode string `gorm:"size:512;not null;default:''" json:"td_code"`
JSPayURL string `gorm:"column:jspay_url;size:512;not null;default:''" json:"jspay_url"`
JSPayInfo string `gorm:"column:jspay_info;type:text" json:"jspay_info"`
RawRequest datatypes.JSON `json:"raw_request"`
RawResponse datatypes.JSON `json:"raw_response"`
RetryCount int `gorm:"not null;default:0" json:"retry_count"`
LastRetryAt *time.Time `json:"last_retry_at"`
NextRetryAt *time.Time `json:"next_retry_at"`
PaidAt *time.Time `json:"paid_at"`
NotifiedAt *time.Time `json:"notified_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (PaymentOrder) TableName() string {
return "payment_orders"
}
+26
View File
@@ -0,0 +1,26 @@
package model
import (
"time"
"gorm.io/datatypes"
)
type UserPaymentAccount struct {
ID uint64 `gorm:"primaryKey" json:"id"`
UserID uint64 `gorm:"not null;index" json:"user_id"`
AccountType string `gorm:"size:32;not null" json:"account_type"`
AccountName string `gorm:"size:128;not null" json:"account_name"`
AccountNo string `gorm:"size:255;not null" json:"account_no"`
BankName string `gorm:"size:128;not null;default:''" json:"bank_name"`
BankBranch string `gorm:"size:255;not null;default:''" json:"bank_branch"`
CertificateURLs datatypes.JSON `json:"certificate_urls"`
IsDefault bool `gorm:"not null;default:0" json:"is_default"`
Status string `gorm:"size:32;not null;default:'active'" json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (UserPaymentAccount) TableName() string {
return "user_payment_accounts"
}
@@ -0,0 +1,103 @@
package model
import (
"database/sql/driver"
"encoding/json"
"time"
)
// PaymentMerchantConfig 支付商户配置
type PaymentMerchantConfig struct {
ID uint64 `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:128;not null" json:"name"`
Provider string `gorm:"size:32;not null;index:idx_payment_merchant_configs_provider" json:"provider"`
MerchantID string `gorm:"size:128;not null;index:idx_payment_merchant_configs_merchant_id" json:"merchant_id"`
GatewayURL string `gorm:"size:512;not null;default:''" json:"gateway_url"`
SignKey string `gorm:"type:text" json:"sign_key"` // 加密存储,拉卡拉使用商户私钥 PEM
NotifyKey string `gorm:"type:text" json:"notify_key"` // 加密存储,拉卡拉使用通知验签证书 PEM
NotifyURL string `gorm:"size:512;not null;default:''" json:"notify_url"`
JumpURL string `gorm:"size:512;not null;default:''" json:"jump_url"`
PayWay string `gorm:"size:32;not null;default:'ZFBZF';index:idx_payment_merchant_configs_pay_way" json:"pay_way"`
JSPayFlag string `gorm:"column:jspay_flag;size:8;not null;default:'2'" json:"jspay_flag"`
SignType string `gorm:"size:32;not null;default:'MD5'" json:"sign_type"`
ExtraConfig JSONMap `gorm:"type:json" json:"extra_config"`
IsDefault bool `gorm:"not null;default:0;index:idx_payment_merchant_configs_default" json:"is_default"`
Status string `gorm:"size:32;not null;default:'active';index:idx_payment_merchant_configs_status" json:"status"`
Environment string `gorm:"size:16;not null;default:'production'" json:"environment"`
BusinessTags JSONArray `gorm:"type:json" json:"business_tags"`
// 统计信息
TotalTransactions int64 `gorm:"not null;default:0" json:"total_transactions"`
TotalAmountCent int64 `gorm:"not null;default:0" json:"total_amount_cent"`
LastUsedAt *time.Time `json:"last_used_at"`
// 审计信息
CreatedBy *uint64 `json:"created_by"`
UpdatedBy *uint64 `json:"updated_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (PaymentMerchantConfig) TableName() string {
return "payment_merchant_configs"
}
// JSONMap 用于 extra_config 字段
type JSONMap map[string]any
func (j JSONMap) Value() (driver.Value, error) {
if j == nil {
return nil, nil
}
return json.Marshal(j)
}
func (j *JSONMap) Scan(value any) error {
if value == nil {
*j = nil
return nil
}
bytes, ok := value.([]byte)
if !ok {
return nil
}
return json.Unmarshal(bytes, j)
}
// JSONArray 用于 business_tags 字段
type JSONArray []string
func (j JSONArray) Value() (driver.Value, error) {
if j == nil {
return nil, nil
}
return json.Marshal(j)
}
func (j *JSONArray) Scan(value any) error {
if value == nil {
*j = nil
return nil
}
bytes, ok := value.([]byte)
if !ok {
return nil
}
return json.Unmarshal(bytes, j)
}
// PaymentConfigUsageLog 支付配置使用日志
type PaymentConfigUsageLog struct {
ID uint64 `gorm:"primaryKey" json:"id"`
ConfigID uint64 `gorm:"not null;index:idx_payment_config_usage_logs_config" json:"config_id"`
PaymentOrderID uint64 `gorm:"not null;index:idx_payment_config_usage_logs_payment" json:"payment_order_id"`
Provider string `gorm:"size:32;not null" json:"provider"`
MerchantID string `gorm:"size:128;not null" json:"merchant_id"`
AmountCent int64 `gorm:"not null" json:"amount_cent"`
BizType string `gorm:"size:32;not null" json:"biz_type"`
CreatedAt time.Time `json:"created_at"`
}
func (PaymentConfigUsageLog) TableName() string {
return "payment_config_usage_logs"
}
+51
View File
@@ -0,0 +1,51 @@
package model
import (
"time"
"gorm.io/datatypes"
)
// AdminPickup 管理员线下提号记录。
// 独立于 rental_orders 和正常订单状态机;完成后进入线下提号财务统计。
// 站内上传完成时入卖家钱包;平台代管完成后进入线下结算。
type AdminPickup struct {
ID uint64 `gorm:"primaryKey" json:"id"`
PickupNo string `gorm:"size:64;not null;uniqueIndex" json:"pickup_no"`
ListingID uint64 `gorm:"not null;index" json:"listing_id"`
AccountID uint64 `gorm:"not null;index" json:"account_id"`
OwnerID uint64 `gorm:"not null;index" json:"owner_id"`
AdminID uint64 `gorm:"not null" json:"admin_id"`
Operator string `gorm:"size:64;not null;default:'';index" json:"operator"`
Platform string `gorm:"size:32;not null;default:''" json:"platform"`
ThirdPartyOrderNo string `gorm:"size:128;not null;default:'';index" json:"third_party_order_no"`
ShopName string `gorm:"size:100;not null;default:'';index" json:"shop_name"`
RawShopName string `gorm:"size:100;not null;default:''" json:"raw_shop_name"`
AccountSource string `gorm:"size:32;not null;default:'internal';index" json:"account_source"`
SourceChannel string `gorm:"size:32;not null;default:''" json:"source_channel"`
SettlementMode string `gorm:"size:32;not null;default:'owner_wallet'" json:"settlement_mode"`
ListingPriceCent int64 `gorm:"not null;default:0" json:"listing_price_cent"`
OrderAmountCent int64 `gorm:"not null;default:0" json:"order_amount_cent"`
DepositAmountCent int64 `gorm:"not null;default:0" json:"deposit_amount_cent"`
OwnerPriceCent int64 `gorm:"not null;default:0" json:"owner_price_cent"`
WebsiteProfitCent int64 `gorm:"not null;default:0" json:"website_profit_cent"`
ProfitAmountCent int64 `gorm:"not null;default:0" json:"profit_amount_cent"`
SellerRatio float64 `gorm:"type:decimal(10,2);not null;default:0" json:"seller_ratio"`
BuyerRatio float64 `gorm:"type:decimal(10,2);not null;default:0" json:"buyer_ratio"`
AccountSnapshot datatypes.JSON `json:"account_snapshot,omitempty"`
SettleAmountCent int64 `gorm:"not null;default:0" json:"settle_amount_cent"`
Status string `gorm:"size:20;not null;default:'picking_up';index" json:"status"`
OfflineSettlementStatus string `gorm:"size:16;not null;default:'none';index" json:"offline_settlement_status"`
OfflineSettlementRemark string `gorm:"size:255;not null;default:''" json:"offline_settlement_remark"`
OfflineSettledBy *uint64 `json:"offline_settled_by,omitempty"`
OfflineSettledAt *time.Time `json:"offline_settled_at,omitempty"`
Remark string `gorm:"size:255;not null;default:''" json:"remark"`
CompleteRemark string `gorm:"size:255;not null;default:''" json:"complete_remark"`
CreatedAt time.Time `json:"created_at"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
CancelledAt *time.Time `json:"cancelled_at,omitempty"`
}
func (AdminPickup) TableName() string {
return "admin_pickups"
}
@@ -0,0 +1,23 @@
package model
import "time"
// AdminPickupFinancialAdjustment 保存已完成提号的财务调整,原始结算快照不可修改。
type AdminPickupFinancialAdjustment struct {
ID uint64 `gorm:"primaryKey" json:"id"`
PickupID uint64 `gorm:"not null;index" json:"pickup_id"`
ProfitDeltaCent int64 `gorm:"not null;default:0" json:"profit_delta_cent"`
SettleDeltaCent int64 `gorm:"not null;default:0" json:"settle_delta_cent"`
SettlementMode string `gorm:"size:32;not null" json:"settlement_mode"`
Status string `gorm:"size:24;not null;index" json:"status"`
Reason string `gorm:"size:255;not null" json:"reason"`
CreatedBy uint64 `gorm:"not null" json:"created_by"`
SettledBy *uint64 `json:"settled_by,omitempty"`
SettledAt *time.Time `json:"settled_at,omitempty"`
SettlementRemark string `gorm:"size:255;not null;default:''" json:"settlement_remark"`
CreatedAt time.Time `json:"created_at"`
}
func (AdminPickupFinancialAdjustment) TableName() string {
return "admin_pickup_financial_adjustments"
}
+36
View File
@@ -0,0 +1,36 @@
package model
import "time"
// PickupShop 线下提号店铺配置模型。
type PickupShop struct {
ID uint64 `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:100;not null;uniqueIndex" json:"name"`
Platform string `gorm:"size:32;not null;default:''" json:"platform"`
Status string `gorm:"size:20;not null;default:'active';index" json:"status"`
SortOrder int `gorm:"not null;default:0" json:"sort_order"`
Remark string `gorm:"size:255;not null;default:''" json:"remark"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (PickupShop) TableName() string {
return "pickup_shops"
}
// PickupShopMapping 线下提号店铺历史映射/别名模型。
type PickupShopMapping struct {
ID uint64 `gorm:"primaryKey" json:"id"`
SourceName string `gorm:"size:100;not null;uniqueIndex" json:"source_name"`
TargetShopID uint64 `gorm:"not null;default:0" json:"target_shop_id"`
TargetShopName string `gorm:"size:100;not null;index" json:"target_shop_name"`
MappedCount int64 `gorm:"not null;default:0" json:"mapped_count"`
Remark string `gorm:"size:255;not null;default:''" json:"remark"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (PickupShopMapping) TableName() string {
return "pickup_shop_mappings"
}
+34
View File
@@ -0,0 +1,34 @@
package model
import (
"time"
"gorm.io/datatypes"
)
// ProcessEvent 是订单、提号等交易流程的不可变操作留痕。
// 当前业务表仍保存当前状态;此表只追加,用于还原每一步由谁填写、修改和确认。
type ProcessEvent struct {
ID uint64 `gorm:"primaryKey" json:"id"`
BusinessType string `gorm:"size:32;not null;index:idx_process_event_business" json:"business_type"`
BusinessID uint64 `gorm:"not null;index:idx_process_event_business" json:"business_id"`
Stage string `gorm:"size:32;not null;default:''" json:"stage"`
Action string `gorm:"size:64;not null" json:"action"`
ActorType string `gorm:"size:16;not null" json:"actor_type"`
ActorID uint64 `gorm:"not null;default:0" json:"actor_id"`
ActorName string `gorm:"size:128;not null;default:''" json:"actor_name"`
TargetType string `gorm:"size:16;not null;default:''" json:"target_type"`
TargetID *uint64 `json:"target_id,omitempty"`
TargetName string `gorm:"size:128;not null;default:''" json:"target_name"`
Content string `gorm:"type:text" json:"content"`
Reason string `gorm:"type:text" json:"reason"`
Payload datatypes.JSON `gorm:"not null" json:"payload"`
AttachmentURLs datatypes.JSON `gorm:"column:attachment_urls;not null" json:"attachment_urls"`
StateBefore datatypes.JSON `gorm:"column:state_before;not null" json:"state_before"`
StateAfter datatypes.JSON `gorm:"column:state_after;not null" json:"state_after"`
CreatedAt time.Time `gorm:"index" json:"created_at"`
}
func (ProcessEvent) TableName() string {
return "process_events"
}
+25
View File
@@ -0,0 +1,25 @@
package model
import "time"
// RenterGrowthLedger 记录租客成长积分变动流水,便于排查订单完成后的积分发放。
type RenterGrowthLedger struct {
ID uint64 `gorm:"primaryKey" json:"id"`
UserID uint64 `gorm:"not null;index" json:"user_id"`
OrderID *uint64 `gorm:"index;uniqueIndex:uk_renter_growth_order_source,priority:1" json:"order_id,omitempty"`
Points int64 `gorm:"not null;default:0" json:"points"`
BasisAmountCent int64 `gorm:"not null;default:0" json:"basis_amount_cent"`
PointsPerYuan int64 `gorm:"not null;default:0" json:"points_per_yuan"`
BeforePoints int64 `gorm:"not null;default:0" json:"before_points"`
AfterPoints int64 `gorm:"not null;default:0" json:"after_points"`
BeforeLevel string `gorm:"size:32;not null;default:'normal'" json:"before_level"`
AfterLevel string `gorm:"size:32;not null;default:'normal'" json:"after_level"`
Source string `gorm:"size:32;not null;default:'order_completed';uniqueIndex:uk_renter_growth_order_source,priority:2" json:"source"`
OperatorAdminID *uint64 `gorm:"index" json:"operator_admin_id,omitempty"`
Remark string `gorm:"size:255;not null;default:''" json:"remark"`
CreatedAt time.Time `json:"created_at"`
}
func (RenterGrowthLedger) TableName() string {
return "renter_growth_ledger"
}
+44
View File
@@ -0,0 +1,44 @@
package model
import "time"
type Role struct {
ID uint64 `gorm:"primaryKey" json:"id"`
Code string `gorm:"size:64;not null;uniqueIndex" json:"code"`
Name string `gorm:"size:64;not null" json:"name"`
Description string `gorm:"size:255;not null;default:''" json:"description"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Role) TableName() string { return "roles" }
type Permission struct {
ID uint64 `gorm:"primaryKey" json:"id"`
Code string `gorm:"size:128;not null;uniqueIndex" json:"code"`
Name string `gorm:"size:64;not null" json:"name"`
Resource string `gorm:"size:128;not null" json:"resource"`
Action string `gorm:"size:64;not null" json:"action"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Permission) TableName() string { return "permissions" }
type AdminUserRole struct {
ID uint64 `gorm:"primaryKey" json:"id"`
AdminID uint64 `gorm:"column:admin_user_id;not null;uniqueIndex:uk_admin_user_roles_user_role" json:"admin_id"`
RoleID uint64 `gorm:"column:role_id;not null;uniqueIndex:uk_admin_user_roles_user_role" json:"role_id"`
CreatedAt time.Time `json:"created_at"`
}
func (AdminUserRole) TableName() string { return "admin_user_roles" }
type RolePermission struct {
ID uint64 `gorm:"primaryKey" json:"id"`
RoleID uint64 `gorm:"column:role_id;not null;uniqueIndex:uk_role_permissions_role_permission" json:"role_id"`
PermissionID uint64 `gorm:"column:permission_id;not null;uniqueIndex:uk_role_permissions_role_permission" json:"permission_id"`
CreatedAt time.Time `json:"created_at"`
}
func (RolePermission) TableName() string { return "role_permissions" }
+41
View File
@@ -0,0 +1,41 @@
package model
import (
"time"
"gorm.io/datatypes"
)
// SmsMessage 是节点短信发送队列记录。
// 业务事务内只写入 pending,真正的发送由后台任务完成,
// 因此短信通道异常不会影响业务提交。
type SmsMessage struct {
ID uint64 `gorm:"primaryKey" json:"id"`
UserID uint64 `gorm:"not null;default:0" json:"user_id"`
Phone string `gorm:"size:32;not null;default:''" json:"phone"`
Node string `gorm:"size:64;not null" json:"node"`
BizType string `gorm:"size:32;not null;default:''" json:"biz_type"`
BizID uint64 `gorm:"not null;default:0" json:"biz_id"`
DistributionSiteID *uint64 `gorm:"index" json:"distribution_site_id,omitempty"`
TemplateCode string `gorm:"size:64;not null;default:''" json:"template_code"`
TemplateParams datatypes.JSON `json:"template_params"`
SignName string `gorm:"size:64;not null;default:''" json:"sign_name"`
Status string `gorm:"size:16;not null;default:'pending'" json:"status"`
SkipReason string `gorm:"size:128;not null;default:''" json:"skip_reason"`
RetryCount int `gorm:"not null;default:0" json:"retry_count"`
LastError string `gorm:"size:512;not null;default:''" json:"last_error"`
SentAt *time.Time `json:"sent_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (SmsMessage) TableName() string {
return "sms_messages"
}
const (
SmsStatusPending = "pending"
SmsStatusSent = "sent"
SmsStatusFailed = "failed"
SmsStatusSkipped = "skipped"
)
+17
View File
@@ -0,0 +1,17 @@
package model
import "time"
type SystemConfig struct {
ID uint64 `gorm:"primaryKey" json:"id"`
Key string `gorm:"column:key;size:128;not null;uniqueIndex" json:"key"`
Value string `gorm:"column:value;type:text;not null" json:"value"`
Description string `gorm:"size:255;not null;default:''" json:"description"`
UpdatedBy *uint64 `json:"updated_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (SystemConfig) TableName() string {
return "system_configs"
}
+34
View File
@@ -0,0 +1,34 @@
package model
import "time"
type User struct {
ID uint64 `gorm:"primaryKey" json:"id"`
Phone string `gorm:"size:32;not null;uniqueIndex" json:"phone"`
PasswordHash string `gorm:"size:255;not null;default:''" json:"-"`
Nickname string `gorm:"size:64;not null;default:''" json:"nickname"`
AvatarURL string `gorm:"size:512;not null;default:''" json:"avatar_url"`
RealnameStatus string `gorm:"size:32;not null;default:'unverified'" json:"realname_status"`
RiskStatus string `gorm:"size:32;not null;default:'normal'" json:"risk_status"`
CreditScore int `gorm:"not null;default:100" json:"credit_score"`
DepositFreeQuotaCent int64 `gorm:"not null;default:0" json:"-"`
RenterGrowthPoints int64 `gorm:"not null;default:0;index:idx_users_renter_growth_level,priority:2" json:"renter_growth_points"`
RenterGrowthLevel string `gorm:"size:32;not null;default:'normal';index:idx_users_renter_growth_level,priority:1" json:"renter_growth_level"`
Status string `gorm:"size:32;not null;default:'active'" json:"status"`
// 会话版本:递增后该用户已签发的全部令牌立即失效。
// 默认值对齐迁移 000052 的 users.token_version DEFAULT 0。
TokenVersion int64 `gorm:"not null;default:0" json:"-"`
// 注册归因:注册时由前端携带的推广短码。
RegisterTrackCode string `gorm:"size:64;index:idx_users_register_track_code" json:"register_track_code,omitempty"`
DistributionSiteID uint64 `gorm:"not null;default:0;index:idx_users_distribution_site_id" json:"distribution_site_id"` // 0=主站, >0=对应分销站点
InviteCode *string `gorm:"size:32;uniqueIndex:uk_users_invite_code" json:"invite_code,omitempty"`
InvitedByID uint64 `gorm:"not null;default:0;index:idx_users_invited_by_id" json:"invited_by_id,omitempty"`
InviteVerifiedAt *time.Time `json:"invite_verified_at,omitempty"`
LastLoginAt *time.Time `json:"last_login_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (User) TableName() string {
return "users"
}
+25
View File
@@ -0,0 +1,25 @@
package model
import "time"
type UserRealname struct {
ID uint64 `gorm:"primaryKey" json:"id"`
UserID uint64 `gorm:"not null;uniqueIndex" json:"user_id"`
Provider string `gorm:"size:32;not null" json:"provider"`
ProviderOrderNo string `gorm:"size:128;not null;default:''" json:"provider_order_no"`
Status string `gorm:"size:32;not null;default:'pending'" json:"status"`
MaskedName string `gorm:"size:64;not null;default:''" json:"masked_name"`
EncryptedName string `gorm:"size:255;not null;default:''" json:"encrypted_name"`
MaskedIDNo string `gorm:"size:64;not null;default:'';index:idx_user_realname_masked_id_no" json:"masked_id_no"`
EncryptedIDNo string `gorm:"size:255;not null;default:''" json:"encrypted_id_no"`
// IDNoHash 是身份证号的不可逆指纹,仅认证通过时写入;唯一索引保证同一身份证只能绑定一个账号。
IDNoHash *string `gorm:"size:64;uniqueIndex:uk_user_realname_id_no_hash" json:"-"`
VerifiedAt *time.Time `json:"verified_at"`
FailReason string `gorm:"size:255;not null;default:''" json:"fail_reason"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (UserRealname) TableName() string {
return "user_realname"
}
+38
View File
@@ -0,0 +1,38 @@
package model
import "time"
type WalletAccount struct {
ID uint64 `gorm:"primaryKey" json:"id"`
UserID uint64 `gorm:"not null;uniqueIndex:uk_wallet_accounts_user_site" json:"user_id"`
DistributionSiteID uint64 `gorm:"not null;default:0;uniqueIndex:uk_wallet_accounts_user_site" json:"distribution_site_id"`
AvailableBalanceCent int64 `gorm:"not null;default:0" json:"-"`
FrozenBalanceCent int64 `gorm:"not null;default:0" json:"-"`
Status string `gorm:"size:32;not null;default:'active'" json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (WalletAccount) TableName() string {
return "wallet_accounts"
}
type WalletLedger struct {
ID uint64 `gorm:"primaryKey" json:"id"`
LedgerNo string `gorm:"size:64;not null;uniqueIndex" json:"ledger_no"`
UserID uint64 `gorm:"not null;index:idx_wallet_ledger_user_site" json:"user_id"`
DistributionSiteID uint64 `gorm:"not null;default:0;index:idx_wallet_ledger_user_site" json:"distribution_site_id"`
OrderID *uint64 `json:"order_id"`
Direction string `gorm:"size:16;not null" json:"direction"`
AmountCent int64 `gorm:"not null;default:0" json:"-"`
BalanceAfterCent int64 `gorm:"not null;default:0" json:"-"`
BalanceType string `gorm:"size:32;not null" json:"balance_type"`
BizType string `gorm:"size:32;not null" json:"biz_type"`
BizNo string `gorm:"size:64;not null" json:"biz_no"`
Remark string `gorm:"size:255;not null;default:''" json:"remark"`
CreatedAt time.Time `json:"created_at"`
}
func (WalletLedger) TableName() string {
return "wallet_ledger"
}
+33
View File
@@ -0,0 +1,33 @@
package model
import "time"
type WithdrawalRequest struct {
ID uint64 `gorm:"primaryKey" json:"id"`
WithdrawNo string `gorm:"size:64;not null;uniqueIndex" json:"withdraw_no"`
UserID uint64 `gorm:"not null;index:idx_withdrawal_user_site" json:"user_id"`
DistributionSiteID uint64 `gorm:"not null;default:0;index:idx_withdrawal_user_site" json:"distribution_site_id"`
AmountCent int64 `gorm:"not null;default:0" json:"-"`
FeeCent int64 `gorm:"not null;default:0" json:"-"`
ActualAmountCent int64 `gorm:"not null;default:0" json:"-"`
PaymentAccountID *uint64 `json:"payment_account_id"`
AccountType string `gorm:"size:32;not null" json:"account_type"`
AccountName string `gorm:"size:128;not null" json:"account_name"`
AccountNo string `gorm:"size:128;not null" json:"account_no"`
BankName string `gorm:"size:128;not null;default:''" json:"bank_name"`
BankBranch string `gorm:"size:255;not null;default:''" json:"bank_branch"`
Status string `gorm:"size:32;not null;default:'pending'" json:"status"`
ReviewedBy *uint64 `json:"reviewed_by"`
ReviewedAt *time.Time `json:"reviewed_at"`
ReviewRemark string `gorm:"size:255;not null;default:''" json:"review_remark"`
PaidBy *uint64 `json:"paid_by"`
PaidAt *time.Time `json:"paid_at"`
PaymentProofURL string `gorm:"size:512;not null;default:''" json:"payment_proof_url"`
PaymentRemark string `gorm:"size:255;not null;default:''" json:"payment_remark"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (WithdrawalRequest) TableName() string {
return "withdrawal_requests"
}