|
@@ -9,16 +9,23 @@ import {
|
|
|
import { UserRole } from '../entities/user.entity'
|
|
import { UserRole } from '../entities/user.entity'
|
|
|
import { TeamMembersService } from '../services/team-members.service'
|
|
import { TeamMembersService } from '../services/team-members.service'
|
|
|
import { TeamService } from '../services/team.service'
|
|
import { TeamService } from '../services/team.service'
|
|
|
|
|
+import { Repository, Between } from 'typeorm'
|
|
|
|
|
+import { Member } from '../entities/member.entity'
|
|
|
|
|
+import { IncomeRecords } from '../entities/income-records.entity'
|
|
|
|
|
|
|
|
export class TeamDomainController {
|
|
export class TeamDomainController {
|
|
|
private teamDomainService: TeamDomainService
|
|
private teamDomainService: TeamDomainService
|
|
|
private teamMembersService: TeamMembersService
|
|
private teamMembersService: TeamMembersService
|
|
|
private teamService: TeamService
|
|
private teamService: TeamService
|
|
|
|
|
+ private memberRepository: Repository<Member>
|
|
|
|
|
+ private incomeRecordsRepository: Repository<IncomeRecords>
|
|
|
|
|
|
|
|
constructor(app: FastifyInstance) {
|
|
constructor(app: FastifyInstance) {
|
|
|
this.teamDomainService = new TeamDomainService(app)
|
|
this.teamDomainService = new TeamDomainService(app)
|
|
|
this.teamMembersService = new TeamMembersService(app)
|
|
this.teamMembersService = new TeamMembersService(app)
|
|
|
this.teamService = new TeamService(app)
|
|
this.teamService = new TeamService(app)
|
|
|
|
|
+ this.memberRepository = app.dataSource.getRepository(Member)
|
|
|
|
|
+ this.incomeRecordsRepository = app.dataSource.getRepository(IncomeRecords)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async create(request: FastifyRequest<{ Body: CreateTeamDomainBody }>, reply: FastifyReply) {
|
|
async create(request: FastifyRequest<{ Body: CreateTeamDomainBody }>, reply: FastifyReply) {
|
|
@@ -177,35 +184,40 @@ export class TeamDomainController {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const { domain } = request.query
|
|
const { domain } = request.query
|
|
|
- let result = await this.teamDomainService.getDailyStatistics(domain)
|
|
|
|
|
|
|
|
|
|
if (user.role === UserRole.PROMOTER) {
|
|
if (user.role === UserRole.PROMOTER) {
|
|
|
- // 推广用户只能查看自己绑定的域名统计
|
|
|
|
|
|
|
+ // 推广用户只能查看自己分得的金额统计
|
|
|
try {
|
|
try {
|
|
|
const teamMembers = await this.teamMembersService.findByUserId(user.id)
|
|
const teamMembers = await this.teamMembersService.findByUserId(user.id)
|
|
|
const teamDomains = await this.teamDomainService.findByTeamMemberId(teamMembers.id)
|
|
const teamDomains = await this.teamDomainService.findByTeamMemberId(teamMembers.id)
|
|
|
const teamDomainIds = teamDomains.map(d => d.id)
|
|
const teamDomainIds = teamDomains.map(d => d.id)
|
|
|
|
|
|
|
|
- // 过滤结果,只保留推广用户绑定的域名统计
|
|
|
|
|
- result = result.filter(stat => teamDomainIds.includes(stat.id))
|
|
|
|
|
|
|
+ // 获取推广用户相关的域名统计(只统计personalIncomeAmount)
|
|
|
|
|
+ const result = await this.getPromoterDailyStatistics(teamDomainIds, domain)
|
|
|
|
|
+ return reply.send(result)
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
// 如果推广用户没有团队成员记录,返回空统计
|
|
// 如果推广用户没有团队成员记录,返回空统计
|
|
|
- result = []
|
|
|
|
|
|
|
+ return reply.send([])
|
|
|
}
|
|
}
|
|
|
- } else if (user.role === UserRole.TEAM) {
|
|
|
|
|
- // 团队用户只能查看该团队的域名统计
|
|
|
|
|
- const team = await this.teamService.findByUserId(user.id)
|
|
|
|
|
- if (team) {
|
|
|
|
|
- const teamDomains = await this.teamDomainService.findByTeamId(team.id)
|
|
|
|
|
- const teamDomainIds = teamDomains.map(d => d.id)
|
|
|
|
|
-
|
|
|
|
|
- result = result.filter(stat => teamDomainIds.includes(stat.id))
|
|
|
|
|
- } else {
|
|
|
|
|
- result = []
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 管理员和团队用户使用原有逻辑
|
|
|
|
|
+ let result = await this.teamDomainService.getDailyStatistics(domain)
|
|
|
|
|
+
|
|
|
|
|
+ if (user.role === UserRole.TEAM) {
|
|
|
|
|
+ // 团队用户只能查看该团队的域名统计
|
|
|
|
|
+ const team = await this.teamService.findByUserId(user.id)
|
|
|
|
|
+ if (team) {
|
|
|
|
|
+ const teamDomains = await this.teamDomainService.findByTeamId(team.id)
|
|
|
|
|
+ const teamDomainIds = teamDomains.map(d => d.id)
|
|
|
|
|
+
|
|
|
|
|
+ result = result.filter(stat => teamDomainIds.includes(stat.id))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ result = []
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- return reply.send(result)
|
|
|
|
|
|
|
+ return reply.send(result)
|
|
|
|
|
+ }
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
return reply.code(500).send({ message: '获取每日统计数据失败' })
|
|
return reply.code(500).send({ message: '获取每日统计数据失败' })
|
|
|
}
|
|
}
|
|
@@ -219,37 +231,261 @@ export class TeamDomainController {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const { domain } = request.query
|
|
const { domain } = request.query
|
|
|
- let result = await this.teamDomainService.getAllStatistics(domain)
|
|
|
|
|
|
|
|
|
|
if (user.role === UserRole.PROMOTER) {
|
|
if (user.role === UserRole.PROMOTER) {
|
|
|
- // 推广用户只能查看自己绑定的域名统计
|
|
|
|
|
|
|
+ // 推广用户只能查看自己分得的金额统计
|
|
|
try {
|
|
try {
|
|
|
const teamMembers = await this.teamMembersService.findByUserId(user.id)
|
|
const teamMembers = await this.teamMembersService.findByUserId(user.id)
|
|
|
const teamDomains = await this.teamDomainService.findByTeamMemberId(teamMembers.id)
|
|
const teamDomains = await this.teamDomainService.findByTeamMemberId(teamMembers.id)
|
|
|
const teamDomainIds = teamDomains.map(d => d.id)
|
|
const teamDomainIds = teamDomains.map(d => d.id)
|
|
|
|
|
|
|
|
- // 过滤结果,只保留推广用户绑定的域名统计
|
|
|
|
|
- result = result.filter(stat => teamDomainIds.includes(stat.id))
|
|
|
|
|
|
|
+ // 获取推广用户相关的域名统计(只统计personalIncomeAmount)
|
|
|
|
|
+ const result = await this.getPromoterAllStatistics(teamDomainIds, domain)
|
|
|
|
|
+ return reply.send(result)
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
// 如果推广用户没有团队成员记录,返回空统计
|
|
// 如果推广用户没有团队成员记录,返回空统计
|
|
|
- result = []
|
|
|
|
|
|
|
+ return reply.send([])
|
|
|
}
|
|
}
|
|
|
- } else if (user.role === UserRole.TEAM) {
|
|
|
|
|
- // 团队用户只能查看该团队的域名统计
|
|
|
|
|
- const team = await this.teamService.findByUserId(user.id)
|
|
|
|
|
- if (team) {
|
|
|
|
|
- const teamDomains = await this.teamDomainService.findByTeamId(team.id)
|
|
|
|
|
- const teamDomainIds = teamDomains.map(d => d.id)
|
|
|
|
|
-
|
|
|
|
|
- result = result.filter(stat => teamDomainIds.includes(stat.id))
|
|
|
|
|
- } else {
|
|
|
|
|
- result = []
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 管理员和团队用户使用原有逻辑
|
|
|
|
|
+ let result = await this.teamDomainService.getAllStatistics(domain)
|
|
|
|
|
+
|
|
|
|
|
+ if (user.role === UserRole.TEAM) {
|
|
|
|
|
+ // 团队用户只能查看该团队的域名统计
|
|
|
|
|
+ const team = await this.teamService.findByUserId(user.id)
|
|
|
|
|
+ if (team) {
|
|
|
|
|
+ const teamDomains = await this.teamDomainService.findByTeamId(team.id)
|
|
|
|
|
+ const teamDomainIds = teamDomains.map(d => d.id)
|
|
|
|
|
+
|
|
|
|
|
+ result = result.filter(stat => teamDomainIds.includes(stat.id))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ result = []
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- return reply.send(result)
|
|
|
|
|
|
|
+ return reply.send(result)
|
|
|
|
|
+ }
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
return reply.code(500).send({ message: '获取总统计数据失败' })
|
|
return reply.code(500).send({ message: '获取总统计数据失败' })
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取推广用户每日统计(只统计personalIncomeAmount)
|
|
|
|
|
+ */
|
|
|
|
|
+ private async getPromoterDailyStatistics(domainIds: number[], domain?: string): Promise<any[]> {
|
|
|
|
|
+ if (domainIds.length === 0) {
|
|
|
|
|
+ return []
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取今天的开始和结束时间(使用UTC时区)
|
|
|
|
|
+ const today = new Date()
|
|
|
|
|
+ today.setUTCHours(0, 0, 0, 0)
|
|
|
|
|
+ const tomorrow = new Date(today)
|
|
|
|
|
+ tomorrow.setUTCDate(tomorrow.getUTCDate() + 1)
|
|
|
|
|
+
|
|
|
|
|
+ const results: any[] = []
|
|
|
|
|
+
|
|
|
|
|
+ for (const domainId of domainIds) {
|
|
|
|
|
+ // 获取域名信息
|
|
|
|
|
+ const teamDomain = await this.teamDomainService.findById(domainId)
|
|
|
|
|
+
|
|
|
|
|
+ // 如果指定了域名过滤,跳过不匹配的域名
|
|
|
|
|
+ if (domain && !teamDomain.domain.includes(domain)) {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 统计今日新增用户数
|
|
|
|
|
+ const todayNewUsers = await this.memberRepository.count({
|
|
|
|
|
+ where: {
|
|
|
|
|
+ domainId: domainId,
|
|
|
|
|
+ createdAt: Between(today, tomorrow)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 统计今日活跃用户数(基于lastLoginAt字段)
|
|
|
|
|
+ const todayActiveUsers = await this.memberRepository.count({
|
|
|
|
|
+ where: {
|
|
|
|
|
+ domainId: domainId,
|
|
|
|
|
+ lastLoginAt: Between(today, tomorrow)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 统计今日收入(只统计personalIncomeAmount)
|
|
|
|
|
+ const todayIncomeRecords = await this.incomeRecordsRepository
|
|
|
|
|
+ .createQueryBuilder('record')
|
|
|
|
|
+ .innerJoin('member', 'm', 'm.userId = record.userId')
|
|
|
|
|
+ .select('SUM(CAST(record.personalIncomeAmount AS DECIMAL(10,5)))', 'totalIncome')
|
|
|
|
|
+ .where('m.domainId = :domainId', { domainId: domainId })
|
|
|
|
|
+ .andWhere('record.createdAt >= :startDate', { startDate: today })
|
|
|
|
|
+ .andWhere('record.createdAt < :endDate', { endDate: tomorrow })
|
|
|
|
|
+ .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
|
|
+ .andWhere('record.status = :status', { status: true })
|
|
|
|
|
+ .andWhere('record.personalIncomeAmount > 0')
|
|
|
|
|
+ .getRawOne()
|
|
|
|
|
+
|
|
|
|
|
+ const todayIncome = todayIncomeRecords?.totalIncome ? parseFloat(todayIncomeRecords.totalIncome) : 0
|
|
|
|
|
+
|
|
|
|
|
+ // 统计历史总收入(只统计personalIncomeAmount)
|
|
|
|
|
+ const totalIncomeRecords = await this.incomeRecordsRepository
|
|
|
|
|
+ .createQueryBuilder('record')
|
|
|
|
|
+ .innerJoin('member', 'm', 'm.userId = record.userId')
|
|
|
|
|
+ .select('SUM(CAST(record.personalIncomeAmount AS DECIMAL(10,5)))', 'totalIncome')
|
|
|
|
|
+ .where('m.domainId = :domainId', { domainId: domainId })
|
|
|
|
|
+ .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
|
|
+ .andWhere('record.status = :status', { status: true })
|
|
|
|
|
+ .andWhere('record.personalIncomeAmount > 0')
|
|
|
|
|
+ .getRawOne()
|
|
|
|
|
+
|
|
|
|
|
+ const totalIncome = totalIncomeRecords?.totalIncome ? parseFloat(totalIncomeRecords.totalIncome) : 0
|
|
|
|
|
+
|
|
|
|
|
+ // 统计历史总销售额
|
|
|
|
|
+ const totalSalesRecords = await this.incomeRecordsRepository
|
|
|
|
|
+ .createQueryBuilder('record')
|
|
|
|
|
+ .innerJoin('member', 'm', 'm.userId = record.userId')
|
|
|
|
|
+ .select('SUM(CAST(record.orderPrice AS DECIMAL(10,5)))', 'totalSales')
|
|
|
|
|
+ .where('m.domainId = :domainId', { domainId: domainId })
|
|
|
|
|
+ .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
|
|
+ .andWhere('record.status = :status', { status: true })
|
|
|
|
|
+ .getRawOne()
|
|
|
|
|
+
|
|
|
|
|
+ const totalSales = totalSalesRecords?.totalSales ? parseFloat(totalSalesRecords.totalSales) : 0
|
|
|
|
|
+
|
|
|
|
|
+ // 统计今日销售额
|
|
|
|
|
+ const todaySalesRecords = await this.incomeRecordsRepository
|
|
|
|
|
+ .createQueryBuilder('record')
|
|
|
|
|
+ .innerJoin('member', 'm', 'm.userId = record.userId')
|
|
|
|
|
+ .select('SUM(CAST(record.orderPrice AS DECIMAL(10,5)))', 'totalSales')
|
|
|
|
|
+ .where('m.domainId = :domainId', { domainId: domainId })
|
|
|
|
|
+ .andWhere('record.createdAt >= :startDate', { startDate: today })
|
|
|
|
|
+ .andWhere('record.createdAt < :endDate', { endDate: tomorrow })
|
|
|
|
|
+ .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
|
|
+ .andWhere('record.status = :status', { status: true })
|
|
|
|
|
+ .getRawOne()
|
|
|
|
|
+
|
|
|
|
|
+ const todaySales = todaySalesRecords?.totalSales ? parseFloat(todaySalesRecords.totalSales) : 0
|
|
|
|
|
+
|
|
|
|
|
+ results.push({
|
|
|
|
|
+ id: teamDomain.id,
|
|
|
|
|
+ domain: teamDomain.domain,
|
|
|
|
|
+ todayNewUsers,
|
|
|
|
|
+ todayIncome,
|
|
|
|
|
+ todayActiveUsers,
|
|
|
|
|
+ totalIncome,
|
|
|
|
|
+ totalSales,
|
|
|
|
|
+ todaySales
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return results
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取推广用户总统计(只统计personalIncomeAmount)
|
|
|
|
|
+ */
|
|
|
|
|
+ private async getPromoterAllStatistics(domainIds: number[], domain?: string): Promise<any[]> {
|
|
|
|
|
+ if (domainIds.length === 0) {
|
|
|
|
|
+ return []
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取今天的开始和结束时间(使用UTC时区)
|
|
|
|
|
+ const today = new Date()
|
|
|
|
|
+ today.setUTCHours(0, 0, 0, 0)
|
|
|
|
|
+ const tomorrow = new Date(today)
|
|
|
|
|
+ tomorrow.setUTCDate(tomorrow.getUTCDate() + 1)
|
|
|
|
|
+
|
|
|
|
|
+ const results: any[] = []
|
|
|
|
|
+
|
|
|
|
|
+ for (const domainId of domainIds) {
|
|
|
|
|
+ // 获取域名信息
|
|
|
|
|
+ const teamDomain = await this.teamDomainService.findById(domainId)
|
|
|
|
|
+
|
|
|
|
|
+ // 如果指定了域名过滤,跳过不匹配的域名
|
|
|
|
|
+ if (domain && !teamDomain.domain.includes(domain)) {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 统计总新增用户数
|
|
|
|
|
+ const totalNewUsers = await this.memberRepository.count({
|
|
|
|
|
+ where: {
|
|
|
|
|
+ domainId: domainId
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 统计今日活跃用户数(基于lastLoginAt字段)
|
|
|
|
|
+ const todayActiveUsers = await this.memberRepository.count({
|
|
|
|
|
+ where: {
|
|
|
|
|
+ domainId: domainId,
|
|
|
|
|
+ lastLoginAt: Between(today, tomorrow)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 统计总收入(只统计personalIncomeAmount)
|
|
|
|
|
+ const totalIncomeRecords = await this.incomeRecordsRepository
|
|
|
|
|
+ .createQueryBuilder('record')
|
|
|
|
|
+ .innerJoin('member', 'm', 'm.userId = record.userId')
|
|
|
|
|
+ .select('SUM(CAST(record.personalIncomeAmount AS DECIMAL(10,5)))', 'totalIncome')
|
|
|
|
|
+ .where('m.domainId = :domainId', { domainId: domainId })
|
|
|
|
|
+ .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
|
|
+ .andWhere('record.status = :status', { status: true })
|
|
|
|
|
+ .andWhere('record.personalIncomeAmount > 0')
|
|
|
|
|
+ .getRawOne()
|
|
|
|
|
+
|
|
|
|
|
+ const totalIncome = totalIncomeRecords?.totalIncome ? parseFloat(totalIncomeRecords.totalIncome) : 0
|
|
|
|
|
+
|
|
|
|
|
+ // 统计今日收入(只统计personalIncomeAmount)
|
|
|
|
|
+ const todayIncomeRecords = await this.incomeRecordsRepository
|
|
|
|
|
+ .createQueryBuilder('record')
|
|
|
|
|
+ .innerJoin('member', 'm', 'm.userId = record.userId')
|
|
|
|
|
+ .select('SUM(CAST(record.personalIncomeAmount AS DECIMAL(10,5)))', 'totalIncome')
|
|
|
|
|
+ .where('m.domainId = :domainId', { domainId: domainId })
|
|
|
|
|
+ .andWhere('record.createdAt >= :startDate', { startDate: today })
|
|
|
|
|
+ .andWhere('record.createdAt < :endDate', { endDate: tomorrow })
|
|
|
|
|
+ .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
|
|
+ .andWhere('record.status = :status', { status: true })
|
|
|
|
|
+ .andWhere('record.personalIncomeAmount > 0')
|
|
|
|
|
+ .getRawOne()
|
|
|
|
|
+
|
|
|
|
|
+ const todayIncome = todayIncomeRecords?.totalIncome ? parseFloat(todayIncomeRecords.totalIncome) : 0
|
|
|
|
|
+
|
|
|
|
|
+ // 统计历史总销售额
|
|
|
|
|
+ const totalSalesRecords = await this.incomeRecordsRepository
|
|
|
|
|
+ .createQueryBuilder('record')
|
|
|
|
|
+ .innerJoin('member', 'm', 'm.userId = record.userId')
|
|
|
|
|
+ .select('SUM(CAST(record.orderPrice AS DECIMAL(10,5)))', 'totalSales')
|
|
|
|
|
+ .where('m.domainId = :domainId', { domainId: domainId })
|
|
|
|
|
+ .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
|
|
+ .andWhere('record.status = :status', { status: true })
|
|
|
|
|
+ .getRawOne()
|
|
|
|
|
+
|
|
|
|
|
+ const totalSales = totalSalesRecords?.totalSales ? parseFloat(totalSalesRecords.totalSales) : 0
|
|
|
|
|
+
|
|
|
|
|
+ // 统计今日销售额
|
|
|
|
|
+ const todaySalesRecords = await this.incomeRecordsRepository
|
|
|
|
|
+ .createQueryBuilder('record')
|
|
|
|
|
+ .innerJoin('member', 'm', 'm.userId = record.userId')
|
|
|
|
|
+ .select('SUM(CAST(record.orderPrice AS DECIMAL(10,5)))', 'totalSales')
|
|
|
|
|
+ .where('m.domainId = :domainId', { domainId: domainId })
|
|
|
|
|
+ .andWhere('record.createdAt >= :startDate', { startDate: today })
|
|
|
|
|
+ .andWhere('record.createdAt < :endDate', { endDate: tomorrow })
|
|
|
|
|
+ .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
|
|
+ .andWhere('record.status = :status', { status: true })
|
|
|
|
|
+ .getRawOne()
|
|
|
|
|
+
|
|
|
|
|
+ const todaySales = todaySalesRecords?.totalSales ? parseFloat(todaySalesRecords.totalSales) : 0
|
|
|
|
|
+
|
|
|
|
|
+ results.push({
|
|
|
|
|
+ id: teamDomain.id,
|
|
|
|
|
+ domain: teamDomain.domain,
|
|
|
|
|
+ totalNewUsers,
|
|
|
|
|
+ totalIncome,
|
|
|
|
|
+ todayActiveUsers,
|
|
|
|
|
+ todayIncome,
|
|
|
|
|
+ totalSales,
|
|
|
|
|
+ todaySales
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return results
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|