Files
digital-psychology/apps/api/internal/service/membership/service.go
T
jackyu66gitandCursor 89756f65b4 feat(ECR-012–016): 合规、题库、时辰刷新、头像、MBTI OEJTS 与埋点
落地输入合规、探索题库、报告日/时辰刷新、账号头像、OEJTS 量表,并补齐 H5 埋点与 Admin 漏斗;同步 ESS 工件、切至自建 Git、清理 GitHub Actions。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 01:27:58 +08:00

88 lines
2.4 KiB
Go

// Package membership handles growth membership status and mock commerce orders.
package membership
import (
"context"
"errors"
"time"
"github.com/google/uuid"
"github.com/yuxingu/digital-psychology/apps/api/internal/repository"
)
// Service is membership + order use-cases (extracted from report service).
type Service struct {
Reports *repository.ReportRepo
}
// CreateOrderInput for commerce.
type CreateOrderInput struct {
Kind string
Plan string
ReportID *uuid.UUID
}
// CreateOrder starts membership, deep_access, or ask_pack order.
func (s *Service) CreateOrder(ctx context.Context, userID uuid.UUID, in CreateOrderInput) (uuid.UUID, error) {
if in.Kind != "membership" && in.Kind != "deep_access" && in.Kind != "ask_pack" {
return uuid.Nil, errors.New("invalid kind")
}
if in.Kind == "deep_access" && in.ReportID == nil {
return uuid.Nil, errors.New("report_id required")
}
amount := 990
plan := in.Plan
if in.Kind == "membership" {
amount = 2500
}
if in.Kind == "ask_pack" {
if plan == "" {
plan = "pack10"
}
amount = repository.AskPackAmountCents(plan)
if amount <= 0 || repository.AskPackQuota(plan) <= 0 {
return uuid.Nil, errors.New("invalid ask_pack plan")
}
}
return s.Reports.CreateOrder(ctx, userID, in.Kind, plan, in.ReportID, amount)
}
// PayMock completes mock payment.
func (s *Service) PayMock(ctx context.Context, userID, orderID uuid.UUID) error {
return s.Reports.PayMock(ctx, userID, orderID)
}
// Me is the public membership snapshot.
type Me struct {
Active bool `json:"active"`
Plan string `json:"plan,omitempty"`
Status string `json:"status"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
AskQuotaLeft int `json:"ask_quota_left,omitempty"`
}
// Get returns current growth membership for the user.
func (s *Service) Get(ctx context.Context, userID uuid.UUID) (*Me, error) {
row, err := s.Reports.GetMembership(ctx, userID)
if err != nil {
return nil, err
}
return &Me{
Active: row.Active,
Plan: row.Plan,
Status: row.Status,
ExpiresAt: row.ExpiresAt,
AskQuotaLeft: row.AskQuotaLeft,
}, nil
}
// IsActive reports whether the user has an active成长会员.
func (s *Service) IsActive(ctx context.Context, userID uuid.UUID) (bool, error) {
me, err := s.Get(ctx, userID)
if err != nil {
return false, err
}
return me.Active, nil
}