package repository import ( "context" "time" "carrotskin/internal/model" "gorm.io/gorm" ) // FriendRepository 好友/屏蔽关系仓储接口 type FriendRepository interface { // Create 创建好友/屏蔽关系记录 Create(ctx context.Context, friend *model.Friend) error // FindRelation 查询 requester->target 方向的关系记录 FindRelation(ctx context.Context, requesterUUID, targetUUID string) (*model.Friend, error) // ListAccepted 返回与 uuid 已互为好友的对端 UUID 集合 // (合并 requester 与 target 两个方向 status=accepted 的记录) ListAccepted(ctx context.Context, uuid string) ([]string, error) // ListIncoming 返回收到的 pending 请求发起者 UUID 列表 ListIncoming(ctx context.Context, uuid string) ([]string, error) // ListOutgoing 返回已发出的 pending 请求目标 UUID 列表 ListOutgoing(ctx context.Context, uuid string) ([]string, error) // UpdateStatus 更新某条记录的状态 UpdateStatus(ctx context.Context, id int64, status model.FriendsStatus) error // Delete 删除记录(物理删除,用于 remove/decline/revoke/unblock) Delete(ctx context.Context, id int64) error // ListBlocked 返回 blocker 屏蔽的 target UUID 列表 ListBlocked(ctx context.Context, blockerUUID string) ([]string, error) } // PlayerAttributeRepository 玩家偏好属性仓储接口 type PlayerAttributeRepository interface { // Get 读取玩家偏好;不存在时返回 model.DefaultPlayerAttributes 的零值结构,错误为 nil Get(ctx context.Context, profileUUID string) (*model.PlayerAttributes, error) // Upsert 插入或更新玩家偏好 Upsert(ctx context.Context, attr *model.PlayerAttributes) error } // friendRepository FriendRepository 的 GORM 实现 type friendRepository struct { db *gorm.DB } // NewFriendRepository 创建 FriendRepository 实例 func NewFriendRepository(db *gorm.DB) FriendRepository { return &friendRepository{db: db} } func (r *friendRepository) Create(ctx context.Context, friend *model.Friend) error { return r.db.WithContext(ctx).Create(friend).Error } func (r *friendRepository) FindRelation(ctx context.Context, requesterUUID, targetUUID string) (*model.Friend, error) { var f model.Friend err := r.db.WithContext(ctx). Where("requester_uuid = ? AND target_uuid = ?", requesterUUID, targetUUID). First(&f).Error return handleNotFoundResult(&f, err) } func (r *friendRepository) ListAccepted(ctx context.Context, uuid string) ([]string, error) { var records []model.Friend // requester 方向 if err := r.db.WithContext(ctx). Select("target_uuid"). Where("requester_uuid = ? AND status = ?", uuid, model.FriendsStatusAccepted). Find(&records).Error; err != nil { return nil, err } result := make([]string, 0, len(records)) for _, r := range records { result = append(result, r.TargetUUID) } // target 方向 records = records[:0] if err := r.db.WithContext(ctx). Select("requester_uuid"). Where("target_uuid = ? AND status = ?", uuid, model.FriendsStatusAccepted). Find(&records).Error; err != nil { return nil, err } for _, r := range records { result = append(result, r.RequesterUUID) } return result, nil } func (r *friendRepository) ListIncoming(ctx context.Context, uuid string) ([]string, error) { var records []model.Friend err := r.db.WithContext(ctx). Select("requester_uuid"). Where("target_uuid = ? AND status = ?", uuid, model.FriendsStatusPending). Find(&records).Error if err != nil { return nil, err } result := make([]string, 0, len(records)) for _, r := range records { result = append(result, r.RequesterUUID) } return result, nil } func (r *friendRepository) ListOutgoing(ctx context.Context, uuid string) ([]string, error) { var records []model.Friend err := r.db.WithContext(ctx). Select("target_uuid"). Where("requester_uuid = ? AND status = ?", uuid, model.FriendsStatusPending). Find(&records).Error if err != nil { return nil, err } result := make([]string, 0, len(records)) for _, r := range records { result = append(result, r.TargetUUID) } return result, nil } func (r *friendRepository) UpdateStatus(ctx context.Context, id int64, status model.FriendsStatus) error { return r.db.WithContext(ctx).Model(&model.Friend{}). Where("id = ?", id). Update("status", status).Error } func (r *friendRepository) Delete(ctx context.Context, id int64) error { return r.db.WithContext(ctx).Delete(&model.Friend{}, id).Error } func (r *friendRepository) ListBlocked(ctx context.Context, blockerUUID string) ([]string, error) { var records []model.Friend err := r.db.WithContext(ctx). Select("target_uuid"). Where("requester_uuid = ? AND status = ?", blockerUUID, model.FriendsStatusBlocked). Find(&records).Error if err != nil { return nil, err } result := make([]string, 0, len(records)) for _, r := range records { result = append(result, r.TargetUUID) } return result, nil } // playerAttributeRepository PlayerAttributeRepository 的 GORM 实现 type playerAttributeRepository struct { db *gorm.DB } // NewPlayerAttributeRepository 创建 PlayerAttributeRepository 实例 func NewPlayerAttributeRepository(db *gorm.DB) PlayerAttributeRepository { return &playerAttributeRepository{db: db} } func (r *playerAttributeRepository) Get(ctx context.Context, profileUUID string) (*model.PlayerAttributes, error) { var attr model.PlayerAttributes err := r.db.WithContext(ctx). Where("profile_uuid = ?", profileUUID). First(&attr).Error if err != nil { if IsNotFound(err) { // 不存在时返回默认值 d := model.DefaultPlayerAttributes(profileUUID) return &d, nil } return nil, err } return &attr, nil } func (r *playerAttributeRepository) Upsert(ctx context.Context, attr *model.PlayerAttributes) error { now := time.Now() attr.UpdatedAt = now // 用 map 显式写出所有字段,规避 gorm 对带 default 标签的零值布尔字段在 Create 时被忽略的问题 fields := map[string]interface{}{ "friends_enabled": attr.FriendsEnabled, "accept_invites": attr.AcceptInvites, "profanity_filter_on": attr.ProfanityFilterOn, "text_communication": attr.TextCommunication, "updated_at": now, } // 优先 UPDATE;若影响行数为 0(记录不存在)再 INSERT res := r.db.WithContext(ctx).Model(&model.PlayerAttributes{}). Where("profile_uuid = ?", attr.ProfileUUID). Updates(fields) if res.Error != nil { return res.Error } if res.RowsAffected > 0 { return nil } // 不存在 -> 插入;用 map 写入避免零值布尔被 default 覆盖 createFields := map[string]interface{}{ "profile_uuid": attr.ProfileUUID, "friends_enabled": attr.FriendsEnabled, "accept_invites": attr.AcceptInvites, "profanity_filter_on": attr.ProfanityFilterOn, "text_communication": attr.TextCommunication, "updated_at": now, "created_at": now, } if err := r.db.WithContext(ctx).Model(&model.PlayerAttributes{}).Create(createFields).Error; err != nil { // 并发情况下可能唯一键冲突,回退为更新一次 return r.db.WithContext(ctx).Model(&model.PlayerAttributes{}). Where("profile_uuid = ?", attr.ProfileUUID). Updates(fields).Error } return nil }