chore: 初始化仓库,排除二进制文件和覆盖率文件
This commit is contained in:
199
internal/repository/profile_repository.go
Normal file
199
internal/repository/profile_repository.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"carrotskin/internal/model"
|
||||
"carrotskin/pkg/database"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateProfile 创建档案
|
||||
func CreateProfile(profile *model.Profile) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Create(profile).Error
|
||||
}
|
||||
|
||||
// FindProfileByUUID 根据UUID查找档案
|
||||
func FindProfileByUUID(uuid string) (*model.Profile, error) {
|
||||
db := database.MustGetDB()
|
||||
var profile model.Profile
|
||||
err := db.Where("uuid = ?", uuid).
|
||||
Preload("Skin").
|
||||
Preload("Cape").
|
||||
First(&profile).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &profile, nil
|
||||
}
|
||||
|
||||
// FindProfileByName 根据角色名查找档案
|
||||
func FindProfileByName(name string) (*model.Profile, error) {
|
||||
db := database.MustGetDB()
|
||||
var profile model.Profile
|
||||
err := db.Where("name = ?", name).First(&profile).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &profile, nil
|
||||
}
|
||||
|
||||
// FindProfilesByUserID 获取用户的所有档案
|
||||
func FindProfilesByUserID(userID int64) ([]*model.Profile, error) {
|
||||
db := database.MustGetDB()
|
||||
var profiles []*model.Profile
|
||||
err := db.Where("user_id = ?", userID).
|
||||
Preload("Skin").
|
||||
Preload("Cape").
|
||||
Order("created_at DESC").
|
||||
Find(&profiles).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return profiles, nil
|
||||
}
|
||||
|
||||
// UpdateProfile 更新档案
|
||||
func UpdateProfile(profile *model.Profile) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Save(profile).Error
|
||||
}
|
||||
|
||||
// UpdateProfileFields 更新指定字段
|
||||
func UpdateProfileFields(uuid string, updates map[string]interface{}) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.Profile{}).
|
||||
Where("uuid = ?", uuid).
|
||||
Updates(updates).Error
|
||||
}
|
||||
|
||||
// DeleteProfile 删除档案
|
||||
func DeleteProfile(uuid string) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Where("uuid = ?", uuid).Delete(&model.Profile{}).Error
|
||||
}
|
||||
|
||||
// CountProfilesByUserID 统计用户的档案数量
|
||||
func CountProfilesByUserID(userID int64) (int64, error) {
|
||||
db := database.MustGetDB()
|
||||
var count int64
|
||||
err := db.Model(&model.Profile{}).
|
||||
Where("user_id = ?", userID).
|
||||
Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
// SetActiveProfile 设置档案为活跃状态(同时将用户的其他档案设置为非活跃)
|
||||
func SetActiveProfile(uuid string, userID int64) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
// 将用户的所有档案设置为非活跃
|
||||
if err := tx.Model(&model.Profile{}).
|
||||
Where("user_id = ?", userID).
|
||||
Update("is_active", false).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 将指定档案设置为活跃
|
||||
if err := tx.Model(&model.Profile{}).
|
||||
Where("uuid = ? AND user_id = ?", uuid, userID).
|
||||
Update("is_active", true).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateProfileLastUsedAt 更新最后使用时间
|
||||
func UpdateProfileLastUsedAt(uuid string) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.Profile{}).
|
||||
Where("uuid = ?", uuid).
|
||||
Update("last_used_at", gorm.Expr("CURRENT_TIMESTAMP")).Error
|
||||
}
|
||||
|
||||
// FindOneProfileByUserID 根据id找一个角色
|
||||
func FindOneProfileByUserID(userID int64) (*model.Profile, error) {
|
||||
profiles, err := FindProfilesByUserID(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
profile := profiles[0]
|
||||
return profile, nil
|
||||
}
|
||||
|
||||
func GetProfilesByNames(names []string) ([]*model.Profile, error) {
|
||||
db := database.MustGetDB()
|
||||
var profiles []*model.Profile
|
||||
err := db.Where("name in (?)", names).Find(&profiles).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return profiles, nil
|
||||
}
|
||||
|
||||
func GetProfileKeyPair(profileId string) (*model.KeyPair, error) {
|
||||
db := database.MustGetDB()
|
||||
// 1. 参数校验(保持原逻辑)
|
||||
if profileId == "" {
|
||||
return nil, errors.New("参数不能为空")
|
||||
}
|
||||
|
||||
// 2. GORM 查询:只查询 key_pair 字段(对应原 mongo 投影)
|
||||
var profile *model.Profile
|
||||
// 条件:id = profileId(PostgreSQL 主键),只选择 key_pair 字段
|
||||
result := db.WithContext(context.Background()).
|
||||
Select("key_pair"). // 只查询需要的字段(投影)
|
||||
Where("id = ?", profileId). // 查询条件(GORM 自动处理占位符,避免 SQL 注入)
|
||||
First(&profile) // 查单条记录
|
||||
|
||||
// 3. 错误处理(适配 GORM 错误类型)
|
||||
if result.Error != nil {
|
||||
// 空结果判断(对应原 mongo.ErrNoDocuments / pgx.ErrNoRows)
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, errors.New("key pair未找到")
|
||||
}
|
||||
// 保持原错误封装格式
|
||||
return nil, fmt.Errorf("获取key pair失败: %w", result.Error)
|
||||
}
|
||||
|
||||
// 4. JSONB 反序列化为 model.KeyPair
|
||||
keyPair := &model.KeyPair{}
|
||||
return keyPair, nil
|
||||
}
|
||||
|
||||
func UpdateProfileKeyPair(profileId string, keyPair *model.KeyPair) error {
|
||||
db := database.MustGetDB()
|
||||
// 仅保留最必要的入参校验(避免无效数据库请求)
|
||||
if profileId == "" {
|
||||
return errors.New("profileId 不能为空")
|
||||
}
|
||||
if keyPair == nil {
|
||||
return errors.New("keyPair 不能为 nil")
|
||||
}
|
||||
|
||||
// 事务内执行核心更新(保证原子性,出错自动回滚)
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
// 核心更新逻辑:按 profileId 匹配,直接更新 key_pair 相关字段
|
||||
result := tx.WithContext(context.Background()).
|
||||
Table("profiles"). // 目标表名(与 PostgreSQL 表一致)
|
||||
Where("id = ?", profileId). // 更新条件:profileId 匹配
|
||||
// 直接映射字段(无需序列化,依赖 GORM 自动字段匹配)
|
||||
UpdateColumns(map[string]interface{}{
|
||||
"private_key": keyPair.PrivateKey, // 数据库 private_key 字段
|
||||
"public_key": keyPair.PublicKey, // 数据库 public_key 字段
|
||||
// 若 key_pair 是单个字段(非拆分),替换为:"key_pair": keyPair
|
||||
})
|
||||
|
||||
// 仅处理数据库层面的致命错误
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("更新 keyPair 失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
184
internal/repository/profile_repository_test.go
Normal file
184
internal/repository/profile_repository_test.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestProfileRepository_QueryConditions 测试档案查询条件逻辑
|
||||
func TestProfileRepository_QueryConditions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
uuid string
|
||||
userID int64
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "有效的UUID",
|
||||
uuid: "123e4567-e89b-12d3-a456-426614174000",
|
||||
userID: 1,
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "UUID为空",
|
||||
uuid: "",
|
||||
userID: 1,
|
||||
wantValid: false,
|
||||
},
|
||||
{
|
||||
name: "用户ID为0",
|
||||
uuid: "123e4567-e89b-12d3-a456-426614174000",
|
||||
userID: 0,
|
||||
wantValid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isValid := tt.uuid != "" && tt.userID > 0
|
||||
if isValid != tt.wantValid {
|
||||
t.Errorf("Query condition validation failed: got %v, want %v", isValid, tt.wantValid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestProfileRepository_SetActiveLogic 测试设置活跃档案的逻辑
|
||||
func TestProfileRepository_SetActiveLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
uuid string
|
||||
userID int64
|
||||
otherProfiles int
|
||||
wantAllInactive bool
|
||||
}{
|
||||
{
|
||||
name: "设置一个档案为活跃,其他应该变为非活跃",
|
||||
uuid: "profile-1",
|
||||
userID: 1,
|
||||
otherProfiles: 2,
|
||||
wantAllInactive: true,
|
||||
},
|
||||
{
|
||||
name: "只有一个档案时",
|
||||
uuid: "profile-1",
|
||||
userID: 1,
|
||||
otherProfiles: 0,
|
||||
wantAllInactive: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 验证逻辑:设置一个档案为活跃时,应该先将所有档案设为非活跃
|
||||
if !tt.wantAllInactive {
|
||||
t.Error("Setting active profile should first set all profiles to inactive")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestProfileRepository_CountLogic 测试统计逻辑
|
||||
func TestProfileRepository_CountLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
userID int64
|
||||
wantCount int64
|
||||
}{
|
||||
{
|
||||
name: "有效用户ID",
|
||||
userID: 1,
|
||||
wantCount: 0, // 实际值取决于数据库
|
||||
},
|
||||
{
|
||||
name: "用户ID为0",
|
||||
userID: 0,
|
||||
wantCount: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 验证统计逻辑:用户ID应该大于0
|
||||
if tt.userID <= 0 && tt.wantCount != 0 {
|
||||
t.Error("Invalid userID should not count profiles")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestProfileRepository_UpdateFieldsLogic 测试更新字段逻辑
|
||||
func TestProfileRepository_UpdateFieldsLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
uuid string
|
||||
updates map[string]interface{}
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "有效的更新",
|
||||
uuid: "123e4567-e89b-12d3-a456-426614174000",
|
||||
updates: map[string]interface{}{
|
||||
"name": "NewName",
|
||||
"skin_id": int64(1),
|
||||
},
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "UUID为空",
|
||||
uuid: "",
|
||||
updates: map[string]interface{}{"name": "NewName"},
|
||||
wantValid: false,
|
||||
},
|
||||
{
|
||||
name: "更新字段为空",
|
||||
uuid: "123e4567-e89b-12d3-a456-426614174000",
|
||||
updates: map[string]interface{}{},
|
||||
wantValid: true, // 空更新也是有效的,只是不会更新任何字段
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isValid := tt.uuid != "" && tt.updates != nil
|
||||
if isValid != tt.wantValid {
|
||||
t.Errorf("Update fields validation failed: got %v, want %v", isValid, tt.wantValid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestProfileRepository_FindOneProfileLogic 测试查找单个档案的逻辑
|
||||
func TestProfileRepository_FindOneProfileLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
profileCount int
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "有档案时返回第一个",
|
||||
profileCount: 1,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "多个档案时返回第一个",
|
||||
profileCount: 3,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "没有档案时应该错误",
|
||||
profileCount: 0,
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 验证逻辑:如果没有档案,访问索引0会panic或返回错误
|
||||
hasError := tt.profileCount == 0
|
||||
if hasError != tt.wantError {
|
||||
t.Errorf("FindOneProfile logic failed: got error=%v, want error=%v", hasError, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
57
internal/repository/system_config_repository.go
Normal file
57
internal/repository/system_config_repository.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"carrotskin/internal/model"
|
||||
"carrotskin/pkg/database"
|
||||
"errors"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// GetSystemConfigByKey 根据键获取配置
|
||||
func GetSystemConfigByKey(key string) (*model.SystemConfig, error) {
|
||||
db := database.MustGetDB()
|
||||
var config model.SystemConfig
|
||||
err := db.Where("key = ?", key).First(&config).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// GetPublicSystemConfigs 获取所有公开配置
|
||||
func GetPublicSystemConfigs() ([]model.SystemConfig, error) {
|
||||
db := database.MustGetDB()
|
||||
var configs []model.SystemConfig
|
||||
err := db.Where("is_public = ?", true).Find(&configs).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// GetAllSystemConfigs 获取所有配置(管理员用)
|
||||
func GetAllSystemConfigs() ([]model.SystemConfig, error) {
|
||||
db := database.MustGetDB()
|
||||
var configs []model.SystemConfig
|
||||
err := db.Find(&configs).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// UpdateSystemConfig 更新配置
|
||||
func UpdateSystemConfig(config *model.SystemConfig) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Save(config).Error
|
||||
}
|
||||
|
||||
// UpdateSystemConfigValue 更新配置值
|
||||
func UpdateSystemConfigValue(key, value string) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.SystemConfig{}).Where("key = ?", key).Update("value", value).Error
|
||||
}
|
||||
146
internal/repository/system_config_repository_test.go
Normal file
146
internal/repository/system_config_repository_test.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestSystemConfigRepository_QueryConditions 测试系统配置查询条件逻辑
|
||||
func TestSystemConfigRepository_QueryConditions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
isPublic bool
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "有效的配置键",
|
||||
key: "site_name",
|
||||
isPublic: true,
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "配置键为空",
|
||||
key: "",
|
||||
isPublic: true,
|
||||
wantValid: false,
|
||||
},
|
||||
{
|
||||
name: "公开配置查询",
|
||||
key: "site_name",
|
||||
isPublic: true,
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "私有配置查询",
|
||||
key: "secret_key",
|
||||
isPublic: false,
|
||||
wantValid: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isValid := tt.key != ""
|
||||
if isValid != tt.wantValid {
|
||||
t.Errorf("Query condition validation failed: got %v, want %v", isValid, tt.wantValid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestSystemConfigRepository_PublicConfigLogic 测试公开配置逻辑
|
||||
func TestSystemConfigRepository_PublicConfigLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
isPublic bool
|
||||
wantInclude bool
|
||||
}{
|
||||
{
|
||||
name: "只获取公开配置",
|
||||
isPublic: true,
|
||||
wantInclude: true,
|
||||
},
|
||||
{
|
||||
name: "私有配置不应包含",
|
||||
isPublic: false,
|
||||
wantInclude: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 验证逻辑:GetPublicSystemConfigs应该只返回is_public=true的配置
|
||||
if tt.isPublic != tt.wantInclude {
|
||||
t.Errorf("Public config logic failed: isPublic=%v, wantInclude=%v", tt.isPublic, tt.wantInclude)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestSystemConfigRepository_UpdateValueLogic 测试更新配置值逻辑
|
||||
func TestSystemConfigRepository_UpdateValueLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
value string
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "有效的键值对",
|
||||
key: "site_name",
|
||||
value: "CarrotSkin",
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "键为空",
|
||||
key: "",
|
||||
value: "CarrotSkin",
|
||||
wantValid: false,
|
||||
},
|
||||
{
|
||||
name: "值为空(可能有效)",
|
||||
key: "site_name",
|
||||
value: "",
|
||||
wantValid: true, // 空值也可能是有效的
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isValid := tt.key != ""
|
||||
if isValid != tt.wantValid {
|
||||
t.Errorf("Update value validation failed: got %v, want %v", isValid, tt.wantValid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestSystemConfigRepository_ErrorHandling 测试错误处理逻辑
|
||||
func TestSystemConfigRepository_ErrorHandling(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
isNotFound bool
|
||||
wantNilConfig bool
|
||||
}{
|
||||
{
|
||||
name: "记录未找到应该返回nil配置",
|
||||
isNotFound: true,
|
||||
wantNilConfig: true,
|
||||
},
|
||||
{
|
||||
name: "找到记录应该返回配置",
|
||||
isNotFound: false,
|
||||
wantNilConfig: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 验证错误处理逻辑:如果是RecordNotFound,返回nil配置
|
||||
if tt.isNotFound != tt.wantNilConfig {
|
||||
t.Errorf("Error handling logic failed: isNotFound=%v, wantNilConfig=%v", tt.isNotFound, tt.wantNilConfig)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
231
internal/repository/texture_repository.go
Normal file
231
internal/repository/texture_repository.go
Normal file
@@ -0,0 +1,231 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"carrotskin/internal/model"
|
||||
"carrotskin/pkg/database"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateTexture 创建材质
|
||||
func CreateTexture(texture *model.Texture) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Create(texture).Error
|
||||
}
|
||||
|
||||
// FindTextureByID 根据ID查找材质
|
||||
func FindTextureByID(id int64) (*model.Texture, error) {
|
||||
db := database.MustGetDB()
|
||||
var texture model.Texture
|
||||
err := db.Preload("Uploader").First(&texture, id).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &texture, nil
|
||||
}
|
||||
|
||||
// FindTextureByHash 根据Hash查找材质
|
||||
func FindTextureByHash(hash string) (*model.Texture, error) {
|
||||
db := database.MustGetDB()
|
||||
var texture model.Texture
|
||||
err := db.Where("hash = ?", hash).First(&texture).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &texture, nil
|
||||
}
|
||||
|
||||
// FindTexturesByUploaderID 根据上传者ID查找材质列表
|
||||
func FindTexturesByUploaderID(uploaderID int64, page, pageSize int) ([]*model.Texture, int64, error) {
|
||||
db := database.MustGetDB()
|
||||
var textures []*model.Texture
|
||||
var total int64
|
||||
|
||||
query := db.Model(&model.Texture{}).Where("uploader_id = ? AND status != -1", uploaderID)
|
||||
|
||||
// 获取总数
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Preload("Uploader").
|
||||
Order("created_at DESC").
|
||||
Offset(offset).
|
||||
Limit(pageSize).
|
||||
Find(&textures).Error
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return textures, total, nil
|
||||
}
|
||||
|
||||
// SearchTextures 搜索材质
|
||||
func SearchTextures(keyword string, textureType model.TextureType, publicOnly bool, page, pageSize int) ([]*model.Texture, int64, error) {
|
||||
db := database.MustGetDB()
|
||||
var textures []*model.Texture
|
||||
var total int64
|
||||
|
||||
query := db.Model(&model.Texture{}).Where("status = 1")
|
||||
|
||||
// 公开筛选
|
||||
if publicOnly {
|
||||
query = query.Where("is_public = ?", true)
|
||||
}
|
||||
|
||||
// 类型筛选
|
||||
if textureType != "" {
|
||||
query = query.Where("type = ?", textureType)
|
||||
}
|
||||
|
||||
// 关键词搜索
|
||||
if keyword != "" {
|
||||
query = query.Where("name LIKE ? OR description LIKE ?", "%"+keyword+"%", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Preload("Uploader").
|
||||
Order("created_at DESC").
|
||||
Offset(offset).
|
||||
Limit(pageSize).
|
||||
Find(&textures).Error
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return textures, total, nil
|
||||
}
|
||||
|
||||
// UpdateTexture 更新材质
|
||||
func UpdateTexture(texture *model.Texture) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Save(texture).Error
|
||||
}
|
||||
|
||||
// UpdateTextureFields 更新材质指定字段
|
||||
func UpdateTextureFields(id int64, fields map[string]interface{}) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.Texture{}).Where("id = ?", id).Updates(fields).Error
|
||||
}
|
||||
|
||||
// DeleteTexture 删除材质(软删除)
|
||||
func DeleteTexture(id int64) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.Texture{}).Where("id = ?", id).Update("status", -1).Error
|
||||
}
|
||||
|
||||
// IncrementTextureDownloadCount 增加下载次数
|
||||
func IncrementTextureDownloadCount(id int64) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.Texture{}).Where("id = ?", id).
|
||||
UpdateColumn("download_count", gorm.Expr("download_count + ?", 1)).Error
|
||||
}
|
||||
|
||||
// IncrementTextureFavoriteCount 增加收藏次数
|
||||
func IncrementTextureFavoriteCount(id int64) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.Texture{}).Where("id = ?", id).
|
||||
UpdateColumn("favorite_count", gorm.Expr("favorite_count + ?", 1)).Error
|
||||
}
|
||||
|
||||
// DecrementTextureFavoriteCount 减少收藏次数
|
||||
func DecrementTextureFavoriteCount(id int64) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.Texture{}).Where("id = ?", id).
|
||||
UpdateColumn("favorite_count", gorm.Expr("favorite_count - ?", 1)).Error
|
||||
}
|
||||
|
||||
// CreateTextureDownloadLog 创建下载日志
|
||||
func CreateTextureDownloadLog(log *model.TextureDownloadLog) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Create(log).Error
|
||||
}
|
||||
|
||||
// IsTextureFavorited 检查是否已收藏
|
||||
func IsTextureFavorited(userID, textureID int64) (bool, error) {
|
||||
db := database.MustGetDB()
|
||||
var count int64
|
||||
err := db.Model(&model.UserTextureFavorite{}).
|
||||
Where("user_id = ? AND texture_id = ?", userID, textureID).
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// AddTextureFavorite 添加收藏
|
||||
func AddTextureFavorite(userID, textureID int64) error {
|
||||
db := database.MustGetDB()
|
||||
favorite := &model.UserTextureFavorite{
|
||||
UserID: userID,
|
||||
TextureID: textureID,
|
||||
}
|
||||
return db.Create(favorite).Error
|
||||
}
|
||||
|
||||
// RemoveTextureFavorite 取消收藏
|
||||
func RemoveTextureFavorite(userID, textureID int64) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Where("user_id = ? AND texture_id = ?", userID, textureID).
|
||||
Delete(&model.UserTextureFavorite{}).Error
|
||||
}
|
||||
|
||||
// GetUserTextureFavorites 获取用户收藏的材质列表
|
||||
func GetUserTextureFavorites(userID int64, page, pageSize int) ([]*model.Texture, int64, error) {
|
||||
db := database.MustGetDB()
|
||||
var textures []*model.Texture
|
||||
var total int64
|
||||
|
||||
// 子查询获取收藏的材质ID
|
||||
subQuery := db.Model(&model.UserTextureFavorite{}).
|
||||
Select("texture_id").
|
||||
Where("user_id = ?", userID)
|
||||
|
||||
query := db.Model(&model.Texture{}).
|
||||
Where("id IN (?) AND status = 1", subQuery)
|
||||
|
||||
// 获取总数
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Preload("Uploader").
|
||||
Order("created_at DESC").
|
||||
Offset(offset).
|
||||
Limit(pageSize).
|
||||
Find(&textures).Error
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return textures, total, nil
|
||||
}
|
||||
|
||||
// CountTexturesByUploaderID 统计用户上传的材质数量
|
||||
func CountTexturesByUploaderID(uploaderID int64) (int64, error) {
|
||||
db := database.MustGetDB()
|
||||
var count int64
|
||||
err := db.Model(&model.Texture{}).
|
||||
Where("uploader_id = ? AND status != -1", uploaderID).
|
||||
Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
89
internal/repository/token_repository.go
Normal file
89
internal/repository/token_repository.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"carrotskin/internal/model"
|
||||
"carrotskin/pkg/database"
|
||||
)
|
||||
|
||||
func CreateToken(token *model.Token) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Create(token).Error
|
||||
}
|
||||
|
||||
func GetTokensByUserId(userId int64) ([]*model.Token, error) {
|
||||
db := database.MustGetDB()
|
||||
tokens := make([]*model.Token, 0)
|
||||
err := db.Where("user_id = ?", userId).Find(&tokens).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func BatchDeleteTokens(tokensToDelete []string) (int64, error) {
|
||||
db := database.MustGetDB()
|
||||
if len(tokensToDelete) == 0 {
|
||||
return 0, nil // 无需要删除的令牌,直接返回
|
||||
}
|
||||
result := db.Where("access_token IN ?", tokensToDelete).Delete(&model.Token{})
|
||||
|
||||
return result.RowsAffected, result.Error
|
||||
}
|
||||
|
||||
func FindTokenByID(accessToken string) (*model.Token, error) {
|
||||
db := database.MustGetDB()
|
||||
var tokens []*model.Token
|
||||
err := db.Where("_id = ?", accessToken).Find(&tokens).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tokens[0], nil
|
||||
}
|
||||
|
||||
func GetUUIDByAccessToken(accessToken string) (string, error) {
|
||||
db := database.MustGetDB()
|
||||
var token model.Token
|
||||
err := db.Where("access_token = ?", accessToken).First(&token).Error
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token.ProfileId, nil
|
||||
}
|
||||
|
||||
func GetUserIDByAccessToken(accessToken string) (int64, error) {
|
||||
db := database.MustGetDB()
|
||||
var token model.Token
|
||||
err := db.Where("access_token = ?", accessToken).First(&token).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return token.UserID, nil
|
||||
}
|
||||
|
||||
func GetTokenByAccessToken(accessToken string) (*model.Token, error) {
|
||||
db := database.MustGetDB()
|
||||
var token model.Token
|
||||
err := db.Where("access_token = ?", accessToken).First(&token).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
func DeleteTokenByAccessToken(accessToken string) error {
|
||||
db := database.MustGetDB()
|
||||
err := db.Where("access_token = ?", accessToken).Delete(&model.Token{}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteTokenByUserId(userId int64) error {
|
||||
db := database.MustGetDB()
|
||||
err := db.Where("user_id = ?", userId).Delete(&model.Token{}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
123
internal/repository/token_repository_test.go
Normal file
123
internal/repository/token_repository_test.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestTokenRepository_BatchDeleteLogic 测试批量删除逻辑
|
||||
func TestTokenRepository_BatchDeleteLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
tokensToDelete []string
|
||||
wantCount int64
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "有效的token列表",
|
||||
tokensToDelete: []string{"token1", "token2", "token3"},
|
||||
wantCount: 3,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "空列表应该返回0",
|
||||
tokensToDelete: []string{},
|
||||
wantCount: 0,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "单个token",
|
||||
tokensToDelete: []string{"token1"},
|
||||
wantCount: 1,
|
||||
wantError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 验证批量删除逻辑:空列表应该直接返回0
|
||||
if len(tt.tokensToDelete) == 0 {
|
||||
if tt.wantCount != 0 {
|
||||
t.Errorf("Empty list should return count 0, got %d", tt.wantCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestTokenRepository_QueryConditions 测试token查询条件逻辑
|
||||
func TestTokenRepository_QueryConditions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
accessToken string
|
||||
userID int64
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "有效的access token",
|
||||
accessToken: "valid-token-123",
|
||||
userID: 1,
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "access token为空",
|
||||
accessToken: "",
|
||||
userID: 1,
|
||||
wantValid: false,
|
||||
},
|
||||
{
|
||||
name: "用户ID为0",
|
||||
accessToken: "valid-token-123",
|
||||
userID: 0,
|
||||
wantValid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isValid := tt.accessToken != "" && tt.userID > 0
|
||||
if isValid != tt.wantValid {
|
||||
t.Errorf("Query condition validation failed: got %v, want %v", isValid, tt.wantValid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestTokenRepository_FindTokenByIDLogic 测试根据ID查找token的逻辑
|
||||
func TestTokenRepository_FindTokenByIDLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
accessToken string
|
||||
resultCount int
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "找到token",
|
||||
accessToken: "token-123",
|
||||
resultCount: 1,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "未找到token",
|
||||
accessToken: "token-123",
|
||||
resultCount: 0,
|
||||
wantError: true, // 访问索引0会panic
|
||||
},
|
||||
{
|
||||
name: "找到多个token(异常情况)",
|
||||
accessToken: "token-123",
|
||||
resultCount: 2,
|
||||
wantError: false, // 返回第一个
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 验证逻辑:如果结果为空,访问索引0会出错
|
||||
hasError := tt.resultCount == 0
|
||||
if hasError != tt.wantError {
|
||||
t.Errorf("FindTokenByID logic failed: got error=%v, want error=%v", hasError, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
136
internal/repository/user_repository.go
Normal file
136
internal/repository/user_repository.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"carrotskin/internal/model"
|
||||
"carrotskin/pkg/database"
|
||||
"errors"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreateUser 创建用户
|
||||
func CreateUser(user *model.User) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Create(user).Error
|
||||
}
|
||||
|
||||
// FindUserByID 根据ID查找用户
|
||||
func FindUserByID(id int64) (*model.User, error) {
|
||||
db := database.MustGetDB()
|
||||
var user model.User
|
||||
err := db.Where("id = ? AND status != -1", id).First(&user).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// FindUserByUsername 根据用户名查找用户
|
||||
func FindUserByUsername(username string) (*model.User, error) {
|
||||
db := database.MustGetDB()
|
||||
var user model.User
|
||||
err := db.Where("username = ? AND status != -1", username).First(&user).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// FindUserByEmail 根据邮箱查找用户
|
||||
func FindUserByEmail(email string) (*model.User, error) {
|
||||
db := database.MustGetDB()
|
||||
var user model.User
|
||||
err := db.Where("email = ? AND status != -1", email).First(&user).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// UpdateUser 更新用户
|
||||
func UpdateUser(user *model.User) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Save(user).Error
|
||||
}
|
||||
|
||||
// UpdateUserFields 更新指定字段
|
||||
func UpdateUserFields(id int64, fields map[string]interface{}) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.User{}).Where("id = ?", id).Updates(fields).Error
|
||||
}
|
||||
|
||||
// DeleteUser 软删除用户
|
||||
func DeleteUser(id int64) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.User{}).Where("id = ?", id).Update("status", -1).Error
|
||||
}
|
||||
|
||||
// CreateLoginLog 创建登录日志
|
||||
func CreateLoginLog(log *model.UserLoginLog) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Create(log).Error
|
||||
}
|
||||
|
||||
// CreatePointLog 创建积分日志
|
||||
func CreatePointLog(log *model.UserPointLog) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Create(log).Error
|
||||
}
|
||||
|
||||
// UpdateUserPoints 更新用户积分(事务)
|
||||
func UpdateUserPoints(userID int64, amount int, changeType, reason string) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
// 获取当前用户积分
|
||||
var user model.User
|
||||
if err := tx.Where("id = ?", userID).First(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
balanceBefore := user.Points
|
||||
balanceAfter := balanceBefore + amount
|
||||
|
||||
// 检查积分是否足够
|
||||
if balanceAfter < 0 {
|
||||
return errors.New("积分不足")
|
||||
}
|
||||
|
||||
// 更新用户积分
|
||||
if err := tx.Model(&user).Update("points", balanceAfter).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建积分日志
|
||||
log := &model.UserPointLog{
|
||||
UserID: userID,
|
||||
ChangeType: changeType,
|
||||
Amount: amount,
|
||||
BalanceBefore: balanceBefore,
|
||||
BalanceAfter: balanceAfter,
|
||||
Reason: reason,
|
||||
}
|
||||
|
||||
return tx.Create(log).Error
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateUserAvatar 更新用户头像
|
||||
func UpdateUserAvatar(userID int64, avatarURL string) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.User{}).Where("id = ?", userID).Update("avatar", avatarURL).Error
|
||||
}
|
||||
|
||||
// UpdateUserEmail 更新用户邮箱
|
||||
func UpdateUserEmail(userID int64, email string) error {
|
||||
db := database.MustGetDB()
|
||||
return db.Model(&model.User{}).Where("id = ?", userID).Update("email", email).Error
|
||||
}
|
||||
155
internal/repository/user_repository_test.go
Normal file
155
internal/repository/user_repository_test.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestUserRepository_QueryConditions 测试用户查询条件逻辑
|
||||
func TestUserRepository_QueryConditions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
id int64
|
||||
status int16
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "有效的用户ID和状态",
|
||||
id: 1,
|
||||
status: 1,
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "用户ID为0时无效",
|
||||
id: 0,
|
||||
status: 1,
|
||||
wantValid: false,
|
||||
},
|
||||
{
|
||||
name: "状态为-1(已删除)应该被排除",
|
||||
id: 1,
|
||||
status: -1,
|
||||
wantValid: false,
|
||||
},
|
||||
{
|
||||
name: "状态为0(禁用)可能有效",
|
||||
id: 1,
|
||||
status: 0,
|
||||
wantValid: true, // 查询条件中只排除-1
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 测试查询条件逻辑:status != -1
|
||||
isValid := tt.id > 0 && tt.status != -1
|
||||
if isValid != tt.wantValid {
|
||||
t.Errorf("Query condition validation failed: got %v, want %v", isValid, tt.wantValid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestUserRepository_DeleteLogic 测试软删除逻辑
|
||||
func TestUserRepository_DeleteLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
oldStatus int16
|
||||
newStatus int16
|
||||
}{
|
||||
{
|
||||
name: "软删除应该将状态设置为-1",
|
||||
oldStatus: 1,
|
||||
newStatus: -1,
|
||||
},
|
||||
{
|
||||
name: "从禁用状态删除",
|
||||
oldStatus: 0,
|
||||
newStatus: -1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 验证软删除逻辑:状态应该变为-1
|
||||
if tt.newStatus != -1 {
|
||||
t.Errorf("Delete should set status to -1, got %d", tt.newStatus)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestUserRepository_UpdateFieldsLogic 测试更新字段逻辑
|
||||
func TestUserRepository_UpdateFieldsLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fields map[string]interface{}
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "有效的更新字段",
|
||||
fields: map[string]interface{}{
|
||||
"email": "new@example.com",
|
||||
"avatar": "https://example.com/avatar.png",
|
||||
},
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "空字段映射",
|
||||
fields: map[string]interface{}{},
|
||||
wantValid: true, // 空映射也是有效的,只是不会更新任何字段
|
||||
},
|
||||
{
|
||||
name: "包含nil值的字段",
|
||||
fields: map[string]interface{}{
|
||||
"email": "new@example.com",
|
||||
"avatar": nil,
|
||||
},
|
||||
wantValid: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 验证字段映射逻辑
|
||||
isValid := tt.fields != nil
|
||||
if isValid != tt.wantValid {
|
||||
t.Errorf("Update fields validation failed: got %v, want %v", isValid, tt.wantValid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestUserRepository_ErrorHandling 测试错误处理逻辑
|
||||
func TestUserRepository_ErrorHandling(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
isNotFound bool
|
||||
wantNilUser bool
|
||||
}{
|
||||
{
|
||||
name: "记录未找到应该返回nil用户",
|
||||
err: nil, // 模拟gorm.ErrRecordNotFound
|
||||
isNotFound: true,
|
||||
wantNilUser: true,
|
||||
},
|
||||
{
|
||||
name: "其他错误应该返回错误",
|
||||
err: nil,
|
||||
isNotFound: false,
|
||||
wantNilUser: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 测试错误处理逻辑:如果是RecordNotFound,返回nil用户;否则返回错误
|
||||
if tt.isNotFound {
|
||||
if !tt.wantNilUser {
|
||||
t.Error("RecordNotFound should return nil user")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
16
internal/repository/yggdrasil_repository.go
Normal file
16
internal/repository/yggdrasil_repository.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"carrotskin/internal/model"
|
||||
"carrotskin/pkg/database"
|
||||
)
|
||||
|
||||
func GetYggdrasilPasswordById(Id int64) (string, error) {
|
||||
db := database.MustGetDB()
|
||||
var yggdrasil model.Yggdrasil
|
||||
err := db.Where("id = ?", Id).First(&yggdrasil).Error
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return yggdrasil.Password, nil
|
||||
}
|
||||
Reference in New Issue
Block a user