Coze源码分析-资源库-编辑数据库-后端源码-应用/领域/数据访问/基础设施层 - 实践

3. 应用服务层

3.1 数据库应用服务

文件位置: backend/application/memory/database.go

func (d *DatabaseApplicationService) UpdateDatabase(ctx context.Context, req *api.UpdateDatabaseRequest) (*api.UpdateDatabaseResponse, error) {
uid := ctxutil.GetUIDFromCtx(ctx)
if uid == nil {
return nil, errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "session required"))
}
// 权限验证
err := d.ValidateAccess(ctx, req.GetDatabaseID(), req.GetTableType())
if err != nil {
return nil, err
}
// 领域服务调用
resp, err := d.DomainSVC.UpdateDatabase(ctx, convertUpdateDatabase(req, *uid))
if err != nil {
return nil, err
}
// 发布资源变更事件
if err := d.publishResourceEvent(ctx, req.GetDatabaseID(), req.GetTableType(), model.EventTypeUpdate); err != nil {
logs.CtxErrorf(ctx, "publish resource event failed: %v", err)
}
return ConvertDatabaseRes(resp), nil
}

3.2 应用服务特点

数据库应用服务的主要特点:

  • 用户身份验证: 验证用户会话有效性
  • 访问权限验证: 通过ValidateAccess方法确保用户有权限编辑指定数据库
  • 空间权限验证: 验证用户对数据库所属空间的访问权限
  • 请求转换: 通过convertUpdateDatabase函数将API层请求转换为领域层请求
  • 领域服务调用: 调用领域服务层的UpdateDatabase方法执行数据库更新操作
  • 事件发布: 更新成功后发布资源更新事件,用于更新搜索索引
  • 结果转换: 将领域层结果转换为API层响应格式返回给前端

3.3 权限验证机制

应用服务层实现了严格的权限验证机制,确保只有授权用户才能编辑数据库:

func (d *DatabaseApplicationService) ValidateAccess(ctx context.Context, databaseID int64, tableType table.TableType) error {
uid := ctxutil.GetUIDFromCtx(ctx)
if uid == nil {
return errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "session uid not found"))
}
do, err := d.DomainSVC.MGetDatabase(ctx, &database.MGetDatabaseRequest{
Basics: []*model.DatabaseBasic{
{
ID:        databaseID,
TableType: tableType,
},
},
})
if err != nil {
return err
}
if len(do.Databases) == 0 {
return errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "database not found"))
}
if do.Databases[0].CreatorID != *uid {
logs.CtxErrorf(ctx, "user(%d) is not the creator(%d) of the database(%d)", *uid, do.Databases[0].CreatorID, databaseID)
return errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("detail", "you are not the creator of the database"))
}
return nil
}

3.4 数据转换逻辑

应用服务层实现了API层与领域层之间的数据转换,确保不同层级间数据格式的一致性:

