feat: 经理中心赠送天数可选并改由 Java 更新用户国家
- SendProps 新增 days 参数(限 30/60/90,缺省按新规则 30 天),VIP 赠送及贵族附属道具统一按所选天数发放 - ChangeUserCountry 不再直写 user_base_info,改调 Java other updateSelectiveById(只传 id+countryId)复用 country->user 锁顺序,避免与后台国家批量同步并发丢数据,写后强制清资料缓存 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
5296562391
commit
fb43e3e9fa
@ -141,6 +141,11 @@ func (g *Gateways) GetUserProfile(ctx context.Context, userID int64) (UserProfil
|
|||||||
return g.Profile.GetUserProfile(ctx, userID)
|
return g.Profile.GetUserProfile(ctx, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUserCountry 透传到 Java 用户国家更新接口。
|
||||||
|
func (g *Gateways) UpdateUserCountry(ctx context.Context, userID int64, countryID int64) error {
|
||||||
|
return g.Profile.UpdateUserCountry(ctx, userID, countryID)
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserRegion 透传到用户地区网关。
|
// GetUserRegion 透传到用户地区网关。
|
||||||
func (g *Gateways) GetUserRegion(ctx context.Context, userID int64, authorization string) (UserRegion, error) {
|
func (g *Gateways) GetUserRegion(ctx context.Context, userID int64, authorization string) (UserRegion, error) {
|
||||||
return g.Profile.GetUserRegion(ctx, userID, authorization)
|
return g.Profile.GetUserRegion(ctx, userID, authorization)
|
||||||
@ -400,6 +405,11 @@ func (g *ProfileGateway) GetUserProfile(ctx context.Context, userID int64) (User
|
|||||||
return g.client.GetUserProfile(ctx, userID)
|
return g.client.GetUserProfile(ctx, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUserCountry 通过 Java other 更新用户国家。
|
||||||
|
func (g *ProfileGateway) UpdateUserCountry(ctx context.Context, userID int64, countryID int64) error {
|
||||||
|
return g.client.UpdateUserCountry(ctx, userID, countryID)
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserRegion 查询用户当前地区。
|
// GetUserRegion 查询用户当前地区。
|
||||||
func (g *ProfileGateway) GetUserRegion(ctx context.Context, userID int64, authorization string) (UserRegion, error) {
|
func (g *ProfileGateway) GetUserRegion(ctx context.Context, userID int64, authorization string) (UserRegion, error) {
|
||||||
return g.client.GetUserRegion(ctx, userID, authorization)
|
return g.client.GetUserRegion(ctx, userID, authorization)
|
||||||
|
|||||||
@ -1034,6 +1034,36 @@ func (c *Client) GetUserProfile(ctx context.Context, userID int64) (UserProfile,
|
|||||||
return resp.Body, nil
|
return resp.Body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUserCountry 只提交稳定的用户 ID 和国家 ID,国家码、名称以及并发锁统一由 Java other 处理。
|
||||||
|
func (c *Client) UpdateUserCountry(ctx context.Context, userID int64, countryID int64) error {
|
||||||
|
if userID <= 0 {
|
||||||
|
return fmt.Errorf("userId is required")
|
||||||
|
}
|
||||||
|
if countryID <= 0 {
|
||||||
|
return fmt.Errorf("countryId is required")
|
||||||
|
}
|
||||||
|
endpoint := strings.TrimRight(c.cfg.Java.OtherBaseURL, "/") + "/user-profile/client/updateSelectiveById"
|
||||||
|
payload := struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
CountryID int64 `json:"countryId"`
|
||||||
|
}{
|
||||||
|
ID: userID,
|
||||||
|
CountryID: countryID,
|
||||||
|
}
|
||||||
|
var resp resultResponse[any]
|
||||||
|
if err := c.postJSON(ctx, endpoint, payload, nil, &resp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.Success != nil && !*resp.Success {
|
||||||
|
message := strings.TrimSpace(resp.Message)
|
||||||
|
if message == "" {
|
||||||
|
message = "user country update failed"
|
||||||
|
}
|
||||||
|
return errors.New(message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) GetUserRegion(ctx context.Context, userID int64, authorization string) (UserRegion, error) {
|
func (c *Client) GetUserRegion(ctx context.Context, userID int64, authorization string) (UserRegion, error) {
|
||||||
if userID <= 0 {
|
if userID <= 0 {
|
||||||
return UserRegion{}, fmt.Errorf("userId is required")
|
return UserRegion{}, fmt.Errorf("userId is required")
|
||||||
|
|||||||
@ -78,6 +78,32 @@ func TestResolveRegionCodeByCountryCodeUsesRegionConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateUserCountryPostsOnlyStableIDs(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost || r.URL.Path != "/user-profile/client/updateSelectiveById" {
|
||||||
|
t.Fatalf("request = %s %s, want POST /user-profile/client/updateSelectiveById", r.Method, r.URL.Path)
|
||||||
|
}
|
||||||
|
var payload map[string]json.RawMessage
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||||
|
t.Fatalf("decode request: %v", err)
|
||||||
|
}
|
||||||
|
if len(payload) != 2 || string(payload["id"]) != "2" || string(payload["countryId"]) != "11" {
|
||||||
|
t.Fatalf("payload = %s, want only id and countryId", payload)
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_, _ = w.Write([]byte(`{"success":true}`))
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client := New(config.Config{
|
||||||
|
HTTP: config.HTTPConfig{Timeout: time.Second},
|
||||||
|
Java: config.JavaConfig{OtherBaseURL: server.URL},
|
||||||
|
})
|
||||||
|
if err := client.UpdateUserCountry(context.Background(), 2, 11); err != nil {
|
||||||
|
t.Fatalf("UpdateUserCountry() error = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetRewardGroupDetailAcceptsDataWrapper(t *testing.T) {
|
func TestGetRewardGroupDetailAcceptsDataWrapper(t *testing.T) {
|
||||||
groupID := int64(2056981527522242600)
|
groupID := int64(2056981527522242600)
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@ -209,6 +209,10 @@ func (s *Service) SendProps(ctx context.Context, user AuthUser, req SendPropsReq
|
|||||||
if req.PropsID.Int64() <= 0 {
|
if req.PropsID.Int64() <= 0 {
|
||||||
return nil, badRequest("props_required", "propsId is required")
|
return nil, badRequest("props_required", "propsId is required")
|
||||||
}
|
}
|
||||||
|
days, err := normalizeGiftDays(req.Days)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if s.java == nil {
|
if s.java == nil {
|
||||||
return nil, serviceUnavailable("java_gateway_unavailable", "java gateway is unavailable")
|
return nil, serviceUnavailable("java_gateway_unavailable", "java gateway is unavailable")
|
||||||
}
|
}
|
||||||
@ -224,7 +228,7 @@ func (s *Service) SendProps(ctx context.Context, user AuthUser, req SendPropsReq
|
|||||||
prop, err := s.loadGiftableProps(ctx, req.PropsID.Int64())
|
prop, err := s.loadGiftableProps(ctx, req.PropsID.Int64())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isAppErrorCode(err, "prop_not_giftable") {
|
if isAppErrorCode(err, "prop_not_giftable") {
|
||||||
if resp, vipErr := s.sendVIPGift(ctx, user, target, req.PropsID.Int64()); vipErr != nil {
|
if resp, vipErr := s.sendVIPGift(ctx, user, target, req.PropsID.Int64(), days); vipErr != nil {
|
||||||
return nil, vipErr
|
return nil, vipErr
|
||||||
} else if resp != nil {
|
} else if resp != nil {
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -233,7 +237,6 @@ func (s *Service) SendProps(ctx context.Context, user AuthUser, req SendPropsReq
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
days := propsGiftDays(prop.Type)
|
|
||||||
if prop.Type == propsTypeBadge {
|
if prop.Type == propsTypeBadge {
|
||||||
if err := s.java.ActivateTemporaryBadge(ctx, req.AcceptUserID.Int64(), prop.ID, days); err != nil {
|
if err := s.java.ActivateTemporaryBadge(ctx, req.AcceptUserID.Int64(), prop.ID, days); err != nil {
|
||||||
return nil, serverError("send_badge_failed", err.Error())
|
return nil, serverError("send_badge_failed", err.Error())
|
||||||
@ -260,7 +263,7 @@ func (s *Service) SendProps(ctx context.Context, user AuthUser, req SendPropsReq
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, serverError("vip_ability_query_failed", err.Error())
|
return nil, serverError("vip_ability_query_failed", err.Error())
|
||||||
}
|
}
|
||||||
grants = append(grants, nobleVIPAccessoryGrants(ability)...)
|
grants = append(grants, nobleVIPAccessoryGrants(ability, days)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, grant := range grants {
|
for _, grant := range grants {
|
||||||
@ -393,6 +396,9 @@ func (s *Service) ChangeUserCountry(ctx context.Context, user AuthUser, req Chan
|
|||||||
if s.db == nil {
|
if s.db == nil {
|
||||||
return nil, serviceUnavailable("database_unavailable", "database is unavailable")
|
return nil, serviceUnavailable("database_unavailable", "database is unavailable")
|
||||||
}
|
}
|
||||||
|
if s.java == nil {
|
||||||
|
return nil, serviceUnavailable("java_gateway_unavailable", "java gateway is unavailable")
|
||||||
|
}
|
||||||
|
|
||||||
target, err := s.loadUserRowByID(ctx, req.UserID.Int64())
|
target, err := s.loadUserRowByID(ctx, req.UserID.Int64())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -409,24 +415,12 @@ func (s *Service) ChangeUserCountry(ctx context.Context, user AuthUser, req Chan
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只改国家三元组,不触碰昵称、头像等资料审核字段;清理 Java 资料缓存后 App/H5 会重新读到新国家。
|
// 国家写入统一交给 Java,复用 country -> user 锁顺序,避免与后台国家批量同步并发时漏数据。
|
||||||
updates := map[string]any{
|
if err := s.java.UpdateUserCountry(ctx, target.ID, country.ID); err != nil {
|
||||||
"country_id": country.ID,
|
|
||||||
"country_code": strings.ToUpper(strings.TrimSpace(country.AlphaTwo)),
|
|
||||||
"country_name": strings.TrimSpace(country.CountryName),
|
|
||||||
"update_time": time.Now(),
|
|
||||||
"update_user": user.UserID,
|
|
||||||
}
|
|
||||||
if err := s.db.WithContext(ctx).
|
|
||||||
Table("user_base_info").
|
|
||||||
Where("id = ? AND is_del = ?", target.ID, false).
|
|
||||||
Updates(updates).Error; err != nil {
|
|
||||||
return nil, serverError("user_country_update_failed", err.Error())
|
return nil, serverError("user_country_update_failed", err.Error())
|
||||||
}
|
}
|
||||||
if s.java != nil {
|
if err := s.java.RemoveUserProfileCacheAll(ctx, target.ID); err != nil {
|
||||||
if err := s.java.RemoveUserProfileCacheAll(ctx, target.ID); err != nil {
|
return nil, serverError("remove_profile_cache_failed", err.Error())
|
||||||
return nil, serverError("remove_profile_cache_failed", err.Error())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target.CountryID = country.ID
|
target.CountryID = country.ID
|
||||||
@ -797,13 +791,13 @@ type propsGrant struct {
|
|||||||
days int
|
days int
|
||||||
}
|
}
|
||||||
|
|
||||||
func nobleVIPAccessoryGrants(ability integration.PropsNobleVIPAbility) []propsGrant {
|
func nobleVIPAccessoryGrants(ability integration.PropsNobleVIPAbility, days int) []propsGrant {
|
||||||
grants := make([]propsGrant, 0, 4)
|
grants := make([]propsGrant, 0, 4)
|
||||||
appendIfPositive := func(propsID int64, typ string) {
|
appendIfPositive := func(propsID int64, typ string) {
|
||||||
if propsID <= 0 {
|
if propsID <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
grants = append(grants, propsGrant{propsID: propsID, typ: typ, days: defaultVIPGiftDays})
|
grants = append(grants, propsGrant{propsID: propsID, typ: typ, days: days})
|
||||||
}
|
}
|
||||||
appendIfPositive(int64(ability.CarID), propsTypeRide)
|
appendIfPositive(int64(ability.CarID), propsTypeRide)
|
||||||
appendIfPositive(int64(ability.AvatarFrameID), propsTypeAvatarFrame)
|
appendIfPositive(int64(ability.AvatarFrameID), propsTypeAvatarFrame)
|
||||||
@ -881,6 +875,19 @@ func propsGiftDays(propsType string) int {
|
|||||||
return defaultGiftDays
|
return defaultGiftDays
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeGiftDays(days int) (int, error) {
|
||||||
|
// days=0 兼容尚未升级的旧 H5,并按新规则默认赠送 30 天。
|
||||||
|
if days == 0 {
|
||||||
|
return defaultGiftDays, nil
|
||||||
|
}
|
||||||
|
switch days {
|
||||||
|
case giftDays30, giftDays60, giftDays90:
|
||||||
|
return days, nil
|
||||||
|
default:
|
||||||
|
return 0, badRequest("invalid_gift_days", "days must be one of 30, 60, 90")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func normalizePropsType(propsType string) string {
|
func normalizePropsType(propsType string) string {
|
||||||
return strings.ToUpper(strings.TrimSpace(propsType))
|
return strings.ToUpper(strings.TrimSpace(propsType))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,9 +26,16 @@ type fakeManagerGateway struct {
|
|||||||
unblockRequests []integration.UnblockAccountRequest
|
unblockRequests []integration.UnblockAccountRequest
|
||||||
switchIDs []int64
|
switchIDs []int64
|
||||||
cacheCleared []int64
|
cacheCleared []int64
|
||||||
|
countryUpdates []fakeCountryUpdate
|
||||||
|
countryUpdateErr error
|
||||||
badgeActivations []fakeBadgeActivation
|
badgeActivations []fakeBadgeActivation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fakeCountryUpdate struct {
|
||||||
|
userID int64
|
||||||
|
countryID int64
|
||||||
|
}
|
||||||
|
|
||||||
type fakeBadgeActivation struct {
|
type fakeBadgeActivation struct {
|
||||||
userID int64
|
userID int64
|
||||||
badgeID int64
|
badgeID int64
|
||||||
@ -39,6 +46,14 @@ func (f *fakeManagerGateway) GetUserProfile(context.Context, int64) (integration
|
|||||||
return integration.UserProfile{}, nil
|
return integration.UserProfile{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fakeManagerGateway) UpdateUserCountry(_ context.Context, userID int64, countryID int64) error {
|
||||||
|
if f.countryUpdateErr != nil {
|
||||||
|
return f.countryUpdateErr
|
||||||
|
}
|
||||||
|
f.countryUpdates = append(f.countryUpdates, fakeCountryUpdate{userID: userID, countryID: countryID})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *fakeManagerGateway) GetUserProfileByAccountOne(context.Context, string) (integration.UserProfile, error) {
|
func (f *fakeManagerGateway) GetUserProfileByAccountOne(context.Context, string) (integration.UserProfile, error) {
|
||||||
return integration.UserProfile{}, nil
|
return integration.UserProfile{}, nil
|
||||||
}
|
}
|
||||||
@ -788,7 +803,7 @@ func TestListCountriesReturnsOpenCountries(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestChangeUserCountryUpdatesCountryAndClearsCache(t *testing.T) {
|
func TestChangeUserCountryDelegatesToJavaAndClearsCache(t *testing.T) {
|
||||||
service, fake := newManagerCenterTestService(t)
|
service, fake := newManagerCenterTestService(t)
|
||||||
db := service.db.(*gorm.DB)
|
db := service.db.(*gorm.DB)
|
||||||
seedManager(t, db, 1)
|
seedManager(t, db, 1)
|
||||||
@ -805,13 +820,16 @@ func TestChangeUserCountryUpdatesCountryAndClearsCache(t *testing.T) {
|
|||||||
if !resp.Success || resp.User.CountryCode != "AE" || resp.User.CountryName != "United Arab Emirates" {
|
if !resp.Success || resp.User.CountryCode != "AE" || resp.User.CountryName != "United Arab Emirates" {
|
||||||
t.Fatalf("resp = %+v, want changed country", resp)
|
t.Fatalf("resp = %+v, want changed country", resp)
|
||||||
}
|
}
|
||||||
|
if len(fake.countryUpdates) != 1 || fake.countryUpdates[0] != (fakeCountryUpdate{userID: 2, countryID: 11}) {
|
||||||
|
t.Fatalf("countryUpdates = %+v, want user 2 country 11", fake.countryUpdates)
|
||||||
|
}
|
||||||
|
|
||||||
var row userBaseInfoRow
|
var row userBaseInfoRow
|
||||||
if err := db.Where("id = ?", int64(2)).First(&row).Error; err != nil {
|
if err := db.Where("id = ?", int64(2)).First(&row).Error; err != nil {
|
||||||
t.Fatalf("load user: %v", err)
|
t.Fatalf("load user: %v", err)
|
||||||
}
|
}
|
||||||
if row.CountryID != 11 || row.CountryCode != "AE" || row.CountryName != "United Arab Emirates" || row.UpdateUser != 1 {
|
if row.CountryID != 10 || row.CountryCode != "SA" || row.CountryName != "Saudi Arabia" {
|
||||||
t.Fatalf("user row = %+v, want AE country and manager updater", row)
|
t.Fatalf("user row = %+v, want Go service not to write country directly", row)
|
||||||
}
|
}
|
||||||
if !equalInt64Slice(fake.cacheCleared, []int64{2}) {
|
if !equalInt64Slice(fake.cacheCleared, []int64{2}) {
|
||||||
t.Fatalf("cacheCleared = %+v, want [2]", fake.cacheCleared)
|
t.Fatalf("cacheCleared = %+v, want [2]", fake.cacheCleared)
|
||||||
|
|||||||
@ -28,8 +28,12 @@ const (
|
|||||||
vipGiftOrigin = "VIP_PURCHASE"
|
vipGiftOrigin = "VIP_PURCHASE"
|
||||||
vipGiftOriginDesc = "VIP purchase"
|
vipGiftOriginDesc = "VIP purchase"
|
||||||
|
|
||||||
defaultGiftDays = 7
|
defaultGiftDays = 30
|
||||||
defaultVIPGiftDays = 30
|
defaultVIPGiftDays = 30
|
||||||
|
// 经理赠送统一限制为运营确认的三个有效期,避免客户端提交任意天数。
|
||||||
|
giftDays30 = 30
|
||||||
|
giftDays60 = 60
|
||||||
|
giftDays90 = 90
|
||||||
// 经理中心 VIP 赠送按当前运营范围放开 VIP1-VIP5;列表和发放校验共用该上限,避免展示可选但提交失败。
|
// 经理中心 VIP 赠送按当前运营范围放开 VIP1-VIP5;列表和发放校验共用该上限,避免展示可选但提交失败。
|
||||||
maxGiftableVIPLevel = 5
|
maxGiftableVIPLevel = 5
|
||||||
|
|
||||||
@ -60,6 +64,7 @@ type managerDB interface {
|
|||||||
|
|
||||||
type managerGateway interface {
|
type managerGateway interface {
|
||||||
GetUserProfile(ctx context.Context, userID int64) (integration.UserProfile, error)
|
GetUserProfile(ctx context.Context, userID int64) (integration.UserProfile, error)
|
||||||
|
UpdateUserCountry(ctx context.Context, userID int64, countryID int64) error
|
||||||
GetUserProfileByAccountOne(ctx context.Context, account string) (integration.UserProfile, error)
|
GetUserProfileByAccountOne(ctx context.Context, account string) (integration.UserProfile, error)
|
||||||
MapUserTotalRecharge(ctx context.Context, userIDs []int64) (map[int64][]integration.UserTotalRecharge, error)
|
MapUserTotalRecharge(ctx context.Context, userIDs []int64) (map[int64][]integration.UserTotalRecharge, error)
|
||||||
ExistsFreightSeller(ctx context.Context, userID int64) (bool, error)
|
ExistsFreightSeller(ctx context.Context, userID int64) (bool, error)
|
||||||
@ -178,8 +183,9 @@ type PropsView struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SendPropsRequest struct {
|
type SendPropsRequest struct {
|
||||||
AcceptUserID ID `json:"acceptUserId"`
|
AcceptUserID ID `json:"acceptUserId"`
|
||||||
PropsID ID `json:"propsId"`
|
PropsID ID `json:"propsId"`
|
||||||
|
Days int `json:"days"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SendPropsResponse struct {
|
type SendPropsResponse struct {
|
||||||
|
|||||||
@ -79,7 +79,7 @@ func (s *Service) listVIPProps(ctx context.Context, user AuthUser) (*PropsListRe
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) sendVIPGift(ctx context.Context, manager AuthUser, target userBaseInfoRow, configID int64) (*SendPropsResponse, error) {
|
func (s *Service) sendVIPGift(ctx context.Context, manager AuthUser, target userBaseInfoRow, configID int64, days int) (*SendPropsResponse, error) {
|
||||||
sysOrigin := normalizeSysOrigin(target.OriginSys)
|
sysOrigin := normalizeSysOrigin(target.OriginSys)
|
||||||
config, found, err := s.loadVIPConfigByID(ctx, sysOrigin, configID)
|
config, found, err := s.loadVIPConfigByID(ctx, sysOrigin, configID)
|
||||||
if err != nil || !found {
|
if err != nil || !found {
|
||||||
@ -91,9 +91,8 @@ func (s *Service) sendVIPGift(ctx context.Context, manager AuthUser, target user
|
|||||||
if config.Level < 1 || config.Level > maxGiftableVIPLevel {
|
if config.Level < 1 || config.Level > maxGiftableVIPLevel {
|
||||||
return nil, badRequest("vip_not_giftable", "vip level is invalid")
|
return nil, badRequest("vip_not_giftable", "vip level is invalid")
|
||||||
}
|
}
|
||||||
if config.DurationDays <= 0 {
|
// 经理选择的赠送天数覆盖 VIP 售卖配置时长,订单、状态和附属权益保持同一到期口径。
|
||||||
config.DurationDays = defaultVIPGiftDays
|
config.DurationDays = days
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
orderID, err := utils.NextID()
|
orderID, err := utils.NextID()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user