merge: sync internal with main
Build docker and publish / build (20.15.1) (push) Failing after 8m22s
Build docker and publish / build (20.15.1) (push) Failing after 8m22s
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -6,7 +6,9 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
authlogic "github.com/perfect-panel/server/internal/logic/auth"
|
||||
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/uuidx"
|
||||
"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")
|
||||
}
|
||||
tool.DeepCopy(resp, u)
|
||||
resp.UseStatus = true
|
||||
|
||||
// 用家庭范围查设备,而不是只看当前用户自己的 UserDevices
|
||||
scopeHelper := newFamilyScopeHelper(l.ctx, l.svcCtx)
|
||||
@@ -65,6 +68,14 @@ func (l *QueryUserInfoLogic) QueryUserInfo() (resp *types.User, err error) {
|
||||
}
|
||||
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 为空时自动生成
|
||||
if resp.ReferCode == "" {
|
||||
resp.ReferCode = uuidx.UserInviteCode(u.Id)
|
||||
@@ -108,6 +119,49 @@ func (l *QueryUserInfoLogic) QueryUserInfo() (resp *types.User, err error) {
|
||||
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 {
|
||||
type familyRelation struct {
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,13 @@ package user
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/user"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/constant"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type QueryWithdrawalLogLogic struct {
|
||||
@@ -24,7 +28,49 @@ func NewQueryWithdrawalLogLogic(ctx context.Context, svcCtx *svc.ServiceContext)
|
||||
}
|
||||
|
||||
func (l *QueryWithdrawalLogLogic) QueryWithdrawalLog(req *types.QueryWithdrawalLogListRequest) (resp *types.QueryWithdrawalLogListResponse, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User)
|
||||
if !ok {
|
||||
l.Error("current user is not found in context")
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")
|
||||
}
|
||||
|
||||
return
|
||||
page := req.Page
|
||||
size := req.Size
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if size <= 0 {
|
||||
size = 10
|
||||
}
|
||||
|
||||
query := l.svcCtx.DB.WithContext(l.ctx).Model(&user.Withdrawal{}).Where("user_id = ?", u.Id)
|
||||
|
||||
var total int64
|
||||
if err = query.Count(&total).Error; err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count withdrawal logs failed: %v", err)
|
||||
}
|
||||
|
||||
var rows []user.Withdrawal
|
||||
if err = query.Order("id DESC").Limit(size).Offset((page - 1) * size).Find(&rows).Error; err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query withdrawal logs failed: %v", err)
|
||||
}
|
||||
|
||||
list := make([]types.WithdrawalLog, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
list = append(list, types.WithdrawalLog{
|
||||
Id: row.Id,
|
||||
UserId: row.UserId,
|
||||
Amount: row.Amount,
|
||||
Content: row.Content,
|
||||
Status: row.Status,
|
||||
Reason: row.Reason,
|
||||
CreatedAt: row.CreatedAt.UnixMilli(),
|
||||
UpdatedAt: row.UpdatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.QueryWithdrawalLogListResponse{
|
||||
List: list,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user