membership_plans 表、admin 套餐页、Grant/CreateOrder 读表; Loop continuous 自动 Approve/Closed。Next:ECR-015 RedemptionCode。 Co-authored-by: Cursor <cursoragent@cursor.com>
95 lines
2.5 KiB
Go
95 lines
2.5 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
// MembershipPlanRow is a configurable growth membership SKU.
|
|
type MembershipPlanRow struct {
|
|
Code string `json:"code"`
|
|
Title string `json:"title"`
|
|
DurationDays int `json:"duration_days"`
|
|
AmountCents int `json:"amount_cents"`
|
|
Active bool `json:"active"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// ListMembershipPlans returns all plans ordered by code.
|
|
func (r *AdminRepo) ListMembershipPlans(ctx context.Context) ([]MembershipPlanRow, error) {
|
|
rows, err := r.Pool.Query(ctx, `
|
|
SELECT code, title, duration_days, amount_cents, active, updated_at
|
|
FROM membership_plans ORDER BY code`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []MembershipPlanRow
|
|
for rows.Next() {
|
|
var p MembershipPlanRow
|
|
if err := rows.Scan(&p.Code, &p.Title, &p.DurationDays, &p.AmountCents, &p.Active, &p.UpdatedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, p)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
// GetMembershipPlan loads one plan by code.
|
|
func (r *AdminRepo) GetMembershipPlan(ctx context.Context, code string) (*MembershipPlanRow, error) {
|
|
var p MembershipPlanRow
|
|
err := r.Pool.QueryRow(ctx, `
|
|
SELECT code, title, duration_days, amount_cents, active, updated_at
|
|
FROM membership_plans WHERE code=$1`, code,
|
|
).Scan(&p.Code, &p.Title, &p.DurationDays, &p.AmountCents, &p.Active, &p.UpdatedAt)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &p, nil
|
|
}
|
|
|
|
// UpdateMembershipPlanWithAudit updates mutable fields and audits.
|
|
func (r *AdminRepo) UpdateMembershipPlanWithAudit(
|
|
ctx context.Context,
|
|
adminID uuid.UUID,
|
|
code, title string,
|
|
days, amountCents int,
|
|
active bool,
|
|
meta json.RawMessage,
|
|
) error {
|
|
tx, err := r.Pool.Begin(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback(ctx)
|
|
tag, err := tx.Exec(ctx, `
|
|
UPDATE membership_plans
|
|
SET title=$2, duration_days=$3, amount_cents=$4, active=$5, updated_at=now()
|
|
WHERE code=$1`, code, title, days, amountCents, active)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return errString("plan not found")
|
|
}
|
|
if meta == nil {
|
|
meta = json.RawMessage(`{}`)
|
|
}
|
|
if _, err := tx.Exec(ctx, `
|
|
INSERT INTO admin_audit_logs(admin_id, action, target_type, target_id, meta)
|
|
VALUES ($1,'membership.plans.update','membership_plan',$2,$3)`,
|
|
adminID, code, meta,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
return tx.Commit(ctx)
|
|
}
|