feat: add userinfo bind-email trial use status
Build docker and publish / build (20.15.1) (push) Successful in 9m2s
Build docker and publish / build (20.15.1) (push) Successful in 9m2s
This commit is contained in:
+1
-1
@@ -27,6 +27,7 @@ type (
|
|||||||
EnableLoginNotify bool `json:"enable_login_notify"`
|
EnableLoginNotify bool `json:"enable_login_notify"`
|
||||||
EnableSubscribeNotify bool `json:"enable_subscribe_notify"`
|
EnableSubscribeNotify bool `json:"enable_subscribe_notify"`
|
||||||
EnableTradeNotify bool `json:"enable_trade_notify"`
|
EnableTradeNotify bool `json:"enable_trade_notify"`
|
||||||
|
UseStatus bool `json:"use_status"` // Whether to show the "bind email to get free trial" prompt
|
||||||
AuthMethods []UserAuthMethod `json:"auth_methods"`
|
AuthMethods []UserAuthMethod `json:"auth_methods"`
|
||||||
UserDevices []UserDevice `json:"user_devices"`
|
UserDevices []UserDevice `json:"user_devices"`
|
||||||
Rules []string `json:"rules"`
|
Rules []string `json:"rules"`
|
||||||
@@ -1004,4 +1005,3 @@ type (
|
|||||||
ConfigSnapshot map[string]interface{} `json:"config_snapshot,omitempty"`
|
ConfigSnapshot map[string]interface{} `json:"config_snapshot,omitempty"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
authlogic "github.com/perfect-panel/server/internal/logic/auth"
|
||||||
logicCommon "github.com/perfect-panel/server/internal/logic/common"
|
logicCommon "github.com/perfect-panel/server/internal/logic/common"
|
||||||
|
modelOrder "github.com/perfect-panel/server/internal/model/order"
|
||||||
"github.com/perfect-panel/server/pkg/constant"
|
"github.com/perfect-panel/server/pkg/constant"
|
||||||
"github.com/perfect-panel/server/pkg/uuidx"
|
"github.com/perfect-panel/server/pkg/uuidx"
|
||||||
"github.com/perfect-panel/server/pkg/xerr"
|
"github.com/perfect-panel/server/pkg/xerr"
|
||||||
@@ -44,6 +46,7 @@ func (l *QueryUserInfoLogic) QueryUserInfo() (resp *types.User, err error) {
|
|||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")
|
||||||
}
|
}
|
||||||
tool.DeepCopy(resp, u)
|
tool.DeepCopy(resp, u)
|
||||||
|
resp.UseStatus = true
|
||||||
|
|
||||||
// 用家庭范围查设备,而不是只看当前用户自己的 UserDevices
|
// 用家庭范围查设备,而不是只看当前用户自己的 UserDevices
|
||||||
scopeHelper := newFamilyScopeHelper(l.ctx, l.svcCtx)
|
scopeHelper := newFamilyScopeHelper(l.ctx, l.svcCtx)
|
||||||
@@ -65,6 +68,14 @@ func (l *QueryUserInfoLogic) QueryUserInfo() (resp *types.User, err error) {
|
|||||||
}
|
}
|
||||||
resp.UserDevices = userDevices
|
resp.UserDevices = userDevices
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useStatus, useStatusErr := l.resolveBindEmailTrialUseStatus(u.Id, scopeUserIds)
|
||||||
|
if useStatusErr != nil {
|
||||||
|
l.Errorw("resolve bind email trial use status failed", logger.Field("user_id", u.Id), logger.Field("error", useStatusErr.Error()))
|
||||||
|
} else {
|
||||||
|
resp.UseStatus = useStatus
|
||||||
|
}
|
||||||
|
|
||||||
// refer_code 为空时自动生成
|
// refer_code 为空时自动生成
|
||||||
if resp.ReferCode == "" {
|
if resp.ReferCode == "" {
|
||||||
resp.ReferCode = uuidx.UserInviteCode(u.Id)
|
resp.ReferCode = uuidx.UserInviteCode(u.Id)
|
||||||
@@ -108,6 +119,49 @@ func (l *QueryUserInfoLogic) QueryUserInfo() (resp *types.User, err error) {
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveBindEmailTrialUseStatus determines whether userinfo should show the
|
||||||
|
// "bind email to get free trial" prompt. `true` means show the prompt.
|
||||||
|
func (l *QueryUserInfoLogic) resolveBindEmailTrialUseStatus(currentUserId int64, scopeUserIds []int64) (bool, error) {
|
||||||
|
if len(scopeUserIds) == 0 {
|
||||||
|
scopeUserIds = []int64{currentUserId}
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasBoundEmailCount int64
|
||||||
|
if err := l.svcCtx.DB.WithContext(l.ctx).
|
||||||
|
Model(&user.AuthMethods{}).
|
||||||
|
Where("user_id IN ? AND auth_type = ? AND auth_identifier != ''", scopeUserIds, "email").
|
||||||
|
Count(&hasBoundEmailCount).Error; err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasPurchaseCount int64
|
||||||
|
if err := l.svcCtx.DB.WithContext(l.ctx).
|
||||||
|
Model(&modelOrder.Order{}).
|
||||||
|
Where("user_id IN ? AND type IN ? AND status IN ?", scopeUserIds, []int64{1, 2}, []int64{2, 5}).
|
||||||
|
Count(&hasPurchaseCount).Error; err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hasTrial := false
|
||||||
|
registerCfg := l.svcCtx.Config.Register
|
||||||
|
if authlogic.IsTrialConfigReady(registerCfg) && registerCfg.TrialSubscribe > 0 {
|
||||||
|
var hasTrialCount int64
|
||||||
|
if err := l.svcCtx.DB.WithContext(l.ctx).
|
||||||
|
Model(&user.Subscribe{}).
|
||||||
|
Where("user_id IN ? AND subscribe_id = ?", scopeUserIds, registerCfg.TrialSubscribe).
|
||||||
|
Count(&hasTrialCount).Error; err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
hasTrial = hasTrialCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return shouldShowBindEmailTrialPrompt(hasBoundEmailCount > 0, hasPurchaseCount > 0, hasTrial), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldShowBindEmailTrialPrompt(hasBoundEmail, hasPurchased, hasTrial bool) bool {
|
||||||
|
return !hasBoundEmail && !hasPurchased && !hasTrial
|
||||||
|
}
|
||||||
|
|
||||||
func (l *QueryUserInfoLogic) fillFamilyContext(resp *types.User, userId int64) *user.AuthMethods {
|
func (l *QueryUserInfoLogic) fillFamilyContext(resp *types.User, userId int64) *user.AuthMethods {
|
||||||
type familyRelation struct {
|
type familyRelation struct {
|
||||||
FamilyId int64
|
FamilyId int64
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestShouldShowBindEmailTrialPrompt(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
hasBoundEmail bool
|
||||||
|
hasPurchased bool
|
||||||
|
hasTrial bool
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "new user should see prompt",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bound email should hide prompt",
|
||||||
|
hasBoundEmail: true,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "paid purchase should hide prompt",
|
||||||
|
hasPurchased: true,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trial claimed should hide prompt",
|
||||||
|
hasTrial: true,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bound email and purchase should hide prompt",
|
||||||
|
hasBoundEmail: true,
|
||||||
|
hasPurchased: true,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bound email and trial should hide prompt",
|
||||||
|
hasBoundEmail: true,
|
||||||
|
hasTrial: true,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "purchase and trial should hide prompt",
|
||||||
|
hasPurchased: true,
|
||||||
|
hasTrial: true,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all blockers should hide prompt",
|
||||||
|
hasBoundEmail: true,
|
||||||
|
hasPurchased: true,
|
||||||
|
hasTrial: true,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := shouldShowBindEmailTrialPrompt(tt.hasBoundEmail, tt.hasPurchased, tt.hasTrial)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Fatalf("shouldShowBindEmailTrialPrompt(%v, %v, %v) = %v, want %v", tt.hasBoundEmail, tt.hasPurchased, tt.hasTrial, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3278,6 +3278,7 @@ type User struct {
|
|||||||
EnableLoginNotify bool `json:"enable_login_notify"`
|
EnableLoginNotify bool `json:"enable_login_notify"`
|
||||||
EnableSubscribeNotify bool `json:"enable_subscribe_notify"`
|
EnableSubscribeNotify bool `json:"enable_subscribe_notify"`
|
||||||
EnableTradeNotify bool `json:"enable_trade_notify"`
|
EnableTradeNotify bool `json:"enable_trade_notify"`
|
||||||
|
UseStatus bool `json:"use_status"`
|
||||||
AuthMethods []UserAuthMethod `json:"auth_methods"`
|
AuthMethods []UserAuthMethod `json:"auth_methods"`
|
||||||
UserDevices []UserDevice `json:"user_devices"`
|
UserDevices []UserDevice `json:"user_devices"`
|
||||||
Rules []string `json:"rules"`
|
Rules []string `json:"rules"`
|
||||||
|
|||||||
Reference in New Issue
Block a user