合并 internal → main: 退款/提现/激活/CI 全面优化 #4
@@ -21,8 +21,10 @@ type (
|
|||||||
InviteManageRecord {
|
InviteManageRecord {
|
||||||
InviterId int64 `json:"inviter_id"`
|
InviterId int64 `json:"inviter_id"`
|
||||||
InviterIdentifier string `json:"inviter_identifier"`
|
InviterIdentifier string `json:"inviter_identifier"`
|
||||||
|
InviterDeviceNo string `json:"inviter_device_no"`
|
||||||
InviteeId int64 `json:"invitee_id"`
|
InviteeId int64 `json:"invitee_id"`
|
||||||
InviteeIdentifier string `json:"invitee_identifier"`
|
InviteeIdentifier string `json:"invitee_identifier"`
|
||||||
|
InviteeDeviceNo string `json:"invitee_device_no"`
|
||||||
InviteeAvatar string `json:"invitee_avatar"`
|
InviteeAvatar string `json:"invitee_avatar"`
|
||||||
InviteeEnable bool `json:"invitee_enable"`
|
InviteeEnable bool `json:"invitee_enable"`
|
||||||
InvitedAt int64 `json:"invited_at"`
|
InvitedAt int64 `json:"invited_at"`
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
modellog "github.com/perfect-panel/server/internal/model/log"
|
modellog "github.com/perfect-panel/server/internal/model/log"
|
||||||
|
"github.com/perfect-panel/server/pkg/tool"
|
||||||
"github.com/perfect-panel/server/pkg/xerr"
|
"github.com/perfect-panel/server/pkg/xerr"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -188,26 +189,35 @@ func fillGiftBenefits(ctx context.Context, db *gorm.DB, benefits map[int64]Benef
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func QueryIdentifiers(ctx context.Context, db *gorm.DB, userIds []int64) (map[int64]string, error) {
|
type DeviceIdentifier struct {
|
||||||
identifiers := make(map[int64]string, len(userIds))
|
Identifier string
|
||||||
|
DeviceNo string
|
||||||
|
}
|
||||||
|
|
||||||
|
func QueryDeviceIdentifiers(ctx context.Context, db *gorm.DB, userIds []int64) (map[int64]DeviceIdentifier, error) {
|
||||||
|
identifiers := make(map[int64]DeviceIdentifier, len(userIds))
|
||||||
if len(userIds) == 0 {
|
if len(userIds) == 0 {
|
||||||
return identifiers, nil
|
return identifiers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type identifierRow struct {
|
type identifierRow struct {
|
||||||
UserId int64 `gorm:"column:user_id"`
|
UserId int64 `gorm:"column:user_id"`
|
||||||
|
DeviceId int64 `gorm:"column:device_id"`
|
||||||
Identifier string `gorm:"column:identifier"`
|
Identifier string `gorm:"column:identifier"`
|
||||||
}
|
}
|
||||||
var rows []identifierRow
|
var rows []identifierRow
|
||||||
if err := db.WithContext(ctx).
|
if err := db.WithContext(ctx).
|
||||||
Table("user_auth_methods uam").
|
Table("user_device ud").
|
||||||
Select("uam.user_id, uam.auth_identifier as identifier").
|
Select("ud.user_id, ud.id as device_id, ud.identifier as identifier").
|
||||||
Joins("JOIN (SELECT user_id, MIN(id) AS id FROM user_auth_methods WHERE user_id IN ? GROUP BY user_id) first_uam ON first_uam.id = uam.id", userIds).
|
Joins("JOIN (SELECT user_id, MIN(id) AS id FROM user_device WHERE user_id IN ? GROUP BY user_id) first_ud ON first_ud.id = ud.id", userIds).
|
||||||
Scan(&rows).Error; err != nil {
|
Scan(&rows).Error; err != nil {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query user identifiers failed: %v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query user device identifiers failed: %v", err)
|
||||||
}
|
}
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
identifiers[row.UserId] = row.Identifier
|
identifiers[row.UserId] = DeviceIdentifier{
|
||||||
|
Identifier: row.Identifier,
|
||||||
|
DeviceNo: tool.DeviceIdToHash(row.DeviceId),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return identifiers, nil
|
return identifiers, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/DATA-DOG/go-sqlmock"
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
"github.com/perfect-panel/server/pkg/tool"
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -73,6 +74,39 @@ func TestQueryBenefitsKeepsDirectInviteeGift(t *testing.T) {
|
|||||||
assertBenefitsExpectations(t, mock)
|
assertBenefitsExpectations(t, mock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestQueryDeviceIdentifiersReturnsDeviceIdentifierAndNo(t *testing.T) {
|
||||||
|
db, mock, cleanup := newBenefitsTestDB(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
mock.ExpectQuery("FROM user_device ud").
|
||||||
|
WithArgs(int64(100), int64(200)).
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"user_id", "device_id", "identifier"}).
|
||||||
|
AddRow(100, 11, "inviter-hash").
|
||||||
|
AddRow(200, 22, "invitee-hash"))
|
||||||
|
|
||||||
|
identifiers, err := QueryDeviceIdentifiers(context.Background(), db, []int64{100, 200})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("QueryDeviceIdentifiers returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
inviter := identifiers[100]
|
||||||
|
if inviter.Identifier != "inviter-hash" {
|
||||||
|
t.Fatalf("inviter identifier = %q, want inviter-hash", inviter.Identifier)
|
||||||
|
}
|
||||||
|
if inviter.DeviceNo != tool.DeviceIdToHash(11) {
|
||||||
|
t.Fatalf("inviter device no = %q, want %q", inviter.DeviceNo, tool.DeviceIdToHash(11))
|
||||||
|
}
|
||||||
|
|
||||||
|
invitee := identifiers[200]
|
||||||
|
if invitee.Identifier != "invitee-hash" {
|
||||||
|
t.Fatalf("invitee identifier = %q, want invitee-hash", invitee.Identifier)
|
||||||
|
}
|
||||||
|
if invitee.DeviceNo != tool.DeviceIdToHash(22) {
|
||||||
|
t.Fatalf("invitee device no = %q, want %q", invitee.DeviceNo, tool.DeviceIdToHash(22))
|
||||||
|
}
|
||||||
|
assertBenefitsExpectations(t, mock)
|
||||||
|
}
|
||||||
|
|
||||||
func newBenefitsTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock, func()) {
|
func newBenefitsTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock, func()) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func (l *GetInviteManageListLogic) GetInviteManageList(req *types.GetInviteManag
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
identifiers, err := QueryIdentifiers(l.ctx, l.svcCtx.DB, userIds)
|
devices, err := QueryDeviceIdentifiers(l.ctx, l.svcCtx.DB, userIds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -78,9 +78,11 @@ func (l *GetInviteManageListLogic) GetInviteManageList(req *types.GetInviteManag
|
|||||||
benefit := benefits[row.InviteeId]
|
benefit := benefits[row.InviteeId]
|
||||||
list = append(list, types.InviteManageRecord{
|
list = append(list, types.InviteManageRecord{
|
||||||
InviterId: row.InviterId,
|
InviterId: row.InviterId,
|
||||||
InviterIdentifier: identifiers[row.InviterId],
|
InviterIdentifier: devices[row.InviterId].Identifier,
|
||||||
|
InviterDeviceNo: devices[row.InviterId].DeviceNo,
|
||||||
InviteeId: row.InviteeId,
|
InviteeId: row.InviteeId,
|
||||||
InviteeIdentifier: identifiers[row.InviteeId],
|
InviteeIdentifier: devices[row.InviteeId].Identifier,
|
||||||
|
InviteeDeviceNo: devices[row.InviteeId].DeviceNo,
|
||||||
InviteeAvatar: row.InviteeAvatar,
|
InviteeAvatar: row.InviteeAvatar,
|
||||||
InviteeEnable: row.InviteeEnable,
|
InviteeEnable: row.InviteeEnable,
|
||||||
InvitedAt: row.InvitedAt,
|
InvitedAt: row.InvitedAt,
|
||||||
|
|||||||
@@ -1845,8 +1845,10 @@ type InviteConfig struct {
|
|||||||
type InviteManageRecord struct {
|
type InviteManageRecord struct {
|
||||||
InviterId int64 `json:"inviter_id"`
|
InviterId int64 `json:"inviter_id"`
|
||||||
InviterIdentifier string `json:"inviter_identifier"`
|
InviterIdentifier string `json:"inviter_identifier"`
|
||||||
|
InviterDeviceNo string `json:"inviter_device_no"`
|
||||||
InviteeId int64 `json:"invitee_id"`
|
InviteeId int64 `json:"invitee_id"`
|
||||||
InviteeIdentifier string `json:"invitee_identifier"`
|
InviteeIdentifier string `json:"invitee_identifier"`
|
||||||
|
InviteeDeviceNo string `json:"invitee_device_no"`
|
||||||
InviteeAvatar string `json:"invitee_avatar"`
|
InviteeAvatar string `json:"invitee_avatar"`
|
||||||
InviteeEnable bool `json:"invitee_enable"`
|
InviteeEnable bool `json:"invitee_enable"`
|
||||||
InvitedAt int64 `json:"invited_at"`
|
InvitedAt int64 `json:"invited_at"`
|
||||||
|
|||||||
Reference in New Issue
Block a user