Просмотр исходного кода

新增用户分享功能,添加分享后创建记录逻辑,新增mcp配置

wilhelm wong 2 месяцев назад
Родитель
Сommit
d049a29ad2

+ 28 - 0
.cursor/mcp.json

@@ -0,0 +1,28 @@
+{
+  "mcpServers": {
+    "dbhub": {
+      "command": "npx",
+      "args": [
+        "-y",
+        "@bytebase/dbhub",
+        "--transport",
+        "stdio",
+        "--dsn",
+        "mysql://zouma:2wsx%40WSX%23EDC@rdsave1o67m1ido6gwp6public.mysql.rds.aliyuncs.com:3306/junma_test"
+      ],
+      "env": {
+        "READONLY": "true"
+      }
+    },
+    "dbhub-demo": {
+      "command": "npx",
+      "args": [
+        "-y",
+        "@bytebase/dbhub",
+        "--transport",
+        "stdio",
+        "--demo"
+      ]
+    }
+  }
+}

+ 8 - 0
src/app.ts

@@ -18,7 +18,9 @@ import teamMembersRoutes from './routes/team-members.routes'
 import promotionLinkRoutes from './routes/promotion-link.routes'
 import paymentRoutes from './routes/payment.routes'
 import memberRoutes from './routes/member.routes'
+import userShareRoutes from './routes/user-invite.routes'
 import teamDomainRoutes from './routes/team-domain.routes'
