feat(kol): repository layer for all KOL tables
This commit is contained in:
@@ -58,6 +58,14 @@ func timeFromTimestamp(value pgtype.Timestamptz) time.Time {
|
|||||||
return value.Time
|
return value.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func optionalTime(value pgtype.Timestamptz) *time.Time {
|
||||||
|
if !value.Valid {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t := value.Time
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
func pgText(value *string) pgtype.Text {
|
func pgText(value *string) pgtype.Text {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return pgtype.Text{}
|
return pgtype.Text{}
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
|
||||||
|
"github.com/geo-platform/tenant-api/internal/tenant/repository/generated"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KolAssistTask struct {
|
||||||
|
ID string
|
||||||
|
TenantID int64
|
||||||
|
KolProfileID int64
|
||||||
|
PromptID *int64
|
||||||
|
OperatorID int64
|
||||||
|
TaskType string
|
||||||
|
Status string
|
||||||
|
RequestJSON []byte
|
||||||
|
ResultJSON []byte
|
||||||
|
ErrorMessage *string
|
||||||
|
StartedAt *time.Time
|
||||||
|
CompletedAt *time.Time
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateKolAssistInput struct {
|
||||||
|
ID string
|
||||||
|
TenantID int64
|
||||||
|
KolProfileID int64
|
||||||
|
PromptID *int64
|
||||||
|
OperatorID int64
|
||||||
|
TaskType string
|
||||||
|
RequestJSON []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolAssistRepository interface {
|
||||||
|
Create(ctx context.Context, input CreateKolAssistInput) error
|
||||||
|
Get(ctx context.Context, tenantID int64, id string) (*KolAssistTask, error)
|
||||||
|
MarkStarted(ctx context.Context, tenantID int64, id string) error
|
||||||
|
MarkCompleted(ctx context.Context, tenantID int64, id string, resultJSON []byte) error
|
||||||
|
MarkFailed(ctx context.Context, tenantID int64, id string, errorMsg string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type kolAssistRepository struct {
|
||||||
|
q generated.Querier
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKolAssistRepository(db generated.DBTX) KolAssistRepository {
|
||||||
|
return &kolAssistRepository{q: newQuerier(db)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolAssistRepository) Create(ctx context.Context, input CreateKolAssistInput) error {
|
||||||
|
return r.q.CreateKolAssistTask(ctx, generated.CreateKolAssistTaskParams{
|
||||||
|
ID: input.ID,
|
||||||
|
TenantID: input.TenantID,
|
||||||
|
KolProfileID: input.KolProfileID,
|
||||||
|
PromptID: pgInt8(input.PromptID),
|
||||||
|
OperatorID: input.OperatorID,
|
||||||
|
TaskType: input.TaskType,
|
||||||
|
RequestJson: input.RequestJSON,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolAssistRepository) Get(ctx context.Context, tenantID int64, id string) (*KolAssistTask, error) {
|
||||||
|
row, err := r.q.GetKolAssistTask(ctx, generated.GetKolAssistTaskParams{
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KolAssistTask{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
KolProfileID: row.KolProfileID,
|
||||||
|
PromptID: nullableInt64(row.PromptID),
|
||||||
|
OperatorID: row.OperatorID,
|
||||||
|
TaskType: row.TaskType,
|
||||||
|
Status: row.Status,
|
||||||
|
RequestJSON: row.RequestJson,
|
||||||
|
ResultJSON: row.ResultJson,
|
||||||
|
ErrorMessage: nullableText(row.ErrorMessage),
|
||||||
|
StartedAt: optionalTime(row.StartedAt),
|
||||||
|
CompletedAt: optionalTime(row.CompletedAt),
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolAssistRepository) MarkStarted(ctx context.Context, tenantID int64, id string) error {
|
||||||
|
return r.q.MarkKolAssistStarted(ctx, generated.MarkKolAssistStartedParams{
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolAssistRepository) MarkCompleted(ctx context.Context, tenantID int64, id string, resultJSON []byte) error {
|
||||||
|
return r.q.MarkKolAssistCompleted(ctx, generated.MarkKolAssistCompletedParams{
|
||||||
|
ResultJson: resultJSON,
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolAssistRepository) MarkFailed(ctx context.Context, tenantID int64, id string, errorMsg string) error {
|
||||||
|
return r.q.MarkKolAssistFailed(ctx, generated.MarkKolAssistFailedParams{
|
||||||
|
ErrorMessage: pgText(&errorMsg),
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
|
||||||
|
"github.com/geo-platform/tenant-api/internal/tenant/repository/generated"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PublishedKolPackage struct {
|
||||||
|
ID int64
|
||||||
|
TenantID int64
|
||||||
|
KolProfileID int64
|
||||||
|
Name string
|
||||||
|
Description *string
|
||||||
|
CoverURL *string
|
||||||
|
Industry *string
|
||||||
|
Tags []byte
|
||||||
|
PriceNote *string
|
||||||
|
Status string
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
KolDisplayName string
|
||||||
|
KolAvatarURL *string
|
||||||
|
KolBio *string
|
||||||
|
PromptCount int64
|
||||||
|
SubscriberCount int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublicKolPrompt struct {
|
||||||
|
ID int64
|
||||||
|
Name string
|
||||||
|
PlatformHint *string
|
||||||
|
Status string
|
||||||
|
PublishedRevisionNo *int
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListPublishedFilter struct {
|
||||||
|
Industry *string
|
||||||
|
Keyword *string
|
||||||
|
Offset int
|
||||||
|
Limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolMarketplaceRepository interface {
|
||||||
|
ListPublished(ctx context.Context, filter ListPublishedFilter) ([]PublishedKolPackage, error)
|
||||||
|
GetPublished(ctx context.Context, id int64) (*PublishedKolPackage, error)
|
||||||
|
ListPublicPrompts(ctx context.Context, packageID int64) ([]PublicKolPrompt, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type kolMarketplaceRepository struct {
|
||||||
|
q generated.Querier
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKolMarketplaceRepository(db generated.DBTX) KolMarketplaceRepository {
|
||||||
|
return &kolMarketplaceRepository{q: newQuerier(db)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolMarketplaceRepository) ListPublished(ctx context.Context, filter ListPublishedFilter) ([]PublishedKolPackage, error) {
|
||||||
|
limit := filter.Limit
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 20
|
||||||
|
}
|
||||||
|
rows, err := r.q.ListPublishedKolPackages(ctx, generated.ListPublishedKolPackagesParams{
|
||||||
|
Industry: pgText(filter.Industry),
|
||||||
|
Keyword: pgText(filter.Keyword),
|
||||||
|
Offset: int32(filter.Offset),
|
||||||
|
Limit: int32(limit),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]PublishedKolPackage, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
result[i] = PublishedKolPackage{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
KolProfileID: row.KolProfileID,
|
||||||
|
Name: row.Name,
|
||||||
|
Description: nullableText(row.Description),
|
||||||
|
CoverURL: nullableText(row.CoverUrl),
|
||||||
|
Industry: nullableText(row.Industry),
|
||||||
|
Tags: row.Tags,
|
||||||
|
PriceNote: nullableText(row.PriceNote),
|
||||||
|
Status: row.Status,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
KolDisplayName: row.KolDisplayName,
|
||||||
|
KolAvatarURL: nullableText(row.KolAvatarUrl),
|
||||||
|
PromptCount: row.PromptCount,
|
||||||
|
SubscriberCount: row.SubscriberCount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolMarketplaceRepository) GetPublished(ctx context.Context, id int64) (*PublishedKolPackage, error) {
|
||||||
|
row, err := r.q.GetPublishedKolPackage(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &PublishedKolPackage{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
KolProfileID: row.KolProfileID,
|
||||||
|
Name: row.Name,
|
||||||
|
Description: nullableText(row.Description),
|
||||||
|
CoverURL: nullableText(row.CoverUrl),
|
||||||
|
Industry: nullableText(row.Industry),
|
||||||
|
Tags: row.Tags,
|
||||||
|
PriceNote: nullableText(row.PriceNote),
|
||||||
|
Status: row.Status,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
KolDisplayName: row.KolDisplayName,
|
||||||
|
KolAvatarURL: nullableText(row.KolAvatarUrl),
|
||||||
|
KolBio: nullableText(row.KolBio),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolMarketplaceRepository) ListPublicPrompts(ctx context.Context, packageID int64) ([]PublicKolPrompt, error) {
|
||||||
|
rows, err := r.q.ListPublicPromptsForPackage(ctx, packageID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]PublicKolPrompt, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
result[i] = PublicKolPrompt{
|
||||||
|
ID: row.ID,
|
||||||
|
Name: row.Name,
|
||||||
|
PlatformHint: nullableText(row.PlatformHint),
|
||||||
|
Status: row.Status,
|
||||||
|
PublishedRevisionNo: nullableIntFromInt4(row.PublishedRevisionNo),
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
|
||||||
|
"github.com/geo-platform/tenant-api/internal/tenant/repository/generated"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KolPackage struct {
|
||||||
|
ID int64
|
||||||
|
TenantID int64
|
||||||
|
KolProfileID int64
|
||||||
|
Name string
|
||||||
|
Description *string
|
||||||
|
CoverURL *string
|
||||||
|
Industry *string
|
||||||
|
Tags []byte
|
||||||
|
PriceNote *string
|
||||||
|
Status string
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateKolPackageInput struct {
|
||||||
|
TenantID int64
|
||||||
|
KolProfileID int64
|
||||||
|
Name string
|
||||||
|
Description *string
|
||||||
|
CoverURL *string
|
||||||
|
Industry *string
|
||||||
|
Tags []byte
|
||||||
|
PriceNote *string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateKolPackageInput struct {
|
||||||
|
ID int64
|
||||||
|
Name string
|
||||||
|
Description *string
|
||||||
|
CoverURL *string
|
||||||
|
Industry *string
|
||||||
|
Tags []byte
|
||||||
|
PriceNote *string
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolPackageRepository interface {
|
||||||
|
Create(ctx context.Context, input CreateKolPackageInput) (*KolPackage, error)
|
||||||
|
GetByID(ctx context.Context, tenantID, id int64) (*KolPackage, error)
|
||||||
|
ListByProfile(ctx context.Context, tenantID, profileID int64) ([]KolPackage, error)
|
||||||
|
ListPackageIDsByProfile(ctx context.Context, tenantID, profileID int64) ([]int64, error)
|
||||||
|
Update(ctx context.Context, tenantID int64, input UpdateKolPackageInput) error
|
||||||
|
UpdateStatus(ctx context.Context, tenantID, id int64, status string) error
|
||||||
|
SoftDelete(ctx context.Context, tenantID, id int64) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type kolPackageRepository struct {
|
||||||
|
q generated.Querier
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKolPackageRepository(db generated.DBTX) KolPackageRepository {
|
||||||
|
return &kolPackageRepository{q: newQuerier(db)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPackageRepository) Create(ctx context.Context, input CreateKolPackageInput) (*KolPackage, error) {
|
||||||
|
row, err := r.q.CreateKolPackage(ctx, generated.CreateKolPackageParams{
|
||||||
|
TenantID: input.TenantID,
|
||||||
|
KolProfileID: input.KolProfileID,
|
||||||
|
Name: input.Name,
|
||||||
|
Description: pgText(input.Description),
|
||||||
|
CoverUrl: pgText(input.CoverURL),
|
||||||
|
Industry: pgText(input.Industry),
|
||||||
|
Tags: input.Tags,
|
||||||
|
PriceNote: pgText(input.PriceNote),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KolPackage{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
KolProfileID: row.KolProfileID,
|
||||||
|
Name: row.Name,
|
||||||
|
Description: nullableText(row.Description),
|
||||||
|
CoverURL: nullableText(row.CoverUrl),
|
||||||
|
Industry: nullableText(row.Industry),
|
||||||
|
Tags: row.Tags,
|
||||||
|
PriceNote: nullableText(row.PriceNote),
|
||||||
|
Status: row.Status,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPackageRepository) GetByID(ctx context.Context, tenantID, id int64) (*KolPackage, error) {
|
||||||
|
row, err := r.q.GetKolPackageByID(ctx, generated.GetKolPackageByIDParams{
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KolPackage{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
KolProfileID: row.KolProfileID,
|
||||||
|
Name: row.Name,
|
||||||
|
Description: nullableText(row.Description),
|
||||||
|
CoverURL: nullableText(row.CoverUrl),
|
||||||
|
Industry: nullableText(row.Industry),
|
||||||
|
Tags: row.Tags,
|
||||||
|
PriceNote: nullableText(row.PriceNote),
|
||||||
|
Status: row.Status,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPackageRepository) ListByProfile(ctx context.Context, tenantID, profileID int64) ([]KolPackage, error) {
|
||||||
|
rows, err := r.q.ListKolPackagesByProfile(ctx, generated.ListKolPackagesByProfileParams{
|
||||||
|
KolProfileID: profileID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]KolPackage, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
result[i] = KolPackage{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
KolProfileID: row.KolProfileID,
|
||||||
|
Name: row.Name,
|
||||||
|
Description: nullableText(row.Description),
|
||||||
|
CoverURL: nullableText(row.CoverUrl),
|
||||||
|
Industry: nullableText(row.Industry),
|
||||||
|
Tags: row.Tags,
|
||||||
|
PriceNote: nullableText(row.PriceNote),
|
||||||
|
Status: row.Status,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPackageRepository) ListPackageIDsByProfile(ctx context.Context, tenantID, profileID int64) ([]int64, error) {
|
||||||
|
ids, err := r.q.ListKolPackageIDsByProfile(ctx, generated.ListKolPackageIDsByProfileParams{
|
||||||
|
KolProfileID: profileID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ids == nil {
|
||||||
|
return []int64{}, nil
|
||||||
|
}
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPackageRepository) Update(ctx context.Context, tenantID int64, input UpdateKolPackageInput) error {
|
||||||
|
return r.q.UpdateKolPackage(ctx, generated.UpdateKolPackageParams{
|
||||||
|
Name: input.Name,
|
||||||
|
Description: pgText(input.Description),
|
||||||
|
CoverUrl: pgText(input.CoverURL),
|
||||||
|
Industry: pgText(input.Industry),
|
||||||
|
Tags: input.Tags,
|
||||||
|
PriceNote: pgText(input.PriceNote),
|
||||||
|
ID: input.ID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPackageRepository) UpdateStatus(ctx context.Context, tenantID, id int64, status string) error {
|
||||||
|
return r.q.UpdateKolPackageStatus(ctx, generated.UpdateKolPackageStatusParams{
|
||||||
|
Status: status,
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPackageRepository) SoftDelete(ctx context.Context, tenantID, id int64) error {
|
||||||
|
return r.q.SoftDeleteKolPackage(ctx, generated.SoftDeleteKolPackageParams{
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
|
||||||
|
"github.com/geo-platform/tenant-api/internal/tenant/repository/generated"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KolProfile struct {
|
||||||
|
ID int64
|
||||||
|
TenantID int64
|
||||||
|
UserID int64
|
||||||
|
DisplayName string
|
||||||
|
Bio *string
|
||||||
|
AvatarURL *string
|
||||||
|
MarketEnabled bool
|
||||||
|
Status string
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateKolProfileInput struct {
|
||||||
|
ID int64
|
||||||
|
DisplayName string
|
||||||
|
Bio *string
|
||||||
|
AvatarURL *string
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolProfileRepository interface {
|
||||||
|
GetByUser(ctx context.Context, tenantID, userID int64) (*KolProfile, error)
|
||||||
|
GetByID(ctx context.Context, id int64) (*KolProfile, error)
|
||||||
|
Update(ctx context.Context, tenantID int64, input UpdateKolProfileInput) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type kolProfileRepository struct {
|
||||||
|
q generated.Querier
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKolProfileRepository(db generated.DBTX) KolProfileRepository {
|
||||||
|
return &kolProfileRepository{q: newQuerier(db)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolProfileRepository) GetByUser(ctx context.Context, tenantID, userID int64) (*KolProfile, error) {
|
||||||
|
row, err := r.q.GetKolProfileByUser(ctx, generated.GetKolProfileByUserParams{
|
||||||
|
UserID: userID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KolProfile{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
UserID: row.UserID,
|
||||||
|
DisplayName: row.DisplayName,
|
||||||
|
Bio: nullableText(row.Bio),
|
||||||
|
AvatarURL: nullableText(row.AvatarUrl),
|
||||||
|
MarketEnabled: row.MarketEnabled,
|
||||||
|
Status: row.Status,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolProfileRepository) GetByID(ctx context.Context, id int64) (*KolProfile, error) {
|
||||||
|
row, err := r.q.GetKolProfileByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KolProfile{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
UserID: row.UserID,
|
||||||
|
DisplayName: row.DisplayName,
|
||||||
|
Bio: nullableText(row.Bio),
|
||||||
|
AvatarURL: nullableText(row.AvatarUrl),
|
||||||
|
MarketEnabled: row.MarketEnabled,
|
||||||
|
Status: row.Status,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolProfileRepository) Update(ctx context.Context, tenantID int64, input UpdateKolProfileInput) error {
|
||||||
|
return r.q.UpdateKolProfile(ctx, generated.UpdateKolProfileParams{
|
||||||
|
DisplayName: input.DisplayName,
|
||||||
|
Bio: pgText(input.Bio),
|
||||||
|
AvatarUrl: pgText(input.AvatarURL),
|
||||||
|
ID: input.ID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
|
||||||
|
"github.com/geo-platform/tenant-api/internal/tenant/repository/generated"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KolPrompt struct {
|
||||||
|
ID int64
|
||||||
|
TenantID int64
|
||||||
|
PackageID int64
|
||||||
|
Name string
|
||||||
|
PlatformHint *string
|
||||||
|
Status string
|
||||||
|
SortOrder int
|
||||||
|
PublishedRevisionNo *int
|
||||||
|
DraftRevisionNo *int
|
||||||
|
CreatedBy int64
|
||||||
|
UpdatedBy int64
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolPromptRevision struct {
|
||||||
|
ID int64
|
||||||
|
TenantID int64
|
||||||
|
PromptID int64
|
||||||
|
RevisionNo int
|
||||||
|
PromptAssetKey string
|
||||||
|
SchemaJSON []byte
|
||||||
|
CardConfigJSON []byte
|
||||||
|
CreatedBy int64
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActiveKolPrompt struct {
|
||||||
|
ID int64
|
||||||
|
TenantID int64
|
||||||
|
PackageID int64
|
||||||
|
PublishedRevisionNo *int
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateKolPromptInput struct {
|
||||||
|
TenantID int64
|
||||||
|
PackageID int64
|
||||||
|
Name string
|
||||||
|
PlatformHint *string
|
||||||
|
SortOrder int
|
||||||
|
CreatedBy int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateKolPromptMetadataInput struct {
|
||||||
|
ID int64
|
||||||
|
TenantID int64
|
||||||
|
Name string
|
||||||
|
PlatformHint *string
|
||||||
|
SortOrder int
|
||||||
|
UpdatedBy int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateKolPromptRevisionInput struct {
|
||||||
|
TenantID int64
|
||||||
|
PromptID int64
|
||||||
|
RevisionNo int
|
||||||
|
PromptAssetKey string
|
||||||
|
SchemaJSON []byte
|
||||||
|
CardConfigJSON []byte
|
||||||
|
CreatedBy int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolPromptRepository interface {
|
||||||
|
Create(ctx context.Context, input CreateKolPromptInput) (*KolPrompt, error)
|
||||||
|
GetByID(ctx context.Context, tenantID, id int64) (*KolPrompt, error)
|
||||||
|
ListByPackage(ctx context.Context, tenantID, packageID int64) ([]KolPrompt, error)
|
||||||
|
ListActiveByPackage(ctx context.Context, tenantID, packageID int64) ([]ActiveKolPrompt, error)
|
||||||
|
UpdateMetadata(ctx context.Context, input UpdateKolPromptMetadataInput) error
|
||||||
|
SetDraftRevision(ctx context.Context, tenantID, promptID int64, revNo int, updatedBy int64) error
|
||||||
|
SetPublishedRevision(ctx context.Context, tenantID, promptID int64, revNo int, updatedBy int64) error
|
||||||
|
UpdateStatus(ctx context.Context, tenantID, id int64, status string, updatedBy int64) error
|
||||||
|
SoftDelete(ctx context.Context, tenantID, id int64) error
|
||||||
|
CreateRevision(ctx context.Context, input CreateKolPromptRevisionInput) (*KolPromptRevision, error)
|
||||||
|
GetRevision(ctx context.Context, tenantID, promptID int64, revNo int) (*KolPromptRevision, error)
|
||||||
|
NextRevisionNo(ctx context.Context, tenantID, promptID int64) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type kolPromptRepository struct {
|
||||||
|
q generated.Querier
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKolPromptRepository(db generated.DBTX) KolPromptRepository {
|
||||||
|
return &kolPromptRepository{q: newQuerier(db)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapKolPrompt(row generated.GetKolPromptByIDRow) *KolPrompt {
|
||||||
|
return &KolPrompt{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
PackageID: row.PackageID,
|
||||||
|
Name: row.Name,
|
||||||
|
PlatformHint: nullableText(row.PlatformHint),
|
||||||
|
Status: row.Status,
|
||||||
|
SortOrder: int(row.SortOrder),
|
||||||
|
PublishedRevisionNo: nullableIntFromInt4(row.PublishedRevisionNo),
|
||||||
|
DraftRevisionNo: nullableIntFromInt4(row.DraftRevisionNo),
|
||||||
|
CreatedBy: row.CreatedBy,
|
||||||
|
UpdatedBy: row.UpdatedBy,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) Create(ctx context.Context, input CreateKolPromptInput) (*KolPrompt, error) {
|
||||||
|
row, err := r.q.CreateKolPrompt(ctx, generated.CreateKolPromptParams{
|
||||||
|
TenantID: input.TenantID,
|
||||||
|
PackageID: input.PackageID,
|
||||||
|
Name: input.Name,
|
||||||
|
PlatformHint: pgText(input.PlatformHint),
|
||||||
|
SortOrder: int32(input.SortOrder),
|
||||||
|
CreatedBy: input.CreatedBy,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KolPrompt{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
PackageID: row.PackageID,
|
||||||
|
Name: row.Name,
|
||||||
|
PlatformHint: nullableText(row.PlatformHint),
|
||||||
|
Status: row.Status,
|
||||||
|
SortOrder: int(row.SortOrder),
|
||||||
|
PublishedRevisionNo: nullableIntFromInt4(row.PublishedRevisionNo),
|
||||||
|
DraftRevisionNo: nullableIntFromInt4(row.DraftRevisionNo),
|
||||||
|
CreatedBy: row.CreatedBy,
|
||||||
|
UpdatedBy: row.UpdatedBy,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) GetByID(ctx context.Context, tenantID, id int64) (*KolPrompt, error) {
|
||||||
|
row, err := r.q.GetKolPromptByID(ctx, generated.GetKolPromptByIDParams{
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mapKolPrompt(row), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) ListByPackage(ctx context.Context, tenantID, packageID int64) ([]KolPrompt, error) {
|
||||||
|
rows, err := r.q.ListKolPromptsByPackage(ctx, generated.ListKolPromptsByPackageParams{
|
||||||
|
PackageID: packageID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]KolPrompt, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
result[i] = KolPrompt{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
PackageID: row.PackageID,
|
||||||
|
Name: row.Name,
|
||||||
|
PlatformHint: nullableText(row.PlatformHint),
|
||||||
|
Status: row.Status,
|
||||||
|
SortOrder: int(row.SortOrder),
|
||||||
|
PublishedRevisionNo: nullableIntFromInt4(row.PublishedRevisionNo),
|
||||||
|
DraftRevisionNo: nullableIntFromInt4(row.DraftRevisionNo),
|
||||||
|
CreatedBy: row.CreatedBy,
|
||||||
|
UpdatedBy: row.UpdatedBy,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) ListActiveByPackage(ctx context.Context, tenantID, packageID int64) ([]ActiveKolPrompt, error) {
|
||||||
|
rows, err := r.q.ListActiveKolPromptsByPackage(ctx, generated.ListActiveKolPromptsByPackageParams{
|
||||||
|
PackageID: packageID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]ActiveKolPrompt, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
result[i] = ActiveKolPrompt{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
PackageID: row.PackageID,
|
||||||
|
PublishedRevisionNo: nullableIntFromInt4(row.PublishedRevisionNo),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) UpdateMetadata(ctx context.Context, input UpdateKolPromptMetadataInput) error {
|
||||||
|
return r.q.UpdateKolPromptMetadata(ctx, generated.UpdateKolPromptMetadataParams{
|
||||||
|
Name: input.Name,
|
||||||
|
PlatformHint: pgText(input.PlatformHint),
|
||||||
|
SortOrder: int32(input.SortOrder),
|
||||||
|
UpdatedBy: input.UpdatedBy,
|
||||||
|
ID: input.ID,
|
||||||
|
TenantID: input.TenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) SetDraftRevision(ctx context.Context, tenantID, promptID int64, revNo int, updatedBy int64) error {
|
||||||
|
return r.q.UpdateKolPromptDraftRevision(ctx, generated.UpdateKolPromptDraftRevisionParams{
|
||||||
|
RevisionNo: pgtype.Int4{Int32: int32(revNo), Valid: true},
|
||||||
|
UpdatedBy: updatedBy,
|
||||||
|
ID: promptID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) SetPublishedRevision(ctx context.Context, tenantID, promptID int64, revNo int, updatedBy int64) error {
|
||||||
|
return r.q.UpdateKolPromptPublishedRevision(ctx, generated.UpdateKolPromptPublishedRevisionParams{
|
||||||
|
RevisionNo: pgtype.Int4{Int32: int32(revNo), Valid: true},
|
||||||
|
UpdatedBy: updatedBy,
|
||||||
|
ID: promptID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) UpdateStatus(ctx context.Context, tenantID, id int64, status string, updatedBy int64) error {
|
||||||
|
return r.q.UpdateKolPromptStatus(ctx, generated.UpdateKolPromptStatusParams{
|
||||||
|
Status: status,
|
||||||
|
UpdatedBy: updatedBy,
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) SoftDelete(ctx context.Context, tenantID, id int64) error {
|
||||||
|
return r.q.SoftDeleteKolPrompt(ctx, generated.SoftDeleteKolPromptParams{
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) CreateRevision(ctx context.Context, input CreateKolPromptRevisionInput) (*KolPromptRevision, error) {
|
||||||
|
row, err := r.q.CreateKolPromptRevision(ctx, generated.CreateKolPromptRevisionParams{
|
||||||
|
TenantID: input.TenantID,
|
||||||
|
PromptID: input.PromptID,
|
||||||
|
RevisionNo: int32(input.RevisionNo),
|
||||||
|
PromptAssetKey: input.PromptAssetKey,
|
||||||
|
SchemaJson: input.SchemaJSON,
|
||||||
|
CardConfigJson: input.CardConfigJSON,
|
||||||
|
CreatedBy: input.CreatedBy,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KolPromptRevision{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: input.TenantID,
|
||||||
|
PromptID: row.PromptID,
|
||||||
|
RevisionNo: int(row.RevisionNo),
|
||||||
|
PromptAssetKey: row.PromptAssetKey,
|
||||||
|
SchemaJSON: row.SchemaJson,
|
||||||
|
CardConfigJSON: row.CardConfigJson,
|
||||||
|
CreatedBy: row.CreatedBy,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) GetRevision(ctx context.Context, tenantID, promptID int64, revNo int) (*KolPromptRevision, error) {
|
||||||
|
row, err := r.q.GetKolPromptRevision(ctx, generated.GetKolPromptRevisionParams{
|
||||||
|
PromptID: promptID,
|
||||||
|
RevisionNo: int32(revNo),
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KolPromptRevision{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
PromptID: row.PromptID,
|
||||||
|
RevisionNo: int(row.RevisionNo),
|
||||||
|
PromptAssetKey: row.PromptAssetKey,
|
||||||
|
SchemaJSON: row.SchemaJson,
|
||||||
|
CardConfigJSON: row.CardConfigJson,
|
||||||
|
CreatedBy: row.CreatedBy,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolPromptRepository) NextRevisionNo(ctx context.Context, tenantID, promptID int64) (int, error) {
|
||||||
|
next, err := r.q.NextKolPromptRevisionNo(ctx, generated.NextKolPromptRevisionNoParams{
|
||||||
|
PromptID: promptID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int(next), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func nullableIntFromInt4(value pgtype.Int4) *int {
|
||||||
|
if !value.Valid {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
v := int(value.Int32)
|
||||||
|
return &v
|
||||||
|
}
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
|
||||||
|
"github.com/geo-platform/tenant-api/internal/tenant/repository/generated"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KolSubscription struct {
|
||||||
|
ID int64
|
||||||
|
TenantID int64
|
||||||
|
PackageID int64
|
||||||
|
Status string
|
||||||
|
ApprovedBy *int64
|
||||||
|
StartAt *time.Time
|
||||||
|
EndAt *time.Time
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolSubscriptionPrompt struct {
|
||||||
|
ID int64
|
||||||
|
TenantID int64
|
||||||
|
SubscriptionID int64
|
||||||
|
PackageID int64
|
||||||
|
PromptID int64
|
||||||
|
Status string
|
||||||
|
GrantedAt *time.Time
|
||||||
|
RevokedAt *time.Time
|
||||||
|
PromptName string
|
||||||
|
PlatformHint *string
|
||||||
|
PackageName string
|
||||||
|
PublishedRevisionNo *int
|
||||||
|
// Fields below only populated by GetSubscriptionPromptByID.
|
||||||
|
SubscriptionStatus string
|
||||||
|
SubscriptionEndAt *time.Time
|
||||||
|
PromptStatus string
|
||||||
|
PackageStatus string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateAccessRowInput struct {
|
||||||
|
TenantID int64
|
||||||
|
SubscriptionID int64
|
||||||
|
PackageID int64
|
||||||
|
PromptID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolSubscriptionRepository interface {
|
||||||
|
Create(ctx context.Context, tenantID, packageID int64) (*KolSubscription, error)
|
||||||
|
GetByID(ctx context.Context, tenantID, id int64) (*KolSubscription, error)
|
||||||
|
GetActiveForTenant(ctx context.Context, tenantID, packageID int64) (*KolSubscription, error)
|
||||||
|
ListByTenant(ctx context.Context, tenantID int64) ([]KolSubscription, error)
|
||||||
|
ListActiveSubscribersForPackage(ctx context.Context, packageID int64) ([]KolSubscription, error)
|
||||||
|
Approve(ctx context.Context, id, tenantID, approvedBy int64, endAt *time.Time) error
|
||||||
|
RevokeByTenant(ctx context.Context, id, tenantID int64) error
|
||||||
|
CreateAccessRow(ctx context.Context, input CreateAccessRowInput) error
|
||||||
|
ListSubscriptionPromptsByTenant(ctx context.Context, tenantID int64) ([]KolSubscriptionPrompt, error)
|
||||||
|
GetSubscriptionPromptByID(ctx context.Context, tenantID, id int64) (*KolSubscriptionPrompt, error)
|
||||||
|
RevokeAccessRowsBySubscription(ctx context.Context, subscriptionID, tenantID int64) error
|
||||||
|
RevokeAccessRowsByPrompt(ctx context.Context, promptID, tenantID int64) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type kolSubscriptionRepository struct {
|
||||||
|
q generated.Querier
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKolSubscriptionRepository(db generated.DBTX) KolSubscriptionRepository {
|
||||||
|
return &kolSubscriptionRepository{q: newQuerier(db)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapKolSubscription(row generated.KolSubscription) KolSubscription {
|
||||||
|
startAt := optionalTime(row.StartAt)
|
||||||
|
endAt := optionalTime(row.EndAt)
|
||||||
|
return KolSubscription{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
PackageID: row.PackageID,
|
||||||
|
Status: row.Status,
|
||||||
|
ApprovedBy: nullableInt64(row.ApprovedBy),
|
||||||
|
StartAt: startAt,
|
||||||
|
EndAt: endAt,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolSubscriptionRepository) Create(ctx context.Context, tenantID, packageID int64) (*KolSubscription, error) {
|
||||||
|
row, err := r.q.CreateKolSubscription(ctx, generated.CreateKolSubscriptionParams{
|
||||||
|
TenantID: tenantID,
|
||||||
|
PackageID: packageID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sub := mapKolSubscription(row)
|
||||||
|
return &sub, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolSubscriptionRepository) GetByID(ctx context.Context, tenantID, id int64) (*KolSubscription, error) {
|
||||||
|
row, err := r.q.GetKolSubscriptionByID(ctx, generated.GetKolSubscriptionByIDParams{
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
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) {
|
||||||
|
row, err := r.q.GetActiveKolSubscription(ctx, generated.GetActiveKolSubscriptionParams{
|
||||||
|
TenantID: tenantID,
|
||||||
|
PackageID: packageID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sub := mapKolSubscription(row)
|
||||||
|
return &sub, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolSubscriptionRepository) ListByTenant(ctx context.Context, tenantID int64) ([]KolSubscription, error) {
|
||||||
|
rows, err := r.q.ListKolSubscriptions(ctx, tenantID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]KolSubscription, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
result[i] = mapKolSubscription(row)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolSubscriptionRepository) ListActiveSubscribersForPackage(ctx context.Context, packageID int64) ([]KolSubscription, error) {
|
||||||
|
rows, err := r.q.ListActiveSubscribersForPackage(ctx, packageID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]KolSubscription, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
result[i] = mapKolSubscription(row)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolSubscriptionRepository) Approve(ctx context.Context, id, tenantID, approvedBy int64, endAt *time.Time) error {
|
||||||
|
return r.q.ApproveKolSubscription(ctx, generated.ApproveKolSubscriptionParams{
|
||||||
|
ApprovedBy: pgInt8(&approvedBy),
|
||||||
|
EndAt: pgTimestamp(endAt),
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolSubscriptionRepository) RevokeByTenant(ctx context.Context, id, tenantID int64) error {
|
||||||
|
return r.q.RevokeKolSubscription(ctx, generated.RevokeKolSubscriptionParams{
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolSubscriptionRepository) CreateAccessRow(ctx context.Context, input CreateAccessRowInput) error {
|
||||||
|
return r.q.CreateSubscriptionPrompt(ctx, generated.CreateSubscriptionPromptParams{
|
||||||
|
TenantID: input.TenantID,
|
||||||
|
SubscriptionID: input.SubscriptionID,
|
||||||
|
PackageID: input.PackageID,
|
||||||
|
PromptID: input.PromptID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolSubscriptionRepository) ListSubscriptionPromptsByTenant(ctx context.Context, tenantID int64) ([]KolSubscriptionPrompt, error) {
|
||||||
|
rows, err := r.q.ListSubscriptionPromptsByTenant(ctx, tenantID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]KolSubscriptionPrompt, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
grantedAt := optionalTime(row.GrantedAt)
|
||||||
|
revokedAt := optionalTime(row.RevokedAt)
|
||||||
|
result[i] = KolSubscriptionPrompt{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
SubscriptionID: row.SubscriptionID,
|
||||||
|
PackageID: row.PackageID,
|
||||||
|
PromptID: row.PromptID,
|
||||||
|
Status: row.Status,
|
||||||
|
GrantedAt: grantedAt,
|
||||||
|
RevokedAt: revokedAt,
|
||||||
|
PromptName: row.PromptName,
|
||||||
|
PlatformHint: nullableText(row.PlatformHint),
|
||||||
|
PackageName: row.PackageName,
|
||||||
|
PublishedRevisionNo: nullableIntFromInt4(row.PublishedRevisionNo),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolSubscriptionRepository) GetSubscriptionPromptByID(ctx context.Context, tenantID, id int64) (*KolSubscriptionPrompt, error) {
|
||||||
|
row, err := r.q.GetSubscriptionPromptByID(ctx, generated.GetSubscriptionPromptByIDParams{
|
||||||
|
ID: id,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
grantedAt := optionalTime(row.GrantedAt)
|
||||||
|
revokedAt := optionalTime(row.RevokedAt)
|
||||||
|
subEndAt := optionalTime(row.SubscriptionEndAt)
|
||||||
|
return &KolSubscriptionPrompt{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
SubscriptionID: row.SubscriptionID,
|
||||||
|
PackageID: row.PackageID,
|
||||||
|
PromptID: row.PromptID,
|
||||||
|
Status: row.Status,
|
||||||
|
GrantedAt: grantedAt,
|
||||||
|
RevokedAt: revokedAt,
|
||||||
|
PromptName: row.PromptName,
|
||||||
|
PlatformHint: nullableText(row.PlatformHint),
|
||||||
|
PackageName: row.PackageName,
|
||||||
|
PublishedRevisionNo: nullableIntFromInt4(row.PublishedRevisionNo),
|
||||||
|
SubscriptionStatus: row.SubscriptionStatus,
|
||||||
|
SubscriptionEndAt: subEndAt,
|
||||||
|
PromptStatus: row.PromptStatus,
|
||||||
|
PackageStatus: row.PackageStatus,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolSubscriptionRepository) RevokeAccessRowsBySubscription(ctx context.Context, subscriptionID, tenantID int64) error {
|
||||||
|
return r.q.RevokeSubscriptionPromptsBySubscription(ctx, generated.RevokeSubscriptionPromptsBySubscriptionParams{
|
||||||
|
SubscriptionID: subscriptionID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolSubscriptionRepository) RevokeAccessRowsByPrompt(ctx context.Context, promptID, tenantID int64) error {
|
||||||
|
return r.q.RevokeSubscriptionPromptsByPrompt(ctx, generated.RevokeSubscriptionPromptsByPromptParams{
|
||||||
|
PromptID: promptID,
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/geo-platform/tenant-api/internal/tenant/repository/generated"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KolUsageLog struct {
|
||||||
|
ID int64
|
||||||
|
TenantID int64
|
||||||
|
GenerationTaskID *int64
|
||||||
|
Status string
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolUsageByTaskRecord struct {
|
||||||
|
ID int64
|
||||||
|
TenantID int64
|
||||||
|
PackageID int64
|
||||||
|
PromptID int64
|
||||||
|
Status string
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateKolUsageLogInput struct {
|
||||||
|
TenantID int64
|
||||||
|
SubscriptionID *int64
|
||||||
|
SubscriptionPromptID *int64
|
||||||
|
PackageID int64
|
||||||
|
PromptID int64
|
||||||
|
PromptRevisionNo int
|
||||||
|
GenerationTaskID *int64
|
||||||
|
VariablesSnapshot []byte
|
||||||
|
SchemaSnapshot []byte
|
||||||
|
RenderedPromptHash *string
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolDashboardOverview struct {
|
||||||
|
TotalSubs int64
|
||||||
|
MonthSubs int64
|
||||||
|
TotalUsage int64
|
||||||
|
MonthUsage int64
|
||||||
|
FailureCount int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolDashboardTrendPoint struct {
|
||||||
|
Day time.Time
|
||||||
|
Usages int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolUsagePackageStats struct {
|
||||||
|
PackageID int64
|
||||||
|
Total int64
|
||||||
|
Completed int64
|
||||||
|
Failed int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type KolUsageRepository interface {
|
||||||
|
Create(ctx context.Context, input CreateKolUsageLogInput) (*KolUsageLog, error)
|
||||||
|
MarkCompletedByTask(ctx context.Context, tenantID, taskID, articleID int64) error
|
||||||
|
MarkFailedByTask(ctx context.Context, tenantID, taskID int64, errorMsg string) error
|
||||||
|
GetByGenerationTask(ctx context.Context, tenantID, taskID int64) (*KolUsageByTaskRecord, error)
|
||||||
|
DashboardOverview(ctx context.Context, packageIDs []int64) (*KolDashboardOverview, error)
|
||||||
|
DashboardTrend(ctx context.Context, packageIDs []int64, periodDays int) ([]KolDashboardTrendPoint, error)
|
||||||
|
PerPackageStats(ctx context.Context, packageIDs []int64) ([]KolUsagePackageStats, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type kolUsageRepository struct {
|
||||||
|
q generated.Querier
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKolUsageRepository(db generated.DBTX) KolUsageRepository {
|
||||||
|
return &kolUsageRepository{q: newQuerier(db)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolUsageRepository) Create(ctx context.Context, input CreateKolUsageLogInput) (*KolUsageLog, error) {
|
||||||
|
row, err := r.q.CreateKolUsageLog(ctx, generated.CreateKolUsageLogParams{
|
||||||
|
TenantID: input.TenantID,
|
||||||
|
SubscriptionID: pgInt8(input.SubscriptionID),
|
||||||
|
SubscriptionPromptID: pgInt8(input.SubscriptionPromptID),
|
||||||
|
PackageID: input.PackageID,
|
||||||
|
PromptID: input.PromptID,
|
||||||
|
PromptRevisionNo: int32(input.PromptRevisionNo),
|
||||||
|
GenerationTaskID: pgInt8(input.GenerationTaskID),
|
||||||
|
VariablesSnapshot: input.VariablesSnapshot,
|
||||||
|
SchemaSnapshot: input.SchemaSnapshot,
|
||||||
|
RenderedPromptHash: pgText(input.RenderedPromptHash),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KolUsageLog{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
GenerationTaskID: nullableInt64(row.GenerationTaskID),
|
||||||
|
Status: row.Status,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolUsageRepository) MarkCompletedByTask(ctx context.Context, tenantID, taskID, articleID int64) error {
|
||||||
|
return r.q.MarkKolUsageCompletedByTask(ctx, generated.MarkKolUsageCompletedByTaskParams{
|
||||||
|
ArticleID: pgInt8(&articleID),
|
||||||
|
GenerationTaskID: pgInt8(&taskID),
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolUsageRepository) MarkFailedByTask(ctx context.Context, tenantID, taskID int64, errorMsg string) error {
|
||||||
|
return r.q.MarkKolUsageFailedByTask(ctx, generated.MarkKolUsageFailedByTaskParams{
|
||||||
|
ErrorMessage: pgText(&errorMsg),
|
||||||
|
GenerationTaskID: pgInt8(&taskID),
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolUsageRepository) GetByGenerationTask(ctx context.Context, tenantID, taskID int64) (*KolUsageByTaskRecord, error) {
|
||||||
|
row, err := r.q.GetKolUsageByGenerationTask(ctx, generated.GetKolUsageByGenerationTaskParams{
|
||||||
|
GenerationTaskID: pgInt8(&taskID),
|
||||||
|
TenantID: tenantID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// Callers use errors.Is(err, pgx.ErrNoRows) for "not found"; preserve err semantics.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KolUsageByTaskRecord{
|
||||||
|
ID: row.ID,
|
||||||
|
TenantID: row.TenantID,
|
||||||
|
PackageID: row.PackageID,
|
||||||
|
PromptID: row.PromptID,
|
||||||
|
Status: row.Status,
|
||||||
|
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolUsageRepository) DashboardOverview(ctx context.Context, packageIDs []int64) (*KolDashboardOverview, error) {
|
||||||
|
if len(packageIDs) == 0 {
|
||||||
|
return &KolDashboardOverview{}, nil
|
||||||
|
}
|
||||||
|
row, err := r.q.KolDashboardOverview(ctx, packageIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KolDashboardOverview{
|
||||||
|
TotalSubs: row.TotalSubs,
|
||||||
|
MonthSubs: row.MonthSubs,
|
||||||
|
TotalUsage: row.TotalUsage,
|
||||||
|
MonthUsage: row.MonthUsage,
|
||||||
|
FailureCount: row.FailureCount,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolUsageRepository) DashboardTrend(ctx context.Context, packageIDs []int64, periodDays int) ([]KolDashboardTrendPoint, error) {
|
||||||
|
if len(packageIDs) == 0 {
|
||||||
|
return []KolDashboardTrendPoint{}, nil
|
||||||
|
}
|
||||||
|
rows, err := r.q.KolDashboardTrend(ctx, generated.KolDashboardTrendParams{
|
||||||
|
PackageIds: packageIDs,
|
||||||
|
PeriodDays: int32(periodDays),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]KolDashboardTrendPoint, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
var day time.Time
|
||||||
|
if row.D.Valid {
|
||||||
|
day = row.D.Time
|
||||||
|
}
|
||||||
|
result[i] = KolDashboardTrendPoint{
|
||||||
|
Day: day,
|
||||||
|
Usages: row.Usages,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *kolUsageRepository) PerPackageStats(ctx context.Context, packageIDs []int64) ([]KolUsagePackageStats, error) {
|
||||||
|
if len(packageIDs) == 0 {
|
||||||
|
return []KolUsagePackageStats{}, nil
|
||||||
|
}
|
||||||
|
rows, err := r.q.KolUsageCountByPackage(ctx, packageIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]KolUsagePackageStats, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
result[i] = KolUsagePackageStats{
|
||||||
|
PackageID: row.PackageID,
|
||||||
|
Total: row.Total,
|
||||||
|
Completed: row.Completed,
|
||||||
|
Failed: row.Failed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user