同步历史版本代码
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type schemaTablePatch struct {
|
||||
table string
|
||||
ddl string
|
||||
}
|
||||
|
||||
type schemaColumnPatch struct {
|
||||
table string
|
||||
column string
|
||||
ddl string
|
||||
}
|
||||
|
||||
func EnsureSchemaCompatibility(ctx *svc.ServiceContext) error {
|
||||
tablePatches := []schemaTablePatch{
|
||||
{
|
||||
table: "log_message",
|
||||
ddl: `CREATE TABLE IF NOT EXISTS log_message (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
platform VARCHAR(32) NOT NULL,
|
||||
app_version VARCHAR(32) NULL,
|
||||
os_name VARCHAR(32) NULL,
|
||||
os_version VARCHAR(32) NULL,
|
||||
device_id VARCHAR(64) NULL,
|
||||
user_id BIGINT NULL DEFAULT NULL,
|
||||
session_id VARCHAR(64) NULL,
|
||||
level TINYINT UNSIGNED NOT NULL DEFAULT 3,
|
||||
error_code VARCHAR(64) NULL,
|
||||
message TEXT NOT NULL,
|
||||
stack MEDIUMTEXT NULL,
|
||||
context JSON NULL,
|
||||
client_ip VARCHAR(45) NULL,
|
||||
user_agent VARCHAR(255) NULL,
|
||||
locale VARCHAR(16) NULL,
|
||||
digest VARCHAR(64) NULL,
|
||||
occurred_at DATETIME NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uniq_digest (digest),
|
||||
KEY idx_platform_time (platform, created_at),
|
||||
KEY idx_user_time (user_id, created_at),
|
||||
KEY idx_device_time (device_id, created_at),
|
||||
KEY idx_error_code (error_code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`,
|
||||
},
|
||||
{
|
||||
table: "user_family",
|
||||
ddl: `CREATE TABLE IF NOT EXISTS user_family (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
owner_user_id BIGINT NOT NULL COMMENT 'Owner User ID',
|
||||
max_members INT NOT NULL DEFAULT 5 COMMENT 'Max members in family',
|
||||
status TINYINT NOT NULL DEFAULT 1 COMMENT 'Status: 1=active, 0=disabled',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
deleted_at DATETIME(3) DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uniq_owner_user_id (owner_user_id),
|
||||
KEY idx_status (status),
|
||||
KEY idx_deleted_at (deleted_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`,
|
||||
},
|
||||
{
|
||||
table: "user_family_member",
|
||||
ddl: `CREATE TABLE IF NOT EXISTS user_family_member (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
family_id BIGINT NOT NULL COMMENT 'Family ID',
|
||||
user_id BIGINT NOT NULL COMMENT 'Member User ID',
|
||||
role TINYINT NOT NULL DEFAULT 2 COMMENT 'Role: 1=owner, 2=member',
|
||||
status TINYINT NOT NULL DEFAULT 1 COMMENT 'Status: 1=active, 2=left, 3=removed',
|
||||
join_source VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'Join source',
|
||||
joined_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
left_at DATETIME(3) DEFAULT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
deleted_at DATETIME(3) DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uniq_user_id (user_id),
|
||||
KEY idx_family_status (family_id, status),
|
||||
KEY idx_deleted_at (deleted_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`,
|
||||
},
|
||||
}
|
||||
|
||||
columnPatches := []schemaColumnPatch{
|
||||
{
|
||||
table: "user",
|
||||
column: "rules",
|
||||
ddl: "ALTER TABLE `user` ADD COLUMN `rules` TEXT NULL COMMENT 'User rules for subscription';",
|
||||
},
|
||||
{
|
||||
table: "user",
|
||||
column: "last_login_time",
|
||||
ddl: "ALTER TABLE `user` ADD COLUMN `last_login_time` DATETIME DEFAULT NULL COMMENT 'Last Login Time';",
|
||||
},
|
||||
{
|
||||
table: "user",
|
||||
column: "member_status",
|
||||
ddl: "ALTER TABLE `user` ADD COLUMN `member_status` VARCHAR(20) NOT NULL DEFAULT '' COMMENT 'Member Status';",
|
||||
},
|
||||
{
|
||||
table: "user",
|
||||
column: "remark",
|
||||
ddl: "ALTER TABLE `user` ADD COLUMN `remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Remark';",
|
||||
},
|
||||
{
|
||||
table: "user_subscribe",
|
||||
column: "note",
|
||||
ddl: "ALTER TABLE `user_subscribe` ADD COLUMN `note` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'User note for subscription';",
|
||||
},
|
||||
{
|
||||
table: "redemption_code",
|
||||
column: "status",
|
||||
ddl: "ALTER TABLE `redemption_code` ADD COLUMN `status` TINYINT NOT NULL DEFAULT 1 COMMENT 'Status: 1=enabled, 0=disabled';",
|
||||
},
|
||||
}
|
||||
|
||||
for _, patch := range tablePatches {
|
||||
exists, err := tableExists(ctx.DB, patch.table)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "check table %s failed", patch.table)
|
||||
}
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
if err = ctx.DB.Exec(patch.ddl).Error; err != nil {
|
||||
return errors.Wrapf(err, "create table %s failed", patch.table)
|
||||
}
|
||||
logger.Infof("[SchemaCompat] created missing table: %s", patch.table)
|
||||
}
|
||||
|
||||
for _, patch := range columnPatches {
|
||||
exists, err := columnExists(ctx.DB, patch.table, patch.column)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "check column %s.%s failed", patch.table, patch.column)
|
||||
}
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
if err = ctx.DB.Exec(patch.ddl).Error; err != nil {
|
||||
return errors.Wrapf(err, "add column %s.%s failed", patch.table, patch.column)
|
||||
}
|
||||
logger.Infof("[SchemaCompat] added missing column: %s.%s", patch.table, patch.column)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tableExists(db *gorm.DB, table string) (bool, error) {
|
||||
var count int64
|
||||
err := db.Raw("SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?", table).Scan(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func columnExists(db *gorm.DB, table, column string) (bool, error) {
|
||||
var count int64
|
||||
err := db.Raw(
|
||||
"SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?",
|
||||
table,
|
||||
column,
|
||||
).Scan(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func _schemaCompatDebug(table, column string) string {
|
||||
if column == "" {
|
||||
return table
|
||||
}
|
||||
return fmt.Sprintf("%s.%s", table, column)
|
||||
}
|
||||
Reference in New Issue
Block a user