import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify' import { FinanceService } from '../services/finance.service' import { CreateFinanceBody, UpdateFinanceBody, ListFinanceQuery, FinanceParams } from '../dto/finance.dto' import { Finance, FinanceStatus } from '../entities/finance.entity' import { UserRole } from '../entities/user.entity' import { Repository } from 'typeorm' import { Team } from '../entities/team.entity' import { TeamMembers } from '../entities/team-members.entity' import { IncomeRecords } from '../entities/income-records.entity' import bcrypt from 'bcryptjs' export class FinanceController { private financeService: FinanceService private teamRepository: Repository private teamMembersRepository: Repository private financeRepository: Repository private incomeRecordsRepository: Repository private app: FastifyInstance constructor(app: FastifyInstance) { this.app = app this.financeService = new FinanceService(app) this.teamRepository = app.dataSource.getRepository(Team) this.teamMembersRepository = app.dataSource.getRepository(TeamMembers) this.financeRepository = app.dataSource.getRepository(Finance) this.incomeRecordsRepository = app.dataSource.getRepository(IncomeRecords) } async create(request: FastifyRequest<{ Body: CreateFinanceBody }>, reply: FastifyReply) { try { const user = request.user if (!user) { return reply.code(403).send({ message: '用户未登录' }) } const { reminderAmount } = request.body // 验证金额为必填且为正数 if (!reminderAmount || reminderAmount <= 0) { return reply.code(400).send({ message: 'reminderAmount 为必填字段且必须大于 0' }) } // 验证提现金额最低100元 const MIN_WITHDRAW_AMOUNT = 100 if (reminderAmount < MIN_WITHDRAW_AMOUNT) { return reply.code(400).send({ message: `提现金额不能低于 ${MIN_WITHDRAW_AMOUNT} 元,当前提现金额为 ${reminderAmount.toFixed(2)}` }) } let teamId: number let userId: number // 如果是团队用户,自动获取 teamId 和 userId if (user.role === UserRole.TEAM) { // 从 team 表中获取团队用户的 teamId const team = await this.teamRepository.findOne({ where: { userId: user.id } }) if (!team) { return reply.code(404).send({ message: '未找到该用户的团队信息' }) } teamId = team.id userId = user.id request.body.teamId = teamId request.body.userId = userId } else { // 管理员需要手动提供 teamId 和 userId const { teamId: bodyTeamId, userId: bodyUserId } = request.body if (!bodyTeamId || !bodyUserId) { return reply.code(400).send({ message: 'teamId 和 userId 为必填字段' }) } teamId = bodyTeamId userId = bodyUserId } // 计算余额:所有收益 - 提现金额(提现中 + 已提现) // 1. 查询总收益(从 incomeRecords 表统计 agentId = userId 的 incomeAmount 总和) const totalRevenueResult = await this.incomeRecordsRepository .createQueryBuilder('record') .select('SUM(record.incomeAmount)', 'totalRevenue') .where('record.agentId = :userId', { userId }) .andWhere('record.delFlag = :delFlag', { delFlag: false }) .andWhere('record.status = :status', { status: true }) .getRawOne() const totalRevenue = totalRevenueResult?.totalRevenue ? Number(totalRevenueResult.totalRevenue) : 0 // 2. 查询提现金额(从 finance 表统计 status 为 PROCESSING 或 WITHDRAWN 的金额) const withdrawAmountResult = await this.financeRepository .createQueryBuilder('finance') .select('SUM(finance.reminderAmount)', 'withdrawAmount') .where('finance.teamId = :teamId', { teamId }) .andWhere('finance.delFlag = :delFlag', { delFlag: false }) .andWhere('finance.status IN (:...statuses)', { statuses: [FinanceStatus.PROCESSING, FinanceStatus.WITHDRAWN] }) .getRawOne() const withdrawAmount = withdrawAmountResult?.withdrawAmount ? Number(withdrawAmountResult.withdrawAmount) : 0 // 3. 计算余额 const balance = Number((totalRevenue - withdrawAmount).toFixed(5)) // 4. 验证提现金额不能大于余额 if (reminderAmount > balance) { return reply.code(400).send({ message: `提现金额不能大于余额,当前余额为 ${balance.toFixed(2)},提现金额为 ${reminderAmount.toFixed(2)}` }) } // 5. 验证提现密码 const { withdrawPassword } = request.body // 获取团队信息 const team = await this.teamRepository.findOne({ where: { id: teamId } }) if (!team) { return reply.code(404).send({ message: '团队不存在' }) } // 判断是团队提现还是团队成员提现 const isTeamWithdraw = team.userId === userId if (isTeamWithdraw) { // 团队提现:验证团队的提现密码 if (!team.withdrawPassword) { return reply.code(400).send({ message: '请先设置团队提现密码' }) } if (!withdrawPassword) { return reply.code(400).send({ message: '请提供提现密码' }) } const isPasswordValid = await bcrypt.compare(withdrawPassword, team.withdrawPassword) if (!isPasswordValid) { return reply.code(400).send({ message: '提现密码错误' }) } } else { // 团队成员提现:验证团队成员的提现密码 const teamMember = await this.teamMembersRepository.findOne({ where: { teamId, userId } }) if (!teamMember) { return reply.code(404).send({ message: '团队成员不存在' }) } if (!teamMember.withdrawPassword) { return reply.code(400).send({ message: '请先设置团队成员提现密码' }) } if (!withdrawPassword) { return reply.code(400).send({ message: '请提供提现密码' }) } const isPasswordValid = await bcrypt.compare(withdrawPassword, teamMember.withdrawPassword) if (!isPasswordValid) { return reply.code(400).send({ message: '提现密码错误' }) } } const finance = await this.financeService.create(request.body) return reply.code(201).send(finance) } catch (error) { this.app.log.error(error, '创建财务记录失败') return reply.code(500).send({ message: '创建财务记录失败' }) } } async findById(request: FastifyRequest<{ Params: FinanceParams }>, reply: FastifyReply) { try { const { id } = request.params const finance = await this.financeService.findById(id) return reply.send(finance) } catch (error) { return reply.code(404).send({ message: '财务记录不存在' }) } } async findAll(request: FastifyRequest<{ Querystring: ListFinanceQuery }>, reply: FastifyReply) { try { const user = request.user if (!user) { return reply.code(403).send({ message: '用户未登录' }) } if (user.role === UserRole.TEAM) { request.query.userId = user.id } const result = await this.financeService.findAll(request.query) return reply.send(result) } catch (error) { return reply.code(500).send({ message: '获取财务记录列表失败' }) } } async update(request: FastifyRequest<{ Params: FinanceParams; Body: UpdateFinanceBody }>, reply: FastifyReply) { try { const { id } = request.params const updateData = { ...request.body, id } try { await this.financeService.findById(id) } catch (error) { return reply.code(404).send({ message: '财务记录不存在' }) } const updatedRecord = await this.financeService.update(updateData) return reply.send(updatedRecord) } catch (error) { return reply.code(500).send({ message: '更新财务记录失败' }) } } async delete(request: FastifyRequest<{ Params: FinanceParams }>, reply: FastifyReply) { try { const { id } = request.params try { await this.financeService.findById(id) } catch (error) { return reply.code(404).send({ message: '财务记录不存在' }) } await this.financeService.delete(id) return reply.send({ message: '财务记录已删除' }) } catch (error) { return reply.code(500).send({ message: '删除财务记录失败' }) } } async hardDelete(request: FastifyRequest<{ Params: FinanceParams }>, reply: FastifyReply) { try { const { id } = request.params try { await this.financeService.findById(id) } catch (error) { return reply.code(404).send({ message: '财务记录不存在' }) } await this.financeService.hardDelete(id) return reply.send({ message: '财务记录已永久删除' }) } catch (error) { return reply.code(500).send({ message: '永久删除财务记录失败' }) } } async updateStatus( request: FastifyRequest<{ Params: FinanceParams Body: { status: FinanceStatus; rejectReason?: string } }>, reply: FastifyReply ) { try { const { id } = request.params const { status, rejectReason } = request.body try { await this.financeService.findById(id) } catch (error) { return reply.code(404).send({ message: '财务记录不存在' }) } const updatedRecord = await this.financeService.updateStatus(id, status, rejectReason) return reply.send(updatedRecord) } catch (error) { return reply.code(500).send({ message: '更新财务状态失败' }) } } async getStatistics( request: FastifyRequest<{ Querystring: { startDate?: string; endDate?: string } }>, reply: FastifyReply ) { try { const { startDate, endDate } = request.query const statistics = await this.financeService.getStatistics(startDate, endDate) return reply.send(statistics) } catch (error) { return reply.code(500).send({ message: '获取统计数据失败' }) } } }