Files
opsx-licence/backend/database/seed.go
T
Logan.Li 2b44e8dcb3 fix: 仅保留 Waffo,并修复调试包年误用永久 Product
调试不再把兼容字段 product_id 覆盖到包年;包年禁止回落永久商品;下单强制走 Waffo。
2026-08-13 00:04:22 +08:00

290 lines
7.7 KiB
Go

package database
import (
"backend/internal/model"
"backend/utils"
"log"
"time"
"gorm.io/gorm"
)
const (
defaultAdminUsername = "admin"
defaultAdminPassword = "admin"
)
// SeedDefaults 初始化默认数据:管理员账号 + 系统设置 + 示例业务数据 // Logan.Li
func SeedDefaults(db *gorm.DB) error {
if err := seedAdmin(db); err != nil {
return err
}
if err := seedSetting(db); err != nil {
return err
}
if err := seedSampleData(db); err != nil {
return err
}
if err := ensureOpsXShellPlanPricing(db); err != nil {
return err
}
if err := ensureWaffoPaymentGateway(db); err != nil {
return err
}
return nil
}
// seedAdmin 初始化默认管理员账号(仅在不存在时创建) // Logan.Li
func seedAdmin(db *gorm.DB) error {
var count int64
db.Model(&model.User{}).Where("username = ?", defaultAdminUsername).Count(&count)
if count > 0 {
return nil
}
admin := model.User{
Username: defaultAdminUsername,
Password: utils.HashPassword(defaultAdminPassword),
Email: "admin@opsx-licence.local",
Role: "admin",
IsActive: true,
}
if err := db.Create(&admin).Error; err != nil {
return err
}
log.Printf("[初始化] 已创建默认管理员账号: %s / %s(请尽快修改密码)", defaultAdminUsername, defaultAdminPassword)
return nil
}
// seedSetting 初始化默认系统设置(仅创建一次) // Logan.Li
func seedSetting(db *gorm.DB) error {
var count int64
db.Model(&model.Setting{}).Count(&count)
if count > 0 {
return nil
}
setting := model.Setting{
SystemName: "opsx-licence",
DefaultDuration: 365,
DefaultMaxActivations: 1,
ExpireReminderDays: 7,
APIRateLimit: 1000,
LogRetentionDays: 365,
}
if err := db.Create(&setting).Error; err != nil {
return err
}
log.Println("[初始化] 已创建默认系统设置")
return nil
}
// seedSampleData 初始化 OpsXShell 产品 / 计划 / Waffo 网关,便于桌面端联调 // Logan.Li
func seedSampleData(db *gorm.DB) error {
var productCount int64
db.Model(&model.Product{}).Count(&productCount)
if productCount > 0 {
return nil
}
product := model.Product{
Name: "OpsXShell",
Code: "opsxshell",
Version: "2.0.0",
Description: "OpsXShell Pro — SSH / SFTP / RDP / VNC / AI",
Features: `["ssh","sftp","rdp","vnc","ai","portForward","proxy","serial","batchExec"]`,
Price: 199.00,
PriceModel: "perpetual",
Status: 1,
}
if err := db.Create(&product).Error; err != nil {
return err
}
customer := model.Customer{
Name: "Demo",
Company: "OpsX",
Email: "demo@opsxshell.local",
Status: 1,
}
if err := db.Create(&customer).Error; err != nil {
return err
}
plans := []model.Plan{
{
ProductID: product.ID,
Name: "体验",
PlanType: "trial",
Price: 0,
Currency: "CNY",
Duration: 180,
MaxDevices: 1,
Features: `["专业功能","体验","1 设备"]`,
SortOrder: 0,
Status: 1,
},
{
ProductID: product.ID,
Name: "包年",
PlanType: "yearly",
Price: 99,
Currency: "CNY",
Duration: 365,
MaxDevices: 2,
Features: `["专业功能","包年","2 设备"]`,
SortOrder: 1,
Status: 1,
},
{
ProductID: product.ID,
Name: "永久",
PlanType: "perpetual",
Price: 199,
Currency: "CNY",
Duration: 0,
MaxDevices: 5,
Features: `["专业功能","永久授权","5 设备"]`,
SortOrder: 2,
Status: 1,
},
}
for i := range plans {
if err := db.Create(&plans[i]).Error; err != nil {
return err
}
}
now := time.Now()
license := model.License{
LicenseKey: utils.GenerateLicenseKey(),
CustomerID: customer.ID,
ProductID: product.ID,
Status: model.LicenseStatusActive,
StartDate: now,
EndDate: model.PermanentEndDate,
Duration: 0,
MaxActivations: 5,
Notes: "开发示例授权",
}
if err := db.Create(&license).Error; err != nil {
return err
}
gateway := model.PaymentGatewayConfig{
Gateway: "waffo",
Enabled: true,
SandboxMode: true,
Currency: "USD",
Priority: 1,
}
if err := db.Create(&gateway).Error; err != nil {
return err
}
log.Printf("[初始化] OpsXShell 产品已就绪 (code=opsxshell),示例 License: %s", license.LicenseKey)
return nil
}
// ensureOpsXShellPlanPricing ensures opsxshell product exists and syncs plan price/seats. // Logan.Li
func ensureOpsXShellPlanPricing(db *gorm.DB) error {
var product model.Product
err := db.Where("code = ?", "opsxshell").First(&product).Error
if err != nil {
// Prefer renaming legacy demo product so existing plans/licenses stay attached.
if renameErr := db.Where("code = ?", "opsx-tools").First(&product).Error; renameErr == nil {
product.Code = "opsxshell"
product.Name = "OpsXShell"
product.Description = "OpsXShell Pro — SSH / SFTP / RDP / VNC / AI"
product.Features = `["ssh","sftp","rdp","vnc","ai","portForward","proxy","serial","batchExec"]`
product.Price = 199
product.PriceModel = "perpetual"
product.Status = 1
if err := db.Save(&product).Error; err != nil {
return err
}
log.Println("[初始化] 已将 opsx-tools 迁移为 opsxshell")
} else {
product = model.Product{
Name: "OpsXShell",
Code: "opsxshell",
Version: "2.0.0",
Description: "OpsXShell Pro — SSH / SFTP / RDP / VNC / AI",
Features: `["ssh","sftp","rdp","vnc","ai","portForward","proxy","serial","batchExec"]`,
Price: 199,
PriceModel: "perpetual",
Status: 1,
}
if err := db.Create(&product).Error; err != nil {
return err
}
log.Println("[初始化] 已创建 opsxshell 产品")
}
}
type planSpec struct {
planType string
name string
price float64
currency string
duration int
maxDevices int
features string
sortOrder int
}
updates := []planSpec{
{"trial", "体验", 0, "CNY", 180, 1, "[\"专业功能\",\"体验\",\"1 设备\"]", 0},
{"yearly", "包年", 99, "CNY", 365, 2, "[\"专业功能\",\"包年\",\"2 设备\"]", 1},
{"perpetual", "永久", 199, "CNY", 0, 5, "[\"专业功能\",\"永久授权\",\"5 设备\"]", 2},
}
for _, u := range updates {
var plan model.Plan
err := db.Where("product_id = ? AND plan_type = ?", product.ID, u.planType).First(&plan).Error
if err == nil {
continue // 已存在:尊重管理员运行时修改,不再覆盖 // Logan.Li
}
plan = model.Plan{
ProductID: product.ID,
Name: u.name,
PlanType: u.planType,
Price: u.price,
Currency: u.currency,
Duration: u.duration,
MaxDevices: u.maxDevices,
Features: u.features,
SortOrder: u.sortOrder,
Status: 1,
}
if err := db.Create(&plan).Error; err != nil {
return err
}
}
log.Printf("[初始化] OpsXShell 套餐已确保存在(仅新建,不覆盖已有价格)")
return nil
}
// ensureWaffoPaymentGateway makes pancake.waffo.ai the preferred checkout gateway. // Logan.Li
func ensureWaffoPaymentGateway(db *gorm.DB) error {
var existing model.PaymentGatewayConfig
err := db.Where("gateway = ?", "waffo").First(&existing).Error
if err != nil {
gw := model.PaymentGatewayConfig{
Gateway: "waffo",
Enabled: true,
SandboxMode: true,
Currency: "CNY",
Priority: 0,
}
if err := db.Create(&gw).Error; err != nil {
return err
}
log.Println("[初始化] 已创建 Waffo 支付网关(默认沙箱,可在后台改 live)")
}
// 已存在则不覆盖 Enabled/SandboxMode,尊重管理员配置;其它网关一律关闭 // Logan.Li
if err := db.Model(&model.PaymentGatewayConfig{}).
Where("gateway <> ?", "waffo").
Updates(map[string]interface{}{"enabled": false, "priority": 100}).Error; err != nil {
return err
}
return nil
}