| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- package solutionmanagement
-
- import (
- "context"
- "fmt"
- "regexp"
-
- "git.x2erp.com/qdy/go-base/ctx"
- "git.x2erp.com/qdy/go-base/logger"
- "git.x2erp.com/qdy/go-base/model/response"
- "git.x2erp.com/qdy/go-base/util"
- "git.x2erp.com/qdy/go-db/factory/database"
- "git.x2erp.com/qdy/go-svc-configure/internal/tables"
- "github.com/jmoiron/sqlx"
- )
-
- // SaveSyncSolution 保存同步方案(主表+子表)
- func SaveSyncSolution(req *SyncSolutionRequest, ctx context.Context, dbFactory *database.DBFactory, reqCtx *ctx.RequestContext) *response.QueryResult[SyncSolutionDetail] {
- logger.Debug("SaveSyncSolution-开始保存同步方案")
-
- // 参数验证
- if err := validateSyncSolutionRequest(req); err != nil {
- logger.ErrorC(reqCtx, fmt.Sprintf("参数验证失败: %v", err))
- return util.CreateErrorResult[SyncSolutionDetail](fmt.Sprintf("参数验证失败: %v", err), reqCtx)
- }
-
- // 获取数据库连接并开始事务
- db := dbFactory.GetDB()
- tx, err := db.BeginTxx(ctx, nil)
- if err != nil {
- logger.ErrorC(reqCtx, fmt.Sprintf("开始事务失败: %v", err))
- return util.CreateErrorResult[SyncSolutionDetail](fmt.Sprintf("开始事务失败: %v", err), reqCtx)
- }
-
- defer func() {
- if p := recover(); p != nil {
- tx.Rollback()
- panic(p)
- }
- }()
-
- // 获取当前用户
- creator := reqCtx.UserID
- if creator == "" {
- creator = "system"
- }
-
- // 1. 处理主表:检查是否存在 → 存在则更新,不存在则插入
- solutionExists, err := checkSolutionExists(ctx, tx, req.SolutionCode)
- if err != nil {
- tx.Rollback()
- logger.ErrorC(reqCtx, fmt.Sprintf("检查方案存在性失败: %v", err))
- return util.CreateErrorResult[SyncSolutionDetail](fmt.Sprintf("检查方案存在性失败: %v", err), reqCtx)
- }
-
- var solution tables.SyncSolutionDB
- if solutionExists {
- // 更新主表
- solution, err = updateSolution(ctx, tx, req, creator)
- if err != nil {
- tx.Rollback()
- logger.ErrorC(reqCtx, fmt.Sprintf("更新方案失败: %v", err))
- return util.CreateErrorResult[SyncSolutionDetail](fmt.Sprintf("更新方案失败: %v", err), reqCtx)
- }
- logger.Debug(fmt.Sprintf("更新方案成功: %s", req.SolutionCode))
- } else {
- // 插入主表
- solution, err = insertSolution(ctx, tx, req, creator)
- if err != nil {
- tx.Rollback()
- logger.ErrorC(reqCtx, fmt.Sprintf("插入方案失败: %v", err))
- return util.CreateErrorResult[SyncSolutionDetail](fmt.Sprintf("插入方案失败: %v", err), reqCtx)
- }
- logger.Debug(fmt.Sprintf("插入方案成功: %s", req.SolutionCode))
- }
-
- // 2. 处理子表:逐条检查 → 存在则更新,不存在则新增
- var sqlScripts []tables.SyncSolutionSQLDB
- for _, script := range req.SQLScripts {
- // 设置关联的solution_id
- script.SolutionID = req.SolutionCode
-
- // 检查子表记录是否存在
- sqlExists, err := checkSQLScriptExists(ctx, tx, script.SyncID, req.SolutionCode)
- if err != nil {
- tx.Rollback()
- logger.ErrorC(reqCtx, fmt.Sprintf("检查SQL脚本存在性失败: %v", err))
- return util.CreateErrorResult[SyncSolutionDetail](fmt.Sprintf("检查SQL脚本存在性失败: %v", err), reqCtx)
- }
-
- var sqlScript tables.SyncSolutionSQLDB
- if sqlExists {
- // 更新子表
- sqlScript, err = updateSQLScript(ctx, tx, &script, creator)
- if err != nil {
- tx.Rollback()
- logger.ErrorC(reqCtx, fmt.Sprintf("更新SQL脚本失败: %v", err))
- return util.CreateErrorResult[SyncSolutionDetail](fmt.Sprintf("更新SQL脚本失败: %v", err), reqCtx)
- }
- logger.Debug(fmt.Sprintf("更新SQL脚本成功: %s", script.SyncID))
- } else {
- // 插入子表
- sqlScript, err = insertSQLScript(ctx, tx, &script, creator)
- if err != nil {
- tx.Rollback()
- logger.ErrorC(reqCtx, fmt.Sprintf("插入SQL脚本失败: %v", err))
- return util.CreateErrorResult[SyncSolutionDetail](fmt.Sprintf("插入SQL脚本失败: %v", err), reqCtx)
- }
- logger.Debug(fmt.Sprintf("插入SQL脚本成功: %s", script.SyncID))
- }
- sqlScripts = append(sqlScripts, sqlScript)
- }
-
- // 提交事务
- if err := tx.Commit(); err != nil {
- logger.ErrorC(reqCtx, fmt.Sprintf("提交事务失败: %v", err))
- return util.CreateErrorResult[SyncSolutionDetail](fmt.Sprintf("提交事务失败: %v", err), reqCtx)
- }
-
- logger.Debug(fmt.Sprintf("成功保存同步方案: %s, 包含 %d 个SQL脚本", req.SolutionCode, len(sqlScripts)))
-
- // 构建返回结果
- detail := SyncSolutionDetail{
- Solution: solution,
- SQLScripts: sqlScripts,
- }
-
- return util.CreateSuccessResultData[SyncSolutionDetail](detail, reqCtx)
- }
-
- // validateSyncSolutionRequest 验证同步方案请求
- func validateSyncSolutionRequest(req *SyncSolutionRequest) error {
- if req.SolutionCode == "" {
- return fmt.Errorf("方案代码不能为空")
- }
-
- // 验证方案代码格式:仅允许字母、数字、下划线
- match, _ := regexp.MatchString("^[a-zA-Z0-9_]+$", req.SolutionCode)
- if !match {
- return fmt.Errorf("方案代码只能包含字母、数字、下划线")
- }
-
- if req.SolutionType == "" {
- return fmt.Errorf("方案类型不能为空")
- }
- // 验证方案类型
- validTypes := map[string]bool{"全量": true, "增量": true, "计算": true}
- if !validTypes[req.SolutionType] {
- return fmt.Errorf("方案类型必须是'全量'、'增量'或'计算'")
- }
-
- if req.SolutionName == "" {
- return fmt.Errorf("方案名称不能为空")
- }
-
- // 验证子表数据
- for i, script := range req.SQLScripts {
- if script.SyncID == "" {
- return fmt.Errorf("第%d个SQL脚本的同步代码不能为空", i+1)
- }
- if script.SyncName == "" {
- return fmt.Errorf("第%d个SQL脚本的同步名称不能为空", i+1)
- }
- if script.CodeSQL == "" {
- return fmt.Errorf("第%d个SQL脚本的SQL代码不能为空", i+1)
- }
- if script.CountSQL == "" {
- return fmt.Errorf("第%d个SQL脚本的统计SQL代码不能为空", i+1)
- }
- // 验证同步代码格式
- match, _ := regexp.MatchString("^[a-zA-Z0-9_.-]+$", script.SyncID)
- if !match {
- return fmt.Errorf("第%d个SQL脚本的同步代码只能包含字母、数字、下划线、点、横线", i+1)
- }
- }
-
- return nil
- }
-
- // checkSolutionExists 检查方案是否存在
- func checkSolutionExists(ctx context.Context, tx *sqlx.Tx, solutionCode string) (bool, error) {
- var count int
- query := "SELECT COUNT(*) FROM sync_solution WHERE solution_code = ? AND deleted_at IS NULL"
- err := tx.GetContext(ctx, &count, query, solutionCode)
- return count > 0, err
- }
-
- // insertSolution 插入方案主表
- func insertSolution(ctx context.Context, tx *sqlx.Tx, req *SyncSolutionRequest, creator string) (tables.SyncSolutionDB, error) {
- query := `
- INSERT INTO sync_solution (solution_code, solution_type, solution_name, description, creator)
- VALUES (?, ?, ?, ?, ?)
- `
- _, err := tx.ExecContext(ctx, query,
- req.SolutionCode,
- req.SolutionType,
- req.SolutionName,
- req.Description,
- creator,
- )
- if err != nil {
- return tables.SyncSolutionDB{}, err
- }
-
- // 查询刚插入的记录
- var solution tables.SyncSolutionDB
- selectQuery := `
- SELECT id, solution_code, solution_type, solution_name, description, creator, created_at, updated_at, deleted_at
- FROM sync_solution
- WHERE solution_code = ? AND deleted_at IS NULL
- `
- err = tx.GetContext(ctx, &solution, selectQuery, req.SolutionCode)
- return solution, err
- }
-
- // updateSolution 更新方案主表
- func updateSolution(ctx context.Context, tx *sqlx.Tx, req *SyncSolutionRequest, creator string) (tables.SyncSolutionDB, error) {
- query := `
- UPDATE sync_solution
- SET solution_type = ?, solution_name = ?, description = ?, updated_at = CURRENT_TIMESTAMP
- WHERE solution_code = ? AND deleted_at IS NULL
- `
- _, err := tx.ExecContext(ctx, query,
- req.SolutionType,
- req.SolutionName,
- req.Description,
- req.SolutionCode,
- )
- if err != nil {
- return tables.SyncSolutionDB{}, err
- }
-
- // 查询更新后的记录
- var solution tables.SyncSolutionDB
- selectQuery := `
- SELECT id, solution_code, solution_type, solution_name, description, creator, created_at, updated_at, deleted_at
- FROM sync_solution
- WHERE solution_code = ? AND deleted_at IS NULL
- `
- err = tx.GetContext(ctx, &solution, selectQuery, req.SolutionCode)
- return solution, err
- }
-
- // checkSQLScriptExists 检查SQL脚本是否存在
- func checkSQLScriptExists(ctx context.Context, tx *sqlx.Tx, syncID, solutionID string) (bool, error) {
- var count int
- query := "SELECT COUNT(*) FROM sync_solution_sql WHERE sync_id = ? AND solution_id = ? AND deleted_at IS NULL"
- err := tx.GetContext(ctx, &count, query, syncID, solutionID)
- return count > 0, err
- }
-
- // insertSQLScript 插入SQL脚本子表
- func insertSQLScript(ctx context.Context, tx *sqlx.Tx, script *SyncSQLScript, creator string) (tables.SyncSolutionSQLDB, error) {
- query := `
- INSERT INTO sync_solution_sql (sync_id, sync_name, solution_id, code_sql, count_sql, is_active, creator)
- VALUES (?, ?, ?, ?, ?, ?, ?)
- `
- _, err := tx.ExecContext(ctx, query,
- script.SyncID,
- script.SyncName,
- script.SolutionID,
- script.CodeSQL,
- script.CountSQL,
- script.IsActive,
- creator,
- )
- if err != nil {
- return tables.SyncSolutionSQLDB{}, err
- }
-
- // 查询刚插入的记录
- var sqlScript tables.SyncSolutionSQLDB
- selectQuery := `
- SELECT id, sync_id, sync_name, solution_id, code_sql, count_sql, is_active, creator, created_at, updated_at, deleted_at
- FROM sync_solution_sql
- WHERE sync_id = ? AND solution_id = ? AND deleted_at IS NULL
- `
- err = tx.GetContext(ctx, &sqlScript, selectQuery, script.SyncID, script.SolutionID)
- return sqlScript, err
- }
-
- // updateSQLScript 更新SQL脚本子表
- func updateSQLScript(ctx context.Context, tx *sqlx.Tx, script *SyncSQLScript, creator string) (tables.SyncSolutionSQLDB, error) {
- query := `
- UPDATE sync_solution_sql
- SET sync_name = ?, code_sql = ?, count_sql = ?, is_active = ?, updated_at = CURRENT_TIMESTAMP
- WHERE sync_id = ? AND solution_id = ? AND deleted_at IS NULL
- `
- _, err := tx.ExecContext(ctx, query,
- script.SyncName,
- script.CodeSQL,
- script.CountSQL,
- script.IsActive,
- script.SyncID,
- script.SolutionID,
- )
- if err != nil {
- return tables.SyncSolutionSQLDB{}, err
- }
-
- // 查询更新后的记录
- var sqlScript tables.SyncSolutionSQLDB
- selectQuery := `
- SELECT id, sync_id, sync_name, solution_id, code_sql, count_sql, is_active, creator, created_at, updated_at, deleted_at
- FROM sync_solution_sql
- WHERE sync_id = ? AND solution_id = ? AND deleted_at IS NULL
- `
- err = tx.GetContext(ctx, &sqlScript, selectQuery, script.SyncID, script.SolutionID)
- return sqlScript, err
- }
|