feat: expand seed data with diverse test entities

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-02-08 18:29:28 +08:00
parent 0f04cc02ed
commit d6550e9e1a

View File

@@ -60,6 +60,7 @@ func Serve(_ *cobra.Command, _ []string) error {
"system_configs",
"notification_templates",
"notifications",
"recharge_codes",
"tenant_invites",
"tenant_join_requests",
"content_access",
@@ -596,6 +597,607 @@ func Serve(_ *cobra.Command, _ []string) error {
fmt.Printf("Create negative user failed: %v\n", err)
}
reviewUser := &models.User{
Username: "review_user",
Phone: "13800009997",
Nickname: "待审核用户",
Avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=ReviewUser",
Balance: 1200,
Status: consts.UserStatusPendingVerify,
Roles: types.Array[consts.Role]{consts.RoleUser},
}
if err := models.UserQuery.WithContext(ctx).Create(reviewUser); err != nil {
fmt.Printf("Create review user failed: %v\n", err)
}
bannedUser := &models.User{
Username: "banned_user",
Phone: "13800009996",
Nickname: "封禁用户",
Avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=BannedUser",
Balance: 200,
Status: consts.UserStatusBanned,
Roles: types.Array[consts.Role]{consts.RoleUser},
}
if err := models.UserQuery.WithContext(ctx).Create(bannedUser); err != nil {
fmt.Printf("Create banned user failed: %v\n", err)
}
inactiveUser := &models.User{
Username: "inactive_user",
Phone: "13800009995",
Nickname: "停用用户",
Avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=InactiveUser",
Balance: 300,
Status: consts.UserStatusInactive,
Roles: types.Array[consts.Role]{consts.RoleUser},
}
if err := models.UserQuery.WithContext(ctx).Create(inactiveUser); err != nil {
fmt.Printf("Create inactive user failed: %v\n", err)
}
creatorApplicant := &models.User{
Username: "creator_apply_user",
Phone: "13800009994",
Nickname: "申请创作者用户",
Avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=CreatorApplyUser",
Balance: 888,
Status: consts.UserStatusVerified,
Roles: types.Array[consts.Role]{consts.RoleUser, consts.RoleCreator},
}
if err := models.UserQuery.WithContext(ctx).Create(creatorApplicant); err != nil {
fmt.Printf("Create creator applicant user failed: %v\n", err)
}
if reviewUser != nil {
if err := models.TenantUserQuery.WithContext(ctx).Create(&models.TenantUser{
TenantID: tenant.ID,
UserID: reviewUser.ID,
Role: types.Array[consts.TenantUserRole]{consts.TenantUserRoleMember},
Status: consts.UserStatusPendingVerify,
}); err != nil {
fmt.Printf("Create pending tenant user failed: %v\n", err)
}
}
if bannedUser != nil {
if err := models.TenantUserQuery.WithContext(ctx).Create(&models.TenantUser{
TenantID: tenant.ID,
UserID: bannedUser.ID,
Role: types.Array[consts.TenantUserRole]{consts.TenantUserRoleMember},
Status: consts.UserStatusBanned,
}); err != nil {
fmt.Printf("Create banned tenant user failed: %v\n", err)
}
}
emptyTenant := &models.Tenant{
UserID: creator.ID,
Name: "空态租户",
Code: "empty_" + tenantCodeSuffix,
UUID: types.UUID(uuid.New()),
Status: consts.TenantStatusVerified,
}
if err := models.TenantQuery.WithContext(ctx).Create(emptyTenant); err != nil {
fmt.Printf("Create empty tenant failed: %v\n", err)
}
pendingTenant := &models.Tenant{
UserID: creator.ID,
Name: "待审核租户",
Code: "pending_" + tenantCodeSuffix,
UUID: types.UUID(uuid.New()),
Status: consts.TenantStatusPendingVerify,
}
if err := models.TenantQuery.WithContext(ctx).Create(pendingTenant); err != nil {
fmt.Printf("Create pending tenant failed: %v\n", err)
}
bannedTenant := &models.Tenant{
UserID: creator.ID,
Name: "封禁租户",
Code: "banned_" + tenantCodeSuffix,
UUID: types.UUID(uuid.New()),
Status: consts.TenantStatusBanned,
}
if err := models.TenantQuery.WithContext(ctx).Create(bannedTenant); err != nil {
fmt.Printf("Create banned tenant failed: %v\n", err)
}
if creatorApplicant != nil {
if err := models.TenantJoinRequestQuery.WithContext(ctx).Create(&models.TenantJoinRequest{
TenantID: pendingTenant.ID,
UserID: creatorApplicant.ID,
Status: string(consts.TenantJoinRequestStatusRejected),
Reason: "资料不完整",
DecidedAt: time.Now().Add(-30 * time.Minute),
DecidedOperatorUserID: creator.ID,
DecidedReason: "资料不完整,驳回",
}); err != nil {
fmt.Printf("Create rejected join request failed: %v\n", err)
}
}
disabledInviteSuffix, err := randomIntWithLimit(100000)
if err != nil {
return fmt.Errorf("generate disabled invite code: %w", err)
}
if err := models.TenantInviteQuery.WithContext(ctx).Create(&models.TenantInvite{
TenantID: tenant.ID,
UserID: creator.ID,
Code: "disabled" + cast.ToString(disabledInviteSuffix),
Status: string(consts.TenantInviteStatusDisabled),
MaxUses: 3,
UsedCount: 1,
DisabledAt: time.Now().Add(-2 * time.Hour),
DisabledOperatorUserID: superAdmin.ID,
Remark: "seed disabled invite",
ExpiresAt: time.Now().Add(3 * 24 * time.Hour),
}); err != nil {
fmt.Printf("Create disabled invite failed: %v\n", err)
}
expiredInviteSuffix, err := randomIntWithLimit(100000)
if err != nil {
return fmt.Errorf("generate expired invite code: %w", err)
}
if err := models.TenantInviteQuery.WithContext(ctx).Create(&models.TenantInvite{
TenantID: tenant.ID,
UserID: creator.ID,
Code: "expired" + cast.ToString(expiredInviteSuffix),
Status: string(consts.TenantInviteStatusExpired),
MaxUses: 2,
UsedCount: 2,
Remark: "seed expired invite",
ExpiresAt: time.Now().Add(-2 * time.Hour),
}); err != nil {
fmt.Printf("Create expired invite failed: %v\n", err)
}
draftContent := &models.Content{
TenantID: tenant.ID,
UserID: creator.ID,
Title: "草稿内容(待完善)",
Description: "用于测试草稿态按钮与筛选",
Genre: "京剧",
Status: consts.ContentStatusDraft,
Visibility: consts.ContentVisibilityPrivate,
Views: 0,
Likes: 0,
}
if err := models.ContentQuery.WithContext(ctx).Create(draftContent); err != nil {
fmt.Printf("Create draft content failed: %v\n", err)
}
reviewingContent := &models.Content{
TenantID: tenant.ID,
UserID: creator.ID,
Title: "审核中内容(待审核)",
Description: "用于测试审核流操作",
Genre: "越剧",
Status: consts.ContentStatusReviewing,
Visibility: consts.ContentVisibilityTenantOnly,
Views: 18,
Likes: 2,
}
if err := models.ContentQuery.WithContext(ctx).Create(reviewingContent); err != nil {
fmt.Printf("Create reviewing content failed: %v\n", err)
}
unpublishedContent := &models.Content{
TenantID: tenant.ID,
UserID: creator.ID,
Title: "下架内容(可重发)",
Description: "用于测试下架态展示",
Genre: "黄梅戏",
Status: consts.ContentStatusUnpublished,
Visibility: consts.ContentVisibilityTenantOnly,
Views: 87,
Likes: 9,
}
if err := models.ContentQuery.WithContext(ctx).Create(unpublishedContent); err != nil {
fmt.Printf("Create unpublished content failed: %v\n", err)
}
blockedContent := &models.Content{
TenantID: tenant.ID,
UserID: creator.ID,
Title: "封禁内容(违规)",
Description: "用于测试封禁态展示",
Genre: "昆曲",
Status: consts.ContentStatusBlocked,
Visibility: consts.ContentVisibilityPrivate,
Views: 120,
Likes: 0,
}
if err := models.ContentQuery.WithContext(ctx).Create(blockedContent); err != nil {
fmt.Printf("Create blocked content failed: %v\n", err)
}
if reviewingContent != nil {
if err := models.ContentReportQuery.WithContext(ctx).Create(&models.ContentReport{
TenantID: tenant.ID,
ContentID: reviewingContent.ID,
ReporterID: buyer.ID,
Reason: "quality",
Detail: "音画不同步",
Status: string(consts.TenantJoinRequestStatusApproved),
HandledBy: superAdmin.ID,
HandledAction: "approve",
HandledReason: "确认问题,已处理",
HandledAt: time.Now().Add(-90 * time.Minute),
}); err != nil {
fmt.Printf("Create approved content report failed: %v\n", err)
}
}
if blockedContent != nil {
if err := models.ContentReportQuery.WithContext(ctx).Create(&models.ContentReport{
TenantID: tenant.ID,
ContentID: blockedContent.ID,
ReporterID: buyer.ID,
Reason: "abuse",
Detail: "标题涉嫌误导",
Status: string(consts.TenantJoinRequestStatusRejected),
HandledBy: superAdmin.ID,
HandledAction: "reject",
HandledReason: "证据不足,驳回",
HandledAt: time.Now().Add(-45 * time.Minute),
}); err != nil {
fmt.Printf("Create rejected content report failed: %v\n", err)
}
}
processingAsset := &models.MediaAsset{
TenantID: tenant.ID,
UserID: creator.ID,
Type: consts.MediaAssetTypeVideo,
Status: consts.MediaAssetStatusProcessing,
Provider: "mock",
ObjectKey: "seed/video-processing.mp4",
Meta: types.NewJSONType(fields.MediaAssetMeta{
Size: 4096,
}),
UpdatedAt: time.Now().Add(-8 * time.Hour),
}
if err := models.MediaAssetQuery.WithContext(ctx).Create(processingAsset); err != nil {
fmt.Printf("Create processing asset failed: %v\n", err)
}
failedAsset := &models.MediaAsset{
TenantID: tenant.ID,
UserID: creator.ID,
Type: consts.MediaAssetTypeVideo,
Status: consts.MediaAssetStatusFailed,
Provider: "mock",
ObjectKey: "seed/video-failed.mp4",
Meta: types.NewJSONType(fields.MediaAssetMeta{
Size: 5120,
}),
}
if err := models.MediaAssetQuery.WithContext(ctx).Create(failedAsset); err != nil {
fmt.Printf("Create failed asset failed: %v\n", err)
}
expiredCoupon := &models.Coupon{
TenantID: tenant.ID,
Title: "过期券",
Type: consts.CouponTypeFixAmount,
Value: 200,
MinOrderAmount: 500,
TotalQuantity: 2,
UsedQuantity: 2,
StartAt: time.Now().Add(-10 * 24 * time.Hour),
EndAt: time.Now().Add(-24 * time.Hour),
}
if err := models.CouponQuery.WithContext(ctx).Create(expiredCoupon); err != nil {
fmt.Printf("Create expired coupon failed: %v\n", err)
}
upcomingCoupon := &models.Coupon{
TenantID: tenant.ID,
Title: "未生效券",
Type: consts.CouponTypeDiscount,
Value: 85,
MaxDiscount: 300,
MinOrderAmount: 1000,
TotalQuantity: 50,
UsedQuantity: 0,
StartAt: time.Now().Add(48 * time.Hour),
EndAt: time.Now().Add(30 * 24 * time.Hour),
}
if err := models.CouponQuery.WithContext(ctx).Create(upcomingCoupon); err != nil {
fmt.Printf("Create upcoming coupon failed: %v\n", err)
}
exhaustedCoupon := &models.Coupon{
TenantID: tenant.ID,
Title: "已领完券",
Type: consts.CouponTypeFixAmount,
Value: 300,
MinOrderAmount: 600,
TotalQuantity: 1,
UsedQuantity: 1,
StartAt: time.Now().Add(-24 * time.Hour),
EndAt: time.Now().Add(3 * 24 * time.Hour),
}
if err := models.CouponQuery.WithContext(ctx).Create(exhaustedCoupon); err != nil {
fmt.Printf("Create exhausted coupon failed: %v\n", err)
}
if expiredCoupon != nil {
if err := models.UserCouponQuery.WithContext(ctx).Create(&models.UserCoupon{
UserID: buyer.ID,
CouponID: expiredCoupon.ID,
Status: consts.UserCouponStatusExpired,
}); err != nil {
fmt.Printf("Create expired user coupon failed: %v\n", err)
}
}
if cp1 != nil {
if err := models.UserCouponQuery.WithContext(ctx).Create(&models.UserCoupon{
UserID: buyer.ID,
CouponID: cp1.ID,
OrderID: 0,
Status: consts.UserCouponStatusUsed,
UsedAt: time.Now().Add(-3 * time.Hour),
}); err != nil {
fmt.Printf("Create used user coupon failed: %v\n", err)
}
}
purchaseCreated := &models.Order{
TenantID: tenant.ID,
UserID: buyer.ID,
Type: consts.OrderTypeContentPurchase,
Status: consts.OrderStatusCreated,
Currency: consts.CurrencyCNY,
AmountOriginal: 1100,
AmountDiscount: 0,
AmountPaid: 1100,
IdempotencyKey: uuid.NewString(),
}
if err := models.OrderQuery.WithContext(ctx).Create(purchaseCreated); err != nil {
fmt.Printf("Create created purchase order failed: %v\n", err)
}
purchaseRefunding := &models.Order{
TenantID: tenant.ID,
UserID: buyer.ID,
Type: consts.OrderTypeContentPurchase,
Status: consts.OrderStatusRefunding,
Currency: consts.CurrencyCNY,
AmountOriginal: 1300,
AmountDiscount: 100,
AmountPaid: 1200,
IdempotencyKey: uuid.NewString(),
PaidAt: time.Now().Add(-5 * time.Hour),
}
if err := models.OrderQuery.WithContext(ctx).Create(purchaseRefunding); err != nil {
fmt.Printf("Create refunding purchase order failed: %v\n", err)
}
purchaseCanceled := &models.Order{
TenantID: tenant.ID,
UserID: buyer.ID,
Type: consts.OrderTypeContentPurchase,
Status: consts.OrderStatusCanceled,
Currency: consts.CurrencyCNY,
AmountOriginal: 900,
AmountDiscount: 0,
AmountPaid: 0,
IdempotencyKey: uuid.NewString(),
}
if err := models.OrderQuery.WithContext(ctx).Create(purchaseCanceled); err != nil {
fmt.Printf("Create canceled purchase order failed: %v\n", err)
}
purchaseFailed := &models.Order{
TenantID: tenant.ID,
UserID: buyer.ID,
Type: consts.OrderTypeContentPurchase,
Status: consts.OrderStatusFailed,
Currency: consts.CurrencyCNY,
AmountOriginal: 1400,
AmountDiscount: 0,
AmountPaid: 1400,
IdempotencyKey: uuid.NewString(),
}
if err := models.OrderQuery.WithContext(ctx).Create(purchaseFailed); err != nil {
fmt.Printf("Create failed purchase order failed: %v\n", err)
}
orderSeedContentID := int64(0)
if len(seededContents) > 1 {
orderSeedContentID = seededContents[1].ID
}
if orderSeedContentID > 0 {
if purchaseCreated != nil {
if err := models.OrderItemQuery.WithContext(ctx).Create(&models.OrderItem{
TenantID: tenant.ID,
UserID: buyer.ID,
OrderID: purchaseCreated.ID,
ContentID: orderSeedContentID,
ContentUserID: creator.ID,
AmountPaid: purchaseCreated.AmountPaid,
}); err != nil {
fmt.Printf("Create order item for created order failed: %v\n", err)
}
}
if purchaseRefunding != nil {
if err := models.OrderItemQuery.WithContext(ctx).Create(&models.OrderItem{
TenantID: tenant.ID,
UserID: buyer.ID,
OrderID: purchaseRefunding.ID,
ContentID: orderSeedContentID,
ContentUserID: creator.ID,
AmountPaid: purchaseRefunding.AmountPaid,
}); err != nil {
fmt.Printf("Create order item for refunding order failed: %v\n", err)
}
}
}
if len(seededContents) > 2 {
if err := models.ContentAccessQuery.WithContext(ctx).Create(&models.ContentAccess{
TenantID: tenant.ID,
UserID: buyer.ID,
ContentID: seededContents[2].ID,
OrderID: purchaseRefunding.ID,
Status: consts.ContentAccessStatusRevoked,
RevokedAt: time.Now().Add(-40 * time.Minute),
}); err != nil {
fmt.Printf("Create revoked content access failed: %v\n", err)
}
}
if len(seededContents) > 3 {
if err := models.ContentAccessQuery.WithContext(ctx).Create(&models.ContentAccess{
TenantID: tenant.ID,
UserID: buyer.ID,
ContentID: seededContents[3].ID,
OrderID: purchaseRefunding.ID,
Status: consts.ContentAccessStatusExpired,
RevokedAt: time.Now().Add(-24 * time.Hour),
}); err != nil {
fmt.Printf("Create expired content access failed: %v\n", err)
}
}
if err := models.PayoutAccountQuery.WithContext(ctx).Create(&models.PayoutAccount{
TenantID: tenant.ID,
UserID: creator.ID,
Type: consts.PayoutAccountTypeBank,
Name: "招商银行",
Account: "6222000000000001",
Realname: "梅派传人小林",
Status: consts.PayoutAccountStatusPending,
}); err != nil {
fmt.Printf("Create pending payout account failed: %v\n", err)
}
if err := models.PayoutAccountQuery.WithContext(ctx).Create(&models.PayoutAccount{
TenantID: tenant.ID,
UserID: creator.ID,
Type: consts.PayoutAccountTypeAlipay,
Name: "支付宝(驳回样例)",
Account: "creator-rejected@example.com",
Realname: "梅派传人小林",
Status: consts.PayoutAccountStatusRejected,
ReviewedBy: superAdmin.ID,
ReviewedAt: time.Now().Add(-2 * time.Hour),
ReviewReason: "账户实名不匹配",
}); err != nil {
fmt.Printf("Create rejected payout account failed: %v\n", err)
}
if err := models.NotificationQuery.WithContext(ctx).Create(&models.Notification{
UserID: buyer.ID,
TenantID: tenant.ID,
Type: string(consts.NotificationTypeOrder),
Title: "订单支付成功",
Content: "您有一笔订单已支付成功。",
IsRead: true,
}); err != nil {
fmt.Printf("Create read notification failed: %v\n", err)
}
if err := models.NotificationQuery.WithContext(ctx).Create(&models.Notification{
UserID: buyer.ID,
TenantID: tenant.ID,
Type: string(consts.NotificationTypeAudit),
Title: "内容审核提醒",
Content: "您提交的内容正在审核中。",
IsRead: false,
}); err != nil {
fmt.Printf("Create unread audit notification failed: %v\n", err)
}
inactiveTemplate := &models.NotificationTemplate{
TenantID: 0,
Name: "已停用模板",
Type: consts.NotificationTypeSystem,
Title: "停用模板",
Content: "该模板用于测试停用状态。",
IsActive: true,
}
if err := models.NotificationTemplateQuery.WithContext(ctx).Create(inactiveTemplate); err != nil {
fmt.Printf("Create inactive notification template failed: %v\n", err)
} else {
if _, err := models.NotificationTemplateQuery.WithContext(ctx).
Where(models.NotificationTemplateQuery.ID.Eq(inactiveTemplate.ID)).
UpdateSimple(models.NotificationTemplateQuery.IsActive.Value(false)); err != nil {
fmt.Printf("Update inactive notification template failed: %v\n", err)
}
}
redeemedRechargeOrder := &models.Order{
TenantID: 0,
UserID: buyer.ID,
Type: consts.OrderTypeRecharge,
Status: consts.OrderStatusPaid,
Currency: consts.CurrencyCNY,
AmountOriginal: 3000,
AmountDiscount: 0,
AmountPaid: 3000,
IdempotencyKey: uuid.NewString(),
PaidAt: time.Now().Add(-90 * time.Minute),
}
if err := models.OrderQuery.WithContext(ctx).Create(redeemedRechargeOrder); err != nil {
fmt.Printf("Create redeemed recharge order failed: %v\n", err)
}
if err := models.RechargeCodeQuery.WithContext(ctx).Create(&models.RechargeCode{
Code: "RC_ACTIVE_1000",
Amount: 100000,
Status: "active",
ActivatedBy: superAdmin.ID,
ActivatedAt: time.Now().Add(-30 * time.Minute),
Remark: "seed active recharge code",
}); err != nil {
fmt.Printf("Create active recharge code failed: %v\n", err)
}
if err := models.RechargeCodeQuery.WithContext(ctx).Create(&models.RechargeCode{
Code: "RC_REDEEMED_3000",
Amount: 300000,
Status: "redeemed",
ActivatedBy: superAdmin.ID,
ActivatedAt: time.Now().Add(-4 * time.Hour),
RedeemedBy: buyer.ID,
RedeemedAt: time.Now().Add(-2 * time.Hour),
RedeemedOrderID: redeemedRechargeOrder.ID,
Remark: "seed redeemed recharge code",
}); err != nil {
fmt.Printf("Create redeemed recharge code failed: %v\n", err)
}
if err := models.RechargeCodeQuery.WithContext(ctx).Create(&models.RechargeCode{
Code: "RC_INACTIVE_500",
Amount: 50000,
Status: "inactive",
ActivatedBy: 0,
Remark: "seed inactive recharge code",
}); err != nil {
fmt.Printf("Create inactive recharge code failed: %v\n", err)
}
for i := 0; i < 12; i++ {
models.NotificationQuery.WithContext(ctx).Create(&models.Notification{
UserID: buyer.ID,
TenantID: tenant.ID,
Type: string(consts.NotificationTypeSystem),
Title: fmt.Sprintf("系统通知 #%d", i+1),
Content: "分页测试通知",
IsRead: i%2 == 0,
})
}
for i := 0; i < 12; i++ {
models.AuditLogQuery.WithContext(ctx).Create(&models.AuditLog{
TenantID: tenant.ID,
OperatorID: superAdmin.ID,
Action: "seed_extra",
TargetID: fmt.Sprintf("extra:%d", i+1),
Detail: "extra audit log for pagination",
})
}
fmt.Println("Seed done.")
return nil