func convertUpdateDatabase(req *api.UpdateDatabaseRequest, uid int64) *database.UpdateDatabaseRequest {
return &database.UpdateDatabaseRequest{
DatabaseID: req.GetDatabaseID(),
TableType:  req.GetTableType(),
Database: &databaseEntity.Database{
ID:             req.GetDatabaseID(),
TableName:      req.GetTableName(),
TableDesc:      req.GetTableDesc(),
FieldList:      req.GetFieldList(),
RwMode:         req.GetRwMode(),
PromptDisabled: req.GetPromptDisabled(),
ExtraInfo:      req.GetExtraInfo(),
IconURI:        req.GetIconURI(),
CreatorID:      uid,
},
}
}
func ConvertDatabaseRes(resp *database.UpdateDatabaseResponse) *api.UpdateDatabaseResponse {
return &api.UpdateDatabaseResponse{
DatabaseInfo: &api.DatabaseInfo{
ID:              resp.Database.ID,
SpaceID:         resp.Database.SpaceID,
ProjectID:       resp.Database.ProjectID,
IconURI:         resp.Database.IconURI,
TableName:       resp.Database.TableName,
TableDesc:       resp.Database.TableDesc,
Status:          resp.Database.Status,
CreatorID:       resp.Database.CreatorID,
CreateTime:      resp.Database.CreatedAtMs,
UpdateTime:      resp.Database.UpdatedAtMs,
FieldList:       resp.Database.FieldList,
ActualTableName: resp.Database.ActualTableName,
RwMode:          resp.Database.RwMode,
PromptDisabled:  resp.Database.PromptDisabled,
IsVisible:       resp.Database.IsVisible,
DraftID:         resp.Database.DraftID,
### 9.1 数据库编辑错误分类体系
数据库编辑错误分类体系将各种可能出现的错误按照性质和严重程度进行分类,便于系统进行统一处理和日志记录。
```go
// 数据库编辑错误类型定义
type DatabaseEditErrorType int
const (
// 业务逻辑错误
EditErrorType_Business DatabaseEditErrorType = iota
// 权限验证错误
EditErrorType_Permission
// 并发冲突错误
EditErrorType_Concurrency
// 存储层错误
EditErrorType_Storage
// 系统错误
EditErrorType_System
// 网络错误
EditErrorType_Network
)
// 数据库编辑错误处理流程
func handleDatabaseEditError(ctx context.Context, err error, req *pb.UpdateDatabaseRequest) *common.ErrResponse {
// 错误类型识别
var errType DatabaseEditErrorType
var errMsg string
switch {
case errors.Is(err, ErrPermissionDenied):
errType = EditErrorType_Permission
errMsg = "无权限执行数据库编辑操作"
case errors.Is(err, ErrDatabaseNotFound):
errType = EditErrorType_Business
errMsg = "数据库不存在"
case errors.Is(err, ErrConcurrentEdit):
errType = EditErrorType_Concurrency
errMsg = "数据库正在被其他用户编辑,请稍后再试"
case errors.Is(err, ErrStorageQuotaExceeded):
errType = EditErrorType_Business
errMsg = "超出存储配额限制"
default:
// 检查是否为系统错误
if isSystemError(err) {
errType = EditErrorType_System
errMsg = "系统内部错误"
} else {
errType = EditErrorType_Business
errMsg = err.Error()
}
}
// 记录错误日志
logDatabaseEditError(ctx, errType, req.DatabaseId, errMsg, err)
// 构造统一错误响应
return &common.ErrResponse{
Code:    getErrorCodeByType(errType),
Message: errMsg,
RequestID: ctxutil.GetRequestID(ctx),
}
}
// 统一错误响应格式
func (s *DatabaseServiceImpl) UpdateDatabase(ctx context.Context, req *pb.UpdateDatabaseRequest) (*pb.UpdateDatabaseResponse, error) {
// 业务处理...
// 错误处理示例
if err != nil {
errResp := handleDatabaseEditError(ctx, err, req)
return nil, errors.New(errResp.Message)
}
return &pb.UpdateDatabaseResponse{/*...*/}, nil
}          return 0
}(),
BotID:            database.BotID,
ExtraInfo:        database.ExtraInfo,
IsAddedToBot:     database.IsAddedToBot,
},
Code: 0,
Msg:  "success",
}
}

4. 领域服务层

4.1 数据库领域服务接口

文件位置: backend/domain/memory/database/service/database.go

数据库领域服务接口定义,其中UpdateDatabase是编辑数据库的核心方法:

type Database interface {
// 数据库基本操作
CreateDatabase(ctx context.Context, req *CreateDatabaseRequest) (*CreateDatabaseResponse, error)
UpdateDatabase(ctx context.Context, req *UpdateDatabaseRequest) (*UpdateDatabaseResponse, error)
DeleteDatabase(ctx context.Context, req *DeleteDatabaseRequest) (*DeleteDatabaseResponse, error)
GetDatabase(ctx context.Context, req *GetDatabaseRequest) (*GetDatabaseResponse, error)
ListDatabase(ctx context.Context, req *ListDatabaseRequest) (*ListDatabaseResponse, error)
MGetDatabase(ctx context.Context, req *MGetDatabaseRequest) (*MGetDatabaseResponse, error)
// 数据库模板管理
GetDatabaseTemplate(ctx context.Context, req *GetDatabaseTemplateRequest) (*GetDatabaseTemplateResponse, error)
// 数据库记录操作
ListDatabaseRecord(ctx context.Context, req *ListDatabaseRecordRequest) (*ListDatabaseRecordResponse, error)
UpdateDatabaseRecords(ctx context.Context, req *UpdateDatabaseRecordsRequest) (*UpdateDatabaseRecordsResponse, error)
ExecuteSQL(ctx context.Context, req *ExecuteSQLRequest) (*ExecuteSQLResponse, error)
// 权限验证
ValidateDatabasePermission(ctx context.Context, req *ValidateDatabasePermissionRequest) (*ValidateDatabasePermissionResponse, error)
// 数据库结构验证
ValidateDatabaseTableSchema(ctx context.Context, req *ValidateDatabaseTableSchemaRequest) (*ValidateDatabaseTableSchemaResponse, error)
// 数据导入
SubmitDatabaseInsertTask(ctx context.Context, req *SubmitDatabaseInsertTaskRequest) error
GetDatabaseFileProgressData(ctx context.Context, req *GetDatabaseFileProgressDataRequest) (*GetDatabaseFileProgressDataResponse, error)
// 表结构相关
GetDatabaseTableSchema(ctx context.Context, req *GetDatabaseTableSchemaRequest) (*GetDatabaseTableSchemaResponse, error)
// SQL执行
ExecuteSQL(ctx context.Context, req *ExecuteSQLRequest) (*ExecuteSQLResponse, error)
// 机器人关联
BindDatabase(ctx context.Context, req *BindDatabaseToAgentRequest) error
UnBindDatabase(ctx context.Context, req *UnBindDatabaseToAgentRequest) error
// 发布管理
PublishDatabase(ctx context.Context, req *PublishDatabaseRequest) (*PublishDatabaseResponse, error)
}
type UpdateDatabaseRequest struct {
DatabaseID int64            `json:"database_id"`
TableType  table.TableType  `json:"table_type"`
Database   *entity.Database `json:"database"`
}
type UpdateDatabaseResponse struct {
Database *entity.Database `json:"database"`
}
### 4.2 数据库领域服务实现
**文件位置**: `backend/domain/memory/database/service/database_impl.go`
UpdateDatabase方法的具体实现,负责数据库信息的更新和物理表结构的调整:
```go
func (d *databaseImpl) UpdateDatabase(ctx context.Context, req *UpdateDatabaseRequest) (*UpdateDatabaseResponse, error) {
// 1. 参数验证
if req.DatabaseID == 0 {
return nil, fmt.Errorf("database id is required")
}
if req.Database == nil {
return nil, fmt.Errorf("database info is required")
}
// 2. 获取数据库信息
var dbInfo interface{}
var err error
if req.TableType == table.TableType_OnlineTable {
dbInfo, err = d.onlineDAO.Get(ctx, req.DatabaseID)
} else {
dbInfo, err = d.draftDAO.Get(ctx, req.DatabaseID)
}
if err != nil {
return nil, fmt.Errorf("get database failed: %w", err)
}
// 3. 开启事务
tx, err := d.db.Begin(ctx)
if err != nil {
return nil, fmt.Errorf("begin transaction failed: %w", err)
}
defer tx.Rollback(ctx)
// 4. 更新物理表结构
if req.TableType == table.TableType_DraftTable {
// 获取草稿数据库信息
draftDB := dbInfo.(*model.DraftDatabaseInfo)
// 更新物理表结构
err = d.physicalTable.UpdatePhysicalTableWithDrops(ctx, tx, draftDB.ActualTableName, req.Database.FieldList)
if err != nil {
return nil, fmt.Errorf("update physical table failed: %w", err)
}
// 更新数据库信息
draftDB.TableName = req.Database.TableName
draftDB.TableDesc = req.Database.TableDesc
draftDB.FieldList = req.Database.FieldList
draftDB.RwMode = req.Database.RwMode
draftDB.PromptDisabled = req.Database.PromptDisabled
draftDB.ExtraInfo = req.Database.ExtraInfo
draftDB.IconURI = req.Database.IconURI
draftDB.UpdatedAtMs = time.Now().UnixMilli()
// 保存数据库信息
_, err = d.draftDAO.UpdateWithTX(ctx, tx, draftDB)
if err != nil {
return nil, fmt.Errorf("update draft database failed: %w", err)
}
}
// 5. 提交事务
if err := tx.Commit(ctx); err != nil {
return nil, fmt.Errorf("commit transaction failed: %w", err)
}
// 6. 返回更新后的数据库信息
var updatedDB *entity.Database
if req.TableType == table.TableType_DraftTable {
draftDB := dbInfo.(*model.DraftDatabaseInfo)
updatedDB = &entity.Database{
ID:              draftDB.ID,
SpaceID:         draftDB.SpaceID,
ProjectID:       draftDB.ProjectID,
TableName:       draftDB.TableName,
TableDesc:       draftDB.TableDesc,
FieldList:       draftDB.FieldList,
Status:          draftDB.Status,
CreatorID:       draftDB.CreatorID,
CreatedAtMs:     draftDB.CreatedAtMs,
UpdatedAtMs:     draftDB.UpdatedAtMs,
ActualTableName: draftDB.ActualTableName,
RwMode:          draftDB.RwMode,
PromptDisabled:  draftDB.PromptDisabled,
IsVisible:       draftDB.IsVisible,
ExtraInfo:       draftDB.ExtraInfo,
IconURI:         draftDB.IconURI,
AppID:           draftDB.AppID,
}
} else {
onlineDB := dbInfo.(*model.OnlineDatabaseInfo)
updatedDB = &entity.Database{
ID:              onlineDB.ID,
SpaceID:         onlineDB.SpaceID,
ProjectID:       onlineDB.ProjectID,
TableName:       onlineDB.TableName,
TableDesc:       onlineDB.TableDesc,
FieldList:       onlineDB.FieldList,
Status:          onlineDB.Status,
CreatorID:       onlineDB.CreatorID,
CreatedAtMs:     onlineDB.CreatedAtMs,
UpdatedAtMs:     onlineDB.UpdatedAtMs,
ActualTableName: onlineDB.ActualTableName,
RwMode:          onlineDB.RwMode,
PromptDisabled:  onlineDB.PromptDisabled,
IsVisible:       onlineDB.IsVisible,
DraftID:         onlineDB.DraftID,
ExtraInfo:       onlineDB.ExtraInfo,
IconURI:         onlineDB.IconURI,
AppID:           onlineDB.AppID,
}
}
return &UpdateDatabaseResponse{
Database: updatedDB,
}, nil
}

5. 数据访问层

数据访问层负责数据库相关数据的持久化操作,采用Repository模式和DAO模式相结合的设计,确保数据访问的一致性和可维护性。在数据库编辑场景中,主要通过UpdateWithTX方法实现草稿态和线上态数据库信息的同步更新。

5.1 数据库Repository接口定义

文件位置: backend/domain/memory/database/repository/repository.go

数据库数据访问层采用Repository模式,提供统一的数据访问接口。由于系统采用了草稿-线上分离的设计,提供了两个主要的Repository接口:DraftDAO和OnlineDAO,分别负责草稿态和线上态的数据库操作。在编辑数据库时,两个DAO都需要进行相应的更新操作:

// DraftDAO 草稿态数据库操作接口
type DraftDAO interface {
Get(ctx context.Context, id int64) (*entity.Database, error) // 获取草稿数据库信息
List(ctx context.Context, filter *entity.DatabaseFilter, page *entity.Pagination, orderBy []*database.OrderBy) ([]*entity.Database, int64, error)
MGet(ctx context.Context, ids []int64) ([]*entity.Database, error) // 批量获取草稿数据库信息
UpdateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database) (*entity.Database, error) // 更新草稿数据库(事务支持)
CreateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database, draftID, onlineID int64, physicalTableName string) (*entity.Database, error)
DeleteWithTX(ctx context.Context, tx *query.QueryTx, id int64) error
BatchDeleteWithTX(ctx context.Context, tx *query.QueryTx, ids []int64) error
}
// OnlineDAO 线上态数据库操作接口
type OnlineDAO interface {
Get(ctx context.Context, id int64) (*entity.Database, error) // 获取线上数据库信息
MGet(ctx context.Context, ids []int64) ([]*entity.Database, error) // 批量获取线上数据库信息
List(ctx context.Context, filter *entity.DatabaseFilter, page *entity.Pagination, orderBy []*database.OrderBy) ([]*entity.Database, int64, error)
UpdateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database) (*entity.Database, error) // 更新线上数据库(事务支持)
CreateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database, draftID, onlineID int64, physicalTableName string) (*entity.Database, error)
DeleteWithTX(ctx context.Context, tx *query.QueryTx, id int64) error
BatchDeleteWithTX(ctx context.Context, tx *query.QueryTx, ids []int64) error
}
// 工厂方法
func NewDraftDatabaseDAO(db *gorm.DB, idGen idgen.IDGenerator) DraftDAO {
return dal.NewDraftDatabaseDAO(db, idGen)
}
func NewOnlineDatabaseDAO(db *gorm.DB, idGen idgen.IDGenerator) OnlineDAO {
return dal.NewOnlineDatabaseDAO(db, idGen)
}

