feat: 完成 opsx-licence 许可证管理平台全栈实现
基于 opsx-web-lite 框架(Go+Gin+Vue3+Element Plus)构建许可证管理服务器,
业务域参考 Licence 服务器 Web 版。
【后端 - 4层架构】
- 认证:单 Admin 表 + JWT(HS256)
- 核心授权域:Product / Customer / License / Activation
· License 生成/验证/激活/机器绑定/心跳/续期/吊销
· 客户端契约接口:/api/v1/license/{verify,activate,deactivate,heartbeat}
- 定价订单:Plan / Order / 支付网关配置
- 防重复购买:CustomerEntitlement + 幂等支付 webhook + 手动发放
- 分析审计:Dashboard 统计 / Analytics / AuditLog / Setting 管理
【前端】
- Dashboard(ECharts 图表)、产品、客户、License、计划、订单、权益、日志、设置
- 9 个完整 CRUD 业务页面 + 登录/布局
【部署】
- SQLite + 单二进制(//go:embed 嵌入前端)
- 开发:./run.sh start(前端5181/后端9001)
- 生产:./run.sh build && ./run.sh binary start
- 默认账号:admin/admin
Author: Logan.Li
This commit is contained in:
commit
4ae318335c
88 files changed
+10562
No files matched your search
@@ -0,0 +1,66 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"backend/config"
|
||||
"backend/internal/model"
|
||||
"fmt"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
// Init 初始化数据库连接并执行迁移 // Logan.Li
|
||||
func Init(cfg *config.Config) (*gorm.DB, error) {
|
||||
logLevel := getLogLevel(cfg.Mode)
|
||||
|
||||
fmt.Printf("[数据库] 使用 SQLite: %s\n", cfg.DBName)
|
||||
db, err := gorm.Open(sqlite.Open(cfg.DBName), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logLevel),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SQLite 连接失败: %v", err)
|
||||
}
|
||||
|
||||
return migrateDatabase(db)
|
||||
}
|
||||
|
||||
// getLogLevel 根据运行模式返回日志级别 // Logan.Li
|
||||
func getLogLevel(mode string) logger.LogLevel {
|
||||
if mode == "release" {
|
||||
return logger.Warn
|
||||
}
|
||||
return logger.Info
|
||||
}
|
||||
|
||||
// migrateDatabase 执行数据库迁移 // Logan.Li
|
||||
func migrateDatabase(db *gorm.DB) (*gorm.DB, error) {
|
||||
err := db.AutoMigrate(
|
||||
// 认证与系统
|
||||
&model.User{}, // 管理员用户 // Logan.Li
|
||||
&model.Setting{}, // 系统设置 // Logan.Li
|
||||
&model.AuditLog{}, // 操作日志 // Logan.Li
|
||||
&model.LicenseTemplate{}, // License 模板 // Logan.Li
|
||||
&model.PaymentGatewayConfig{}, // 支付网关配置 // Logan.Li
|
||||
// 核心授权域
|
||||
&model.Product{}, // 产品 // Logan.Li
|
||||
&model.Customer{}, // 客户 // Logan.Li
|
||||
&model.License{}, // License // Logan.Li
|
||||
&model.Activation{}, // 激活记录 // Logan.Li
|
||||
// 定价与订单
|
||||
&model.Plan{}, // 定价计划 // Logan.Li
|
||||
&model.Order{}, // 订单 // Logan.Li
|
||||
// 防重复购买
|
||||
&model.CustomerEntitlement{}, // 客户权益 // Logan.Li
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("数据库迁移失败: %v", err)
|
||||
}
|
||||
|
||||
// 初始化种子数据(默认管理员、默认设置、示例数据) // Logan.Li
|
||||
if err := SeedDefaults(db); err != nil {
|
||||
return nil, fmt.Errorf("种子数据初始化失败: %v", err)
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
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
|
||||
}
|
||||
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 初始化示例业务数据(产品/客户/计划/支付网关),便于开发联调 // Logan.Li
|
||||
func seedSampleData(db *gorm.DB) error {
|
||||
var productCount int64
|
||||
db.Model(&model.Product{}).Count(&productCount)
|
||||
if productCount > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 示例产品 // Logan.Li
|
||||
product := model.Product{
|
||||
Name: "OpsX Tools Suite",
|
||||
Code: "opsx-tools",
|
||||
Version: "1.0.0",
|
||||
Description: "OpsX 工具集 - 一站式运维工具箱",
|
||||
Features: `["CLI","GUI","Web Console","API"]`,
|
||||
Price: 99.00,
|
||||
PriceModel: "subscription",
|
||||
Status: 1,
|
||||
}
|
||||
if err := db.Create(&product).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 示例客户 // Logan.Li
|
||||
customer := model.Customer{
|
||||
Name: "示例客户",
|
||||
Company: "Acme Inc.",
|
||||
Email: "demo@acme.com",
|
||||
Phone: "13800000000",
|
||||
Status: 1,
|
||||
}
|
||||
if err := db.Create(&customer).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 示例定价计划(买断 + 月订阅 + 年订阅) // Logan.Li
|
||||
plans := []model.Plan{
|
||||
{ProductID: product.ID, Name: "个人永久版", PlanType: "perpetual", Price: 199, Currency: "USD", Duration: 0, MaxDevices: 1, Features: `["全部功能","永久使用","1 设备"]`, SortOrder: 1, Status: 1},
|
||||
{ProductID: product.ID, Name: "专业版(月)", PlanType: "monthly", Price: 19, Currency: "USD", Duration: 30, MaxDevices: 3, Features: `["全部功能","按月订阅","3 设备"]`, SortOrder: 2, Status: 1},
|
||||
{ProductID: product.ID, Name: "企业版(年)", PlanType: "yearly", Price: 199, Currency: "USD", Duration: 365, MaxDevices: 10, Features: `["全部功能","按年订阅优惠","10 设备","优先支持"]`, SortOrder: 3, Status: 1},
|
||||
}
|
||||
for i := range plans {
|
||||
if err := db.Create(&plans[i]).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 示例 License // Logan.Li
|
||||
now := time.Now()
|
||||
license := model.License{
|
||||
LicenseKey: utils.GenerateLicenseKey(),
|
||||
CustomerID: customer.ID,
|
||||
ProductID: product.ID,
|
||||
Status: model.LicenseStatusActive,
|
||||
StartDate: now,
|
||||
EndDate: now.AddDate(0, 0, 365),
|
||||
Duration: 365,
|
||||
MaxActivations: 3,
|
||||
Notes: "示例授权,初始化生成",
|
||||
}
|
||||
if err := db.Create(&license).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 示例支付网关配置(沙箱) // Logan.Li
|
||||
gateway := model.PaymentGatewayConfig{
|
||||
Gateway: "creem",
|
||||
Enabled: true,
|
||||
SandboxMode: true,
|
||||
Currency: "USD",
|
||||
Priority: 1,
|
||||
}
|
||||
if err := db.Create(&gateway).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("[初始化] 已创建示例产品、客户、计划、License、支付网关")
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user