refactor(admin): remove auth migration reports
This commit is contained in:
@@ -39,10 +39,7 @@ type AdminService interface {
|
||||
// codeType is optional - pass empty string to return all types.
|
||||
// Also returns totalRecharged (sum of all positive balance top-ups).
|
||||
GetUserBalanceHistory(ctx context.Context, userID int64, page, pageSize int, codeType string) ([]RedeemCode, int64, float64, error)
|
||||
ListAuthIdentityMigrationReports(ctx context.Context, reportType string, page, pageSize int) ([]AuthIdentityMigrationReport, int64, error)
|
||||
GetAuthIdentityMigrationReportSummary(ctx context.Context) (*AuthIdentityMigrationReportSummary, error)
|
||||
BindUserAuthIdentity(ctx context.Context, userID int64, input AdminBindAuthIdentityInput) (*AdminBoundAuthIdentity, error)
|
||||
ResolveAuthIdentityMigrationReport(ctx context.Context, reportID, resolvedByUserID int64, resolutionNote string) (*AuthIdentityMigrationReport, error)
|
||||
|
||||
// Group management
|
||||
ListGroups(ctx context.Context, page, pageSize int, platform, status, search string, isExclusive *bool, sortBy, sortOrder string) ([]Group, int64, error)
|
||||
@@ -137,24 +134,6 @@ type UpdateUserInput struct {
|
||||
GroupRates map[int64]*float64
|
||||
}
|
||||
|
||||
type AuthIdentityMigrationReport struct {
|
||||
ID int64 `json:"id"`
|
||||
ReportType string `json:"report_type"`
|
||||
ReportKey string `json:"report_key"`
|
||||
Details map[string]any `json:"details"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ResolvedAt *time.Time `json:"resolved_at,omitempty"`
|
||||
ResolvedByUserID *int64 `json:"resolved_by_user_id,omitempty"`
|
||||
ResolutionNote string `json:"resolution_note,omitempty"`
|
||||
}
|
||||
|
||||
type AuthIdentityMigrationReportSummary struct {
|
||||
Total int64 `json:"total"`
|
||||
OpenTotal int64 `json:"open_total"`
|
||||
ResolvedTotal int64 `json:"resolved_total"`
|
||||
ByType map[string]int64 `json:"by_type"`
|
||||
}
|
||||
|
||||
type AdminBindAuthIdentityInput struct {
|
||||
ProviderType string
|
||||
ProviderKey string
|
||||
@@ -874,152 +853,6 @@ func (s *adminServiceImpl) GetUserBalanceHistory(ctx context.Context, userID int
|
||||
return codes, result.Total, totalRecharged, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) ListAuthIdentityMigrationReports(ctx context.Context, reportType string, page, pageSize int) ([]AuthIdentityMigrationReport, int64, error) {
|
||||
db, err := s.adminSQLDB()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
reportType = strings.TrimSpace(reportType)
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
var total int64
|
||||
if err := db.QueryRowContext(ctx, `
|
||||
SELECT COUNT(*)
|
||||
FROM auth_identity_migration_reports
|
||||
WHERE ($1 = '' OR report_type = $1)`,
|
||||
reportType,
|
||||
).Scan(&total); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
rows, err := db.QueryContext(ctx, `
|
||||
SELECT id, report_type, report_key, details, created_at, resolved_at, resolved_by_user_id, resolution_note
|
||||
FROM auth_identity_migration_reports
|
||||
WHERE ($1 = '' OR report_type = $1)
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT $2 OFFSET $3`,
|
||||
reportType,
|
||||
pageSize,
|
||||
offset,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
reports := make([]AuthIdentityMigrationReport, 0)
|
||||
for rows.Next() {
|
||||
report, scanErr := scanAuthIdentityMigrationReport(rows)
|
||||
if scanErr != nil {
|
||||
return nil, 0, scanErr
|
||||
}
|
||||
reports = append(reports, report)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return reports, total, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) GetAuthIdentityMigrationReportSummary(ctx context.Context) (*AuthIdentityMigrationReportSummary, error) {
|
||||
db, err := s.adminSQLDB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := db.QueryContext(ctx, `
|
||||
SELECT
|
||||
report_type,
|
||||
COUNT(*),
|
||||
SUM(CASE WHEN resolved_at IS NULL THEN 1 ELSE 0 END),
|
||||
SUM(CASE WHEN resolved_at IS NOT NULL THEN 1 ELSE 0 END)
|
||||
FROM auth_identity_migration_reports
|
||||
GROUP BY report_type
|
||||
ORDER BY report_type ASC`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
summary := &AuthIdentityMigrationReportSummary{
|
||||
ByType: make(map[string]int64),
|
||||
}
|
||||
for rows.Next() {
|
||||
var reportType string
|
||||
var count int64
|
||||
var openCount int64
|
||||
var resolvedCount int64
|
||||
if err := rows.Scan(&reportType, &count, &openCount, &resolvedCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
summary.ByType[reportType] = count
|
||||
summary.Total += count
|
||||
summary.OpenTotal += openCount
|
||||
summary.ResolvedTotal += resolvedCount
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) ResolveAuthIdentityMigrationReport(ctx context.Context, reportID, resolvedByUserID int64, resolutionNote string) (*AuthIdentityMigrationReport, error) {
|
||||
if reportID <= 0 {
|
||||
return nil, infraerrors.BadRequest("INVALID_INPUT", "report id must be greater than 0")
|
||||
}
|
||||
if resolvedByUserID <= 0 {
|
||||
return nil, infraerrors.BadRequest("INVALID_INPUT", "resolved_by_user_id must be greater than 0")
|
||||
}
|
||||
|
||||
db, err := s.adminSQLDB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
result, err := db.ExecContext(ctx, `
|
||||
UPDATE auth_identity_migration_reports
|
||||
SET
|
||||
resolved_at = COALESCE(resolved_at, $2),
|
||||
resolved_by_user_id = COALESCE(resolved_by_user_id, $3),
|
||||
resolution_note = $4
|
||||
WHERE id = $1`,
|
||||
reportID,
|
||||
now,
|
||||
resolvedByUserID,
|
||||
strings.TrimSpace(resolutionNote),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if affected == 0 {
|
||||
return nil, infraerrors.NotFound("AUTH_IDENTITY_MIGRATION_REPORT_NOT_FOUND", "auth identity migration report not found")
|
||||
}
|
||||
|
||||
row := db.QueryRowContext(ctx, `
|
||||
SELECT id, report_type, report_key, details, created_at, resolved_at, resolved_by_user_id, resolution_note
|
||||
FROM auth_identity_migration_reports
|
||||
WHERE id = $1`,
|
||||
reportID,
|
||||
)
|
||||
report, err := scanAuthIdentityMigrationReport(row)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &report, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) BindUserAuthIdentity(ctx context.Context, userID int64, input AdminBindAuthIdentityInput) (*AdminBoundAuthIdentity, error) {
|
||||
if userID <= 0 {
|
||||
return nil, infraerrors.BadRequest("INVALID_INPUT", "user_id must be greater than 0")
|
||||
@@ -1252,44 +1085,6 @@ func cloneAdminAuthIdentityMetadata(input map[string]any) map[string]any {
|
||||
return out
|
||||
}
|
||||
|
||||
func scanAuthIdentityMigrationReport(scanner interface{ Scan(dest ...any) error }) (AuthIdentityMigrationReport, error) {
|
||||
var (
|
||||
report AuthIdentityMigrationReport
|
||||
details []byte
|
||||
resolvedAt sql.NullTime
|
||||
resolvedByUserID sql.NullInt64
|
||||
resolutionNote sql.NullString
|
||||
)
|
||||
if err := scanner.Scan(
|
||||
&report.ID,
|
||||
&report.ReportType,
|
||||
&report.ReportKey,
|
||||
&details,
|
||||
&report.CreatedAt,
|
||||
&resolvedAt,
|
||||
&resolvedByUserID,
|
||||
&resolutionNote,
|
||||
); err != nil {
|
||||
return AuthIdentityMigrationReport{}, err
|
||||
}
|
||||
report.Details = map[string]any{}
|
||||
if len(details) > 0 {
|
||||
if err := json.Unmarshal(details, &report.Details); err != nil {
|
||||
return AuthIdentityMigrationReport{}, err
|
||||
}
|
||||
}
|
||||
if resolvedAt.Valid {
|
||||
report.ResolvedAt = &resolvedAt.Time
|
||||
}
|
||||
if resolvedByUserID.Valid {
|
||||
report.ResolvedByUserID = &resolvedByUserID.Int64
|
||||
}
|
||||
if resolutionNote.Valid {
|
||||
report.ResolutionNote = resolutionNote.String
|
||||
}
|
||||
return report, nil
|
||||
}
|
||||
|
||||
// Group management implementations
|
||||
func (s *adminServiceImpl) ListGroups(ctx context.Context, page, pageSize int, platform, status, search string, isExclusive *bool, sortBy, sortOrder string) ([]Group, int64, error) {
|
||||
params := pagination.PaginationParams{Page: page, PageSize: pageSize, SortBy: sortBy, SortOrder: sortOrder}
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
dbent "github.com/Wei-Shaw/sub2api/ent"
|
||||
"github.com/Wei-Shaw/sub2api/ent/enttest"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"entgo.io/ent/dialect"
|
||||
entsql "entgo.io/ent/dialect/sql"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func newAdminServiceMigrationReportTestClient(t *testing.T) *dbent.Client {
|
||||
t.Helper()
|
||||
|
||||
db, err := sql.Open("sqlite", "file:admin_service_migration_reports?mode=memory&cache=shared&_fk=1")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = db.Close() })
|
||||
|
||||
_, err = db.Exec("PRAGMA foreign_keys = ON")
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = db.Exec(`CREATE TABLE auth_identity_migration_reports (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
report_type TEXT NOT NULL,
|
||||
report_key TEXT NOT NULL,
|
||||
details TEXT NOT NULL DEFAULT '{}',
|
||||
created_at DATETIME NOT NULL,
|
||||
resolved_at DATETIME NULL,
|
||||
resolved_by_user_id INTEGER NULL,
|
||||
resolution_note TEXT NOT NULL DEFAULT ''
|
||||
)`)
|
||||
require.NoError(t, err)
|
||||
|
||||
drv := entsql.OpenDB(dialect.SQLite, db)
|
||||
client := enttest.NewClient(t, enttest.WithOptions(dbent.Driver(drv)))
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
return client
|
||||
}
|
||||
|
||||
func TestAdminServiceListAuthIdentityMigrationReports(t *testing.T) {
|
||||
client := newAdminServiceMigrationReportTestClient(t)
|
||||
driver, ok := client.Driver().(*entsql.Driver)
|
||||
require.True(t, ok)
|
||||
|
||||
now := time.Now().UTC()
|
||||
_, err := driver.DB().ExecContext(context.Background(), `
|
||||
INSERT INTO auth_identity_migration_reports (report_type, report_key, details, created_at)
|
||||
VALUES
|
||||
($1, $2, $3, $4),
|
||||
($5, $6, $7, $8)`,
|
||||
"oidc_synthetic_email_requires_manual_recovery", "u-1", `{"user_id":1}`, now,
|
||||
"wechat_provider_key_conflict", "u-2", `{"user_id":2}`, now.Add(-time.Minute),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
svc := &adminServiceImpl{entClient: client}
|
||||
reports, total, err := svc.ListAuthIdentityMigrationReports(context.Background(), "oidc_synthetic_email_requires_manual_recovery", 1, 20)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), total)
|
||||
require.Len(t, reports, 1)
|
||||
require.Equal(t, "oidc_synthetic_email_requires_manual_recovery", reports[0].ReportType)
|
||||
require.Equal(t, float64(1), reports[0].Details["user_id"])
|
||||
}
|
||||
|
||||
func TestAdminServiceGetAuthIdentityMigrationReportSummary(t *testing.T) {
|
||||
client := newAdminServiceMigrationReportTestClient(t)
|
||||
driver, ok := client.Driver().(*entsql.Driver)
|
||||
require.True(t, ok)
|
||||
|
||||
now := time.Now().UTC()
|
||||
_, err := driver.DB().ExecContext(context.Background(), `
|
||||
INSERT INTO auth_identity_migration_reports (report_type, report_key, details, created_at)
|
||||
VALUES
|
||||
($1, $2, $3, $4),
|
||||
($5, $6, $7, $8),
|
||||
($9, $10, $11, $12)`,
|
||||
"oidc_synthetic_email_requires_manual_recovery", "u-1", `{"user_id":1}`, now,
|
||||
"wechat_provider_key_conflict", "u-2", `{"user_id":2}`, now.Add(-time.Minute),
|
||||
"wechat_provider_key_conflict", "u-3", `{"user_id":3}`, now.Add(-2*time.Minute),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
svc := &adminServiceImpl{entClient: client}
|
||||
summary, err := svc.GetAuthIdentityMigrationReportSummary(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(3), summary.Total)
|
||||
require.Equal(t, int64(3), summary.OpenTotal)
|
||||
require.Zero(t, summary.ResolvedTotal)
|
||||
require.Equal(t, int64(1), summary.ByType["oidc_synthetic_email_requires_manual_recovery"])
|
||||
require.Equal(t, int64(2), summary.ByType["wechat_provider_key_conflict"])
|
||||
}
|
||||
|
||||
func TestAdminServiceResolveAuthIdentityMigrationReport(t *testing.T) {
|
||||
client := newAdminServiceMigrationReportTestClient(t)
|
||||
driver, ok := client.Driver().(*entsql.Driver)
|
||||
require.True(t, ok)
|
||||
|
||||
now := time.Now().UTC()
|
||||
_, err := driver.DB().ExecContext(context.Background(), `
|
||||
INSERT INTO auth_identity_migration_reports (report_type, report_key, details, created_at)
|
||||
VALUES ($1, $2, $3, $4)`,
|
||||
"oidc_synthetic_email_requires_manual_recovery", "u-1", `{"user_id":1}`, now,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
svc := &adminServiceImpl{entClient: client}
|
||||
report, err := svc.ResolveAuthIdentityMigrationReport(context.Background(), 1, 99, "resolved by admin binding")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, report.ResolvedAt)
|
||||
require.NotNil(t, report.ResolvedByUserID)
|
||||
require.Equal(t, int64(99), *report.ResolvedByUserID)
|
||||
require.Equal(t, "resolved by admin binding", report.ResolutionNote)
|
||||
|
||||
summary, err := svc.GetAuthIdentityMigrationReportSummary(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, summary.OpenTotal)
|
||||
require.Equal(t, int64(1), summary.ResolvedTotal)
|
||||
}
|
||||
Reference in New Issue
Block a user