|
|
@@ -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
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|