137 lines
5.3 KiB
Go
137 lines
5.3 KiB
Go
// Package cp 承载 CP/兄弟/姐妹关系的用例编排。
|
||
package cp
|
||
|
||
import (
|
||
"context"
|
||
"strings"
|
||
"time"
|
||
|
||
"hyapp/pkg/xerr"
|
||
cpdomain "hyapp/services/user-service/internal/domain/cp"
|
||
)
|
||
|
||
// Repository 描述 CP 关系 service 需要的持久化能力,便于后续单元测试替换 MySQL 实现。
|
||
type Repository interface {
|
||
ListApplications(ctx context.Context, userID int64, direction string, status string, page int32, pageSize int32, nowMs int64) ([]cpdomain.Application, int64, error)
|
||
AcceptApplication(ctx context.Context, userID int64, applicationID string, nowMs int64) (cpdomain.Application, cpdomain.Relationship, error)
|
||
RejectApplication(ctx context.Context, userID int64, applicationID string, reason string, nowMs int64) (cpdomain.Application, error)
|
||
ListRelationships(ctx context.Context, userID int64, relationType string, page int32, pageSize int32) ([]cpdomain.Relationship, int64, error)
|
||
ConsumeGiftEvent(ctx context.Context, event cpdomain.GiftEvent, nowMs int64) (cpdomain.ConsumeResult, error)
|
||
}
|
||
|
||
// Service 只编排 CP 用例,不直接投递 IM;IM 由 user_outbox 下游消费者按事实事件处理。
|
||
type Service struct {
|
||
repo Repository
|
||
clock func() time.Time
|
||
}
|
||
|
||
// New 创建 CP 关系 service。
|
||
func New(repo Repository) *Service {
|
||
return &Service{
|
||
repo: repo,
|
||
clock: func() time.Time { return time.Now().UTC() },
|
||
}
|
||
}
|
||
|
||
// ListApplications 返回用户收到或发出的 CP 申请;过期固化在 repository 中完成。
|
||
func (s *Service) ListApplications(ctx context.Context, userID int64, direction string, status string, page int32, pageSize int32) ([]cpdomain.Application, int64, int64, error) {
|
||
if err := requireUserID(userID); err != nil {
|
||
return nil, 0, 0, err
|
||
}
|
||
nowMs := s.nowMs()
|
||
applications, total, err := s.repo.ListApplications(ctx, userID, normalizeDirection(direction), normalizeStatus(status), page, pageSize, nowMs)
|
||
return applications, total, nowMs, err
|
||
}
|
||
|
||
// AcceptApplication 同意申请并建立关系;同一用户只能有一个 active 关系的互斥由 repository 事务保证。
|
||
func (s *Service) AcceptApplication(ctx context.Context, userID int64, applicationID string) (cpdomain.Application, cpdomain.Relationship, error) {
|
||
if err := requireUserID(userID); err != nil {
|
||
return cpdomain.Application{}, cpdomain.Relationship{}, err
|
||
}
|
||
if strings.TrimSpace(applicationID) == "" {
|
||
return cpdomain.Application{}, cpdomain.Relationship{}, xerr.New(xerr.InvalidArgument, "application_id is required")
|
||
}
|
||
return s.repo.AcceptApplication(ctx, userID, strings.TrimSpace(applicationID), s.nowMs())
|
||
}
|
||
|
||
// RejectApplication 拒绝申请;拒绝不阻断同一对用户其他类型的 pending 申请。
|
||
func (s *Service) RejectApplication(ctx context.Context, userID int64, applicationID string, reason string) (cpdomain.Application, error) {
|
||
if err := requireUserID(userID); err != nil {
|
||
return cpdomain.Application{}, err
|
||
}
|
||
if strings.TrimSpace(applicationID) == "" {
|
||
return cpdomain.Application{}, xerr.New(xerr.InvalidArgument, "application_id is required")
|
||
}
|
||
return s.repo.RejectApplication(ctx, userID, strings.TrimSpace(applicationID), reason, s.nowMs())
|
||
}
|
||
|
||
// ListRelationships 返回用户当前 active 关系;当前产品固定每个用户同时只能有一条关系。
|
||
func (s *Service) ListRelationships(ctx context.Context, userID int64, relationType string, page int32, pageSize int32) ([]cpdomain.Relationship, int64, error) {
|
||
if err := requireUserID(userID); err != nil {
|
||
return nil, 0, err
|
||
}
|
||
return s.repo.ListRelationships(ctx, userID, normalizeRelationType(relationType), page, pageSize)
|
||
}
|
||
|
||
// ConsumeGiftEvent 消费 room-service 已扣费并落房间状态的送礼事实。
|
||
func (s *Service) ConsumeGiftEvent(ctx context.Context, event cpdomain.GiftEvent) (cpdomain.ConsumeResult, error) {
|
||
event.CPRelationType = normalizeRelationType(event.CPRelationType)
|
||
if event.CPRelationType == "" {
|
||
return cpdomain.ConsumeResult{Consumed: false}, nil
|
||
}
|
||
// gift_value 是亲密值增量,沿用 room 热度结算后的值;0 值仍可创建申请,但不会增加已有关联亲密值。
|
||
if event.GiftValue < 0 {
|
||
event.GiftValue = 0
|
||
}
|
||
return s.repo.ConsumeGiftEvent(ctx, event, s.nowMs())
|
||
}
|
||
|
||
func (s *Service) nowMs() int64 {
|
||
if s == nil || s.clock == nil {
|
||
return time.Now().UTC().UnixMilli()
|
||
}
|
||
return s.clock().UTC().UnixMilli()
|
||
}
|
||
|
||
func requireUserID(userID int64) error {
|
||
if userID <= 0 {
|
||
return xerr.New(xerr.InvalidArgument, "user_id is required")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func normalizeDirection(value string) string {
|
||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||
case "outgoing":
|
||
return "outgoing"
|
||
default:
|
||
return "incoming"
|
||
}
|
||
}
|
||
|
||
func normalizeStatus(value string) string {
|
||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||
case cpdomain.ApplicationStatusPending,
|
||
cpdomain.ApplicationStatusAccepted,
|
||
cpdomain.ApplicationStatusRejected,
|
||
cpdomain.ApplicationStatusExpired,
|
||
cpdomain.ApplicationStatusBlocked:
|
||
return strings.ToLower(strings.TrimSpace(value))
|
||
default:
|
||
return ""
|
||
}
|
||
}
|
||
|
||
func normalizeRelationType(value string) string {
|
||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||
case cpdomain.RelationTypeCP:
|
||
return cpdomain.RelationTypeCP
|
||
case cpdomain.RelationTypeBrother:
|
||
return cpdomain.RelationTypeBrother
|
||
case cpdomain.RelationTypeSister:
|
||
return cpdomain.RelationTypeSister
|
||
default:
|
||
return ""
|
||
}
|
||
}
|