增加任意送礼增加亲密度
This commit is contained in:
parent
f4eee4db4c
commit
f995d80610
@ -758,10 +758,8 @@ func roomGiftCPEventFromRoomMessage(body []byte) (cpdomain.GiftEvent, bool, erro
|
|||||||
if err := proto.Unmarshal(envelope.GetBody(), &sent); err != nil {
|
if err := proto.Unmarshal(envelope.GetBody(), &sent); err != nil {
|
||||||
return cpdomain.GiftEvent{}, false, err
|
return cpdomain.GiftEvent{}, false, err
|
||||||
}
|
}
|
||||||
// 只有明确标记 cp_relation_type 的礼物才进入关系逻辑;普通礼物仍由其他读模型消费。
|
// CP worker 必须接收所有送礼事实:没有 active 关系时仓储层只让 CP/兄弟/姐妹礼物生成申请;
|
||||||
if strings.TrimSpace(sent.GetCpRelationType()) == "" {
|
// 已有 active 关系时,普通、幸运和超级幸运礼物都按 wallet 已结算的 GiftValue 增加亲密值。
|
||||||
return cpdomain.GiftEvent{}, false, nil
|
|
||||||
}
|
|
||||||
return cpdomain.GiftEvent{
|
return cpdomain.GiftEvent{
|
||||||
AppCode: appcode.Normalize(envelope.GetAppCode()),
|
AppCode: appcode.Normalize(envelope.GetAppCode()),
|
||||||
EventID: envelope.GetEventId(),
|
EventID: envelope.GetEventId(),
|
||||||
|
|||||||
49
services/user-service/internal/app/app_test.go
Normal file
49
services/user-service/internal/app/app_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
|
||||||
|
"hyapp/pkg/roommq"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRoomGiftCPEventFromRoomMessageIncludesNormalGift(t *testing.T) {
|
||||||
|
body, err := proto.Marshal(&roomeventsv1.RoomGiftSent{
|
||||||
|
SenderUserId: 1001,
|
||||||
|
TargetUserId: 1002,
|
||||||
|
GiftId: "rose",
|
||||||
|
GiftCount: 1,
|
||||||
|
GiftValue: 100,
|
||||||
|
BillingReceiptId: "receipt-normal",
|
||||||
|
CommandId: "cmd-normal",
|
||||||
|
GiftTypeCode: "normal",
|
||||||
|
GiftName: "Rose",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("marshal room gift sent failed: %v", err)
|
||||||
|
}
|
||||||
|
encoded, err := roommq.EncodeRoomOutboxMessage(&roomeventsv1.EventEnvelope{
|
||||||
|
AppCode: "lalu",
|
||||||
|
EventId: "room_evt_normal_gift",
|
||||||
|
EventType: "RoomGiftSent",
|
||||||
|
RoomId: "room-1",
|
||||||
|
RoomVersion: 7,
|
||||||
|
OccurredAtMs: 1700000000000,
|
||||||
|
Body: body,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("encode room outbox message failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
event, ok, err := roomGiftCPEventFromRoomMessage(encoded)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("convert room gift event failed: %v", err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("normal gifts must reach CP consumer so active relationships can gain intimacy")
|
||||||
|
}
|
||||||
|
if event.CPRelationType != "" || event.GiftTypeCode != "normal" || event.GiftValue != 100 || event.TargetUserID != 1002 {
|
||||||
|
t.Fatalf("normal gift event mismatch: %+v", event)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -514,7 +514,7 @@ func (r *Repository) ConsumeGiftEvent(ctx context.Context, event cpdomain.GiftEv
|
|||||||
return cpdomain.ConsumeResult{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
return cpdomain.ConsumeResult{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||||
}
|
}
|
||||||
relationType := normalizeRelationType(event.CPRelationType)
|
relationType := normalizeRelationType(event.CPRelationType)
|
||||||
if relationType == "" || event.SenderUserID <= 0 || event.TargetUserID <= 0 || event.SenderUserID == event.TargetUserID {
|
if event.SenderUserID <= 0 || event.TargetUserID <= 0 || event.SenderUserID == event.TargetUserID {
|
||||||
return cpdomain.ConsumeResult{Consumed: false}, nil
|
return cpdomain.ConsumeResult{Consumed: false}, nil
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(event.EventID) == "" {
|
if strings.TrimSpace(event.EventID) == "" {
|
||||||
@ -540,7 +540,7 @@ func (r *Repository) ConsumeGiftEvent(ctx context.Context, event cpdomain.GiftEv
|
|||||||
return cpdomain.ConsumeResult{Consumed: false}, tx.Commit()
|
return cpdomain.ConsumeResult{Consumed: false}, tx.Commit()
|
||||||
}
|
}
|
||||||
userAID, userBID := orderedPair(event.SenderUserID, event.TargetUserID)
|
userAID, userBID := orderedPair(event.SenderUserID, event.TargetUserID)
|
||||||
// 已有 active 关系时,任何关系礼物都只增加这对关系的亲密值,不再生成新的申请。
|
// 已有 active 关系时,任意礼物都只增加这对关系的亲密值;GiftValue 已由 wallet-service 按普通/幸运/超级幸运比例结算。
|
||||||
if relationship, found, err := lockActiveRelationshipTx(ctx, tx, appCode, userAID, userBID); err != nil {
|
if relationship, found, err := lockActiveRelationshipTx(ctx, tx, appCode, userAID, userBID); err != nil {
|
||||||
return cpdomain.ConsumeResult{}, err
|
return cpdomain.ConsumeResult{}, err
|
||||||
} else if found {
|
} else if found {
|
||||||
@ -556,6 +556,10 @@ func (r *Repository) ConsumeGiftEvent(ctx context.Context, event cpdomain.GiftEv
|
|||||||
}
|
}
|
||||||
return cpdomain.ConsumeResult{Consumed: true, Relationship: updated}, tx.Commit()
|
return cpdomain.ConsumeResult{Consumed: true, Relationship: updated}, tx.Commit()
|
||||||
}
|
}
|
||||||
|
if relationType == "" {
|
||||||
|
// 普通礼物在双方还没有 active 关系时不能创建申请;这类事件不写消费位点,后续若 MQ 重投仍然是无副作用 no-op。
|
||||||
|
return cpdomain.ConsumeResult{Consumed: false}, tx.Commit()
|
||||||
|
}
|
||||||
// 没有 active 关系时,CP/兄弟/姐妹礼物只生成 pending 申请;真正建立关系必须由 B 显式同意。
|
// 没有 active 关系时,CP/兄弟/姐妹礼物只生成 pending 申请;真正建立关系必须由 B 显式同意。
|
||||||
if err := ensurePairHasNoActiveRelationshipTx(ctx, tx, appCode, userAID, userBID, event.TargetUserID); err != nil {
|
if err := ensurePairHasNoActiveRelationshipTx(ctx, tx, appCode, userAID, userBID, event.TargetUserID); err != nil {
|
||||||
if err := insertConsumptionTx(ctx, tx, appCode, event.EventID, "skipped", "", "", "existing_relationship", nowMs); err != nil {
|
if err := insertConsumptionTx(ctx, tx, appCode, event.EventID, "skipped", "", "", "existing_relationship", nowMs); err != nil {
|
||||||
|
|||||||
@ -176,6 +176,101 @@ func TestListActiveIntimacyLeaderboardEntriesOrdersAndIncludesBothUsers(t *testi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConsumeGiftEventAddsIntimacyForNormalGiftWhenRelationshipExists(t *testing.T) {
|
||||||
|
schema := mysqlschema.New(t)
|
||||||
|
repo := New(schema.DB)
|
||||||
|
ctx := appcode.WithContext(context.Background(), appcode.Default)
|
||||||
|
nowMS := int64(1700000000000)
|
||||||
|
|
||||||
|
seedCPTestUser(t, schema.DB, 4201, "Normal Gift Alice", nowMS)
|
||||||
|
seedCPTestUser(t, schema.DB, 4202, "Normal Gift Bob", nowMS)
|
||||||
|
seedCPLevelRules(t, schema.DB, cpdomain.RelationTypeCP, []int64{0, 100, 300}, nowMS)
|
||||||
|
seedActiveCPRelationship(t, schema.DB, "normal_gift_cp", cpdomain.RelationTypeCP, 4201, 4202, 90, 1, nowMS)
|
||||||
|
|
||||||
|
result, err := repo.ConsumeGiftEvent(ctx, cpdomain.GiftEvent{
|
||||||
|
AppCode: appcode.Default,
|
||||||
|
EventID: "room_evt_normal_gift_cp",
|
||||||
|
RoomID: "room-normal",
|
||||||
|
RoomVersion: 3,
|
||||||
|
OccurredAtMS: nowMS + 1,
|
||||||
|
SenderUserID: 4201,
|
||||||
|
TargetUserID: 4202,
|
||||||
|
GiftID: "rose",
|
||||||
|
GiftCount: 1,
|
||||||
|
GiftValue: 37,
|
||||||
|
BillingReceiptID: "receipt-normal-gift-cp",
|
||||||
|
CommandID: "cmd-normal-gift-cp",
|
||||||
|
GiftTypeCode: "normal",
|
||||||
|
}, nowMS+2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("consume normal gift for active cp failed: %v", err)
|
||||||
|
}
|
||||||
|
if !result.Consumed || result.Relationship.RelationshipID != "normal_gift_cp" {
|
||||||
|
t.Fatalf("normal gift must update existing relationship: %+v", result)
|
||||||
|
}
|
||||||
|
if result.Relationship.IntimacyValue != 127 || result.Relationship.Level != 2 {
|
||||||
|
t.Fatalf("relationship intimacy/level mismatch: %+v", result.Relationship)
|
||||||
|
}
|
||||||
|
|
||||||
|
var status string
|
||||||
|
if err := schema.DB.QueryRowContext(ctx, `
|
||||||
|
SELECT status
|
||||||
|
FROM user_cp_gift_event_consumption
|
||||||
|
WHERE app_code = ? AND room_event_id = ?`,
|
||||||
|
appcode.Default, "room_evt_normal_gift_cp",
|
||||||
|
).Scan(&status); err != nil {
|
||||||
|
t.Fatalf("read gift consumption failed: %v", err)
|
||||||
|
}
|
||||||
|
if status != "relationship_intimacy_added" {
|
||||||
|
t.Fatalf("normal gift consumption status mismatch: %s", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConsumeGiftEventIgnoresNormalGiftWithoutRelationship(t *testing.T) {
|
||||||
|
schema := mysqlschema.New(t)
|
||||||
|
repo := New(schema.DB)
|
||||||
|
ctx := appcode.WithContext(context.Background(), appcode.Default)
|
||||||
|
nowMS := int64(1700000000000)
|
||||||
|
|
||||||
|
seedCPTestUser(t, schema.DB, 4301, "No CP Alice", nowMS)
|
||||||
|
seedCPTestUser(t, schema.DB, 4302, "No CP Bob", nowMS)
|
||||||
|
|
||||||
|
result, err := repo.ConsumeGiftEvent(ctx, cpdomain.GiftEvent{
|
||||||
|
AppCode: appcode.Default,
|
||||||
|
EventID: "room_evt_normal_no_cp",
|
||||||
|
RoomID: "room-normal",
|
||||||
|
RoomVersion: 3,
|
||||||
|
OccurredAtMS: nowMS + 1,
|
||||||
|
SenderUserID: 4301,
|
||||||
|
TargetUserID: 4302,
|
||||||
|
GiftID: "rose",
|
||||||
|
GiftCount: 1,
|
||||||
|
GiftValue: 37,
|
||||||
|
BillingReceiptID: "receipt-normal-no-cp",
|
||||||
|
CommandID: "cmd-normal-no-cp",
|
||||||
|
GiftTypeCode: "normal",
|
||||||
|
}, nowMS+2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("consume normal gift without cp failed: %v", err)
|
||||||
|
}
|
||||||
|
if result.Consumed {
|
||||||
|
t.Fatalf("normal gift without active relationship must be ignored: %+v", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
var applicationCount int64
|
||||||
|
if err := schema.DB.QueryRowContext(ctx, `
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM user_cp_applications
|
||||||
|
WHERE app_code = ? AND requester_user_id = ? AND target_user_id = ?`,
|
||||||
|
appcode.Default, 4301, 4302,
|
||||||
|
).Scan(&applicationCount); err != nil {
|
||||||
|
t.Fatalf("count cp applications failed: %v", err)
|
||||||
|
}
|
||||||
|
if applicationCount != 0 {
|
||||||
|
t.Fatalf("normal gift without relationship must not create application, got %d", applicationCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBreakRelationshipPrepareConfirmIsIdempotent(t *testing.T) {
|
func TestBreakRelationshipPrepareConfirmIsIdempotent(t *testing.T) {
|
||||||
schema := mysqlschema.New(t)
|
schema := mysqlschema.New(t)
|
||||||
repo := New(schema.DB)
|
repo := New(schema.DB)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user