This commit is contained in:
toom1996
2026-08-31 19:59:56 +08:00
parent a325e438be
commit f4f6e02e3c
14 changed files with 594 additions and 20 deletions

View File

@ -0,0 +1,41 @@
// Command migrate_refresh 一次性建表脚本(与 scripts/sql/*.sql 同源,手写 DDL 以保持与项目"SQL 托管表结构"约定一致)。
//
// 运行:go run ./scripts/migrate_refresh
// 可用环境变量 DB_DSN 覆盖连接串(默认 root:root@127.0.0.1:3306/db_dev)。
package main
import (
"log"
"os"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
dsn := os.Getenv("DB_DSN")
if dsn == "" {
dsn = "root:root@tcp(127.0.0.1:3306)/db_dev?charset=utf8mb4&parseTime=True&loc=Local"
}
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatalf("open db: %v", err)
}
stmt := `CREATE TABLE IF NOT EXISTS refresh_tokens (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id INT UNSIGNED NOT NULL,
token_hash VARCHAR(64) NOT NULL,
expires_at INT UNSIGNED NOT NULL DEFAULT 0,
revoked TINYINT UNSIGNED NOT NULL DEFAULT 0,
user_agent VARCHAR(255) NOT NULL DEFAULT '',
ip VARCHAR(64) NOT NULL DEFAULT '',
created_at INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id),
UNIQUE KEY uk_token_hash (token_hash),
KEY idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`
if err := db.Exec(stmt).Error; err != nil {
log.Fatalf("create refresh_tokens: %v", err)
}
log.Println("✓ refresh_tokens ready")
}

View File

@ -0,0 +1,95 @@
// Command seed_users 预置内部账号(运营/编辑用,不开放公开注册)。
//
// 项目已移除 AutoMigrate,表结构由 scripts/sql/005_create_users.sql 托管;
// 本脚本用 CREATE TABLE IF NOT EXISTS 兜底建表,再 upsert 一个内部账号。
//
// 用法:
// go run ./scripts/seed_users
// SEED_ADMIN_PASSWORD='你的强密码' go run ./scripts/seed_users
//
// 默认账号 admin / admin@studio.local,密码取环境变量 SEED_ADMIN_PASSWORD,
// 缺省回落强密码常量。生产部署请务必通过环境变量指定并尽快修改。
package main
import (
"errors"
"flag"
"log"
"os"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"fashionapi/internal/config"
"fashionapi/internal/database"
"fashionapi/internal/model"
)
func main() {
configPath := flag.String("config", "", "配置文件路径,默认查找 configs/config.yml")
flag.Parse()
cfg, err := config.Load(*configPath)
if err != nil {
log.Fatalf("✗ 加载配置失败: %v", err)
}
db, err := database.New(cfg.Database)
if err != nil {
log.Fatalf("✗ 连接数据库失败: %v", err)
}
defer func() {
if e := database.Close(db); e != nil {
log.Printf("! 关闭数据库连接失败: %v", e)
}
}()
// 1. 兜底建表(与 scripts/sql/005_create_users.sql 完全一致)
createSQL := `CREATE TABLE IF NOT EXISTS users (
id int unsigned NOT NULL AUTO_INCREMENT,
created_at int unsigned NOT NULL DEFAULT 0,
updated_at int unsigned NOT NULL DEFAULT 0,
username varchar(64) NOT NULL,
email varchar(191) NOT NULL,
password_hash varchar(255) NOT NULL DEFAULT '',
is_deleted tinyint unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (id),
UNIQUE KEY uk_username (username),
UNIQUE KEY uk_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`
if e := db.Exec(createSQL).Error; e != nil {
log.Fatalf("✗ 建表失败: %v", e)
}
log.Println("✓ users 表就绪")
// 2. 预置内部账号
const username = "admin"
const email = "admin@studio.local"
password := os.Getenv("SEED_ADMIN_PASSWORD")
if password == "" {
password = "Studio#2026!Admin"
}
hash, e := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if e != nil {
log.Fatalf("✗ 生成密码哈希失败: %v", e)
}
var existing model.User
qerr := db.Where("username = ?", username).First(&existing).Error
switch {
case errors.Is(qerr, gorm.ErrRecordNotFound):
user := model.User{Username: username, Email: email, PasswordHash: string(hash)}
if cerr := db.Create(&user).Error; cerr != nil {
log.Fatalf("✗ 创建内部账号失败: %v", cerr)
}
log.Printf("✓ 已创建内部账号: %s / %s", username, email)
case qerr != nil:
log.Fatalf("✗ 查询账号失败: %v", qerr)
default:
existing.PasswordHash = string(hash)
if uerr := db.Save(&existing).Error; uerr != nil {
log.Fatalf("✗ 更新账号密码失败: %v", uerr)
}
log.Printf("✓ 内部账号已存在,已刷新密码哈希: %s", username)
}
log.Printf("ℹ 登录账号: %s 密码: %s(请尽快修改默认密码)", username, password)
}

View File

@ -0,0 +1,14 @@
-- 账号体系用户表(内部运营/编辑账号,不开放公开注册)。
-- 与 scripts/seed_users/main.go 中内置的建表语句保持一致。
CREATE TABLE IF NOT EXISTS `users` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`created_at` int unsigned NOT NULL DEFAULT 0,
`updated_at` int unsigned NOT NULL DEFAULT 0,
`username` varchar(64) NOT NULL,
`email` varchar(191) NOT NULL,
`password_hash` varchar(255) NOT NULL DEFAULT '',
`is_deleted` tinyint unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
UNIQUE KEY `uk_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -0,0 +1,15 @@
-- 006: refresh_tokens —— 刷新令牌表(双令牌续期 + 踢下线的服务端状态)
-- 与项目约定一致:表结构由 SQL 托管,不依赖 GORM AutoMigrate。
CREATE TABLE IF NOT EXISTS refresh_tokens (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id INT UNSIGNED NOT NULL,
token_hash VARCHAR(64) NOT NULL,
expires_at INT UNSIGNED NOT NULL DEFAULT 0,
revoked TINYINT UNSIGNED NOT NULL DEFAULT 0,
user_agent VARCHAR(255) NOT NULL DEFAULT '',
ip VARCHAR(64) NOT NULL DEFAULT '',
created_at INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id),
UNIQUE KEY uk_token_hash (token_hash),
KEY idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;