+import { authenticate } from './middlewares/auth.middleware'
 
 const options: FastifyEnvOptions = {
   schema: schema,
@@ -94,6 +96,12 @@ export const createApp = async () => {
   app.register(promotionLinkRoutes, { prefix: '/api/links' })
   app.register(paymentRoutes, { prefix: '/api/payment' })
   app.register(memberRoutes, { prefix: '/api/member' })
+  app.register(userShareRoutes, { prefix: '/api/user-share' })
+
+  // 添加 /account 路由重定向到用户资料
+  app.get('/account', { onRequest: [authenticate] }, async (request, reply) => {
+    return reply.redirect('/api/users/profile')
+  })
 
   const dataSource = createDataSource(app)
   await dataSource.initialize()

+ 243 - 0
src/controllers/user-invite.controller.ts

@@ -0,0 +1,243 @@
+import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'
+import { UserShareService } from '../services/user-invite.service'
+import { authenticate } from '../middlewares/auth.middleware'
+
+export class UserShareController {
+  private userShareService: UserShareService
+
+  constructor(app: FastifyInstance) {
+    this.userShareService = new UserShareService(app)
+  }
+
+  /**
+   * 记录用户分享单片
+   */
+  async recordShare(request: FastifyRequest<{ Body: { resourceId: string; resourceName?: string } }>, reply: FastifyReply) {
+    try {
+      const user = request.user
+      if (!user) {
+        return reply.code(403).send({ message: '用户未登录' })
+      }
+
+      const { resourceId, resourceName } = request.body
+      if (!resourceId) {
+        return reply.code(400).send({ message: '资源ID不能为空' })
+      }
+
+      const shareRecord = await this.userShareService.recordShare(user.id, resourceId, resourceName)
+
+      return reply.send({
+        id: shareRecord.id,
+        resourceId: shareRecord.resourceId,
+        resourceName: shareRecord.resourceName,
+        inviterName: shareRecord.inviterName,
+        teamId: shareRecord.teamId,
+        teamName: shareRecord.teamName,
+        createdAt: shareRecord.createdAt
+      })
+    } catch (error) {
+      return reply.code(500).send({ message: '记录分享失败' })
+    }
+  }
+
+  /**
+   * 记录分享链接点击
+   */
+  async recordShareClick(request: FastifyRequest<{ 
+    Querystring: { 
+      userId: number
+      resourceId: string
+      resourceName?: string
+    } 
+  }>, reply: FastifyReply) {
+    try {
+      const { userId, resourceId, resourceName } = request.query
+      
+      if (!userId || !resourceId) {
+        return reply.code(400).send({ message: '用户ID和资源ID不能为空' })
+      }
+
+      const ip = request.ip || 
+        (request.headers['x-forwarded-for'] as string) || 
+        (request.headers['x-real-ip'] as string) || 
+        'unknown'
+
+      const shareRecord = await this.userShareService.recordShareClick(userId, resourceId, ip, resourceName)
+
+      return reply.send({
+        id: shareRecord.id,
+        inviterId: shareRecord.inviterId,
+        resourceId: shareRecord.resourceId,
+        resourceName: shareRecord.resourceName,
+        inviterName: shareRecord.inviterName,
+        teamId: shareRecord.teamId,
+        teamName: shareRecord.teamName,
+        createdAt: shareRecord.createdAt
+      })
+    } catch (error) {
+      return reply.code(500).send({ message: '记录分享点击失败' })
+    }
+  }
+
+  /**
+   * 获取分享统计
+   */
+  async getShareStats(request: FastifyRequest, reply: FastifyReply) {
+    try {
+      const user = request.user
+      if (!user) {
+        return reply.code(403).send({ message: '用户未登录' })
+      }
+
+      const stats = await this.userShareService.getUserShareStats(user.id)
+      return reply.send(stats)
+    } catch (error) {
+      return reply.code(500).send({ message: '获取分享统计失败' })
+    }
+  }
+
+  /**
+   * 获取分享记录列表
+   */
+  async getShareRecords(request: FastifyRequest<{ 
+    Querystring: { 
+      page?: number
+      size?: number
+      status?: boolean
+      startDate?: string
+      endDate?: string
+      resourceId?: string
+      inviterId?: number
+      teamId?: number
+      inviterName?: string
+      sortField?: string
+      sortOrder?: string
+    } 
+  }>, reply: FastifyReply) {
+    try {
+      const user = request.user
+      if (!user) {
+        return reply.code(403).send({ message: '用户未登录' })
+      }
+
+      const { 
+        page = 0, 
+        size = 20, 
+        status, 
+        startDate, 
+        endDate, 
+        resourceId, 
+        inviterId, 
+        teamId,
+        inviterName,
+        sortField,
+        sortOrder
+      } = request.query
+
+      // 构建筛选条件
+      const filters: any = {}
+      if (status !== undefined) filters.status = status
+      if (startDate) filters.startDate = startDate
+      if (endDate) filters.endDate = endDate
+      if (resourceId) filters.resourceId = resourceId
+      if (inviterId) filters.inviterId = inviterId
+      if (teamId) filters.teamId = teamId
+      if (inviterName) filters.inviterName = inviterName
+      if (sortField) filters.sortField = sortField
+      if (sortOrder) filters.sortOrder = sortOrder
+
+      const records = await this.userShareService.getShareRecords(user.id, page, size, filters)
+      
+      return reply.send(records)
+    } catch (error) {
+      return reply.code(500).send({ message: '获取分享记录失败' })
+    }
+  }
+
+  /**
+   * 验证邀请者
+   */
+  async validateInviter(request: FastifyRequest<{ Querystring: { inviterId: number } }>, reply: FastifyReply) {
+    try {
+      const { inviterId } = request.query
+      
+      if (!inviterId) {
+        return reply.code(400).send({ message: '邀请者ID不能为空' })
+      }
+
+      const isValid = await this.userShareService.validateInviter(inviterId)
+      
+      return reply.send({
+        valid: isValid
+      })
+    } catch (error) {
+      return reply.code(500).send({ message: '验证邀请者失败' })
+    }
+  }
+
+  /**
+   * 通过分享链接注册
+   */
+  async registerByShareLink(
+    request: FastifyRequest<{ 
+      Body: { 
+        name: string
+        password: string
+        inviterId: number
+        resourceId: string
+        resourceName?: string
+        email?: string
+        phone?: string
+      } 
+    }>, 
+    reply: FastifyReply
+  ) {
+    try {
+      const { name, password, inviterId, resourceId, resourceName, email, phone } = request.body
+      
+      if (!name || !password || !inviterId || !resourceId) {
+        return reply.code(400).send({ message: '用户名、密码、邀请者ID和资源ID不能为空' })
+      }
+
+      const ip = request.ip || 
+        (request.headers['x-forwarded-for'] as string) || 
+        (request.headers['x-real-ip'] as string) || 
+        'unknown'
+
+      const result = await this.userShareService.registerByShareLink(
+        name,
+        password,
+        inviterId,
+        resourceId,
+        ip,
+        email,
+        phone,
+        resourceName
+      )
+
+      // 生成JWT token
+      const token = await reply.jwtSign({ 
+        id: result.user.id, 
+        name: result.user.name, 
+        role: result.user.role 
+      })
+
+      return reply.code(201).send({
+        user: {
+          id: result.user.id,
+          name: result.user.name,
+          role: result.user.role
+        },
+        member: {
+          id: result.member.id,
+          vipLevel: result.member.vipLevel,
+          status: result.member.status
+        },
+        inviterId: result.inviterId,
+        token
+      })
+    } catch (error) {
+      return reply.code(400).send({ message: (error as Error).message || '注册失败' })
+    }
+  }
+}

+ 1 - 0
src/dto/income-records.dto.ts

@@ -12,6 +12,7 @@ export interface CreateIncomeRecordBody {
   payChannel: string
   payNo: string
   resourceId?: string
+  status?: boolean
 }
 
 export interface UpdateIncomeRecordBody {

+ 15 - 0
src/dto/user.dto.ts

@@ -32,3 +32,18 @@ export interface UpdateUserBody {
   password?: string
   role?: UserRole
 }
+
+export interface RecordShareBody {
+  resourceId: string
+  resourceName?: string
+}
+
+export interface RegisterByShareLinkBody {
+  name: string
+  password: string
+  inviterId: number
+  resourceId: string
+  resourceName?: string
+  email?: string
+  phone?: string
+}

+ 46 - 0
src/entities/user-invite-record.entity.ts

@@ -0,0 +1,46 @@
+import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, Index } from 'typeorm'
+
+@Entity()
+@Index('idx_inviter_user', ['inviterId', 'invitedUserId'])
+@Index('idx_resource_user', ['resourceId', 'inviterId'])
+@Index('idx_status_created', ['status', 'createdAt'])
+export class UserShareRecord {
+  @PrimaryGeneratedColumn()
+  id: number
+
+  @Column()
+  inviterId: number
+
+  @Column()
+  invitedUserId: number
+
+  @Column({ length: 100 })
+  resourceId: string
+
+  @Column({ length: 200, nullable: true })
+  resourceName: string
+
+  @Column({ length: 45, nullable: true })
+  invitedUserIp: string
+
+  @Column({ length: 100, nullable: true })
+  inviterName: string
+
+  @Column({ nullable: true })
+  teamId: number
+
+  @Column({ length: 100, nullable: true })
+  teamName: string
+
+  @Column({ default: false })
+  status: boolean
+
+  @Column({ default: false })
+  delFlag: boolean
+
+  @CreateDateColumn()
+  createdAt: Date
+
+  @UpdateDateColumn()
+  updatedAt: Date
+}

+ 70 - 0
src/routes/user-invite.routes.ts

@@ -0,0 +1,70 @@
+import { FastifyInstance } from 'fastify'
+import { UserShareController } from '../controllers/user-invite.controller'
+import { authenticate } from '../middlewares/auth.middleware'
+
+export default async function userShareRoutes(fastify: FastifyInstance) {
+  const userShareController = new UserShareController(fastify)
+
+  // 记录用户分享单片
+  fastify.post<{ Body: { resourceId: string; resourceName?: string } }>(
+    '/record',
+    { onRequest: [authenticate] },
+    userShareController.recordShare.bind(userShareController)
+  )
+
+  // 记录分享链接点击
+  fastify.get<{ Querystring: { userId: number; resourceId: string; resourceName?: string } }>(
+    '/click',
+    userShareController.recordShareClick.bind(userShareController)
+  )
+
+  // 获取分享统计
+  fastify.get(
+    '/stats',
+    { onRequest: [authenticate] },
+    userShareController.getShareStats.bind(userShareController)
+  )
+
+  // 获取分享记录列表
+  fastify.get<{ 
+    Querystring: { 
+      page?: number
+      size?: number
+      status?: boolean
+      startDate?: string
+      endDate?: string
+      resourceId?: string
+      inviterId?: number
+      teamId?: number
+      inviterName?: string
+      sortField?: string
+      sortOrder?: string
+    } 
+  }>(
+    '/records',
+    { onRequest: [authenticate] },
+    userShareController.getShareRecords.bind(userShareController)
+  )
+
+  // 验证邀请者
+  fastify.get<{ Querystring: { inviterId: number } }>(
+    '/validate',
+    userShareController.validateInviter.bind(userShareController)
+  )
+
+  // 通过分享链接注册
+  fastify.post<{ 
+    Body: { 
+      name: string
+      password: string
+      inviterId: number
+      resourceId: string
+      resourceName?: string
+      email?: string
+      phone?: string
+    } 
+  }>(
+    '/register',
+    userShareController.registerByShareLink.bind(userShareController)
+  )
+}

+ 2 - 1
src/services/income-records.service.ts

@@ -25,7 +25,8 @@ export class IncomeRecordsService {
       orderNo: data.orderNo,
       payChannel: data.payChannel,
       payNo: data.payNo,
-      resourceId: data.resourceId
+      resourceId: data.resourceId,
+      status: data.status ?? true
     })
     return this.incomeRecordsRepository.save(incomeRecord)
   }

