瀏覽代碼

团队统计页面增加ip成交率字段

wilhelm wong 2 月之前
父節點
當前提交
d31fadeb0f
共有 3 個文件被更改,包括 107 次插入1 次删除
  1. 16 0
      src/controllers/team.controller.ts
  2. 7 0
      src/routes/team.routes.ts
  3. 84 1
      src/services/team.service.ts

+ 16 - 0
src/controllers/team.controller.ts

@@ -148,4 +148,20 @@ export class TeamController {
       return reply.code(404).send({ message: '团队不存在' })
     }
   }
+
+  async getIpConversionRate(request: FastifyRequest, reply: FastifyReply) {
+    try {
+      const user = request.user
+      if (!user) {
+        return reply.code(403).send({ message: '用户未登录' })
+      }
+      if (user.role !== UserRole.TEAM) {
+        return reply.code(403).send({ message: '只有团队用户可以使用此接口' })
+      }
+      const statistics = await this.teamService.getIpConversionRate(user.id)
+      return reply.send(statistics)
+    } catch (error) {
+      return reply.code(500).send({ message: '获取IP成交率失败' })
+    }
+  }
 }

+ 7 - 0
src/routes/team.routes.ts

@@ -75,4 +75,11 @@ export default async function teamRoutes(fastify: FastifyInstance) {
     { onRequest: [authenticate, hasAnyRole(UserRole.ADMIN, UserRole.TEAM)] },
     teamController.findByUserId.bind(teamController)
   )
+
+  // 获取团队IP成交率统计
+  fastify.get(
+    '/statistics/ip-conversion-rate',
+    { onRequest: [authenticate, hasRole(UserRole.TEAM)] },
+    teamController.getIpConversionRate.bind(teamController)
+  )
 }

+ 84 - 1
src/services/team.service.ts

@@ -1,4 +1,4 @@
-import { Repository, Like } from 'typeorm'
+import { Repository, Like, Between } from 'typeorm'
 import { FastifyInstance } from 'fastify'
 import { Team } from '../entities/team.entity'
 import { IncomeRecords, IncomeType } from '../entities/income-records.entity'
@@ -453,4 +453,87 @@ export class TeamService {
   async findByUserId(userId: number): Promise<Team> {
     return this.teamRepository.findOneOrFail({ where: { userId } })
   }
+
+  /**
+   * 获取团队IP成交率统计
+   * 今日IP成交率 = 今日付费用户数 / 今日登录用户数
+   * 总IP成交率 = 总付费用户数 / 总用户数
+   */
+  async getIpConversionRate(userId: number): Promise<{
+    todayIpConversionRate: number
+    totalIpConversionRate: number
+    todayPaidUsers: number
+    todayLoginUsers: number
+    totalPaidUsers: number
+    totalUsers: number
+  }> {
+    // 获取团队信息
+    const team = await this.findByUserId(userId)
+    const teamId = team.id
+
+    // 获取今天的开始时间(使用本地时区),并使用半开区间 [today, tomorrow)
+    const today = new Date()
+    today.setHours(0, 0, 0, 0)
+    const tomorrow = new Date(today)
+    tomorrow.setDate(tomorrow.getDate() + 1)
+
+    // 统计今日登录用户数(基于会员的lastLoginAt字段)
+    const todayLoginUsers = await this.memberRepository.count({
+      where: {
+        teamId: teamId,
+        lastLoginAt: Between(today, tomorrow)
+      }
+    })
+
+    // 统计总用户数(基于会员的teamId)
+    const totalUsers = await this.memberRepository.count({
+      where: {
+        teamId: teamId
+      }
+    })
+
+    // 统计今日付费用户数(通过IncomeRecords表,查询今日有付费记录的用户,去重)
+    const todayPaidUsersResult = await this.incomeRecordsRepository
+      .createQueryBuilder('record')
+      .innerJoin('member', 'm', 'm.userId = record.userId')
+      .select('COUNT(DISTINCT record.userId)', 'count')
+      .where('m.teamId = :teamId', { teamId })
+      .andWhere('record.createdAt >= :today', { today })
+      .andWhere('record.createdAt < :tomorrow', { tomorrow })
+      .andWhere('record.delFlag = :delFlag', { delFlag: false })
+      .andWhere('record.status = :status', { status: true })
+      .getRawOne()
+
+    const todayPaidUsers = Number(todayPaidUsersResult?.count) || 0
+
+    // 统计总付费用户数(通过IncomeRecords表,查询有付费记录的用户,去重)
+    const totalPaidUsersResult = await this.incomeRecordsRepository
+      .createQueryBuilder('record')
+      .innerJoin('member', 'm', 'm.userId = record.userId')
+      .select('COUNT(DISTINCT record.userId)', 'count')
+      .where('m.teamId = :teamId', { teamId })
+      .andWhere('record.delFlag = :delFlag', { delFlag: false })
+      .andWhere('record.status = :status', { status: true })
+      .getRawOne()
+
+    const totalPaidUsers = Number(totalPaidUsersResult?.count) || 0
+
+    // 计算成交率(避免除零错误)
+    const todayIpConversionRate = todayLoginUsers > 0 
+      ? Number((todayPaidUsers / todayLoginUsers).toFixed(4)) 
+      : 0
+
+    const totalIpConversionRate = totalUsers > 0 
+      ? Number((totalPaidUsers / totalUsers).toFixed(4)) 
+      : 0
+
+    return {
+      todayIpConversionRate,
+      totalIpConversionRate,
+      todayPaidUsers,
+      todayLoginUsers,
+      totalPaidUsers,
+      totalUsers
+    }
+  }
 }