5.2 数据访问对象(DAO)实现

文件位置: backend/domain/memory/database/internal/dal/

5.2.1 DraftImpl的UpdateWithTX方法实现

文件位置: backend/domain/memory/database/internal/dal/draft_database_info.go

// UpdateWithTX 更新草稿数据库记录(事务支持)
func (d *DraftImpl) UpdateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database) (*entity.Database, error) {
now := time.Now().UnixMilli()
// 构建更新数据
updates := make(map[string]interface{})
// 序列化字段列表为JSON
if database.FieldList != nil {
updates["table_field"] = database.FieldList
}
// 设置更新字段
if database.AppID != 0 {
updates["app_id"] = database.AppID
}
if database.TableName != "" {
updates["table_name"] = database.TableName
}
if database.TableDesc != "" {
updates["table_desc"] = database.TableDesc
}
if database.IconURI != "" {
updates["icon_uri"] = database.IconURI
}
if database.RwMode != 0 {
updates["rw_mode"] = database.RwMode
}
// 设置布尔类型字段
updates["prompt_disabled"] = func() int32 {
if database.PromptDisabled {
return 1
}
return 0
}()
// 设置更新时间戳
updates["updated_at"] = now
// 执行更新操作
table := tx.DraftDatabaseInfo
err := table.WithContext(ctx).Where(table.ID.Eq(database.ID)).Updates(updates)
if err != nil {
return nil, fmt.Errorf("update draft database failed: %w", err)
}
// 更新时间戳
database.UpdatedAtMs = now
return database, nil
}
5.2.2 OnlineImpl的UpdateWithTX方法实现

