feat(kol): marketplace and admin subscription repo queries

This commit is contained in:
2026-04-17 14:23:25 +08:00
parent 6eb663bd6e
commit df334c1e26
7 changed files with 96 additions and 32 deletions
@@ -15,7 +15,12 @@ const getPublishedKolPackage = `-- name: GetPublishedKolPackage :one
SELECT kp.id, kp.tenant_id, kp.kol_profile_id, kp.name, kp.description, SELECT kp.id, kp.tenant_id, kp.kol_profile_id, kp.name, kp.description,
kp.cover_url, kp.industry, kp.tags, kp.price_note, kp.status, kp.cover_url, kp.industry, kp.tags, kp.price_note, kp.status,
kp.created_at, kp.updated_at, kp.created_at, kp.updated_at,
pf.display_name AS kol_display_name, pf.avatar_url AS kol_avatar_url, pf.bio AS kol_bio pf.display_name AS kol_display_name, pf.avatar_url AS kol_avatar_url, pf.bio AS kol_bio,
(SELECT COUNT(*) FROM kol_prompts p
WHERE p.package_id = kp.id AND p.status = 'active' AND p.deleted_at IS NULL) AS prompt_count,
(SELECT COUNT(*) FROM kol_subscriptions s
WHERE s.package_id = kp.id AND s.status = 'active'
AND (s.end_at IS NULL OR s.end_at > NOW())) AS subscriber_count
FROM kol_packages kp FROM kol_packages kp
JOIN kol_profiles pf ON pf.id = kp.kol_profile_id JOIN kol_profiles pf ON pf.id = kp.kol_profile_id
AND pf.status = 'active' AND pf.status = 'active'
@@ -27,21 +32,23 @@ WHERE kp.id = $1
` `
type GetPublishedKolPackageRow struct { type GetPublishedKolPackageRow struct {
ID int64 `json:"id"` ID int64 `json:"id"`
TenantID int64 `json:"tenant_id"` TenantID int64 `json:"tenant_id"`
KolProfileID int64 `json:"kol_profile_id"` KolProfileID int64 `json:"kol_profile_id"`
Name string `json:"name"` Name string `json:"name"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
CoverUrl pgtype.Text `json:"cover_url"` CoverUrl pgtype.Text `json:"cover_url"`
Industry pgtype.Text `json:"industry"` Industry pgtype.Text `json:"industry"`
Tags []byte `json:"tags"` Tags []byte `json:"tags"`
PriceNote pgtype.Text `json:"price_note"` PriceNote pgtype.Text `json:"price_note"`
Status string `json:"status"` Status string `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"` CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"`
KolDisplayName string `json:"kol_display_name"` KolDisplayName string `json:"kol_display_name"`
KolAvatarUrl pgtype.Text `json:"kol_avatar_url"` KolAvatarUrl pgtype.Text `json:"kol_avatar_url"`
KolBio pgtype.Text `json:"kol_bio"` KolBio pgtype.Text `json:"kol_bio"`
PromptCount int64 `json:"prompt_count"`
SubscriberCount int64 `json:"subscriber_count"`
} }
func (q *Queries) GetPublishedKolPackage(ctx context.Context, id int64) (GetPublishedKolPackageRow, error) { func (q *Queries) GetPublishedKolPackage(ctx context.Context, id int64) (GetPublishedKolPackageRow, error) {
@@ -63,6 +70,8 @@ func (q *Queries) GetPublishedKolPackage(ctx context.Context, id int64) (GetPubl
&i.KolDisplayName, &i.KolDisplayName,
&i.KolAvatarUrl, &i.KolAvatarUrl,
&i.KolBio, &i.KolBio,
&i.PromptCount,
&i.SubscriberCount,
) )
return i, err return i, err
} }
@@ -151,6 +151,31 @@ func (q *Queries) GetKolSubscriptionByID(ctx context.Context, arg GetKolSubscrip
return i, err return i, err
} }
const getKolSubscriptionByIDAny = `-- name: GetKolSubscriptionByIDAny :one
SELECT id, tenant_id, package_id, status, approved_by, start_at, end_at, created_at, updated_at
FROM kol_subscriptions
WHERE id = $1
`
// Cross-tenant read: V1 tenant-admin endpoints temporarily manage subscriptions here
// until a dedicated platform-api exists.
func (q *Queries) GetKolSubscriptionByIDAny(ctx context.Context, id int64) (KolSubscription, error) {
row := q.db.QueryRow(ctx, getKolSubscriptionByIDAny, id)
var i KolSubscription
err := row.Scan(
&i.ID,
&i.TenantID,
&i.PackageID,
&i.Status,
&i.ApprovedBy,
&i.StartAt,
&i.EndAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getSubscriptionPromptByID = `-- name: GetSubscriptionPromptByID :one const getSubscriptionPromptByID = `-- name: GetSubscriptionPromptByID :one
SELECT sp.id, sp.tenant_id, sp.subscription_id, sp.package_id, sp.prompt_id, SELECT sp.id, sp.tenant_id, sp.subscription_id, sp.package_id, sp.prompt_id,
sp.status, sp.granted_at, sp.revoked_at, sp.status, sp.granted_at, sp.revoked_at,
@@ -62,6 +62,9 @@ type Querier interface {
GetKolPromptByID(ctx context.Context, arg GetKolPromptByIDParams) (GetKolPromptByIDRow, error) GetKolPromptByID(ctx context.Context, arg GetKolPromptByIDParams) (GetKolPromptByIDRow, error)
GetKolPromptRevision(ctx context.Context, arg GetKolPromptRevisionParams) (GetKolPromptRevisionRow, error) GetKolPromptRevision(ctx context.Context, arg GetKolPromptRevisionParams) (GetKolPromptRevisionRow, error)
GetKolSubscriptionByID(ctx context.Context, arg GetKolSubscriptionByIDParams) (KolSubscription, error) GetKolSubscriptionByID(ctx context.Context, arg GetKolSubscriptionByIDParams) (KolSubscription, error)
// Cross-tenant read: V1 tenant-admin endpoints temporarily manage subscriptions here
// until a dedicated platform-api exists.
GetKolSubscriptionByIDAny(ctx context.Context, id int64) (KolSubscription, error)
GetKolUsageByGenerationTask(ctx context.Context, arg GetKolUsageByGenerationTaskParams) (GetKolUsageByGenerationTaskRow, error) GetKolUsageByGenerationTask(ctx context.Context, arg GetKolUsageByGenerationTaskParams) (GetKolUsageByGenerationTaskRow, error)
GetPromptRuleByID(ctx context.Context, arg GetPromptRuleByIDParams) (GetPromptRuleByIDRow, error) GetPromptRuleByID(ctx context.Context, arg GetPromptRuleByIDParams) (GetPromptRuleByIDRow, error)
GetPublishedKolPackage(ctx context.Context, id int64) (GetPublishedKolPackageRow, error) GetPublishedKolPackage(ctx context.Context, id int64) (GetPublishedKolPackageRow, error)
@@ -107,21 +107,23 @@ func (r *kolMarketplaceRepository) GetPublished(ctx context.Context, id int64) (
return nil, err return nil, err
} }
return &PublishedKolPackage{ return &PublishedKolPackage{
ID: row.ID, ID: row.ID,
TenantID: row.TenantID, TenantID: row.TenantID,
KolProfileID: row.KolProfileID, KolProfileID: row.KolProfileID,
Name: row.Name, Name: row.Name,
Description: nullableText(row.Description), Description: nullableText(row.Description),
CoverURL: nullableText(row.CoverUrl), CoverURL: nullableText(row.CoverUrl),
Industry: nullableText(row.Industry), Industry: nullableText(row.Industry),
Tags: row.Tags, Tags: row.Tags,
PriceNote: nullableText(row.PriceNote), PriceNote: nullableText(row.PriceNote),
Status: row.Status, Status: row.Status,
CreatedAt: timeFromTimestamp(row.CreatedAt), CreatedAt: timeFromTimestamp(row.CreatedAt),
UpdatedAt: timeFromTimestamp(row.UpdatedAt), UpdatedAt: timeFromTimestamp(row.UpdatedAt),
KolDisplayName: row.KolDisplayName, KolDisplayName: row.KolDisplayName,
KolAvatarURL: nullableText(row.KolAvatarUrl), KolAvatarURL: nullableText(row.KolAvatarUrl),
KolBio: nullableText(row.KolBio), KolBio: nullableText(row.KolBio),
PromptCount: row.PromptCount,
SubscriberCount: row.SubscriberCount,
}, nil }, nil
} }
@@ -54,6 +54,7 @@ type CreateAccessRowInput struct {
type KolSubscriptionRepository interface { type KolSubscriptionRepository interface {
Create(ctx context.Context, tenantID, packageID int64) (*KolSubscription, error) Create(ctx context.Context, tenantID, packageID int64) (*KolSubscription, error)
GetByID(ctx context.Context, tenantID, id int64) (*KolSubscription, error) GetByID(ctx context.Context, tenantID, id int64) (*KolSubscription, error)
GetByIDAny(ctx context.Context, id int64) (*KolSubscription, error)
GetActiveForTenant(ctx context.Context, tenantID, packageID int64) (*KolSubscription, error) GetActiveForTenant(ctx context.Context, tenantID, packageID int64) (*KolSubscription, error)
ListByTenant(ctx context.Context, tenantID int64) ([]KolSubscription, error) ListByTenant(ctx context.Context, tenantID int64) ([]KolSubscription, error)
ListActiveSubscribersForPackage(ctx context.Context, packageID int64) ([]KolSubscription, error) ListActiveSubscribersForPackage(ctx context.Context, packageID int64) ([]KolSubscription, error)
@@ -117,6 +118,18 @@ func (r *kolSubscriptionRepository) GetByID(ctx context.Context, tenantID, id in
return &sub, nil return &sub, nil
} }
func (r *kolSubscriptionRepository) GetByIDAny(ctx context.Context, id int64) (*KolSubscription, error) {
row, err := r.q.GetKolSubscriptionByIDAny(ctx, id)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
return nil, err
}
sub := mapKolSubscription(row)
return &sub, nil
}
func (r *kolSubscriptionRepository) GetActiveForTenant(ctx context.Context, tenantID, packageID int64) (*KolSubscription, error) { func (r *kolSubscriptionRepository) GetActiveForTenant(ctx context.Context, tenantID, packageID int64) (*KolSubscription, error) {
row, err := r.q.GetActiveKolSubscription(ctx, generated.GetActiveKolSubscriptionParams{ row, err := r.q.GetActiveKolSubscription(ctx, generated.GetActiveKolSubscriptionParams{
TenantID: tenantID, TenantID: tenantID,
@@ -26,7 +26,12 @@ LIMIT sqlc.arg('limit') OFFSET sqlc.arg('offset');
SELECT kp.id, kp.tenant_id, kp.kol_profile_id, kp.name, kp.description, SELECT kp.id, kp.tenant_id, kp.kol_profile_id, kp.name, kp.description,
kp.cover_url, kp.industry, kp.tags, kp.price_note, kp.status, kp.cover_url, kp.industry, kp.tags, kp.price_note, kp.status,
kp.created_at, kp.updated_at, kp.created_at, kp.updated_at,
pf.display_name AS kol_display_name, pf.avatar_url AS kol_avatar_url, pf.bio AS kol_bio pf.display_name AS kol_display_name, pf.avatar_url AS kol_avatar_url, pf.bio AS kol_bio,
(SELECT COUNT(*) FROM kol_prompts p
WHERE p.package_id = kp.id AND p.status = 'active' AND p.deleted_at IS NULL) AS prompt_count,
(SELECT COUNT(*) FROM kol_subscriptions s
WHERE s.package_id = kp.id AND s.status = 'active'
AND (s.end_at IS NULL OR s.end_at > NOW())) AS subscriber_count
FROM kol_packages kp FROM kol_packages kp
JOIN kol_profiles pf ON pf.id = kp.kol_profile_id JOIN kol_profiles pf ON pf.id = kp.kol_profile_id
AND pf.status = 'active' AND pf.status = 'active'
@@ -9,6 +9,13 @@ FROM kol_subscriptions
WHERE id = sqlc.arg(id) WHERE id = sqlc.arg(id)
AND tenant_id = sqlc.arg(tenant_id)::bigint; AND tenant_id = sqlc.arg(tenant_id)::bigint;
-- name: GetKolSubscriptionByIDAny :one
-- Cross-tenant read: V1 tenant-admin endpoints temporarily manage subscriptions here
-- until a dedicated platform-api exists.
SELECT id, tenant_id, package_id, status, approved_by, start_at, end_at, created_at, updated_at
FROM kol_subscriptions
WHERE id = sqlc.arg(id);
-- name: GetActiveKolSubscription :one -- name: GetActiveKolSubscription :one
SELECT id, tenant_id, package_id, status, approved_by, start_at, end_at, created_at, updated_at SELECT id, tenant_id, package_id, status, approved_by, start_at, end_at, created_at, updated_at
FROM kol_subscriptions FROM kol_subscriptions