# All files / modules/member member.service.ts

0% Statements 0/114
0% Branches 0/1
0% Functions 0/1
0% Lines 0/114

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142

     import { prisma } from '../../models';
    import { MemberLevel, MEMBER_QUOTA } from '../../types';
    import { generateOrderNo, handlePaymentCallback } from '../payment/payment.service';
    import { safeParseInt } from '../../utils/safe-parse';

    // 获取会员权益信息（新5级体系）
    export function getMemberBenefits() {
      return {
        levels: [
          {
            level: 0,
            name: '免费版',
            price: 0,
            quota: MEMBER_QUOTA[0],
            features: ['每天3次生成', '每次最多5000字', '基础音色'],
          },
          {
            level: 1,
            name: '入门版',
            price: MEMBER_PRICES.monthly.price,
            quota: MEMBER_QUOTA[1],
            features: ['每天10次生成', '每次最多10000字', '高清音质', '超出¥8/分钟'],
          },
          {
            level: 2,
            name: '专业版',
            price: MEMBER_PRICES.monthly.price,
            quota: MEMBER_QUOTA[2],
            features: ['无限次生成', '每次最多10000字', '全部音色', '超出¥7/分钟'],
          },
          {
            level: 3,
            name: '旗舰版',
            price: MEMBER_PRICES.monthly.price,
            quota: MEMBER_QUOTA[3],
            features: ['无限次生成', '无字数限制', '全部音色', 'VIP优先队列', '超出¥6/分钟'],
          },
          {
            level: 4,
            name: '企业版',
            price: MEMBER_PRICES.monthly.price,
            quota: MEMBER_QUOTA[4],
            features: ['无限次生成', '无字数限制', '全部功能', '批量处理', '团队管理', '超出¥5/分钟'],
          },
        ],
      };
    }

    // 获取用户会员状态
    export async function getMemberStatus(userId: string) {
      const user = await prisma.user.findUnique({ where: { id: safeParseInt(userId) } });
      if (!user) {
        throw new Error('用户不存在');
      }

      const today = new Date().toISOString().slice(0, 10);
      let dailyUsage = user.dailyUsage;
      const memberLevel = user.memberLevel;

      // 如果 memberLevel 超出范围，使用最高级别
      const safeLevel = (memberLevel in MEMBER_QUOTA) ? (memberLevel as MemberLevel) : 4;
      const quota = MEMBER_QUOTA[safeLevel];

      // 重置每日使用次数
      if (user.lastUsageDate !== today) {
        dailyUsage = 0;
      }

      const isValid = memberLevel > 0 && user.memberExpireAt && new Date() < user.memberExpireAt;
      const levelNames = ['免费版', '入门版', '专业版', '旗舰版', '企业版'];

      return {
        level: memberLevel,
        levelName: levelNames[memberLevel] || levelNames[4],
        expireAt: user.memberExpireAt,
        isValid,
        quota: {
          dailyLimit: quota.dailyLimit,
          dailyUsed: dailyUsage,
          dailyRemaining: quota.dailyLimit === -1 ? -1 : Math.max(0, quota.dailyLimit - dailyUsage),
          wordLimit: quota.wordLimit,
        },
      };
    }

    // 会员价格（与 subscription 套餐体系对齐）
    export const MEMBER_PRICES = {
      monthly: { price: 19, days: 30 },
      yearly: { price: 190, days: 365 },
    };

    // 创建订单（使用统一订单号生成）
    export async function createOrder(
      userId: string,
      productType: 'monthly' | 'yearly'
    ): Promise<{
      orderNo: string;
      amount: number;
    }> {
      const priceInfo = MEMBER_PRICES[productType];
      const orderNo = generateOrderNo();

      const order = await prisma.order.create({
        data: {
          userId: safeParseInt(userId),
          orderNo,
          productType,
          amount: priceInfo.price,
          status: 'pending',
        },
      });

      return {
        orderNo: order.orderNo,
        amount: Number(order.amount),
      };
    }

    // 模拟支付成功（委托给新支付系统）
    export async function mockPaymentSuccess(orderNo: string, userId: string) {
      return handlePaymentCallback(orderNo, 'MOCK_' + Date.now(), 'success');
    }

    // 获取订单列表
    export async function getOrders(userId: string, page: number = 1, pageSize: number = 10) {
      const uid = safeParseInt(userId);
      const total = await prisma.order.count({ where: { userId: uid } });
      const list = await prisma.order.findMany({
        where: { userId: uid },
        orderBy: { createdAt: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      });

      return {
        list,
        total,
        page,
        pageSize,
        totalPages: Math.ceil(total / pageSize),
      };
    }