|
|
@@ -1,18 +1,21 @@
|
|
|
-import { Repository, Like, Not } from 'typeorm'
|
|
|
+import { Repository, Like, Not, In } from 'typeorm'
|
|
|
import { FastifyInstance } from 'fastify'
|
|
|
import { TeamMembers } from '../entities/team-members.entity'
|
|
|
import { IncomeRecords } from '../entities/income-records.entity'
|
|
|
import { Member } from '../entities/member.entity'
|
|
|
import { TeamDomain } from '../entities/team-domain.entity'
|
|
|
import { Team } from '../entities/team.entity'
|
|
|
+import { User } from '../entities/user.entity'
|
|
|
+import { AgentCommissionIndex } from '../entities/agent-commission-index.entity'
|
|
|
import { PaginationResponse } from '../dto/common.dto'
|
|
|
-import { CreateTeamMembersBody, UpdateTeamMembersBody, ListTeamMembersQuery, TeamMemberStatsQuery, TeamMemberStatsResponse, TeamLeaderStatsQuery, TeamLeaderStatsResponse } from '../dto/team-members.dto'
|
|
|
+import { CreateTeamMembersBody, UpdateTeamMembersBody, ListTeamMembersQuery, TeamMemberStatsQuery, TeamMemberStatsResponse, TeamLeaderStatsQuery, TeamLeaderStatsResponse, TeamMemberTreeNode, TeamMemberStatsTreeNode } from '../dto/team-members.dto'
|
|
|
import { UserService } from './user.service'
|
|
|
import { UserRole } from '../entities/user.entity'
|
|
|
import * as randomstring from 'randomstring'
|
|
|
import { SysConfigService } from './sys-config.service'
|
|
|
import { PromotionLinkService } from './promotion-link.service'
|
|
|
import { LinkType } from '../entities/promotion-link.entity'
|
|
|
+import { MultiLevelCommissionService } from './multi-level-commission.service'
|
|
|
|
|
|
export class TeamMembersService {
|
|
|
private teamMembersRepository: Repository<TeamMembers>
|
|
|
@@ -21,8 +24,11 @@ export class TeamMembersService {
|
|
|
private userService: UserService
|
|
|
private teamDomainRepository: Repository<TeamDomain>
|
|
|
private teamRepository: Repository<Team>
|
|
|
+ private userRepository: Repository<User>
|
|
|
+ private indexRepository: Repository<AgentCommissionIndex>
|
|
|
private sysConfigService: SysConfigService
|
|
|
private promotionLinkService: PromotionLinkService
|
|
|
+ private multiLevelCommissionService: MultiLevelCommissionService
|
|
|
private app: FastifyInstance
|
|
|
|
|
|
constructor(app: FastifyInstance) {
|
|
|
@@ -33,8 +39,11 @@ export class TeamMembersService {
|
|
|
this.userService = new UserService(app)
|
|
|
this.teamDomainRepository = app.dataSource.getRepository(TeamDomain)
|
|
|
this.teamRepository = app.dataSource.getRepository(Team)
|
|
|
+ this.userRepository = app.dataSource.getRepository(User)
|
|
|
+ this.indexRepository = app.dataSource.getRepository(AgentCommissionIndex)
|
|
|
this.sysConfigService = new SysConfigService(app)
|
|
|
this.promotionLinkService = new PromotionLinkService(app)
|
|
|
+ this.multiLevelCommissionService = new MultiLevelCommissionService(app)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -42,8 +51,14 @@ export class TeamMembersService {
|
|
|
* @param teamId 团队ID
|
|
|
* @param commissionRate 个人分成比例
|
|
|
* @param excludeMemberId 排除的成员ID(用于更新时排除自己)
|
|
|
+ * @param parentMemberId 父级团队成员ID(如果有父级,则验证不能大于父级比例)
|
|
|
*/
|
|
|
- private async validateCommissionRate(teamId: number, commissionRate: number, excludeMemberId?: number): Promise<void> {
|
|
|
+ private async validateCommissionRate(
|
|
|
+ teamId: number,
|
|
|
+ commissionRate: number,
|
|
|
+ excludeMemberId?: number,
|
|
|
+ parentMemberId?: number | null
|
|
|
+ ): Promise<void> {
|
|
|
if (commissionRate <= 0) {
|
|
|
return // 0或负数不需要验证
|
|
|
}
|
|
|
@@ -63,24 +78,81 @@ export class TeamMembersService {
|
|
|
if (commissionRate > teamCommissionRate) {
|
|
|
throw new Error(`个人分成比例(${commissionRate}%)不能高于团队分成比例(${teamCommissionRate}%)`)
|
|
|
}
|
|
|
+
|
|
|
+ // 如果有父级团队成员,验证不能大于父级的分成比例
|
|
|
+ if (parentMemberId !== undefined && parentMemberId !== null) {
|
|
|
+ const parentMember = await this.teamMembersRepository.findOne({ where: { id: parentMemberId } })
|
|
|
+ if (parentMember) {
|
|
|
+ const parentCommissionRate = Number(parentMember.commissionRate)
|
|
|
+ if (parentCommissionRate > 0 && commissionRate > parentCommissionRate) {
|
|
|
+ throw new Error(`个人分成比例(${commissionRate}%)不能高于上级分成比例(${parentCommissionRate}%)`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async create(data: CreateTeamMembersBody, creatorId: number): Promise<TeamMembers> {
|
|
|
- const { password, teamUserId, ...teamMemberData } = data
|
|
|
+ const { password, teamUserId, memberId, ...teamMemberData } = data
|
|
|
|
|
|
- // 验证分成比例
|
|
|
- if (teamMemberData.commissionRate !== undefined && teamMemberData.commissionRate > 0) {
|
|
|
- await this.validateCommissionRate(teamMemberData.teamId, teamMemberData.commissionRate)
|
|
|
+ // 获取团队信息
|
|
|
+ const team = await this.teamRepository.findOne({ where: { id: teamMemberData.teamId } })
|
|
|
+ if (!team) {
|
|
|
+ throw new Error('团队不存在')
|
|
|
}
|
|
|
|
|
|
- const existingUser = await this.userService.findByName(teamMemberData.name)
|
|
|
- if (existingUser) {
|
|
|
+ let createdUser: User
|
|
|
+ let parentId: number
|
|
|
+ let parentTeamMember: TeamMembers | null = null
|
|
|
+
|
|
|
+ // 如果提供了 memberId,则该 memberId 是 team_members 表的 id(现有团队成员的 id)
|
|
|
+ // 新创建的团队成员作为该现有团队成员的下级
|
|
|
+ if (memberId) {
|
|
|
+ // 查询父级团队成员信息(team_members 表)
|
|
|
+ parentTeamMember = await this.teamMembersRepository.findOne({ where: { id: memberId } })
|
|
|
+ if (!parentTeamMember) {
|
|
|
+ throw new Error('父级团队成员不存在')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取父级团队成员对应的用户
|
|
|
+ const parentTeamMemberUser = await this.userRepository.findOne({ where: { id: parentTeamMember.userId } })
|
|
|
+ if (!parentTeamMemberUser) {
|
|
|
+ throw new Error('父级团队成员对应的用户不存在')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查用户名是否已存在
|
|
|
+ const existingUser = await this.userService.findByName(teamMemberData.name)
|
|
|
+ if (existingUser) {
|
|
|
throw new Error('操作失败')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果用户提供了密码且密码不为空,使用用户提供的密码;否则使用默认密码
|
|
|
+ const userPassword = (password && password.trim() !== '') ? password : 'password123'
|
|
|
+ // 新成员的 parentId 设置为父级团队成员的 userId
|
|
|
+ parentId = parentTeamMemberUser.id
|
|
|
+ createdUser = await this.userService.create(userPassword, teamMemberData.name, UserRole.PROMOTER, parentId)
|
|
|
+ } else {
|
|
|
+ // 没有提供 memberId,创建新用户作为团队下的二级代理
|
|
|
+ const existingUser = await this.userService.findByName(teamMemberData.name)
|
|
|
+ if (existingUser) {
|
|
|
+ throw new Error('操作失败')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果用户提供了密码且密码不为空,使用用户提供的密码;否则使用默认密码
|
|
|
+ const userPassword = (password && password.trim() !== '') ? password : 'password123'
|
|
|
+ // 二级代理的 parentId 设置为团队用户ID
|
|
|
+ parentId = teamUserId || team.userId
|
|
|
+ createdUser = await this.userService.create(userPassword, teamMemberData.name, UserRole.PROMOTER, parentId)
|
|
|
}
|
|
|
|
|
|
- const userPassword = password || 'password123'
|
|
|
- const parentId = teamUserId || creatorId
|
|
|
- const createdUser = await this.userService.create(userPassword, teamMemberData.name, UserRole.PROMOTER, parentId)
|
|
|
+ // 验证分成比例(如果有父级,需要验证不能大于父级比例)
|
|
|
+ if (teamMemberData.commissionRate !== undefined && teamMemberData.commissionRate > 0) {
|
|
|
+ await this.validateCommissionRate(
|
|
|
+ teamMemberData.teamId,
|
|
|
+ teamMemberData.commissionRate,
|
|
|
+ undefined,
|
|
|
+ parentTeamMember ? parentTeamMember.id : null
|
|
|
+ )
|
|
|
+ }
|
|
|
|
|
|
// 生成推广码
|
|
|
const randomSuffix = randomstring.generate({
|
|
|
@@ -94,11 +166,13 @@ export class TeamMembersService {
|
|
|
finalPromoCode = `${randomSuffix}${counter}`
|
|
|
}
|
|
|
|
|
|
- const teamMember = this.teamMembersRepository.create({
|
|
|
+ const teamMemberDataWithParent = {
|
|
|
...teamMemberData,
|
|
|
userId: createdUser.id,
|
|
|
- promoCode: finalPromoCode
|
|
|
- })
|
|
|
+ promoCode: finalPromoCode,
|
|
|
+ parentId: memberId || undefined // 如果提供了 memberId,设置为父级团队成员的 id;否则为 undefined(二级代理)
|
|
|
+ }
|
|
|
+ const teamMember = this.teamMembersRepository.create(teamMemberDataWithParent)
|
|
|
return this.teamMembersRepository.save(teamMember)
|
|
|
}
|
|
|
|
|
|
@@ -106,55 +180,358 @@ export class TeamMembersService {
|
|
|
return this.teamMembersRepository.findOneOrFail({ where: { id } })
|
|
|
}
|
|
|
|
|
|
- async findAll(query: ListTeamMembersQuery): Promise<PaginationResponse<TeamMembers>> {
|
|
|
+ /**
|
|
|
+ * 获取团队成员列表(树状结构)
|
|
|
+ * 返回树状结构:团队 -> 团队下级代理 -> 其他下级代理
|
|
|
+ */
|
|
|
+ async findAll(query: ListTeamMembersQuery): Promise<PaginationResponse<TeamMemberTreeNode>> {
|
|
|
const { page, size, name, teamId, userId } = query
|
|
|
|
|
|
+ // 构建查询条件
|
|
|
const where: any = {}
|
|
|
-
|
|
|
if (name) {
|
|
|
where.name = Like(`%${name}%`)
|
|
|
}
|
|
|
-
|
|
|
if (teamId) {
|
|
|
where.teamId = teamId
|
|
|
}
|
|
|
-
|
|
|
if (userId) {
|
|
|
where.userId = userId
|
|
|
}
|
|
|
|
|
|
- const [members, total] = await this.teamMembersRepository.findAndCount({
|
|
|
+ // 获取所有团队成员(不分页,用于构建树)
|
|
|
+ const allMembers = await this.teamMembersRepository.find({
|
|
|
where,
|
|
|
- skip: (Number(page) || 0) * (Number(size) || 20),
|
|
|
- take: Number(size) || 20,
|
|
|
order: { createdAt: 'DESC' }
|
|
|
})
|
|
|
|
|
|
+ // 获取团队信息(如果指定了 teamId)
|
|
|
+ let team: Team | null = null
|
|
|
+ if (teamId) {
|
|
|
+ team = await this.teamRepository.findOne({ where: { id: teamId } })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建树状结构
|
|
|
+ const tree = await this.buildTeamMemberTree(allMembers, team)
|
|
|
+
|
|
|
+ // 应用分页(对树的第一层进行分页)
|
|
|
+ const pageNum = Number(page) || 0
|
|
|
+ const sizeNum = Number(size) || 20
|
|
|
+ const start = pageNum * sizeNum
|
|
|
+ const end = start + sizeNum
|
|
|
+ const paginatedTree = tree.slice(start, end)
|
|
|
+
|
|
|
return {
|
|
|
- content: members,
|
|
|
+ content: paginatedTree,
|
|
|
metadata: {
|
|
|
- total: Number(total),
|
|
|
- page: Number(page) || 0,
|
|
|
- size: Number(size) || 20
|
|
|
+ total: tree.length,
|
|
|
+ page: pageNum,
|
|
|
+ size: sizeNum
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 构建团队成员树状结构
|
|
|
+ * 结构:基于 team_members.parentId 构建树状层级关系
|
|
|
+ */
|
|
|
+ private async buildTeamMemberTree(
|
|
|
+ members: TeamMembers[],
|
|
|
+ team: Team | null
|
|
|
+ ): Promise<TeamMemberTreeNode[]> {
|
|
|
+ // 如果没有团队信息,基于 parentId 构建树状结构
|
|
|
+ if (!team) {
|
|
|
+ // 构建成员映射(id -> TeamMembers)
|
|
|
+ const memberMap = new Map<number, TeamMembers>()
|
|
|
+ members.forEach(member => {
|
|
|
+ memberMap.set(member.id, member)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 构建父子关系映射(parentId -> TeamMembers[])
|
|
|
+ const childrenMap = new Map<number | null, TeamMembers[]>()
|
|
|
+ members.forEach(member => {
|
|
|
+ const parentId = member.parentId
|
|
|
+ if (!childrenMap.has(parentId)) {
|
|
|
+ childrenMap.set(parentId, [])
|
|
|
+ }
|
|
|
+ childrenMap.get(parentId)!.push(member)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 递归构建树节点
|
|
|
+ const buildNode = (member: TeamMembers): TeamMemberTreeNode => {
|
|
|
+ const children = childrenMap.get(member.id) || []
|
|
|
+ return {
|
|
|
+ id: member.id,
|
|
|
+ name: member.name,
|
|
|
+ userId: member.userId,
|
|
|
+ teamId: member.teamId,
|
|
|
+ commissionRate: Number(member.commissionRate),
|
|
|
+ totalRevenue: Number(member.totalRevenue),
|
|
|
+ todayRevenue: Number(member.todayRevenue),
|
|
|
+ promoCode: member.promoCode,
|
|
|
+ createdAt: member.createdAt,
|
|
|
+ updatedAt: member.updatedAt,
|
|
|
+ type: 'teamMember',
|
|
|
+ children: children.map(child => buildNode(child))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 找到所有根节点(parentId 为 null 的成员)
|
|
|
+ const rootMembers = childrenMap.get(null) || []
|
|
|
+ return rootMembers.map(member => buildNode(member))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果有团队信息,构建包含团队节点的树
|
|
|
+ // 构建成员映射(id -> TeamMembers)
|
|
|
+ const memberMap = new Map<number, TeamMembers>()
|
|
|
+ members.forEach(member => {
|
|
|
+ memberMap.set(member.id, member)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 构建父子关系映射(parentId -> TeamMembers[])
|
|
|
+ const childrenMap = new Map<number | null, TeamMembers[]>()
|
|
|
+ members.forEach(member => {
|
|
|
+ const parentId = member.parentId
|
|
|
+ if (!childrenMap.has(parentId)) {
|
|
|
+ childrenMap.set(parentId, [])
|
|
|
+ }
|
|
|
+ childrenMap.get(parentId)!.push(member)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 递归构建团队成员节点
|
|
|
+ const buildMemberNode = (member: TeamMembers): TeamMemberTreeNode => {
|
|
|
+ const children = childrenMap.get(member.id) || []
|
|
|
+ return {
|
|
|
+ id: member.id,
|
|
|
+ name: member.name,
|
|
|
+ userId: member.userId,
|
|
|
+ teamId: member.teamId,
|
|
|
+ commissionRate: Number(member.commissionRate),
|
|
|
+ totalRevenue: Number(member.totalRevenue),
|
|
|
+ todayRevenue: Number(member.todayRevenue),
|
|
|
+ promoCode: member.promoCode,
|
|
|
+ createdAt: member.createdAt,
|
|
|
+ updatedAt: member.updatedAt,
|
|
|
+ type: 'teamMember',
|
|
|
+ children: children.map(child => buildMemberNode(child))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 找到所有根节点(parentId 为 null 的成员,这些是团队的直接下级)
|
|
|
+ const rootMembers = childrenMap.get(null) || []
|
|
|
+
|
|
|
+ // 构建根节点(团队)
|
|
|
+ const rootNode: TeamMemberTreeNode = {
|
|
|
+ id: 0,
|
|
|
+ name: team.name,
|
|
|
+ userId: team.userId,
|
|
|
+ teamId: team.id,
|
|
|
+ commissionRate: Number(team.commissionRate),
|
|
|
+ totalRevenue: Number(team.totalRevenue),
|
|
|
+ todayRevenue: Number(team.todayRevenue),
|
|
|
+ promoCode: null,
|
|
|
+ createdAt: team.createdAt,
|
|
|
+ updatedAt: team.updatedAt,
|
|
|
+ type: 'team',
|
|
|
+ children: rootMembers.map(member => buildMemberNode(member))
|
|
|
+ }
|
|
|
+
|
|
|
+ return [rootNode]
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 递归获取下级节点
|
|
|
+ * 优化:批量查询所有下级用户和团队,减少数据库查询次数
|
|
|
+ */
|
|
|
+ private async getChildrenRecursive(
|
|
|
+ parentUserId: number,
|
|
|
+ memberMap: Map<number, TeamMembers>,
|
|
|
+ allUsersMap?: Map<number, User[]>,
|
|
|
+ allTeamsMap?: Map<number, Team>
|
|
|
+ ): Promise<TeamMemberTreeNode[]> {
|
|
|
+ const children: TeamMemberTreeNode[] = []
|
|
|
+
|
|
|
+ // 如果提供了预加载的数据,使用预加载的数据
|
|
|
+ let childUsers: User[] = []
|
|
|
+ if (allUsersMap) {
|
|
|
+ childUsers = allUsersMap.get(parentUserId) || []
|
|
|
+ } else {
|
|
|
+ // 否则查询数据库
|
|
|
+ childUsers = await this.userRepository.find({
|
|
|
+ where: { parentId: parentUserId }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ for (const childUser of childUsers) {
|
|
|
+ // 检查是否是团队成员
|
|
|
+ const teamMember = memberMap.get(childUser.id)
|
|
|
+
|
|
|
+ if (teamMember) {
|
|
|
+ // 是团队成员
|
|
|
+ children.push({
|
|
|
+ id: teamMember.id,
|
|
|
+ name: teamMember.name,
|
|
|
+ userId: teamMember.userId,
|
|
|
+ teamId: teamMember.teamId,
|
|
|
+ commissionRate: Number(teamMember.commissionRate),
|
|
|
+ totalRevenue: Number(teamMember.totalRevenue),
|
|
|
+ todayRevenue: Number(teamMember.todayRevenue),
|
|
|
+ promoCode: teamMember.promoCode,
|
|
|
+ createdAt: teamMember.createdAt,
|
|
|
+ updatedAt: teamMember.updatedAt,
|
|
|
+ type: 'teamMember',
|
|
|
+ children: await this.getChildrenRecursive(childUser.id, memberMap, allUsersMap, allTeamsMap)
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 检查是否是团队
|
|
|
+ let childTeam: Team | null = null
|
|
|
+ if (allTeamsMap) {
|
|
|
+ childTeam = allTeamsMap.get(childUser.id) || null
|
|
|
+ } else {
|
|
|
+ childTeam = await this.teamRepository.findOne({ where: { userId: childUser.id } })
|
|
|
+ }
|
|
|
+
|
|
|
+ if (childTeam) {
|
|
|
+ // 是团队
|
|
|
+ children.push({
|
|
|
+ id: 0,
|
|
|
+ name: childTeam.name,
|
|
|
+ userId: childUser.id,
|
|
|
+ teamId: childTeam.id,
|
|
|
+ commissionRate: Number(childTeam.commissionRate),
|
|
|
+ totalRevenue: Number(childTeam.totalRevenue),
|
|
|
+ todayRevenue: Number(childTeam.todayRevenue),
|
|
|
+ promoCode: null,
|
|
|
+ createdAt: childTeam.createdAt,
|
|
|
+ updatedAt: childTeam.updatedAt,
|
|
|
+ type: 'team',
|
|
|
+ children: await this.getChildrenRecursive(childUser.id, memberMap, allUsersMap, allTeamsMap)
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 其他类型的用户(可能是推广员或其他)
|
|
|
+ children.push({
|
|
|
+ id: 0,
|
|
|
+ name: childUser.name,
|
|
|
+ userId: childUser.id,
|
|
|
+ teamId: 0,
|
|
|
+ commissionRate: 0,
|
|
|
+ totalRevenue: 0,
|
|
|
+ todayRevenue: 0,
|
|
|
+ promoCode: null,
|
|
|
+ createdAt: childUser.createdAt,
|
|
|
+ updatedAt: childUser.updatedAt,
|
|
|
+ type: 'other',
|
|
|
+ children: await this.getChildrenRecursive(childUser.id, memberMap, allUsersMap, allTeamsMap)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return children
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
async update(data: UpdateTeamMembersBody): Promise<TeamMembers> {
|
|
|
- const { id, ...updateData } = data
|
|
|
+ const { id, memberId, ...updateData } = data
|
|
|
+
|
|
|
+ // 获取当前成员信息
|
|
|
+ const currentMember = await this.findById(id)
|
|
|
+
|
|
|
+ // 构建更新数据对象
|
|
|
+ const finalUpdateData: any = { ...updateData }
|
|
|
+
|
|
|
+ // 确定最终的父级ID(如果修改了 memberId,使用新的;否则使用当前的)
|
|
|
+ let finalParentId: number | null = currentMember.parentId
|
|
|
+ let parentTeamMember: TeamMembers | null = null
|
|
|
+
|
|
|
+ // 处理 memberId(更新 parentId)
|
|
|
+ if (memberId !== undefined) {
|
|
|
+ if (memberId === null) {
|
|
|
+ // 设置为 null,表示成为团队的直接下级
|
|
|
+ finalUpdateData.parentId = null
|
|
|
+ finalParentId = null
|
|
|
+ } else {
|
|
|
+ // 验证父级团队成员是否存在
|
|
|
+ parentTeamMember = await this.teamMembersRepository.findOne({ where: { id: memberId } })
|
|
|
+ if (!parentTeamMember) {
|
|
|
+ throw new Error('父级团队成员不存在')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证不能设置为自己
|
|
|
+ if (memberId === id) {
|
|
|
+ throw new Error('不能将自己设置为父级')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证不能设置为自己的子节点(避免循环引用)
|
|
|
+ const isDescendant = await this.isDescendant(id, memberId)
|
|
|
+ if (isDescendant) {
|
|
|
+ throw new Error('不能将自己的子节点设置为父级')
|
|
|
+ }
|
|
|
|
|
|
- // 验证分成比例
|
|
|
+ // 验证父级团队成员必须属于同一个团队
|
|
|
+ if (parentTeamMember.teamId !== currentMember.teamId) {
|
|
|
+ throw new Error('父级团队成员必须属于同一个团队')
|
|
|
+ }
|
|
|
+
|
|
|
+ finalUpdateData.parentId = memberId
|
|
|
+ finalParentId = memberId
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 如果没有修改 parentId,使用当前的父级ID来验证分润比例
|
|
|
+ if (currentMember.parentId !== null) {
|
|
|
+ parentTeamMember = await this.teamMembersRepository.findOne({ where: { id: currentMember.parentId } })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证分成比例(如果有父级,需要验证不能大于父级比例)
|
|
|
if (updateData.commissionRate !== undefined && updateData.commissionRate > 0) {
|
|
|
- // 获取当前成员信息以获取teamId
|
|
|
- const currentMember = await this.findById(id)
|
|
|
- await this.validateCommissionRate(currentMember.teamId, updateData.commissionRate, id)
|
|
|
+ await this.validateCommissionRate(
|
|
|
+ currentMember.teamId,
|
|
|
+ updateData.commissionRate,
|
|
|
+ id,
|
|
|
+ finalParentId
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
- await this.teamMembersRepository.update(id, updateData)
|
|
|
+ await this.teamMembersRepository.update(id, finalUpdateData)
|
|
|
return this.findById(id)
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 检查 targetId 是否是 ancestorId 的后代节点
|
|
|
+ * 用于防止循环引用
|
|
|
+ */
|
|
|
+ private async isDescendant(ancestorId: number, targetId: number): Promise<boolean> {
|
|
|
+ // 获取目标节点的所有祖先节点
|
|
|
+ let currentId: number | null = targetId
|
|
|
+ const visited = new Set<number>()
|
|
|
+
|
|
|
+ while (currentId !== null) {
|
|
|
+ if (visited.has(currentId)) {
|
|
|
+ // 检测到循环,返回 false(这种情况不应该发生,但作为安全措施)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ visited.add(currentId)
|
|
|
+
|
|
|
+ if (currentId === ancestorId) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ const member = await this.teamMembersRepository.findOne({
|
|
|
+ where: { id: currentId },
|
|
|
+ select: ['parentId']
|
|
|
+ })
|
|
|
+
|
|
|
+ if (!member || member.parentId === null) {
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ currentId = member.parentId
|
|
|
+ }
|
|
|
+
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
async delete(id: number): Promise<void> {
|
|
|
await this.teamMembersRepository.delete(id)
|
|
|
}
|
|
|
@@ -532,7 +909,7 @@ export class TeamMembersService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 获取团队统计数据(队长视角)
|
|
|
+ * 获取团队统计数据(队长视角)- 返回树状结构
|
|
|
*/
|
|
|
async getTeamLeaderStats(query: TeamLeaderStatsQuery, teamLeaderUserId: number): Promise<TeamLeaderStatsResponse> {
|
|
|
const { teamId } = query
|
|
|
@@ -554,83 +931,146 @@ export class TeamMembersService {
|
|
|
const todayStart = new Date(today.getFullYear(), today.getMonth(), today.getDate())
|
|
|
const todayEnd = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59)
|
|
|
|
|
|
- // 一次性查询团队成员的全部收入记录
|
|
|
+ // 获取团队成员的用户ID列表
|
|
|
const memberUserIds = members.map(m => m.userId)
|
|
|
- const allMemberIncomeRecords = await this.incomeRecordsRepository
|
|
|
- .createQueryBuilder('record')
|
|
|
- .where('record.personalAgentId IN (:...memberUserIds)', { memberUserIds })
|
|
|
- .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
- .andWhere('record.status = :status', { status: true })
|
|
|
- .getMany()
|
|
|
|
|
|
- // 按 personalAgentId 分组
|
|
|
- const recordsByAgent = new Map<number, any[]>()
|
|
|
- for (const rec of allMemberIncomeRecords) {
|
|
|
- const key = Number(rec.personalAgentId)
|
|
|
- if (!recordsByAgent.has(key)) recordsByAgent.set(key, [])
|
|
|
- recordsByAgent.get(key)!.push(rec)
|
|
|
- }
|
|
|
+ // 检查索引表中是否有数据(用于调试)
|
|
|
+ const indexTableCount = await this.indexRepository.count()
|
|
|
+ this.app.log.info({
|
|
|
+ teamId,
|
|
|
+ teamLeaderUserId,
|
|
|
+ memberCount: members.length,
|
|
|
+ memberUserIds,
|
|
|
+ indexTableCount
|
|
|
+ }, 'getTeamLeaderStats: 开始统计,检查索引表数据')
|
|
|
+
|
|
|
+ // 构建成员统计映射
|
|
|
+ const memberStatsMap = new Map<number, {
|
|
|
+ totalRevenue: number
|
|
|
+ todayRevenue: number
|
|
|
+ totalSales: number
|
|
|
+ todaySales: number
|
|
|
+ teamLeaderTotalIncome: number
|
|
|
+ teamLeaderTodayIncome: number
|
|
|
+ }>()
|
|
|
+
|
|
|
+ // 为每个成员计算统计数据(从索引表查询,如果索引表没有数据则回退到 income_records)
|
|
|
+ for (const member of members) {
|
|
|
+ // 1. 先尝试从索引表查询该成员的所有分润记录
|
|
|
+ // 注意:不区分 level(1、2、3等),团队成员的所有分润都要统计
|
|
|
+ let memberIndexRecords = await this.indexRepository
|
|
|
+ .createQueryBuilder('index')
|
|
|
+ .where('index.agentId = :agentId', { agentId: member.userId })
|
|
|
+ .andWhere('index.status = :status', { status: true })
|
|
|
+ .getMany()
|
|
|
+
|
|
|
+ // 如果查询不到数据,可能是因为索引表中的 agentId 对应的是团队用户ID
|
|
|
+ // 尝试通过 income_records 表的 personalAgentId 查询
|
|
|
+ if (memberIndexRecords.length === 0) {
|
|
|
+ // 从 income_records 表查询该成员的记录(通过 personalAgentId)
|
|
|
+ const incomeRecords = await this.incomeRecordsRepository
|
|
|
+ .createQueryBuilder('record')
|
|
|
+ .where('record.personalAgentId = :personalAgentId', { personalAgentId: member.userId })
|
|
|
+ .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
+ .andWhere('record.status = :status', { status: true })
|
|
|
+ .andWhere('record.personalIncomeAmount > 0')
|
|
|
+ .getMany()
|
|
|
+
|
|
|
+ // 将 income_records 转换为类似索引表的结构
|
|
|
+ memberIndexRecords = incomeRecords.map(record => ({
|
|
|
+ id: record.id,
|
|
|
+ agentId: member.userId,
|
|
|
+ commissionAmount: Number(record.personalIncomeAmount || 0),
|
|
|
+ orderNo: record.orderNo,
|
|
|
+ orderPrice: Number(record.orderPrice || 0),
|
|
|
+ userId: record.userId,
|
|
|
+ createdAt: record.createdAt,
|
|
|
+ status: record.status
|
|
|
+ } as any))
|
|
|
+ }
|
|
|
|
|
|
- // 为每个成员计算统计数据(内存聚合,无额外查询)
|
|
|
- const membersStats = await Promise.all(
|
|
|
- members.map(async (member) => {
|
|
|
- const memberRecords = recordsByAgent.get(member.userId) || []
|
|
|
+ // 2. 计算总收入(该成员获得的所有分润)
|
|
|
+ const totalRevenue = memberIndexRecords.reduce((sum, r) => sum + Number(r.commissionAmount || 0), 0)
|
|
|
+
|
|
|
+ // 3. 计算今日收入
|
|
|
+ const todayRevenue = memberIndexRecords
|
|
|
+ .filter(r => {
|
|
|
+ const d = new Date(r.createdAt)
|
|
|
+ return d >= todayStart && d <= todayEnd
|
|
|
+ })
|
|
|
+ .reduce((sum, r) => sum + Number(r.commissionAmount || 0), 0)
|
|
|
+
|
|
|
+ // 4. 计算总销售额(按订单号去重)
|
|
|
+ const uniqueOrders = new Set<string>()
|
|
|
+ const totalSales = memberIndexRecords.reduce((sum, r) => {
|
|
|
+ if (!uniqueOrders.has(r.orderNo)) {
|
|
|
+ uniqueOrders.add(r.orderNo)
|
|
|
+ return sum + Number(r.orderPrice || 0)
|
|
|
+ }
|
|
|
+ return sum
|
|
|
+ }, 0)
|
|
|
|
|
|
- const totalRevenue = memberRecords
|
|
|
- .filter(r => Number(r.personalIncomeAmount || 0) > 0)
|
|
|
- .reduce((sum, r) => sum + Number(r.personalIncomeAmount || 0), 0)
|
|
|
+ // 5. 计算今日销售额(按订单号去重)
|
|
|
+ const todayUniqueOrders = new Set<string>()
|
|
|
+ const todaySales = memberIndexRecords
|
|
|
+ .filter(r => {
|
|
|
+ const d = new Date(r.createdAt)
|
|
|
+ return d >= todayStart && d <= todayEnd
|
|
|
+ })
|
|
|
+ .reduce((sum, r) => {
|
|
|
+ if (!todayUniqueOrders.has(r.orderNo)) {
|
|
|
+ todayUniqueOrders.add(r.orderNo)
|
|
|
+ return sum + Number(r.orderPrice || 0)
|
|
|
+ }
|
|
|
+ return sum
|
|
|
+ }, 0)
|
|
|
|
|
|
- const todayRevenue = memberRecords
|
|
|
- .filter(r => Number(r.personalIncomeAmount || 0) > 0)
|
|
|
- .filter(r => {
|
|
|
- const d = new Date(r.createdAt)
|
|
|
- return d >= todayStart && d <= todayEnd
|
|
|
- })
|
|
|
- .reduce((sum, r) => sum + Number(r.personalIncomeAmount || 0), 0)
|
|
|
+ // 6. 查询该成员的下级用户列表(通过 income_records 表的 personalAgentId)
|
|
|
+ const memberOrderUserIds = await this.incomeRecordsRepository
|
|
|
+ .createQueryBuilder('record')
|
|
|
+ .select('DISTINCT record.userId', 'userId')
|
|
|
+ .where('record.personalAgentId = :personalAgentId', { personalAgentId: member.userId })
|
|
|
+ .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
+ .andWhere('record.status = :status', { status: true })
|
|
|
+ .getRawMany()
|
|
|
+
|
|
|
+ const orderUserIds = memberOrderUserIds.map(item => item.userId).filter(id => id !== null && id !== undefined)
|
|
|
+
|
|
|
+ // 7. 计算队长从该成员获得的实际收入(队长在这些订单中获得的分润)
|
|
|
+ let teamLeaderTotalIncome = 0
|
|
|
+ let teamLeaderTodayIncome = 0
|
|
|
+
|
|
|
+ if (orderUserIds.length > 0) {
|
|
|
+ // 查询队长在这些订单中获得的分润
|
|
|
+ const leaderIndexRecords = await this.indexRepository
|
|
|
+ .createQueryBuilder('index')
|
|
|
+ .where('index.agentId = :agentId', { agentId: teamLeaderUserId })
|
|
|
+ .andWhere('index.userId IN (:...userIds)', { userIds: orderUserIds })
|
|
|
+ .andWhere('index.status = :status', { status: true })
|
|
|
+ .getMany()
|
|
|
|
|
|
- const totalSales = memberRecords.reduce((sum, r) => sum + Number(r.orderPrice || 0), 0)
|
|
|
+ teamLeaderTotalIncome = leaderIndexRecords.reduce((sum, r) => sum + Number(r.commissionAmount || 0), 0)
|
|
|
|
|
|
- const todaySales = memberRecords
|
|
|
+ teamLeaderTodayIncome = leaderIndexRecords
|
|
|
.filter(r => {
|
|
|
const d = new Date(r.createdAt)
|
|
|
return d >= todayStart && d <= todayEnd
|
|
|
})
|
|
|
- .reduce((sum, r) => sum + Number(r.orderPrice || 0), 0)
|
|
|
-
|
|
|
- // 计算队长从该成员获得的实际收入(该成员订单中队长分得的部分)
|
|
|
- const teamLeaderTotalIncome = memberRecords.reduce((sum, record) => {
|
|
|
- const leaderPart = Number(record.incomeAmount || 0)
|
|
|
- return sum + leaderPart
|
|
|
- }, 0)
|
|
|
-
|
|
|
- const teamLeaderTodayIncome = memberRecords
|
|
|
- .filter(record => {
|
|
|
- const recordDate = new Date(record.createdAt)
|
|
|
- return recordDate >= todayStart && recordDate <= todayEnd
|
|
|
- })
|
|
|
- .reduce((sum, record) => {
|
|
|
- const leaderPart = Number(record.incomeAmount || 0)
|
|
|
- return sum + leaderPart
|
|
|
- }, 0)
|
|
|
-
|
|
|
- const personalCommissionRate = Number(member.commissionRate || 0)
|
|
|
- const teamCommissionRate = Number(team.commissionRate || 0)
|
|
|
- const memberActualRate = teamCommissionRate - personalCommissionRate
|
|
|
+ .reduce((sum, r) => sum + Number(r.commissionAmount || 0), 0)
|
|
|
+ }
|
|
|
|
|
|
- return {
|
|
|
- memberId: member.id,
|
|
|
- memberName: member.name,
|
|
|
- personalCommissionRate: Number(personalCommissionRate.toFixed(2)),
|
|
|
- actualRate: Number(memberActualRate.toFixed(2)),
|
|
|
- totalRevenue: Number(totalRevenue.toFixed(5)),
|
|
|
- todayRevenue: Number(todayRevenue.toFixed(5)),
|
|
|
- totalSales: Number(totalSales.toFixed(5)),
|
|
|
- todaySales: Number(todaySales.toFixed(5)),
|
|
|
- teamLeaderTotalIncome: Number(teamLeaderTotalIncome.toFixed(5)),
|
|
|
- teamLeaderTodayIncome: Number(teamLeaderTodayIncome.toFixed(5))
|
|
|
- }
|
|
|
+ memberStatsMap.set(member.id, {
|
|
|
+ totalRevenue: Number(totalRevenue.toFixed(5)),
|
|
|
+ todayRevenue: Number(todayRevenue.toFixed(5)),
|
|
|
+ totalSales: Number(totalSales.toFixed(5)),
|
|
|
+ todaySales: Number(todaySales.toFixed(5)),
|
|
|
+ teamLeaderTotalIncome: Number(teamLeaderTotalIncome.toFixed(5)),
|
|
|
+ teamLeaderTodayIncome: Number(teamLeaderTodayIncome.toFixed(5))
|
|
|
})
|
|
|
- )
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建树状结构
|
|
|
+ const membersStats = await this.buildTeamMemberStatsTree(members, team, memberStatsMap)
|
|
|
|
|
|
return {
|
|
|
teamId: team.id,
|
|
|
@@ -640,6 +1080,94 @@ export class TeamMembersService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 构建团队成员统计树状结构
|
|
|
+ */
|
|
|
+ private async buildTeamMemberStatsTree(
|
|
|
+ members: TeamMembers[],
|
|
|
+ team: Team,
|
|
|
+ memberStatsMap: Map<number, {
|
|
|
+ totalRevenue: number
|
|
|
+ todayRevenue: number
|
|
|
+ totalSales: number
|
|
|
+ todaySales: number
|
|
|
+ teamLeaderTotalIncome: number
|
|
|
+ teamLeaderTodayIncome: number
|
|
|
+ }>
|
|
|
+ ): Promise<TeamMemberStatsTreeNode[]> {
|
|
|
+ // 构建成员映射(id -> TeamMembers)
|
|
|
+ const memberMap = new Map<number, TeamMembers>()
|
|
|
+ members.forEach(member => {
|
|
|
+ memberMap.set(member.id, member)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 构建父子关系映射(parentId -> TeamMembers[])
|
|
|
+ const childrenMap = new Map<number | null, TeamMembers[]>()
|
|
|
+ members.forEach(member => {
|
|
|
+ const parentId = member.parentId
|
|
|
+ if (!childrenMap.has(parentId)) {
|
|
|
+ childrenMap.set(parentId, [])
|
|
|
+ }
|
|
|
+ childrenMap.get(parentId)!.push(member)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 递归构建团队成员统计节点
|
|
|
+ const buildMemberNode = (member: TeamMembers): TeamMemberStatsTreeNode => {
|
|
|
+ const children = childrenMap.get(member.id) || []
|
|
|
+ const stats = memberStatsMap.get(member.id) || {
|
|
|
+ totalRevenue: 0,
|
|
|
+ todayRevenue: 0,
|
|
|
+ totalSales: 0,
|
|
|
+ todaySales: 0,
|
|
|
+ teamLeaderTotalIncome: 0,
|
|
|
+ teamLeaderTodayIncome: 0
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ id: member.id,
|
|
|
+ name: member.name,
|
|
|
+ userId: member.userId,
|
|
|
+ teamId: member.teamId,
|
|
|
+ commissionRate: Number(member.commissionRate),
|
|
|
+ parentId: member.parentId,
|
|
|
+ totalRevenue: stats.totalRevenue,
|
|
|
+ todayRevenue: stats.todayRevenue,
|
|
|
+ totalSales: stats.totalSales,
|
|
|
+ todaySales: stats.todaySales,
|
|
|
+ todayDAU: 0,
|
|
|
+ todayNewUsers: 0,
|
|
|
+ teamLeaderTotalIncome: stats.teamLeaderTotalIncome,
|
|
|
+ teamLeaderTodayIncome: stats.teamLeaderTodayIncome,
|
|
|
+ type: 'teamMember',
|
|
|
+ children: children.map(child => buildMemberNode(child))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 找到所有根节点(parentId 为 null 的成员,这些是团队的直接下级)
|
|
|
+ const rootMembers = childrenMap.get(null) || []
|
|
|
+
|
|
|
+ // 构建根节点(团队)
|
|
|
+ const rootNode: TeamMemberStatsTreeNode = {
|
|
|
+ id: 0,
|
|
|
+ name: team.name,
|
|
|
+ userId: team.userId,
|
|
|
+ teamId: team.id,
|
|
|
+ commissionRate: Number(team.commissionRate),
|
|
|
+ parentId: null,
|
|
|
+ totalRevenue: 0,
|
|
|
+ todayRevenue: 0,
|
|
|
+ totalSales: 0,
|
|
|
+ todaySales: 0,
|
|
|
+ todayDAU: 0,
|
|
|
+ todayNewUsers: 0,
|
|
|
+ type: 'team',
|
|
|
+ children: rootMembers.map(member => buildMemberNode(member))
|
|
|
+ }
|
|
|
+
|
|
|
+ return [rootNode]
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* 生成团队成员的推广链接
|
|
|
* @param teamMemberId 团队成员ID
|
|
|
@@ -689,7 +1217,7 @@ export class TeamMembersService {
|
|
|
})
|
|
|
} catch (error) {
|
|
|
// 如果创建或更新记录失败,记录日志但不影响返回链接
|
|
|
- this.app.log.warn('创建或更新推广链接记录失败:', error)
|
|
|
+ this.app.log.warn({ err: error }, '创建或更新推广链接记录失败')
|
|
|
}
|
|
|
|
|
|
return promotionLink
|
|
|
@@ -888,29 +1416,11 @@ export class TeamMembersService {
|
|
|
.getCount()
|
|
|
: 0
|
|
|
|
|
|
- // 统计总收入(只统计personalIncomeAmount)
|
|
|
- const totalIncomeRecords = await this.incomeRecordsRepository
|
|
|
- .createQueryBuilder('record')
|
|
|
- .select('SUM(CAST(record.personalIncomeAmount AS DECIMAL(10,5)))', 'totalIncome')
|
|
|
- .where('record.personalAgentId = :personalAgentId', { personalAgentId: userId })
|
|
|
- .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
- .andWhere('record.status = :status', { status: true })
|
|
|
- .getRawOne()
|
|
|
-
|
|
|
- const totalIncome = totalIncomeRecords?.totalIncome ? parseFloat(totalIncomeRecords.totalIncome) : 0
|
|
|
-
|
|
|
- // 统计今日收入(只统计personalIncomeAmount)
|
|
|
- const todayIncomeRecords = await this.incomeRecordsRepository
|
|
|
- .createQueryBuilder('record')
|
|
|
- .select('SUM(CAST(record.personalIncomeAmount AS DECIMAL(10,5)))', 'totalIncome')
|
|
|
- .where('record.personalAgentId = :personalAgentId', { personalAgentId: userId })
|
|
|
- .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 totalIncome = await this.multiLevelCommissionService.getAgentTotalCommissionByDateRange(userId)
|
|
|
|
|
|
- const todayIncome = todayIncomeRecords?.totalIncome ? parseFloat(todayIncomeRecords.totalIncome) : 0
|
|
|
+ // 统计今日收入(使用索引表,统计该代理的所有分润)
|
|
|
+ const todayIncome = await this.multiLevelCommissionService.getAgentTotalCommissionByDateRange(userId, today, tomorrow)
|
|
|
|
|
|
// 统计历史总销售额
|
|
|
const totalSalesRecords = await this.incomeRecordsRepository
|
|
|
@@ -996,29 +1506,11 @@ export class TeamMembersService {
|
|
|
.getCount()
|
|
|
: 0
|
|
|
|
|
|
- // 统计今日收入(只统计personalIncomeAmount)
|
|
|
- const todayIncomeRecords = await this.incomeRecordsRepository
|
|
|
- .createQueryBuilder('record')
|
|
|
- .select('SUM(CAST(record.personalIncomeAmount AS DECIMAL(10,5)))', 'totalIncome')
|
|
|
- .where('record.personalAgentId = :personalAgentId', { personalAgentId: userId })
|
|
|
- .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 todayIncome = todayIncomeRecords?.totalIncome ? parseFloat(todayIncomeRecords.totalIncome) : 0
|
|
|
-
|
|
|
- // 统计历史总收入(只统计personalIncomeAmount)
|
|
|
- const totalIncomeRecords = await this.incomeRecordsRepository
|
|
|
- .createQueryBuilder('record')
|
|
|
- .select('SUM(CAST(record.personalIncomeAmount AS DECIMAL(10,5)))', 'totalIncome')
|
|
|
- .where('record.personalAgentId = :personalAgentId', { personalAgentId: userId })
|
|
|
- .andWhere('record.delFlag = :delFlag', { delFlag: false })
|
|
|
- .andWhere('record.status = :status', { status: true })
|
|
|
- .getRawOne()
|
|
|
+ // 统计今日收入(使用索引表,统计该代理的所有分润)
|
|
|
+ const todayIncome = await this.multiLevelCommissionService.getAgentTotalCommissionByDateRange(userId, today, tomorrow)
|
|
|
|
|
|
- const totalIncome = totalIncomeRecords?.totalIncome ? parseFloat(totalIncomeRecords.totalIncome) : 0
|
|
|
+ // 统计历史总收入(使用索引表,统计该代理的所有分润)
|
|
|
+ const totalIncome = await this.multiLevelCommissionService.getAgentTotalCommissionByDateRange(userId)
|
|
|
|
|
|
// 统计历史总销售额
|
|
|
const totalSalesRecords = await this.incomeRecordsRepository
|