文件位置: backend/domain/memory/database/internal/dal/online_database_info.go

// UpdateWithTX 更新线上数据库记录(事务支持)
func (o *OnlineImpl) UpdateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database) (*entity.Database, error) {
now := time.Now().UnixMilli()
// 构建更新数据
updates := make(map[string]interface{})
// 序列化字段列表为JSON
if database.FieldList != nil {
updates["table_field"] = database.FieldList
}
// 设置更新字段
if database.AppID != 0 {
updates["app_id"] = database.AppID
}
if database.TableName != "" {
updates["table_name"] = database.TableName
}
if database.TableDesc != "" {
updates["table_desc"] = database.TableDesc
}
if database.IconURI != "" {
updates["icon_uri"] = database.IconURI
}
// 设置布尔类型字段
updates["prompt_disabled"] = func() int32 {
if database.PromptDisabled {
return 1
}
return 0
}()
// 设置读写模式
updates["rw_mode"] = database.RwMode
// 设置更新时间戳
updates["updated_at"] = now
// 执行更新操作
table := tx.OnlineDatabaseInfo
err := table.WithContext(ctx).Where(table.ID.Eq(database.ID)).Updates(updates)
if err != nil {
return nil, fmt.Errorf("update online database failed: %w", err)
}
// 更新时间戳
database.UpdatedAtMs = now
return database, nil
}
### 5.3 数据库操作特点
在数据库编辑场景中,数据访问层具有以下特点:
1. **双重状态管理**:同时维护草稿态和线上态的数据库信息,确保数据一致性
2. **事务支持**:所有写操作都在事务内执行,保证原子性
3. **单例模式**:DAO实现采用单例模式,提高性能和资源利用率
4. **字段映射**:将实体对象映射到数据库字段,支持复杂数据类型如JSON序列化
### 5.4 数据模型定义
**文件位置**: `backend/domain/memory/database/entity/database.go`
数据库实体模型定义了编辑操作中涉及的核心字段:
```go
// Database 数据库实体
type Database struct {
ID            int64              `json:"id"`
AppID         int64              `json:"app_id"`
SpaceID       int64              `json:"space_id"`
TableName     string             `json:"table_name"`
TableDesc     string             `json:"table_desc"`
IconURI       string             `json:"icon_uri"`
FieldList     []*DatabaseField   `json:"field_list"`
PromptDisabled bool              `json:"prompt_disabled"`
RwMode        int64              `json:"rw_mode"`
CreatorID     int64              `json:"creator_id"`
CreatedAtMs   int64              `json:"created_at_ms"`
UpdatedAtMs   int64              `json:"updated_at_ms"`
IsDraft       bool               `json:"is_draft"`
IsVisible     bool               `json:"is_visible"`
ActualTableName string            `json:"actual_table_name"`
Status        int                `json:"status"`
ExtraInfo     string             `json:"extra_info"`
DraftID       int64              `json:"draft_id"`
ProjectID     int64              `json:"project_id"`
OnlineID      *int64             `json:"online_id"`
}
// DatabaseField 数据库字段实体
type DatabaseField struct {
Name        string      `json:"name"`
Desc        string      `json:"desc"`
Type        string      `json:"type"`
PrimaryKey  bool        `json:"primary_key"`
Required    bool        `json:"required"`
DefaultVal  string      `json:"default_val"`
SortIndex   int         `json:"sort_index"`
}

在编辑数据库时,主要涉及修改TableName、TableDesc、FieldList、PromptDisabled和RwMode等字段,这些变更会通过数据访问层持久化到数据库中。

6. 基础设施层

基础设施层提供了数据库编辑功能所需的底层资源和技术支持,特别是在数据库编辑场景中,负责物理表结构的更新、数据迁移和事务管理等核心操作。

6.1 物理表更新实现

文件位置: backend/infrastructure/database/physical.go

在数据库编辑过程中,物理表结构的更新是关键环节,负责将编辑后的表结构变更应用到实际的数据库中:

// UpdateFieldInfo 更新字段信息
func UpdateFieldInfo(ctx context.Context, tx *gorm.DB, tableName string, fields []*entity.DatabaseField) error {
// 获取表的当前结构
currentFields, err := getTableFields(ctx, tx, tableName)
if err != nil {
return fmt.Errorf("get current fields failed: %w", err)
}
// 构建字段差异
fieldDiffs := buildFieldDifferences(currentFields, fields)
// 执行字段添加、修改和删除操作
if err := applyFieldChanges(ctx, tx, tableName, fieldDiffs); err != nil {
return fmt.Errorf("apply field changes failed: %w", err)
}
return nil
}
// UpdatePhysicalTableWithDrops 支持删除字段的物理表更新
func UpdatePhysicalTableWithDrops(ctx context.Context, tx *gorm.DB, tableName string, fields []*entity.DatabaseField) error {
// 在支持删除字段的场景下,首先备份表数据
backupTableName := fmt.Sprintf("%s_backup_%d", tableName, time.Now().Unix())
// 创建备份表
if err := createBackupTable(ctx, tx, tableName, backupTableName); err != nil {
return fmt.Errorf("create backup table failed: %w", err)
}
// 删除原表
if err := tx.Exec("DROP TABLE IF EXISTS " + tableName).Error; err != nil {
return fmt.Errorf("drop table failed: %w", err)
}
// 创建新结构的表
if err := createNewTable(ctx, tx, tableName, fields); err != nil {
// 如果创建失败,尝试恢复备份
recoverFromBackup(ctx, tx, backupTableName, tableName)
return fmt.Errorf("create new table failed: %w", err)
}
// 从备份表恢复数据
if err := recoverDataFromBackup(ctx, tx, backupTableName, tableName, fields); err != nil {
return fmt.Errorf("recover data failed: %w", err)
}
// 删除备份表
tx.Exec("DROP TABLE IF EXISTS " + backupTableName)
return nil
}

6.2 数据库实现服务

文件位置: backend/infrastructure/database/database_impl.go

数据库实现服务将物理表操作与事务管理结合,为上层提供统一的数据库操作接口:

// UpdateDatabase 更新数据库表结构
func (impl *DatabaseImpl) UpdateDatabase(ctx context.Context, tx *gorm.DB, tableName string, fields []*entity.DatabaseField, allowDrop bool) error {
// 开始事务
if tx == nil {
return fmt.Errorf("transaction is required for database update")
}
// 根据配置选择更新策略
if allowDrop {
// 支持删除字段的更新策略
if err := UpdatePhysicalTableWithDrops(ctx, tx, tableName, fields); err != nil {
return fmt.Errorf("update table with drops failed: %w", err)
}
} else {
// 不支持删除字段的更新策略
if err := UpdateFieldInfo(ctx, tx, tableName, fields); err != nil {
return fmt.Errorf("update field info failed: %w", err)
}
}
// 更新数据库元数据
if err := impl.updateMetadata(ctx, tx, tableName, fields); err != nil {
return fmt.Errorf("update metadata failed: %w", err)
}
return nil
}
// UpdateDatabaseRecord 更新数据库记录
func (impl *DatabaseImpl) UpdateDatabaseRecord(ctx context.Context, tx *gorm.DB, tableName string, id interface{}, data map[string]interface{}) error {
// 构建更新语句
result := tx.Table(tableName).Where("id = ?", id).Updates(data)
if result.Error != nil {
return fmt.Errorf("update database record failed: %w", result.Error)
}
if result.RowsAffected == 0 {
return fmt.Errorf("record not found")
}
return nil
}

6.3 事务管理

在数据库编辑过程中,事务管理确保了所有操作的原子性和一致性:

// WithTransaction 事务管理包装器
func WithTransaction(db *gorm.DB, fn func(tx *gorm.DB) error) error {
// 开始事务
tx := db.Begin()
if tx.Error != nil {
return fmt.Errorf("begin transaction failed: %w", tx.Error)
}
// 执行事务函数
if err := fn(tx); err != nil {
// 回滚事务
if rollbackErr := tx.Rollback().Error; rollbackErr != nil {
return fmt.Errorf("transaction rollback failed: %w, original error: %v", rollbackErr, err)
}
return err
}
// 提交事务
if err := tx.Commit().Error; err != nil {
return fmt.Errorf("transaction commit failed: %w", err)
}
return nil
}

6.4 数据迁移和备份机制

在编辑数据库时,系统实现了完善的数据迁移和备份机制,确保数据安全:

  1. 表结构变更前备份:在修改表结构前创建临时备份表
  2. 增量更新策略:优先采用ALTER TABLE语句进行增量更新,减少对数据的影响
  3. 完全重建策略:在需要删除字段等复杂操作时,采用表重建和数据恢复的方式
  4. 数据完整性校验:迁移后验证数据完整性,确保没有数据丢失

6.5 查询生成器接口

系统定义了查询生成器接口,为数据库操作提供了统一的访问方式:

// IOnlineDatabaseInfoDo 线上数据库查询构建器接口
type IOnlineDatabaseInfoDo interface {
// 上下文设置
WithContext(ctx context.Context) IOnlineDatabaseInfoDo
// 条件查询
Where(conds ...expr.Condition) IOnlineDatabaseInfoDo
// 更新操作
Updates(m map[string]interface{}) error
// 查询操作
Find(out interface{}) error
First(out interface{}) error
Count(column string, out interface{}) error
// 其他操作方法...
}
// ServerConfig 服务器配置
type ServerConfig struct {
Host string `yaml:"host"`
Port int    `yaml:"port"`
Mode string `yaml:"mode"` // dev, test, prod
}
// DatabaseConfig 数据库配置
type DatabaseConfig struct {
MySQL   MySQLConfig   `yaml:"mysql"`
OceanBase OceanBaseConfig `yaml:"oceanbase"`
}
// AsyncConfig 异步任务配置
type AsyncConfig struct {
WorkerCount     int    `yaml:"worker_count"`
QueueBufferSize int    `yaml:"queue_buffer_size"`
RetryCount      int    `yaml:"retry_count"`
RetryInterval   int    `yaml:"retry_interval_seconds"`
}
// SearchConfig 搜索服务配置
type SearchConfig struct {
Enabled        bool   `yaml:"enabled"`
CacheEnabled   bool   `yaml:"cache_enabled"`
CacheTTL       int    `yaml:"cache_ttl_seconds"`
MaxResultSize  int    `yaml:"max_result_size"`
}
// SecurityConfig 安全配置
type SecurityConfig struct {
JWTSecret      string `yaml:"jwt_secret"`
JWTExpireHours int    `yaml:"jwt_expire_hours"`
RateLimit      int    `yaml:"rate_limit_per_minute"`
}
// LoadConfig 加载配置
func LoadConfig(configPath string) (*Config, error) {
// 1. 读取配置文件
data, err := ioutil.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("读取配置文件失败: %w", err)
}
// 2. 解析YAML配置
var config Config
err = yaml.Unmarshal(data, &config)
if err != nil {
return nil, fmt.Errorf("解析配置文件失败: %w", err)
}
// 3. 环境变量覆盖
err = envconfig.Process("", &config)
if err != nil {
return nil, fmt.Errorf("处理环境变量失败: %w", err)
}
return &config, nil
}

6.8 基础设施层总结

基础设施层为数据库系统提供了完整的技术支撑,构建了高性能、高可用、可扩展的数据库服务架构:

  1. 数据存储: MySQL/OceanBase主数据库 + Redis多级缓存
  2. 数据处理: Excel文件解析 + 数据格式校验 + 批量数据处理
  3. 异步任务: Kafka消息队列 + 分布式任务调度
  4. 搜索优化: 全文索引 + 高级查询优化器
  5. 元数据管理: 元数据版本控制 + 元数据缓存
  6. 安全机制: 细粒度权限控制 + 数据加密 + 审计日志
  7. 配置管理: 统一配置中心 + 环境变量覆盖 + 配置热更新

这些基础设施服务通过依赖注入的方式集成到上层业务逻辑中,确保了系统的可扩展性、可维护性和高可用性。系统设计遵循微服务架构理念,各组件之间松耦合,便于独立部署和横向扩展。

posted @ 2025-11-04 09:51  gccbuaa  阅读(13)  评论(0)    收藏  举报