Files
kenaito-conf/server/internal/dao/engine.go
T
odboy 92c09df103 feat(config): 增强配置历史管理并统一数据库时间
- 新增 dao.Now() 函数获取数据库毫秒精度时间,避免多实例时钟偏差
- 增量同步查询增加上界(updated_at <= upperBound)防止竞态条件
- 配置创建、更新、删除、回滚统一使用数据库时间替代 time.Now()
- 配置历史新增 operate 字段记录操作类型(update/delete/rollback)
- 删除配置和回滚操作前增加历史快照记录
- 新增 DeleteHistory 接口支持手动删除单条历史记录
- 移除 GetHistory 的 Limit(5) 限制,返回完整历史列表
- 删除应用时同步更新关联配置的 updated_at 以便增量同步识别
- version 字段改为必填,前后端同步增加校验规则
- 前端历史列表增加操作类型列、删除按钮及表格最大高度限制
- 数据库表结构 datetime 升级为 datetime(3) 支持毫秒精度
2026-09-06 21:04:25 +08:00

124 lines
2.6 KiB
Go

package dao
import (
"errors"
"kenaito-conf/server/internal/config"
"kenaito-conf/server/internal/model"
"kenaito-conf/server/internal/util"
"time"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
"xorm.io/xorm"
)
var Engine *xorm.Engine
func Init() error {
dsn := config.Cfg.Database.DSN()
var err error
Engine, err = xorm.NewEngine("mysql", dsn)
if err != nil {
return err
}
Engine.SetMaxOpenConns(20)
Engine.SetMaxIdleConns(5)
if config.Cfg.Server.Mode == gin.DebugMode {
Engine.ShowSQL(true)
}
// sync table structures
if err := Engine.Sync2(
new(model.BusinessGroup),
new(model.Profile),
new(model.Application),
new(model.AppExtrAttr),
new(model.AppConfig),
new(model.AppConfigHistory),
new(model.User),
new(model.ClientInstance),
); err != nil {
return err
}
// insert initial data if tables are empty
return initSeedData()
}
func initSeedData() error {
// check if profiles table is empty
count, err := Engine.Count(new(model.Profile))
if err != nil {
return err
}
if count > 0 {
return nil
}
// insert profiles
profiles := []model.Profile{
{Name: "daily", Alias: "测试环境", IsDefault: true, Enabled: true},
{Name: "stage", Alias: "预发布环境", IsDefault: true, Enabled: true},
{Name: "online", Alias: "线上环境", IsDefault: true, Enabled: true},
}
for _, p := range profiles {
if _, err := Engine.Insert(&p); err != nil {
return err
}
}
// insert admin user
now := time.Now()
hashedPassword, err := util.HashPassword("admin123")
if err != nil {
return err
}
admin := model.User{
Name: "admin",
Password: hashedPassword,
Type: "superAdmin",
Enabled: true,
CreatedAt: &now,
}
if _, err := Engine.Insert(&admin); err != nil {
return err
}
// insert default business group
group := model.BusinessGroup{
Name: "默认组",
Enabled: true,
CreatedAt: &now,
CreatedBy: "admin",
}
if _, err := Engine.Insert(&group); err != nil {
return err
}
return nil
}
// Now 返回数据库当前时间(毫秒精度),用于增量同步游标和 updated_at 写入。
// 统一使用数据库时间可以避免多实例应用服务器之间的时钟偏差。
func Now() (time.Time, error) {
rows, err := Engine.DB().Query("SELECT NOW(3)")
if err != nil {
return time.Time{}, err
}
defer rows.Close()
var now time.Time
if !rows.Next() {
if err := rows.Err(); err != nil {
return time.Time{}, err
}
return time.Time{}, errors.New("no database time result")
}
if err := rows.Scan(&now); err != nil {
return time.Time{}, err
}
return now, nil
}