增加范围以及首充版本识别
This commit is contained in:
parent
ee5b4f5eaf
commit
f1b781e3be
@ -235,14 +235,15 @@ type OfficialNoticeCustomizeRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RewardGroupItem struct {
|
type RewardGroupItem struct {
|
||||||
ID Int64Value `json:"id"`
|
ID Int64Value `json:"id"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Name string `json:"name"`
|
DetailType string `json:"detailType"`
|
||||||
Content string `json:"content"`
|
Name string `json:"name"`
|
||||||
Quantity Int64Value `json:"quantity"`
|
Content string `json:"content"`
|
||||||
Cover string `json:"cover"`
|
Quantity Int64Value `json:"quantity"`
|
||||||
SourceURL string `json:"sourceUrl"`
|
Cover string `json:"cover"`
|
||||||
Remark string `json:"remark"`
|
SourceURL string `json:"sourceUrl"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RewardGroupDetail struct {
|
type RewardGroupDetail struct {
|
||||||
|
|||||||
@ -41,6 +41,9 @@ func (s *Service) ProcessRechargeEvent(ctx context.Context, event RechargeEvent)
|
|||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if !isFirstRechargeRewardAppVersionSupported(normalized.AppVersion) {
|
||||||
|
return &ProcessRechargeResponse{Processed: false, Reason: "app_version_not_supported"}, nil
|
||||||
|
}
|
||||||
if !isAcceptedPaymentMethod(normalized.PaymentMethod, normalized.PayPlatform) {
|
if !isAcceptedPaymentMethod(normalized.PaymentMethod, normalized.PayPlatform) {
|
||||||
return &ProcessRechargeResponse{Processed: false, Reason: "unsupported_payment_method"}, nil
|
return &ProcessRechargeResponse{Processed: false, Reason: "unsupported_payment_method"}, nil
|
||||||
}
|
}
|
||||||
@ -115,6 +118,7 @@ func (s *Service) normalizeRechargeEvent(event RechargeEvent) (normalizedRecharg
|
|||||||
|
|
||||||
payPlatform := strings.ToUpper(strings.TrimSpace(event.PayPlatform))
|
payPlatform := strings.ToUpper(strings.TrimSpace(event.PayPlatform))
|
||||||
paymentMethod := strings.ToUpper(strings.TrimSpace(event.PaymentMethod))
|
paymentMethod := strings.ToUpper(strings.TrimSpace(event.PaymentMethod))
|
||||||
|
appVersion := normalizeRechargeAppVersion(event)
|
||||||
sourceOrderID := firstNonEmpty(event.SourceOrderID.String(), event.OrderID.String())
|
sourceOrderID := firstNonEmpty(event.SourceOrderID.String(), event.OrderID.String())
|
||||||
eventID := strings.TrimSpace(event.EventID)
|
eventID := strings.TrimSpace(event.EventID)
|
||||||
if eventID == "" && sourceOrderID != "" {
|
if eventID == "" && sourceOrderID != "" {
|
||||||
@ -135,6 +139,7 @@ func (s *Service) normalizeRechargeEvent(event RechargeEvent) (normalizedRecharg
|
|||||||
Currency: firstNonEmpty(strings.ToUpper(strings.TrimSpace(event.Currency)), "USD"),
|
Currency: firstNonEmpty(strings.ToUpper(strings.TrimSpace(event.Currency)), "USD"),
|
||||||
PayPlatform: payPlatform,
|
PayPlatform: payPlatform,
|
||||||
PaymentMethod: paymentMethod,
|
PaymentMethod: paymentMethod,
|
||||||
|
AppVersion: appVersion,
|
||||||
SourceOrderID: sourceOrderID,
|
SourceOrderID: sourceOrderID,
|
||||||
OccurredAt: occurredAt,
|
OccurredAt: occurredAt,
|
||||||
}, nil
|
}, nil
|
||||||
@ -357,6 +362,34 @@ func isAcceptedPaymentMethod(paymentMethod string, _ string) bool {
|
|||||||
return paymentMethod == paymentMethodGoogle || paymentMethod == paymentMethodThirdParty
|
return paymentMethod == paymentMethodGoogle || paymentMethod == paymentMethodThirdParty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeRechargeAppVersion(event RechargeEvent) string {
|
||||||
|
return firstNonEmpty(
|
||||||
|
event.AppVersion.String(),
|
||||||
|
event.ClientVersion.String(),
|
||||||
|
event.Version.String(),
|
||||||
|
event.ReqVersion.String(),
|
||||||
|
rechargePayloadString(event.Payload, "appVersion", "clientVersion", "version", "reqVersion"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rechargePayloadString(payload json.RawMessage, keys ...string) string {
|
||||||
|
if len(payload) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var values map[string]any
|
||||||
|
if err := json.Unmarshal(payload, &values); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
if value, ok := values[key]; ok {
|
||||||
|
if text := strings.TrimSpace(fmt.Sprint(value)); text != "" && text != "<nil>" {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func firstPositiveAmountCents(values ...string) int64 {
|
func firstPositiveAmountCents(values ...string) int64 {
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
cents, err := parseAmountCents(value)
|
cents, err := parseAmountCents(value)
|
||||||
|
|||||||
@ -120,7 +120,7 @@ func rewardItemsFromGroup(detail integration.RewardGroupDetail) []RewardItem {
|
|||||||
for _, item := range detail.RewardConfigList {
|
for _, item := range detail.RewardConfigList {
|
||||||
items = append(items, RewardItem{
|
items = append(items, RewardItem{
|
||||||
ID: int64(item.ID),
|
ID: int64(item.ID),
|
||||||
Type: strings.TrimSpace(item.Type),
|
Type: rewardItemDisplayType(item),
|
||||||
Name: strings.TrimSpace(item.Name),
|
Name: strings.TrimSpace(item.Name),
|
||||||
Content: strings.TrimSpace(item.Content),
|
Content: strings.TrimSpace(item.Content),
|
||||||
Quantity: int64(item.Quantity),
|
Quantity: int64(item.Quantity),
|
||||||
@ -131,6 +131,29 @@ func rewardItemsFromGroup(detail integration.RewardGroupDetail) []RewardItem {
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rewardItemDisplayType(item integration.RewardGroupItem) string {
|
||||||
|
itemType := strings.ToUpper(strings.TrimSpace(item.Type))
|
||||||
|
detailType := strings.ToUpper(strings.TrimSpace(item.DetailType))
|
||||||
|
if itemType == "PROPS" {
|
||||||
|
if detailType != "" && detailType != "PROPS" {
|
||||||
|
return detailType
|
||||||
|
}
|
||||||
|
if isVipRewardItem(item) {
|
||||||
|
return "NOBLE_VIP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return itemType
|
||||||
|
}
|
||||||
|
|
||||||
|
func isVipRewardItem(item integration.RewardGroupItem) bool {
|
||||||
|
name := strings.ToUpper(strings.TrimSpace(item.Name))
|
||||||
|
remark := strings.ToUpper(strings.TrimSpace(item.Remark))
|
||||||
|
return strings.Contains(name, "VIP") ||
|
||||||
|
strings.Contains(name, "NOBLE") ||
|
||||||
|
strings.Contains(remark, "VIP") ||
|
||||||
|
strings.Contains(remark, "NOBLE")
|
||||||
|
}
|
||||||
|
|
||||||
func formatDateTime(t time.Time) string {
|
func formatDateTime(t time.Time) string {
|
||||||
if t.IsZero() {
|
if t.IsZero() {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@ -50,14 +50,15 @@ func TestSaveConfigReturnsVipCoverFromSourceURL(t *testing.T) {
|
|||||||
Name: "VIP礼包",
|
Name: "VIP礼包",
|
||||||
RewardConfigList: []integration.RewardGroupItem{
|
RewardConfigList: []integration.RewardGroupItem{
|
||||||
{
|
{
|
||||||
ID: integration.Int64Value(11),
|
ID: integration.Int64Value(11),
|
||||||
Type: "PROPS",
|
Type: "PROPS",
|
||||||
Name: "VIP 1",
|
DetailType: "NOBLE_VIP",
|
||||||
Content: "2049402425177018400",
|
Name: "VIP 1",
|
||||||
Quantity: integration.Int64Value(30),
|
Content: "2049402425177018400",
|
||||||
Cover: "",
|
Quantity: integration.Int64Value(30),
|
||||||
SourceURL: "https://example.com/vip-short-badge.png",
|
Cover: "",
|
||||||
Remark: "30 天",
|
SourceURL: "https://example.com/vip-short-badge.png",
|
||||||
|
Remark: "30 天",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -78,6 +79,59 @@ func TestSaveConfigReturnsVipCoverFromSourceURL(t *testing.T) {
|
|||||||
if got := resp.LevelConfigs[0].RewardItems[0].Cover; got != "https://example.com/vip-short-badge.png" {
|
if got := resp.LevelConfigs[0].RewardItems[0].Cover; got != "https://example.com/vip-short-badge.png" {
|
||||||
t.Fatalf("vip cover = %q", got)
|
t.Fatalf("vip cover = %q", got)
|
||||||
}
|
}
|
||||||
|
if got := resp.LevelConfigs[0].RewardItems[0].Type; got != "NOBLE_VIP" {
|
||||||
|
t.Fatalf("vip type = %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRewardItemsReturnConcretePropsType(t *testing.T) {
|
||||||
|
service, gateway, _ := newTestService(t)
|
||||||
|
gateway.groups[3001] = integration.RewardGroupDetail{
|
||||||
|
ID: integration.Int64Value(3001),
|
||||||
|
Name: "道具礼包",
|
||||||
|
RewardConfigList: []integration.RewardGroupItem{
|
||||||
|
{
|
||||||
|
ID: integration.Int64Value(21),
|
||||||
|
Type: "PROPS",
|
||||||
|
DetailType: "RIDE",
|
||||||
|
Name: "Luxury sports car",
|
||||||
|
Content: "2044620024357908482",
|
||||||
|
Quantity: integration.Int64Value(7),
|
||||||
|
Cover: "https://example.com/ride.png",
|
||||||
|
Remark: "Luxury sports car",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: integration.Int64Value(22),
|
||||||
|
Type: "PROPS",
|
||||||
|
Name: "VIP 3",
|
||||||
|
Content: "2050200000000000003",
|
||||||
|
Quantity: integration.Int64Value(3),
|
||||||
|
Cover: "https://example.com/vip3.png",
|
||||||
|
Remark: "VIP 3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := service.SaveConfig(context.Background(), mustDecodeSaveConfigRequest(t, `{
|
||||||
|
"sysOrigin": "LIKEI",
|
||||||
|
"enabled": true,
|
||||||
|
"levelConfigs": [
|
||||||
|
{"level": 1, "rechargeAmount": "9.99", "rewardGroupId": "3001", "rewardGroupName": "道具礼包", "enabled": true}
|
||||||
|
]
|
||||||
|
}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SaveConfig() error = %v", err)
|
||||||
|
}
|
||||||
|
items := resp.LevelConfigs[0].RewardItems
|
||||||
|
if len(items) != 2 {
|
||||||
|
t.Fatalf("reward items = %+v", items)
|
||||||
|
}
|
||||||
|
if items[0].Type != "RIDE" {
|
||||||
|
t.Fatalf("ride type = %q", items[0].Type)
|
||||||
|
}
|
||||||
|
if items[1].Type != "NOBLE_VIP" {
|
||||||
|
t.Fatalf("vip fallback type = %q", items[1].Type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProcessRechargeEventFiltersAndGrantsOnce(t *testing.T) {
|
func TestProcessRechargeEventFiltersAndGrantsOnce(t *testing.T) {
|
||||||
@ -108,6 +162,7 @@ func TestProcessRechargeEventFiltersAndGrantsOnce(t *testing.T) {
|
|||||||
"amountCents": "999",
|
"amountCents": "999",
|
||||||
"payPlatform": "APPLE",
|
"payPlatform": "APPLE",
|
||||||
"paymentMethod": "APPLE",
|
"paymentMethod": "APPLE",
|
||||||
|
"appVersion": "1.5.0",
|
||||||
"sourceOrderId": "1"
|
"sourceOrderId": "1"
|
||||||
}`))
|
}`))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -124,6 +179,7 @@ func TestProcessRechargeEventFiltersAndGrantsOnce(t *testing.T) {
|
|||||||
"amountCents": "499",
|
"amountCents": "499",
|
||||||
"payPlatform": "GOOGLE",
|
"payPlatform": "GOOGLE",
|
||||||
"paymentMethod": "GOOGLE",
|
"paymentMethod": "GOOGLE",
|
||||||
|
"appVersion": "1.5.0",
|
||||||
"sourceOrderId": "LOW"
|
"sourceOrderId": "LOW"
|
||||||
}`))
|
}`))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -140,6 +196,7 @@ func TestProcessRechargeEventFiltersAndGrantsOnce(t *testing.T) {
|
|||||||
"amountCents": "999",
|
"amountCents": "999",
|
||||||
"payPlatform": "GOOGLE",
|
"payPlatform": "GOOGLE",
|
||||||
"paymentMethod": "GOOGLE",
|
"paymentMethod": "GOOGLE",
|
||||||
|
"appVersion": "1.5.0",
|
||||||
"sourceOrderId": "OK",
|
"sourceOrderId": "OK",
|
||||||
"occurredAt": "2026-05-21T12:00:00Z"
|
"occurredAt": "2026-05-21T12:00:00Z"
|
||||||
}`))
|
}`))
|
||||||
@ -166,6 +223,7 @@ func TestProcessRechargeEventFiltersAndGrantsOnce(t *testing.T) {
|
|||||||
"amountCents": "999",
|
"amountCents": "999",
|
||||||
"payPlatform": "GOOGLE",
|
"payPlatform": "GOOGLE",
|
||||||
"paymentMethod": "GOOGLE",
|
"paymentMethod": "GOOGLE",
|
||||||
|
"appVersion": "1.5.0",
|
||||||
"sourceOrderId": "OK"
|
"sourceOrderId": "OK"
|
||||||
}`))
|
}`))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -185,6 +243,7 @@ func TestProcessRechargeEventFiltersAndGrantsOnce(t *testing.T) {
|
|||||||
"amountCents": "999",
|
"amountCents": "999",
|
||||||
"payPlatform": "CUSTOM_PAY",
|
"payPlatform": "CUSTOM_PAY",
|
||||||
"paymentMethod": "THIRD_PARTY",
|
"paymentMethod": "THIRD_PARTY",
|
||||||
|
"appVersion": "1.5.1",
|
||||||
"sourceOrderId": "CUSTOM"
|
"sourceOrderId": "CUSTOM"
|
||||||
}`))
|
}`))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -204,6 +263,7 @@ func TestProcessRechargeEventFiltersAndGrantsOnce(t *testing.T) {
|
|||||||
"amountCents": "999",
|
"amountCents": "999",
|
||||||
"payPlatform": "CUSTOM_GOOGLE_GATEWAY",
|
"payPlatform": "CUSTOM_GOOGLE_GATEWAY",
|
||||||
"paymentMethod": "GOOGLE",
|
"paymentMethod": "GOOGLE",
|
||||||
|
"appVersion": "1.5.1",
|
||||||
"sourceOrderId": "CUSTOM_GOOGLE"
|
"sourceOrderId": "CUSTOM_GOOGLE"
|
||||||
}`))
|
}`))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -225,6 +285,108 @@ func TestProcessRechargeEventFiltersAndGrantsOnce(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProcessRechargeEventStartsCountingAtSupportedAppVersion(t *testing.T) {
|
||||||
|
service, gateway, db := newTestService(t)
|
||||||
|
gateway.groups[1001] = testRewardGroup(1001, "首冲礼包")
|
||||||
|
|
||||||
|
_, err := service.SaveConfig(context.Background(), mustDecodeSaveConfigRequest(t, `{
|
||||||
|
"sysOrigin": "LIKEI",
|
||||||
|
"enabled": true,
|
||||||
|
"levelConfigs": [
|
||||||
|
{"level": 1, "rechargeAmount": "5.00", "rewardGroupId": "1001", "rewardGroupName": "首冲礼包", "enabled": true}
|
||||||
|
]
|
||||||
|
}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SaveConfig() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVersion, err := service.ProcessRechargeEvent(context.Background(), mustDecodeRechargeEvent(t, `{
|
||||||
|
"eventId": "RECHARGE_SUCCESS:GOOGLE:OLD_VERSION",
|
||||||
|
"sysOrigin": "LIKEI",
|
||||||
|
"userId": "123",
|
||||||
|
"amountCents": "999",
|
||||||
|
"payPlatform": "GOOGLE",
|
||||||
|
"paymentMethod": "GOOGLE",
|
||||||
|
"appVersion": "1.4.9",
|
||||||
|
"sourceOrderId": "OLD_VERSION"
|
||||||
|
}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("old version ProcessRechargeEvent() error = %v", err)
|
||||||
|
}
|
||||||
|
if oldVersion.Processed || oldVersion.Reason != "app_version_not_supported" {
|
||||||
|
t.Fatalf("old version response = %+v", oldVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
missingVersion, err := service.ProcessRechargeEvent(context.Background(), mustDecodeRechargeEvent(t, `{
|
||||||
|
"eventId": "RECHARGE_SUCCESS:GOOGLE:MISSING_VERSION",
|
||||||
|
"sysOrigin": "LIKEI",
|
||||||
|
"userId": "123",
|
||||||
|
"amountCents": "999",
|
||||||
|
"payPlatform": "GOOGLE",
|
||||||
|
"paymentMethod": "GOOGLE",
|
||||||
|
"sourceOrderId": "MISSING_VERSION"
|
||||||
|
}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("missing version ProcessRechargeEvent() error = %v", err)
|
||||||
|
}
|
||||||
|
if missingVersion.Processed || missingVersion.Reason != "app_version_not_supported" {
|
||||||
|
t.Fatalf("missing version response = %+v", missingVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
if err := db.Model(&model.FirstRechargeRewardGrantRecord{}).Count(&count).Error; err != nil {
|
||||||
|
t.Fatalf("count grant records after unsupported versions: %v", err)
|
||||||
|
}
|
||||||
|
if count != 0 {
|
||||||
|
t.Fatalf("grant record count after unsupported versions = %d", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
supported, err := service.ProcessRechargeEvent(context.Background(), mustDecodeRechargeEvent(t, `{
|
||||||
|
"eventId": "RECHARGE_SUCCESS:GOOGLE:SUPPORTED_VERSION",
|
||||||
|
"sysOrigin": "LIKEI",
|
||||||
|
"userId": "123",
|
||||||
|
"amountCents": "999",
|
||||||
|
"payPlatform": "GOOGLE",
|
||||||
|
"paymentMethod": "GOOGLE",
|
||||||
|
"appVersion": "1.5.0",
|
||||||
|
"sourceOrderId": "SUPPORTED_VERSION"
|
||||||
|
}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("supported version ProcessRechargeEvent() error = %v", err)
|
||||||
|
}
|
||||||
|
if !supported.Processed || supported.Status != statusSuccess {
|
||||||
|
t.Fatalf("supported version response = %+v", supported)
|
||||||
|
}
|
||||||
|
if err := db.Model(&model.FirstRechargeRewardGrantRecord{}).Count(&count).Error; err != nil {
|
||||||
|
t.Fatalf("count grant records after supported version: %v", err)
|
||||||
|
}
|
||||||
|
if count != 1 {
|
||||||
|
t.Fatalf("grant record count after supported version = %d", count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFirstRechargeRewardAppVersionSupported(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
appVersion string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{name: "missing", appVersion: "", want: false},
|
||||||
|
{name: "below", appVersion: "1.4.9", want: false},
|
||||||
|
{name: "equal", appVersion: "1.5.0", want: true},
|
||||||
|
{name: "short equal", appVersion: "1.5", want: true},
|
||||||
|
{name: "above", appVersion: "1.5.1", want: true},
|
||||||
|
{name: "suffix", appVersion: "1.5.0+100", want: true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := isFirstRechargeRewardAppVersionSupported(tt.appVersion); got != tt.want {
|
||||||
|
t.Fatalf("isFirstRechargeRewardAppVersionSupported(%q) = %v, want %v", tt.appVersion, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newTestService(t *testing.T) (*Service, *fakeGateway, *gorm.DB) {
|
func newTestService(t *testing.T) (*Service, *fakeGateway, *gorm.DB) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
|
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
|
||||||
@ -274,12 +436,13 @@ func testRewardGroup(id int64, name string) integration.RewardGroupDetail {
|
|||||||
Name: name,
|
Name: name,
|
||||||
RewardConfigList: []integration.RewardGroupItem{
|
RewardConfigList: []integration.RewardGroupItem{
|
||||||
{
|
{
|
||||||
ID: integration.Int64Value(11),
|
ID: integration.Int64Value(11),
|
||||||
Type: "PROPS",
|
Type: "PROPS",
|
||||||
Name: name + "奖励",
|
DetailType: "RIDE",
|
||||||
Content: "10001",
|
Name: name + "奖励",
|
||||||
Quantity: integration.Int64Value(1),
|
Content: "10001",
|
||||||
Cover: "https://example.com/reward.png",
|
Quantity: integration.Int64Value(1),
|
||||||
|
Cover: "https://example.com/reward.png",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -176,6 +176,10 @@ type RechargeEvent struct {
|
|||||||
Currency string `json:"currency"`
|
Currency string `json:"currency"`
|
||||||
PayPlatform string `json:"payPlatform"`
|
PayPlatform string `json:"payPlatform"`
|
||||||
PaymentMethod string `json:"paymentMethod"`
|
PaymentMethod string `json:"paymentMethod"`
|
||||||
|
AppVersion flexibleString `json:"appVersion"`
|
||||||
|
ClientVersion flexibleString `json:"clientVersion"`
|
||||||
|
Version flexibleString `json:"version"`
|
||||||
|
ReqVersion flexibleString `json:"reqVersion"`
|
||||||
SourceOrderID flexibleString `json:"sourceOrderId"`
|
SourceOrderID flexibleString `json:"sourceOrderId"`
|
||||||
OrderID flexibleString `json:"orderId"`
|
OrderID flexibleString `json:"orderId"`
|
||||||
OccurredAt flexibleString `json:"occurredAt"`
|
OccurredAt flexibleString `json:"occurredAt"`
|
||||||
@ -219,6 +223,7 @@ type normalizedRechargeEvent struct {
|
|||||||
Currency string
|
Currency string
|
||||||
PayPlatform string
|
PayPlatform string
|
||||||
PaymentMethod string
|
PaymentMethod string
|
||||||
|
AppVersion string
|
||||||
SourceOrderID string
|
SourceOrderID string
|
||||||
OccurredAt time.Time
|
OccurredAt time.Time
|
||||||
}
|
}
|
||||||
|
|||||||
75
internal/service/firstrechargereward/version.go
Normal file
75
internal/service/firstrechargereward/version.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package firstrechargereward
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const firstRechargeRewardMinAppVersion = "1.5.0"
|
||||||
|
|
||||||
|
func isFirstRechargeRewardAppVersionSupported(appVersion string) bool {
|
||||||
|
appVersion = strings.TrimSpace(appVersion)
|
||||||
|
if appVersion == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return compareAppVersion(appVersion, firstRechargeRewardMinAppVersion) >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareAppVersion(left, right string) int {
|
||||||
|
leftParts := parseAppVersion(left)
|
||||||
|
rightParts := parseAppVersion(right)
|
||||||
|
maxLen := len(leftParts)
|
||||||
|
if len(rightParts) > maxLen {
|
||||||
|
maxLen = len(rightParts)
|
||||||
|
}
|
||||||
|
for i := 0; i < maxLen; i++ {
|
||||||
|
var lv, rv int
|
||||||
|
if i < len(leftParts) {
|
||||||
|
lv = leftParts[i]
|
||||||
|
}
|
||||||
|
if i < len(rightParts) {
|
||||||
|
rv = rightParts[i]
|
||||||
|
}
|
||||||
|
if lv > rv {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if lv < rv {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAppVersion(version string) []int {
|
||||||
|
version = strings.TrimSpace(version)
|
||||||
|
if version == "" {
|
||||||
|
return []int{0}
|
||||||
|
}
|
||||||
|
parts := strings.Split(version, ".")
|
||||||
|
result := make([]int, 0, len(parts))
|
||||||
|
for _, part := range parts {
|
||||||
|
part = strings.TrimSpace(part)
|
||||||
|
if part == "" {
|
||||||
|
result = append(result, 0)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
digits := strings.Builder{}
|
||||||
|
for _, r := range part {
|
||||||
|
if r < '0' || r > '9' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
digits.WriteRune(r)
|
||||||
|
}
|
||||||
|
if digits.Len() == 0 {
|
||||||
|
result = append(result, 0)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
value, err := strconv.Atoi(digits.String())
|
||||||
|
if err != nil {
|
||||||
|
result = append(result, 0)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, value)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user