a617d39a4a
SaaS 侧人工审核后才创建 publish job,desktop 不再做二次审核:移除 manual/waiting_user/parked/from_parked 状态机与 LeaseFromParked 查询, desktop client 只执行发布并新增"发布管理"页(待发布队列 / 历史 / 再次 发送)。同时抽离 @geo/publisher-platforms 共享适配器包、新增 Redis-based desktop_presence 与 publish_record_support,刷新 admin-web 发布弹窗与 媒体库;plan A / spec 文档同步口径。
473 lines
12 KiB
Go
473 lines
12 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
|
|
"github.com/geo-platform/tenant-api/internal/tenant/repository/generated"
|
|
)
|
|
|
|
type DesktopAccount struct {
|
|
DesktopID uuid.UUID
|
|
TenantID int64
|
|
WorkspaceID int64
|
|
UserID int64
|
|
ClientID *uuid.UUID
|
|
Platform string
|
|
PlatformUID string
|
|
AccountFingerprint *string
|
|
DisplayName string
|
|
AvatarURL *string
|
|
Health string
|
|
VerifiedAt *time.Time
|
|
Tags []string
|
|
SyncVersion int64
|
|
DeletedAt *time.Time
|
|
DeleteRequestedAt *time.Time
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
type UpsertDesktopAccountParams struct {
|
|
DesktopID uuid.UUID
|
|
TenantID int64
|
|
WorkspaceID int64
|
|
UserID int64
|
|
ClientID uuid.UUID
|
|
Platform string
|
|
PlatformUID string
|
|
AccountFingerprint *string
|
|
DisplayName string
|
|
AvatarURL *string
|
|
Health string
|
|
VerifiedAt *time.Time
|
|
Tags []string
|
|
IfSyncVersion *int64
|
|
}
|
|
|
|
type PatchDesktopAccountParams struct {
|
|
DesktopID uuid.UUID
|
|
WorkspaceID int64
|
|
DisplayName *string
|
|
Health *string
|
|
VerifiedAt *time.Time
|
|
Tags *[]string
|
|
IfSyncVersion int64
|
|
}
|
|
|
|
type DesktopAccountRepository interface {
|
|
ListByUser(ctx context.Context, workspaceID, userID int64) ([]DesktopAccount, error)
|
|
GetByDesktopID(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error)
|
|
GetByIdentity(ctx context.Context, workspaceID int64, platform, platformUID string) (*DesktopAccount, error)
|
|
Upsert(ctx context.Context, params UpsertDesktopAccountParams) (*DesktopAccount, error)
|
|
Patch(ctx context.Context, params PatchDesktopAccountParams) (*DesktopAccount, error)
|
|
Tombstone(ctx context.Context, desktopID uuid.UUID, workspaceID, ifSyncVersion int64) (*DesktopAccount, error)
|
|
MarkDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error)
|
|
ClearDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error)
|
|
}
|
|
|
|
type desktopAccountRepository struct {
|
|
q generated.Querier
|
|
}
|
|
|
|
func NewDesktopAccountRepository(db generated.DBTX) DesktopAccountRepository {
|
|
return &desktopAccountRepository{q: newQuerier(db)}
|
|
}
|
|
|
|
func (r *desktopAccountRepository) ListByUser(ctx context.Context, workspaceID, userID int64) ([]DesktopAccount, error) {
|
|
rows, err := r.q.ListDesktopAccountsByUser(ctx, generated.ListDesktopAccountsByUserParams{
|
|
WorkspaceID: workspaceID,
|
|
UserID: userID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
items := make([]DesktopAccount, 0, len(rows))
|
|
for _, row := range rows {
|
|
item, err := desktopAccountFromListRow(row)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, *item)
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
func (r *desktopAccountRepository) GetByDesktopID(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error) {
|
|
row, err := r.q.GetDesktopAccountByDesktopID(ctx, generated.GetDesktopAccountByDesktopIDParams{
|
|
DesktopID: pgUUID(desktopID),
|
|
WorkspaceID: workspaceID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return desktopAccountFromGetRow(row)
|
|
}
|
|
|
|
func (r *desktopAccountRepository) GetByIdentity(ctx context.Context, workspaceID int64, platform, platformUID string) (*DesktopAccount, error) {
|
|
row, err := r.q.GetDesktopAccountByIdentity(ctx, generated.GetDesktopAccountByIdentityParams{
|
|
WorkspaceID: workspaceID,
|
|
PlatformID: platform,
|
|
PlatformUid: platformUID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return desktopAccountFromIdentityRow(row)
|
|
}
|
|
|
|
func (r *desktopAccountRepository) Upsert(ctx context.Context, params UpsertDesktopAccountParams) (*DesktopAccount, error) {
|
|
tagsJSON, err := marshalJSON(params.Tags)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal tags: %w", err)
|
|
}
|
|
|
|
var metadataJSON []byte
|
|
if len(tagsJSON) > 0 {
|
|
metadataJSON, err = marshalJSON(map[string]any{"tags": params.Tags})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal metadata: %w", err)
|
|
}
|
|
}
|
|
|
|
row, err := r.q.UpsertDesktopAccount(ctx, generated.UpsertDesktopAccountParams{
|
|
DesktopID: pgUUID(params.DesktopID),
|
|
TenantID: params.TenantID,
|
|
WorkspaceID: params.WorkspaceID,
|
|
UserID: params.UserID,
|
|
ClientID: pgUUID(params.ClientID),
|
|
PlatformID: params.Platform,
|
|
PlatformUid: params.PlatformUID,
|
|
DisplayName: params.DisplayName,
|
|
AvatarUrl: pgText(params.AvatarURL),
|
|
LegacyStatus: legacyStatusFromHealth(params.Health),
|
|
MetadataJson: metadataJSON,
|
|
VerifiedAt: pgTimestamp(params.VerifiedAt),
|
|
AccountFingerprint: pgText(params.AccountFingerprint),
|
|
Health: params.Health,
|
|
Tags: tagsJSON,
|
|
IfSyncVersion: pgInt8(params.IfSyncVersion),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return desktopAccountFromUpsertRow(row)
|
|
}
|
|
|
|
func (r *desktopAccountRepository) Patch(ctx context.Context, params PatchDesktopAccountParams) (*DesktopAccount, error) {
|
|
var tagsJSON []byte
|
|
var err error
|
|
if params.Tags != nil {
|
|
tagsJSON, err = marshalJSON(*params.Tags)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal tags: %w", err)
|
|
}
|
|
}
|
|
|
|
row, err := r.q.PatchDesktopAccount(ctx, generated.PatchDesktopAccountParams{
|
|
DisplayName: pgText(params.DisplayName),
|
|
Health: pgText(params.Health),
|
|
LegacyStatus: pgText(legacyStatusPtrFromHealth(params.Health)),
|
|
VerifiedAt: pgTimestamp(params.VerifiedAt),
|
|
Tags: tagsJSON,
|
|
DesktopID: pgUUID(params.DesktopID),
|
|
WorkspaceID: params.WorkspaceID,
|
|
IfSyncVersion: params.IfSyncVersion,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return desktopAccountFromPatchRow(row)
|
|
}
|
|
|
|
func (r *desktopAccountRepository) Tombstone(ctx context.Context, desktopID uuid.UUID, workspaceID, ifSyncVersion int64) (*DesktopAccount, error) {
|
|
row, err := r.q.TombstoneDesktopAccount(ctx, generated.TombstoneDesktopAccountParams{
|
|
DesktopID: pgUUID(desktopID),
|
|
WorkspaceID: workspaceID,
|
|
IfSyncVersion: ifSyncVersion,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return desktopAccountFromTombstoneRow(row)
|
|
}
|
|
|
|
func (r *desktopAccountRepository) MarkDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error) {
|
|
row, err := r.q.MarkDesktopAccountDeleteRequested(ctx, generated.MarkDesktopAccountDeleteRequestedParams{
|
|
DesktopID: pgUUID(desktopID),
|
|
WorkspaceID: workspaceID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return desktopAccountFromMarkDeleteRow(row)
|
|
}
|
|
|
|
func (r *desktopAccountRepository) ClearDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error) {
|
|
row, err := r.q.ClearDesktopAccountDeleteRequested(ctx, generated.ClearDesktopAccountDeleteRequestedParams{
|
|
DesktopID: pgUUID(desktopID),
|
|
WorkspaceID: workspaceID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return desktopAccountFromClearDeleteRow(row)
|
|
}
|
|
|
|
func desktopAccountFromListRow(row generated.ListDesktopAccountsByUserRow) (*DesktopAccount, error) {
|
|
return buildDesktopAccount(
|
|
row.DesktopID,
|
|
row.TenantID,
|
|
row.WorkspaceID,
|
|
row.UserID,
|
|
row.ClientID,
|
|
row.PlatformID,
|
|
row.PlatformUid,
|
|
row.AccountFingerprint,
|
|
row.DisplayName,
|
|
row.AvatarUrl,
|
|
row.Health,
|
|
row.VerifiedAt,
|
|
row.Tags,
|
|
row.SyncVersion,
|
|
row.DeletedAt,
|
|
row.DeleteRequestedAt,
|
|
row.CreatedAt,
|
|
row.UpdatedAt,
|
|
)
|
|
}
|
|
|
|
func desktopAccountFromGetRow(row generated.GetDesktopAccountByDesktopIDRow) (*DesktopAccount, error) {
|
|
return buildDesktopAccount(
|
|
row.DesktopID,
|
|
row.TenantID,
|
|
row.WorkspaceID,
|
|
row.UserID,
|
|
row.ClientID,
|
|
row.PlatformID,
|
|
row.PlatformUid,
|
|
row.AccountFingerprint,
|
|
row.DisplayName,
|
|
row.AvatarUrl,
|
|
row.Health,
|
|
row.VerifiedAt,
|
|
row.Tags,
|
|
row.SyncVersion,
|
|
row.DeletedAt,
|
|
row.DeleteRequestedAt,
|
|
row.CreatedAt,
|
|
row.UpdatedAt,
|
|
)
|
|
}
|
|
|
|
func desktopAccountFromIdentityRow(row generated.GetDesktopAccountByIdentityRow) (*DesktopAccount, error) {
|
|
return buildDesktopAccount(
|
|
row.DesktopID,
|
|
row.TenantID,
|
|
row.WorkspaceID,
|
|
row.UserID,
|
|
row.ClientID,
|
|
row.PlatformID,
|
|
row.PlatformUid,
|
|
row.AccountFingerprint,
|
|
row.DisplayName,
|
|
row.AvatarUrl,
|
|
row.Health,
|
|
row.VerifiedAt,
|
|
row.Tags,
|
|
row.SyncVersion,
|
|
row.DeletedAt,
|
|
row.DeleteRequestedAt,
|
|
row.CreatedAt,
|
|
row.UpdatedAt,
|
|
)
|
|
}
|
|
|
|
func desktopAccountFromUpsertRow(row generated.UpsertDesktopAccountRow) (*DesktopAccount, error) {
|
|
return buildDesktopAccount(
|
|
row.DesktopID,
|
|
row.TenantID,
|
|
row.WorkspaceID,
|
|
row.UserID,
|
|
row.ClientID,
|
|
row.PlatformID,
|
|
row.PlatformUid,
|
|
row.AccountFingerprint,
|
|
row.DisplayName,
|
|
row.AvatarUrl,
|
|
row.Health,
|
|
row.VerifiedAt,
|
|
row.Tags,
|
|
row.SyncVersion,
|
|
row.DeletedAt,
|
|
row.DeleteRequestedAt,
|
|
row.CreatedAt,
|
|
row.UpdatedAt,
|
|
)
|
|
}
|
|
|
|
func desktopAccountFromPatchRow(row generated.PatchDesktopAccountRow) (*DesktopAccount, error) {
|
|
return buildDesktopAccount(
|
|
row.DesktopID,
|
|
row.TenantID,
|
|
row.WorkspaceID,
|
|
row.UserID,
|
|
row.ClientID,
|
|
row.PlatformID,
|
|
row.PlatformUid,
|
|
row.AccountFingerprint,
|
|
row.DisplayName,
|
|
row.AvatarUrl,
|
|
row.Health,
|
|
row.VerifiedAt,
|
|
row.Tags,
|
|
row.SyncVersion,
|
|
row.DeletedAt,
|
|
row.DeleteRequestedAt,
|
|
row.CreatedAt,
|
|
row.UpdatedAt,
|
|
)
|
|
}
|
|
|
|
func desktopAccountFromTombstoneRow(row generated.TombstoneDesktopAccountRow) (*DesktopAccount, error) {
|
|
return buildDesktopAccount(
|
|
row.DesktopID,
|
|
row.TenantID,
|
|
row.WorkspaceID,
|
|
row.UserID,
|
|
row.ClientID,
|
|
row.PlatformID,
|
|
row.PlatformUid,
|
|
row.AccountFingerprint,
|
|
row.DisplayName,
|
|
row.AvatarUrl,
|
|
row.Health,
|
|
row.VerifiedAt,
|
|
row.Tags,
|
|
row.SyncVersion,
|
|
row.DeletedAt,
|
|
row.DeleteRequestedAt,
|
|
row.CreatedAt,
|
|
row.UpdatedAt,
|
|
)
|
|
}
|
|
|
|
func desktopAccountFromMarkDeleteRow(row generated.MarkDesktopAccountDeleteRequestedRow) (*DesktopAccount, error) {
|
|
return buildDesktopAccount(
|
|
row.DesktopID,
|
|
row.TenantID,
|
|
row.WorkspaceID,
|
|
row.UserID,
|
|
row.ClientID,
|
|
row.PlatformID,
|
|
row.PlatformUid,
|
|
row.AccountFingerprint,
|
|
row.DisplayName,
|
|
row.AvatarUrl,
|
|
row.Health,
|
|
row.VerifiedAt,
|
|
row.Tags,
|
|
row.SyncVersion,
|
|
row.DeletedAt,
|
|
row.DeleteRequestedAt,
|
|
row.CreatedAt,
|
|
row.UpdatedAt,
|
|
)
|
|
}
|
|
|
|
func desktopAccountFromClearDeleteRow(row generated.ClearDesktopAccountDeleteRequestedRow) (*DesktopAccount, error) {
|
|
return buildDesktopAccount(
|
|
row.DesktopID,
|
|
row.TenantID,
|
|
row.WorkspaceID,
|
|
row.UserID,
|
|
row.ClientID,
|
|
row.PlatformID,
|
|
row.PlatformUid,
|
|
row.AccountFingerprint,
|
|
row.DisplayName,
|
|
row.AvatarUrl,
|
|
row.Health,
|
|
row.VerifiedAt,
|
|
row.Tags,
|
|
row.SyncVersion,
|
|
row.DeletedAt,
|
|
row.DeleteRequestedAt,
|
|
row.CreatedAt,
|
|
row.UpdatedAt,
|
|
)
|
|
}
|
|
|
|
func buildDesktopAccount(
|
|
desktopID pgtype.UUID,
|
|
tenantID int64,
|
|
workspaceID int64,
|
|
userID int64,
|
|
clientID pgtype.UUID,
|
|
platform string,
|
|
platformUID string,
|
|
accountFingerprint pgtype.Text,
|
|
displayName string,
|
|
avatarURL pgtype.Text,
|
|
health string,
|
|
verifiedAt pgtype.Timestamptz,
|
|
tags []byte,
|
|
syncVersion int64,
|
|
deletedAt pgtype.Timestamptz,
|
|
deleteRequestedAt pgtype.Timestamptz,
|
|
createdAt pgtype.Timestamptz,
|
|
updatedAt pgtype.Timestamptz,
|
|
) (*DesktopAccount, error) {
|
|
tagList, err := unmarshalStringSlice(tags)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unmarshal desktop account tags: %w", err)
|
|
}
|
|
|
|
return &DesktopAccount{
|
|
DesktopID: uuidFromPG(desktopID),
|
|
TenantID: tenantID,
|
|
WorkspaceID: workspaceID,
|
|
UserID: userID,
|
|
ClientID: nullableUUID(clientID),
|
|
Platform: platform,
|
|
PlatformUID: platformUID,
|
|
AccountFingerprint: nullableText(accountFingerprint),
|
|
DisplayName: displayName,
|
|
AvatarURL: nullableText(avatarURL),
|
|
Health: health,
|
|
VerifiedAt: optionalTime(verifiedAt),
|
|
Tags: tagList,
|
|
SyncVersion: syncVersion,
|
|
DeletedAt: optionalTime(deletedAt),
|
|
DeleteRequestedAt: optionalTime(deleteRequestedAt),
|
|
CreatedAt: timeFromTimestamp(createdAt),
|
|
UpdatedAt: timeFromTimestamp(updatedAt),
|
|
}, nil
|
|
}
|
|
|
|
func legacyStatusFromHealth(health string) string {
|
|
switch health {
|
|
case "live":
|
|
return "active"
|
|
case "captcha":
|
|
return "captcha"
|
|
case "risk":
|
|
return "risk"
|
|
default:
|
|
return "expired"
|
|
}
|
|
}
|
|
|
|
func legacyStatusPtrFromHealth(health *string) *string {
|
|
if health == nil {
|
|
return nil
|
|
}
|
|
value := legacyStatusFromHealth(*health)
|
|
return &value
|
|
}
|