chore(init): 初始化解忧商行项目并完成 Phase 0 基座
This commit is contained in:
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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" }
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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" }
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user