Files
sub2api/backend/internal/repository/error_translate.go
yangjianbo 3d617de577 refactor(数据库): 迁移持久层到 Ent 并清理 GORM
将仓储层/基础设施改为 Ent + 原生 SQL 执行路径,并移除 AutoMigrate 与 GORM 依赖。
重构内容包括:
- 仓储层改用 Ent/SQL(含 usage_log/account 等复杂查询),统一错误映射
- 基础设施与 setup 初始化切换为 Ent + SQL migrations
- 集成测试与 fixtures 迁移到 Ent 事务模型
- 清理遗留 GORM 模型/依赖,补充迁移与文档说明
- 增加根目录 Makefile 便于前后端编译

测试:
- go test -tags unit ./...
- go test -tags integration ./...
2025-12-29 10:03:27 +08:00

78 lines
2.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repository
import (
"database/sql"
"errors"
"strings"
dbent "github.com/Wei-Shaw/sub2api/ent"
infraerrors "github.com/Wei-Shaw/sub2api/internal/infrastructure/errors"
"github.com/lib/pq"
)
// translatePersistenceError 将数据库层错误翻译为业务层错误。
//
// 这是 Repository 层的核心错误处理函数,确保数据库细节不会泄露到业务层。
// 通过统一的错误翻译,业务层可以使用语义明确的错误类型(如 ErrUserNotFound
// 而不是依赖于特定数据库的错误(如 sql.ErrNoRows
//
// 参数:
// - err: 原始数据库错误
// - notFound: 当记录不存在时返回的业务错误(可为 nil 表示不处理)
// - conflict: 当违反唯一约束时返回的业务错误(可为 nil 表示不处理)
//
// 返回:
// - 翻译后的业务错误,或原始错误(如果不匹配任何规则)
//
// 示例:
//
// err := translatePersistenceError(dbErr, service.ErrUserNotFound, service.ErrEmailExists)
func translatePersistenceError(err error, notFound, conflict *infraerrors.ApplicationError) error {
if err == nil {
return nil
}
// 兼容 Ent ORM 和标准 database/sql 的 NotFound 行为。
// Ent 使用自定义的 NotFoundError而标准库使用 sql.ErrNoRows。
// 这里同时处理两种情况,保持业务错误映射一致。
if notFound != nil && (errors.Is(err, sql.ErrNoRows) || dbent.IsNotFound(err)) {
return notFound.WithCause(err)
}
// 处理唯一约束冲突(如邮箱已存在、名称重复等)
if conflict != nil && isUniqueConstraintViolation(err) {
return conflict.WithCause(err)
}
// 未匹配任何规则,返回原始错误
return err
}
// isUniqueConstraintViolation 判断错误是否为唯一约束冲突。
//
// 支持多种检测方式:
// 1. PostgreSQL 特定错误码 23505唯一约束冲突
// 2. 错误消息中包含的通用关键词
//
// 这种多层次的检测确保了对不同数据库驱动和 ORM 的兼容性。
func isUniqueConstraintViolation(err error) bool {
if err == nil {
return false
}
// 优先检测 PostgreSQL 特定错误码(最精确)。
// 错误码 23505 对应 unique_violation。
// 参考https://www.postgresql.org/docs/current/errcodes-appendix.html
var pgErr *pq.Error
if errors.As(err, &pgErr) {
return pgErr.Code == "23505"
}
// 回退到错误消息检测(兼容其他场景)。
// 这些关键词覆盖了 PostgreSQL、MySQL 等主流数据库的错误消息。
msg := strings.ToLower(err.Error())
return strings.Contains(msg, "duplicate key") ||
strings.Contains(msg, "unique constraint") ||
strings.Contains(msg, "duplicate entry")
}