+ 3 - 1
src/services/sys-config.service.ts

@@ -295,7 +295,9 @@ export class SysConfigService {
       { name: 'yearly', value: '150', remark: '包年会员', type: ConfigType.Number },
       { name: 'lifetime', value: '300', remark: '终生会员', type: ConfigType.Number },
       { name: 'single', value: '10', remark: '单片价格', type: ConfigType.Number },
-      { name: 'preview_duration', value: '30', remark: '试看时间(秒)', type: ConfigType.Number }
+      { name: 'preview_duration', value: '30', remark: '试看时间(秒)', type: ConfigType.Number },
+      { name: 'invite_reward_count', value: '3', remark: '邀请奖励所需次数', type: ConfigType.Number },
+      { name: 'invite_ip_limit', value: '1', remark: '同一IP每日邀请注册限制次数', type: ConfigType.Number }
     ]
 
     const configs = defaultConfigs.map(config =>

+ 606 - 0
src/services/user-invite.service.ts

@@ -0,0 +1,606 @@
+import { Repository, Between, MoreThanOrEqual, LessThanOrEqual, Like } from 'typeorm'
+import { FastifyInstance } from 'fastify'
+import { UserShareRecord } from '../entities/user-invite-record.entity'
+import { User } from '../entities/user.entity'
+import { Member } from '../entities/member.entity'
+import { Team } from '../entities/team.entity'
+import { TeamMembers } from '../entities/team-members.entity'
+import { SysConfigService } from './sys-config.service'
+import { IncomeRecordsService } from './income-records.service'
+import { IncomeRecords, IncomeType, OrderType } from '../entities/income-records.entity'
+import { PaginationResponse } from '../dto/common.dto'
+import { UserRole } from '../entities/user.entity'
+
+export class UserShareService {
+  private userShareRecordRepository: Repository<UserShareRecord>
+  private userRepository: Repository<User>
+  private memberRepository: Repository<Member>
+  private teamRepository: Repository<Team>
+  private teamMembersRepository: Repository<TeamMembers>
+  private sysConfigService: SysConfigService
+  private incomeRecordsService: IncomeRecordsService
+
+  constructor(app: FastifyInstance) {
+    this.userShareRecordRepository = app.dataSource.getRepository(UserShareRecord)
+    this.userRepository = app.dataSource.getRepository(User)
+    this.memberRepository = app.dataSource.getRepository(Member)
+    this.teamRepository = app.dataSource.getRepository(Team)
+    this.teamMembersRepository = app.dataSource.getRepository(TeamMembers)
+    this.sysConfigService = new SysConfigService(app)
+    this.incomeRecordsService = new IncomeRecordsService(app)
+  }
+
+  /**
+   * 获取用户昵称和团队信息
+   */
+  private async getUserAndTeamInfo(userId: number): Promise<{ inviterName: string; teamId: number | null; teamName: string | null }> {
+    // 获取用户信息
+    const user = await this.userRepository.findOne({ where: { id: userId } })
+    if (!user) {
+      throw new Error('用户不存在')
+    }
+
+    let teamId: number | null = null
+    let teamName: string | null = null
+
+    // 根据用户角色获取团队信息
+    if (user.role === UserRole.TEAM) {
+      // 队长从team表中获取团队信息
+      const team = await this.teamRepository.findOne({ where: { userId } })
+      if (team) {
+        teamId = team.id
+        teamName = team.name
+      }
+    } else if (user.role === UserRole.PROMOTER) {
+      // 推广员从team-members表中获取团队信息
+      const teamMember = await this.teamMembersRepository.findOne({ where: { userId } })
+      if (teamMember) {
+        teamId = teamMember.teamId
+        // 获取团队名称
+        const team = await this.teamRepository.findOne({ where: { id: teamMember.teamId } })
+        if (team) {
+          teamName = team.name
+        }
+      }
+    }
+
+    return {
+      inviterName: user.name,
+      teamId,
+      teamName
+    }
+  }
+
+  /**
+   * 记录用户分享单片
+   */
+  async recordShare(userId: number, resourceId: string, resourceName?: string): Promise<UserShareRecord> {
+    // 检查是否已经分享过该资源
+    const existingShare = await this.userShareRecordRepository.findOne({
+      where: { 
+        inviterId: userId, 
+        resourceId, 
+        delFlag: false 
+      }
+    })
+
+    if (existingShare) {
+      return existingShare
+    }
+
+    // 获取用户和团队信息
+    const { inviterName, teamId, teamName } = await this.getUserAndTeamInfo(userId)
+
+    // 创建分享记录
+    const shareRecord = this.userShareRecordRepository.create({
+      inviterId: userId,
+      resourceId,
+      resourceName,
+      inviterName,
+      teamId,
+      teamName,
+      status: true
+    } as Partial<UserShareRecord>)
+
+    const savedRecord = await this.userShareRecordRepository.save(shareRecord)
+
+    // 检查是否需要给予单片资格奖励
+    await this.checkAndRewardInviter(userId, resourceId)
+
+    return savedRecord
+  }
+
+  /**
+   * 记录分享链接点击
+   */
+  async recordShareClick(userId: number, resourceId: string, clickerIp: string, resourceName?: string): Promise<UserShareRecord> {
+    // 获取用户和团队信息
+    const { inviterName, teamId, teamName } = await this.getUserAndTeamInfo(userId)
+
+    // 校验当天是否已有相同 inviterId + resourceId + ip 的记录
+    const today = new Date()
+    today.setHours(0, 0, 0, 0)
+    const tomorrow = new Date(today)
+    tomorrow.setDate(tomorrow.getDate() + 1)
+
+    const duplicatedToday = await this.userShareRecordRepository.count({
+      where: {
+        inviterId: userId,
+        resourceId,
+        invitedUserIp: clickerIp,
+        createdAt: Between(today, tomorrow),
+        delFlag: false
+      }
+    })
+
+    // 校验邀请人在 member 表中的登录 IP 是否与当前相同
+    const inviterMember = await this.memberRepository.findOne({ where: { userId } })
+    const isSelfIp = inviterMember?.ip && inviterMember.ip === clickerIp
+
+    // 创建分享记录(重复则标记失败)
+    const shareRecord = this.userShareRecordRepository.create({
+      inviterId: userId,
+      resourceId,
+      resourceName,
+      invitedUserIp: clickerIp,
+      inviterName,
+      teamId,
+      teamName,
+      status: (duplicatedToday > 0 || isSelfIp) ? false : true
+    } as Partial<UserShareRecord>)
+
+    const savedRecord = await this.userShareRecordRepository.save(shareRecord)
+
+    // 仅对成功记录检查奖励
+    if (savedRecord.status) {
+      await this.checkAndRewardInviter(userId, resourceId)
+    }
+
+    return savedRecord
+  }
+
+  /**
+   * 通过分享链接注册用户
+   */
+  async registerByShareLink(
+    name: string,
+    password: string,
+    inviterId: number,
+    resourceId: string,
+    ip: string,
+    email?: string,
+    phone?: string,
+    resourceName?: string
+  ): Promise<{ user: User; member: Member; inviterId: number }> {
+    // 验证邀请者是否存在
+    const inviter = await this.userRepository.findOne({ where: { id: inviterId } })
+    if (!inviter) {
+      throw new Error('邀请者不存在')
+    }
+
+    // 检查IP是否已被使用(防薅羊毛)
+    const ipUsedToday = await this.checkIpUsageToday(ip)
+    if (ipUsedToday) {
+      throw new Error('该IP今日已使用过邀请注册')
+    }
+
+    // 检查用户名是否已存在
+    const existingUser = await this.userRepository.findOne({ where: { name } })
+    if (existingUser) {
+      throw new Error('用户名已存在')
+    }
+
+    // 检查邮箱是否已存在
+    if (email) {
+      const existingEmail = await this.memberRepository.findOne({ where: { email } })
+      if (existingEmail) {
+        throw new Error('邮箱已存在')
+      }
+    }
+
+    // 检查手机号是否已存在
+    if (phone) {
+      const existingPhone = await this.memberRepository.findOne({ where: { phone } })
+      if (existingPhone) {
+        throw new Error('手机号已存在')
+      }
+    }
+
+    // 创建用户
+    const hashedPassword = await require('bcryptjs').hash(password, 10)
+    const user = this.userRepository.create({
+      name,
+      password: hashedPassword,
+      role: 'user' as any,
+      parentId: inviterId
+    })
+    const savedUser = await this.userRepository.save(user)
+
+    // 创建会员记录
+    const member = this.memberRepository.create({
+      userId: savedUser.id,
+      teamId: 1, // 默认团队
+      domainId: 0,
+      email,
+      phone,
+      vipLevel: 'free' as any,
+      status: 'active' as any,
+      ip: ip || 'unknown',
+      lastLoginAt: new Date()
+    })
+    const savedMember = await this.memberRepository.save(member)
+
+    // 分享记录已在点击时创建,这里不需要再创建
+    // 只需要更新已存在的分享记录,关联被邀请用户
+    const existingShare = await this.userShareRecordRepository.findOne({
+      where: {
+        inviterId,
+        resourceId,
+        delFlag: false
+      }
+    })
+
+    if (existingShare) {
+      // 更新分享记录,关联被邀请用户
+      await this.userShareRecordRepository.update(existingShare.id, {
+        invitedUserId: savedUser.id
+      })
+    }
+
+    return {
+      user: savedUser,
+      member: savedMember,
+      inviterId
+    }
+  }
+
+  /**
+   * 检查IP今日使用情况(防薅羊毛)
+   */
+  private async checkIpUsageToday(ip: string): Promise<boolean> {
+    const today = new Date()
+    today.setHours(0, 0, 0, 0)
+    const tomorrow = new Date(today)
+    tomorrow.setDate(tomorrow.getDate() + 1)
+
+    const count = await this.userShareRecordRepository.count({
+      where: {
+        invitedUserIp: ip,
+        createdAt: Between(today, tomorrow),
+        status: true
+      }
+    })
+
+    return count > 0
+  }
+
+  /**
+   * 检查并奖励邀请者
+   */
+  private async checkAndRewardInviter(inviterId: number, resourceId?: string): Promise<void> {
+    // 获取系统配置:需要多少次邀请才能获得单片资格
+    const configs = await this.sysConfigService.getUserTeamConfigs(inviterId)
+    const inviteRewardConfig = configs.find((c: any) => c.name === 'invite_reward_count')
+    const requiredCount = inviteRewardConfig ? parseInt(inviteRewardConfig.value) : 3
+
+    // 统计用户的有效邀请数
+    const successCount = await this.userShareRecordRepository.count({
+      where: {
+        inviterId,
+        status: true,
+        delFlag: false
+      }
+    })
+
+    // 检查是否达到奖励条件
+    if (successCount >= requiredCount) {
+      // 检查是否已经给过该资源的奖励
+      const existingReward = await this.incomeRecordsService.findAll({
+        page: 0,
+        size: 1,
+        userId: inviterId,
+        incomeType: IncomeType.TIP,
+        orderType: OrderType.SINGLE_TIP,
+        resourceId: resourceId || 'invite_reward'
+      })
+
+      // 如果已经给过该资源的奖励,则不再发放
+      if (existingReward.content.length > 0) {
+        return
+      }
+
+      // 检查今日是否已达到每日限制(最多3条)
+      const today = new Date()
+      today.setHours(0, 0, 0, 0)
+      const tomorrow = new Date(today)
+      tomorrow.setDate(tomorrow.getDate() + 1)
+
+      const todayRewards = await this.incomeRecordsService.findAll({
+        page: 0,
+        size: 10, // 获取足够多的记录来检查
+        userId: inviterId,
+        incomeType: IncomeType.TIP,
+        orderType: OrderType.SINGLE_TIP,
+        startDate: today.toISOString().split('T')[0],
+        endDate: today.toISOString().split('T')[0]
+      })
+
+      // 如果今日已达到3条限制,则不再发放
+      if (todayRewards.content.length >= 3) {
+        return
+      }
+
+      // 生成正常的支付订单号
+      const orderNo = `INVITE_REWARD_${inviterId}_${Date.now()}${Math.floor(Math.random() * 10000).toString().padStart(4, '0')}`
+      
+      // 给予单片资格奖励
+      await this.incomeRecordsService.create({
+        agentId: 0,
+        userId: inviterId,
+        incomeAmount: 0, // 免费单片
+        incomeType: IncomeType.TIP,
+        resourceId: resourceId || 'invite_reward', // 使用实际的resourceId,如果没有则使用默认值
+        orderType: OrderType.SINGLE_TIP,
+        orderNo: orderNo,
+        orderPrice: 0,
+        payChannel: 'system',
+        payNo: orderNo, // 使用正常的订单号作为payNo
+        status: true
+      })
+    }
+  }
+
+  /**
+   * 获取用户的分享统计
+   */
+  async getUserShareStats(userId: number): Promise<{
+    totalShares: number
+    successShares: number
+    todayShares: number
+    hasReward: boolean
+    todayRewards: number
+    dailyLimitReached: boolean
+  }> {
+    const today = new Date()
+    today.setHours(0, 0, 0, 0)
+    const tomorrow = new Date(today)
+    tomorrow.setDate(tomorrow.getDate() + 1)
+
+    // 获取当前用户信息
+    const currentUser = await this.userRepository.findOne({ where: { id: userId } })
+    if (!currentUser) {
+      throw new Error('用户不存在')
+    }
+
+    let whereCondition: any = { delFlag: false }
+    let successWhereCondition: any = { status: true, delFlag: false }
+    let todayWhereCondition: any = {
+      createdAt: Between(today, tomorrow),
+      delFlag: false
+    }
+
+    // 根据用户角色设置查询条件
+    if (currentUser.role === 'admin') {
+      // 管理员可以查看所有记录
+      whereCondition = { delFlag: false }
+      successWhereCondition = { status: true, delFlag: false }
+      todayWhereCondition = {
+        createdAt: Between(today, tomorrow),
+        delFlag: false
+      }
+    } else if (currentUser.role === 'team') {
+      // 团队账号可以查看团队下辖邀请人的记录
+      const teamMembers = await this.userRepository.find({
+        where: { parentId: userId }
+      })
+      const teamMemberIds = teamMembers.map(member => member.id)
+      
+      if (teamMemberIds.length > 0) {
+        whereCondition = { 
+          inviterId: teamMemberIds,
+          delFlag: false 
+        }
+        successWhereCondition = { 
+          inviterId: teamMemberIds,
+          status: true, 
+          delFlag: false 
+        }
+        todayWhereCondition = {
+          inviterId: teamMemberIds,
+          createdAt: Between(today, tomorrow),
+          delFlag: false
+        }
+      } else {
+        // 如果没有下辖用户,返回0
+        whereCondition = { inviterId: -1, delFlag: false }
+        successWhereCondition = { inviterId: -1, status: true, delFlag: false }
+        todayWhereCondition = { inviterId: -1, createdAt: Between(today, tomorrow), delFlag: false }
+      }
+    } else {
+      // 推广员和普通用户只能查看自己的记录
+      whereCondition = { inviterId: userId, delFlag: false }
+      successWhereCondition = { inviterId: userId, status: true, delFlag: false }
+      todayWhereCondition = {
+        inviterId: userId,
+        createdAt: Between(today, tomorrow),
+        delFlag: false
+      }
+    }
+
+    // 总分享数
+    const totalShares = await this.userShareRecordRepository.count({
+      where: whereCondition
+    })
+
+    // 成功分享数
+    const successShares = await this.userShareRecordRepository.count({
+      where: successWhereCondition
+    })
+
+    // 今日分享数
+    const todayShares = await this.userShareRecordRepository.count({
+      where: todayWhereCondition
+    })
+
+    // 检查是否已获得奖励
+    const hasReward = await this.incomeRecordsService.findAll({
+      page: 0,
+      size: 1,
+      userId,
+      incomeType: IncomeType.TIP,
+      orderType: OrderType.SINGLE_TIP,
+      resourceId: 'invite_reward'
+    })
+
+    // 获取今日奖励数量
+    const todayRewards = await this.incomeRecordsService.findAll({
+      page: 0,
+      size: 10,
+      userId,
+      incomeType: IncomeType.TIP,
+      orderType: OrderType.SINGLE_TIP,
+      startDate: today.toISOString().split('T')[0],
+      endDate: today.toISOString().split('T')[0]
+    })
+
+    return {
+      totalShares,
+      successShares,
+      todayShares,
+      hasReward: hasReward.content.length > 0,
+      todayRewards: todayRewards.content.length,
+      dailyLimitReached: todayRewards.content.length >= 3
+    }
+  }
+
+  /**
+   * 获取分享记录列表
+   */
+  async getShareRecords(
+    userId: number,
+    page: number = 0,
+    size: number = 20,
+    filters?: {
+      status?: boolean
+      startDate?: string
+      endDate?: string
+      resourceId?: string
+      inviterId?: number
+      teamId?: number
+      inviterName?: string
+      sortField?: string
+      sortOrder?: string
+    }
+  ): Promise<PaginationResponse<UserShareRecord>> {
+    // 获取当前用户信息
+    const currentUser = await this.userRepository.findOne({ where: { id: userId } })
+    if (!currentUser) {
+      throw new Error('用户不存在')
+    }
+
+    let whereCondition: any = { delFlag: false }
+
+    // 根据用户角色设置查询条件
+    if (currentUser.role === 'admin') {
+      // 管理员可以查看所有记录
+      whereCondition = { delFlag: false }
+    } else if (currentUser.role === 'team') {
+      // 团队账号可以查看团队下辖邀请人的记录
+      // 查找所有parentId为当前团队ID的用户(推广员和普通用户)
+      const teamMembers = await this.userRepository.find({
+        where: { parentId: userId }
+      })
+      const teamMemberIds = teamMembers.map(member => member.id)
+      
+      if (teamMemberIds.length > 0) {
+        whereCondition = { 
+          inviterId: teamMemberIds,
+          delFlag: false 
+        }
+      } else {
+        // 如果没有下辖用户,返回空结果
+        whereCondition = { inviterId: -1, delFlag: false }
+      }
+    } else {
+      // 推广员和普通用户只能查看自己的记录
+      whereCondition = { inviterId: userId, delFlag: false }
+    }
+
+    // 应用筛选条件
+    if (filters) {
+      if (filters.status !== undefined) {
+        // 将布尔值转换为数字:true -> 1, false -> 0
+        whereCondition.status = filters.status ? 1 : 0
+      }
+      
+      if (filters.resourceId) {
+        whereCondition.resourceId = filters.resourceId
+      }
+      
+      if (filters.inviterId) {
+        whereCondition.inviterId = filters.inviterId
+      }
+      
+      if (filters.teamId) {
+        whereCondition.teamId = filters.teamId
+      }
+      
+      // 邀请者姓名筛选
+      if (filters.inviterName) {
+        whereCondition.inviterName = Like(`%${filters.inviterName}%`)
+      }
+      
+      // 时间范围筛选
+      if (filters.startDate || filters.endDate) {
+        const startDate = filters.startDate ? new Date(filters.startDate) : undefined
+        const endDate = filters.endDate ? new Date(filters.endDate) : undefined
+        
+        if (endDate) {
+          endDate.setHours(23, 59, 59, 999) // 设置为当天的最后一刻
+        }
+        
+        if (startDate && endDate) {
+          whereCondition.createdAt = Between(startDate, endDate)
+        } else if (startDate) {
+          whereCondition.createdAt = MoreThanOrEqual(startDate)
+        } else if (endDate) {
+          whereCondition.createdAt = LessThanOrEqual(endDate)
+        }
+      }
+    }
+
+    // 构建排序条件
+    let orderCondition: any = { createdAt: 'DESC' } // 默认按创建时间倒序
+    
+    if (filters?.sortField) {
+      const sortOrder = filters.sortOrder === '1' ? 'ASC' : 'DESC'
+      orderCondition = { [filters.sortField]: sortOrder }
+    }
+
+    const [records, total] = await this.userShareRecordRepository.findAndCount({
+      where: whereCondition,
+      skip: page * size,
+      take: size,
+      order: orderCondition
+    })
+
+    return {
+      content: records,
+      metadata: {
+        total: Number(total),
+        page: Number(page),
+        size: Number(size)
+      }
+    }
+  }
+
+  /**
+   * 验证邀请者是否存在
+   */
+  async validateInviter(inviterId: number): Promise<boolean> {
+    const inviter = await this.userRepository.findOne({
+      where: { id: inviterId }
+    })
+    return !!inviter
+  }
+}