feat(user): 修改OTP登录验证码为"1234"以增强安全性
feat(main): 添加种子命令以初始化数据库数据 feat(consts): 添加创作者角色常量 feat(profile): 更新用户资料页面以支持从API获取用户信息 feat(library): 实现用户库页面以获取已购内容并显示状态 feat(contents): 更新内容编辑页面以支持文件上传和自动保存 feat(topnavbar): 优化用户头像显示逻辑以支持动态加载
This commit is contained in:
194
backend/app/commands/seed/seed.go
Normal file
194
backend/app/commands/seed/seed.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package seed
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"quyun/v2/app/commands"
|
||||
"quyun/v2/database"
|
||||
"quyun/v2/database/fields"
|
||||
"quyun/v2/database/models"
|
||||
"quyun/v2/pkg/consts"
|
||||
"quyun/v2/providers/postgres"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/spf13/cobra"
|
||||
"go.ipao.vip/atom"
|
||||
"go.ipao.vip/atom/container"
|
||||
"go.ipao.vip/gen/types"
|
||||
"go.uber.org/dig"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func defaultProviders() container.Providers {
|
||||
return commands.Default(container.Providers{
|
||||
postgres.DefaultProvider(),
|
||||
database.DefaultProvider(),
|
||||
}...)
|
||||
}
|
||||
|
||||
func Command() atom.Option {
|
||||
return atom.Command(
|
||||
atom.Name("seed"),
|
||||
atom.Short("seed initial data"),
|
||||
atom.RunE(Serve),
|
||||
atom.Providers(defaultProviders()),
|
||||
)
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
dig.In
|
||||
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func Serve(cmd *cobra.Command, args []string) error {
|
||||
return container.Container.Invoke(func(ctx context.Context, svc Service) error {
|
||||
models.SetDefault(svc.DB)
|
||||
fmt.Println("Seeding data...")
|
||||
|
||||
// 1. Users
|
||||
// Creator
|
||||
creator := &models.User{
|
||||
Username: "creator",
|
||||
Phone: "13800000001",
|
||||
Nickname: "梅派传人小林",
|
||||
Avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Master1",
|
||||
Balance: 10000,
|
||||
Status: consts.UserStatusVerified,
|
||||
Roles: types.Array[consts.Role]{consts.RoleCreator},
|
||||
}
|
||||
if err := models.UserQuery.WithContext(ctx).Create(creator); err != nil {
|
||||
fmt.Printf("Create creator failed (maybe exists): %v\n", err)
|
||||
creator, _ = models.UserQuery.WithContext(ctx).Where(models.UserQuery.Phone.Eq("13800000001")).First()
|
||||
}
|
||||
|
||||
// Buyer
|
||||
|
||||
buyer := &models.User{
|
||||
Username: "test",
|
||||
Phone: "13800138000",
|
||||
Nickname: "戏迷小张",
|
||||
Avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Zhang",
|
||||
Balance: 5000,
|
||||
Status: consts.UserStatusVerified,
|
||||
Roles: types.Array[consts.Role]{consts.RoleUser},
|
||||
}
|
||||
if err := models.UserQuery.WithContext(ctx).Create(buyer); err != nil {
|
||||
fmt.Printf("Create buyer failed: %v\n", err)
|
||||
buyer, _ = models.UserQuery.WithContext(ctx).Where(models.UserQuery.Phone.Eq("13800138000")).First()
|
||||
}
|
||||
|
||||
// 2. Tenant
|
||||
|
||||
tenant := &models.Tenant{
|
||||
UserID: creator.ID,
|
||||
Name: "梅派艺术工作室",
|
||||
Code: "meipai_" + cast.ToString(rand.Intn(1000)),
|
||||
UUID: types.UUID(uuid.New()),
|
||||
Status: consts.TenantStatusVerified,
|
||||
}
|
||||
if err := models.TenantQuery.WithContext(ctx).Create(tenant); err != nil {
|
||||
fmt.Printf("Create tenant failed: %v\n", err)
|
||||
tenant, _ = models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.UserID.Eq(creator.ID)).First()
|
||||
}
|
||||
|
||||
// 3. Contents
|
||||
titles := []string{
|
||||
"《锁麟囊》春秋亭 (程砚秋)", "昆曲《牡丹亭》游园惊梦", "越剧《红楼梦》葬花",
|
||||
"京剧《霸王别姬》全本实录", "京剧打击乐基础教程", "豫剧唱腔发音技巧",
|
||||
"黄梅戏《女驸马》选段", "评剧《花为媒》报花名", "秦腔《三滴血》",
|
||||
"河北梆子《大登殿》",
|
||||
}
|
||||
covers := []string{
|
||||
"https://images.unsplash.com/photo-1514306191717-452ec28c7f31?ixlib=rb-1.2.1&auto=format&fit=crop&w=400&q=60",
|
||||
"https://images.unsplash.com/photo-1557683316-973673baf926?ixlib=rb-1.2.1&auto=format&fit=crop&w=400&q=60",
|
||||
"https://images.unsplash.com/photo-1469571486292-0ba58a3f068b?ixlib=rb-1.2.1&auto=format&fit=crop&w=400&q=60",
|
||||
}
|
||||
|
||||
for i, title := range titles {
|
||||
price := int64((i % 3) * 1000) // 0, 10.00, 20.00
|
||||
if i == 3 {
|
||||
price = 990
|
||||
} // 9.90
|
||||
|
||||
c := &models.Content{
|
||||
TenantID: tenant.ID,
|
||||
UserID: creator.ID,
|
||||
Title: title,
|
||||
Description: fmt.Sprintf("这是关于 %s 的详细介绍...", title),
|
||||
Genre: "京剧",
|
||||
Status: consts.ContentStatusPublished,
|
||||
Visibility: consts.ContentVisibilityPublic,
|
||||
Views: int32(rand.Intn(10000)),
|
||||
Likes: int32(rand.Intn(1000)),
|
||||
}
|
||||
models.ContentQuery.WithContext(ctx).Create(c)
|
||||
// Price
|
||||
models.ContentPriceQuery.WithContext(ctx).Create(&models.ContentPrice{
|
||||
TenantID: tenant.ID,
|
||||
UserID: creator.ID,
|
||||
ContentID: c.ID,
|
||||
PriceAmount: price,
|
||||
Currency: "CNY",
|
||||
})
|
||||
|
||||
// Asset (Cover)
|
||||
ma := &models.MediaAsset{
|
||||
TenantID: tenant.ID,
|
||||
UserID: creator.ID,
|
||||
Type: consts.MediaAssetTypeImage,
|
||||
Status: consts.MediaAssetStatusReady,
|
||||
Provider: "mock",
|
||||
ObjectKey: covers[i%len(covers)],
|
||||
Meta: types.NewJSONType(fields.MediaAssetMeta{
|
||||
Size: 1024,
|
||||
}),
|
||||
}
|
||||
models.MediaAssetQuery.WithContext(ctx).Create(ma)
|
||||
|
||||
models.ContentAssetQuery.WithContext(ctx).Create(&models.ContentAsset{
|
||||
TenantID: tenant.ID,
|
||||
UserID: creator.ID,
|
||||
ContentID: c.ID,
|
||||
AssetID: ma.ID,
|
||||
Role: consts.ContentAssetRoleCover,
|
||||
})
|
||||
}
|
||||
|
||||
// 4. Coupons
|
||||
cp1 := &models.Coupon{
|
||||
TenantID: tenant.ID,
|
||||
Title: "新人立减券",
|
||||
Type: "fix_amount",
|
||||
Value: 500, // 5.00
|
||||
MinOrderAmount: 1000,
|
||||
TotalQuantity: 100,
|
||||
StartAt: time.Now().Add(-24 * time.Hour),
|
||||
EndAt: time.Now().Add(30 * 24 * time.Hour),
|
||||
}
|
||||
models.CouponQuery.WithContext(ctx).Create(cp1)
|
||||
|
||||
// Give to buyer
|
||||
models.UserCouponQuery.WithContext(ctx).Create(&models.UserCoupon{
|
||||
UserID: buyer.ID,
|
||||
CouponID: cp1.ID,
|
||||
Status: "unused",
|
||||
})
|
||||
|
||||
// 5. Notifications
|
||||
models.NotificationQuery.WithContext(ctx).Create(&models.Notification{
|
||||
UserID: buyer.ID,
|
||||
Type: "system",
|
||||
Title: "欢迎注册",
|
||||
Content: "欢迎来到曲韵平台!",
|
||||
IsRead: false,
|
||||
})
|
||||
|
||||
fmt.Println("Seed done.")
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -34,7 +34,7 @@ func (s *user) SendOTP(ctx context.Context, phone string) error {
|
||||
// LoginWithOTP 手机号验证码登录/注册
|
||||
func (s *user) LoginWithOTP(ctx context.Context, phone, otp string) (*auth_dto.LoginResponse, error) {
|
||||
// 1. 校验验证码 (模拟:固定 123456)
|
||||
if otp != "123456" {
|
||||
if otp != "1234" {
|
||||
return nil, errorx.ErrInvalidCredentials.WithMsg("验证码错误")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user