Просмотр исходного кода

新增财务、收入记录、推广链接和团队的控制器、服务及路由,完善相关DTO定义,支持CRUD操作和统计功能。

wuyi 4 месяцев назад
Родитель
Сommit
1be620f3b6

+ 8 - 0
src/app.ts

@@ -11,6 +11,10 @@ import { createDataSource } from './config/database'
 import userRoutes from './routes/user.routes'
 import fileRoutes from './routes/file.routes'
 import sysConfigRoutes from './routes/sys-config.routes'
+import incomeRecordsRoutes from './routes/income-records.routes'
+import financeRoutes from './routes/finance.routes'
+import teamRoutes from './routes/team.routes'
+import promotionLinkRoutes from './routes/promotion-link.routes'
 
 const options: FastifyEnvOptions = {
   schema: schema,
@@ -77,6 +81,10 @@ export const createApp = async () => {
   app.register(userRoutes, { prefix: '/api/users' })
   app.register(fileRoutes, { prefix: '/api/files' })
   app.register(sysConfigRoutes, { prefix: '/api/sys-config' })
+  app.register(incomeRecordsRoutes, { prefix: '/api/income' })
+  app.register(financeRoutes, { prefix: '/api/finance' })
+  app.register(teamRoutes, { prefix: '/api/teams' })
+  app.register(promotionLinkRoutes, { prefix: '/api/links' })
 
   const dataSource = createDataSource(app)
   await dataSource.initialize()

+ 13 - 9
src/controllers/file.controller.ts

@@ -14,7 +14,7 @@ export class FileController {
   async uploadZip(request: FastifyRequest, reply: FastifyReply) {
     try {
       const data = await request.file()
-      
+
       if (!data) {
         return reply.code(400).send({ message: '请选择要上传的ZIP文件' })
       }
@@ -52,7 +52,7 @@ export class FileController {
   async uploadFile(request: FastifyRequest, reply: FastifyReply) {
     try {
       const data = await request.file()
-      
+
       if (!data) {
         return reply.code(400).send({ message: '请选择要上传的文件' })
       }
@@ -86,7 +86,7 @@ export class FileController {
   async uploadImage(request: FastifyRequest, reply: FastifyReply) {
     try {
       const data = await request.file()
-      
+
       if (!data) {
         return reply.code(400).send({ message: '请选择要上传的图片' })
       }
@@ -95,14 +95,15 @@ export class FileController {
       const filename = data.filename
 
       const result = await this.fileService.uploadImage(buffer, filename, {
-        maxSize: 5 * 1024 * 1024 // 5MB
+        maxSize: 50 * 1024 * 1024,
+        useSignedUrl: true
       })
 
       return reply.send({
         message: '图片上传成功',
         data: {
           ...result,
-          dateFolder: new Date().toISOString().split('T')[0] // YYYY-MM-DD格式
+          dateFolder: new Date().toISOString().split('T')[0]
         }
       })
     } catch (error) {
@@ -119,7 +120,7 @@ export class FileController {
   async uploadDocument(request: FastifyRequest, reply: FastifyReply) {
     try {
       const data = await request.file()
-      
+
       if (!data) {
         return reply.code(400).send({ message: '请选择要上传的文档' })
       }
@@ -216,13 +217,16 @@ export class FileController {
 
       // 获取文件信息
       const fileInfo = await this.fileService.getFileInfo(key)
-      
+
       // 获取文件内容
       const fileBuffer = await this.fileService.downloadFile(key)
 
       // 设置响应头
       reply.header('Content-Type', fileInfo.res.headers['content-type'] || 'application/octet-stream')
-      reply.header('Content-Disposition', `attachment; filename="${encodeURIComponent(key.split('/').pop() || 'file')}"`)
+      reply.header(
+        'Content-Disposition',
+        `attachment; filename="${encodeURIComponent(key.split('/').pop() || 'file')}"`
+      )
       reply.header('Content-Length', fileInfo.res.headers['content-length'] || fileBuffer.length)
       reply.header('Cache-Control', 'no-cache')
 
@@ -235,4 +239,4 @@ export class FileController {
       })
     }
   }
-} 
+}

+ 128 - 0
src/controllers/finance.controller.ts

@@ -0,0 +1,128 @@
+import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'
+import { FinanceService } from '../services/finance.service'
+import {
+  CreateFinanceBody,
+  UpdateFinanceBody,
+  ListFinanceQuery,
+  FinanceParams
+} from '../dto/finance.dto'
+import { FinanceStatus } from '../entities/finance.entity'
+
+export class FinanceController {
+  private financeService: FinanceService
+
+  constructor(app: FastifyInstance) {
+    this.financeService = new FinanceService(app)
+  }
+
+  async create(request: FastifyRequest<{ Body: CreateFinanceBody }>, reply: FastifyReply) {
+    try {
+      const finance = await this.financeService.create(request.body)
+      return reply.code(201).send(finance)
+    } catch (error) {
+      return reply.code(500).send({ message: '创建财务记录失败', error })
+    }
+  }
+
+  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 result = await this.financeService.findAll(request.query)
+      return reply.send(result)
+    } catch (error) {
+      return reply.code(500).send({ message: '获取财务记录列表失败', error })
+    }
+  }
+
+  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: '更新财务记录失败', error })
+    }
+  }
+
+  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: '删除财务记录失败', error })
+    }
+  }
+
+  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: '永久删除财务记录失败', error })
+    }
+  }
+
+  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: '更新财务状态失败', error })
+    }
+  }
+
+  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: '获取统计数据失败', error })
+    }
+  }
+}

+ 106 - 0
src/controllers/income-records.controller.ts

@@ -0,0 +1,106 @@
+import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'
+import { IncomeRecordsService } from '../services/income-records.service'
+import {
+  CreateIncomeRecordBody,
+  UpdateIncomeRecordBody,
+  ListIncomeRecordsQuery,
+  IncomeRecordParams
+} from '../dto/income-records.dto'
+
+export class IncomeRecordsController {
+  private incomeRecordsService: IncomeRecordsService
+
+  constructor(app: FastifyInstance) {
+    this.incomeRecordsService = new IncomeRecordsService(app)
+  }
+
+  async create(request: FastifyRequest<{ Body: CreateIncomeRecordBody }>, reply: FastifyReply) {
+    try {
+      const incomeRecord = await this.incomeRecordsService.create(request.body)
+      return reply.code(201).send(incomeRecord)
+    } catch (error) {
+      return reply.code(500).send({ message: '创建收入记录失败', error })
+    }
+  }
+
+  async findById(request: FastifyRequest<{ Params: IncomeRecordParams }>, reply: FastifyReply) {
+    try {
+      const { id } = request.params
+      const incomeRecord = await this.incomeRecordsService.findById(id)
+      return reply.send(incomeRecord)
+    } catch (error) {
+      return reply.code(404).send({ message: '收入记录不存在' })
+    }
+  }
+
+  async findAll(request: FastifyRequest<{ Querystring: ListIncomeRecordsQuery }>, reply: FastifyReply) {
+    try {
+      const result = await this.incomeRecordsService.findAll(request.query)
+      return reply.send(result)
+    } catch (error) {
+      return reply.code(500).send({ message: '获取收入记录列表失败', error })
+    }
+  }
+
+  async update(request: FastifyRequest<{ Params: IncomeRecordParams; Body: UpdateIncomeRecordBody }>, reply: FastifyReply) {
+    try {
+      const { id } = request.params
+      const updateData = { ...request.body, id }
+      
+      try {
+        await this.incomeRecordsService.findById(id)
+      } catch (error) {
+        return reply.code(404).send({ message: '收入记录不存在' })
+      }
+
+      const updatedRecord = await this.incomeRecordsService.update(updateData)
+      return reply.send(updatedRecord)
+    } catch (error) {
+      return reply.code(500).send({ message: '更新收入记录失败', error })
+    }
+  }
+
+  async delete(request: FastifyRequest<{ Params: IncomeRecordParams }>, reply: FastifyReply) {
+    try {
+      const { id } = request.params
+      
+      try {
+        await this.incomeRecordsService.findById(id)
+      } catch (error) {
+        return reply.code(404).send({ message: '收入记录不存在' })
+      }
+
+      await this.incomeRecordsService.delete(id)
+      return reply.send({ message: '收入记录已删除' })
+    } catch (error) {
+      return reply.code(500).send({ message: '删除收入记录失败', error })
+    }
+  }
+
+  async hardDelete(request: FastifyRequest<{ Params: IncomeRecordParams }>, reply: FastifyReply) {
+    try {
+      const { id } = request.params
+      
+      try {
+        await this.incomeRecordsService.findById(id)
+      } catch (error) {
+        return reply.code(404).send({ message: '收入记录不存在' })
+      }
+
+      await this.incomeRecordsService.hardDelete(id)
+      return reply.send({ message: '收入记录已永久删除' })
+    } catch (error) {
+      return reply.code(500).send({ message: '永久删除收入记录失败', error })
+    }
+  }
+
+  async getStatistics(request: FastifyRequest<{ Querystring: { startDate?: string; endDate?: string } }>, reply: FastifyReply) {
+    try {
+      const { startDate, endDate } = request.query
+      const statistics = await this.incomeRecordsService.getStatistics(startDate, endDate)
+      return reply.send(statistics)
+    } catch (error) {
+      return reply.code(500).send({ message: '获取统计数据失败', error })
+    }
+  }
+}

+ 99 - 0
src/controllers/promotion-link.controller.ts

@@ -0,0 +1,99 @@
+import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'
+import { PromotionLinkService } from '../services/promotion-link.service'
+import {
+  CreatePromotionLinkBody,
+  UpdatePromotionLinkBody,
+  ListPromotionLinkQuery,
+  PromotionLinkParams
+} from '../dto/promotion-link.dto'
+import { LinkType } from '../entities/promotion-link.entity'
+
+export class PromotionLinkController {
+  private promotionLinkService: PromotionLinkService
+
+  constructor(app: FastifyInstance) {
+    this.promotionLinkService = new PromotionLinkService(app)
+  }
+
+  async create(request: FastifyRequest<{ Body: CreatePromotionLinkBody }>, reply: FastifyReply) {
+    try {
+      const promotionLink = await this.promotionLinkService.create(request.body)
+      return reply.code(201).send(promotionLink)
+    } catch (error) {
+      return reply.code(500).send({ message: '创建推广链接失败', error })
+    }
+  }
+
+  async findById(request: FastifyRequest<{ Params: PromotionLinkParams }>, reply: FastifyReply) {
+    try {
+      const { id } = request.params
+      const promotionLink = await this.promotionLinkService.findById(id)
+      return reply.send(promotionLink)
+    } catch (error) {
+      return reply.code(404).send({ message: '推广链接不存在' })
+    }
+  }
+
+  async findAll(request: FastifyRequest<{ Querystring: ListPromotionLinkQuery }>, reply: FastifyReply) {
+    try {
+      const result = await this.promotionLinkService.findAll(request.query)
+      return reply.send(result)
+    } catch (error) {
+      return reply.code(500).send({ message: '获取推广链接列表失败', error })
+    }
+  }
+
+  async update(request: FastifyRequest<{ Params: PromotionLinkParams; Body: UpdatePromotionLinkBody }>, reply: FastifyReply) {
+    try {
+      const { id } = request.params
+      const updateData = { ...request.body, id }
+      
+      try {
+        await this.promotionLinkService.findById(id)
+      } catch (error) {
+        return reply.code(404).send({ message: '推广链接不存在' })
+      }
+
+      const updatedLink = await this.promotionLinkService.update(updateData)
+      return reply.send(updatedLink)
+    } catch (error) {
+      return reply.code(500).send({ message: '更新推广链接失败', error })
+    }
+  }
+
+  async delete(request: FastifyRequest<{ Params: PromotionLinkParams }>, reply: FastifyReply) {
+    try {
+      const { id } = request.params
+      
+      try {
+        await this.promotionLinkService.findById(id)
+      } catch (error) {
+        return reply.code(404).send({ message: '推广链接不存在' })
+      }
+
+      await this.promotionLinkService.delete(id)
+      return reply.send({ message: '推广链接已删除' })
+    } catch (error) {
+      return reply.code(500).send({ message: '删除推广链接失败', error })
+    }
+  }
+
+  async findByType(request: FastifyRequest<{ Querystring: { type: LinkType } }>, reply: FastifyReply) {
+    try {
+      const { type } = request.query
+      const links = await this.promotionLinkService.findByType(type)
+      return reply.send(links)
+    } catch (error) {
+      return reply.code(500).send({ message: '获取指定类型推广链接失败', error })
+    }
+  }
+
+  async getStatistics(request: FastifyRequest, reply: FastifyReply) {
+    try {
+      const statistics = await this.promotionLinkService.getStatistics()
+      return reply.send(statistics)
+    } catch (error) {
+      return reply.code(500).send({ message: '获取统计数据失败', error })
+    }
+  }
+}

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

@@ -0,0 +1,115 @@
+import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'
+import { TeamService } from '../services/team.service'
+import {
+  CreateTeamBody,
+  UpdateTeamBody,
+  ListTeamQuery,
+  TeamParams,
+  UpdateRevenueBody
+} from '../dto/team.dto'
+
+export class TeamController {
+  private teamService: TeamService
+
+  constructor(app: FastifyInstance) {
+    this.teamService = new TeamService(app)
+  }
+
+  async create(request: FastifyRequest<{ Body: CreateTeamBody }>, reply: FastifyReply) {
+    try {
+      const team = await this.teamService.create(request.body)
+      return reply.code(201).send(team)
+    } catch (error) {
+      return reply.code(500).send({ message: '创建团队失败', error })
+    }
+  }
+
+  async findById(request: FastifyRequest<{ Params: TeamParams }>, reply: FastifyReply) {
+    try {
+      const { id } = request.params
+      const team = await this.teamService.findById(id)
+      return reply.send(team)
+    } catch (error) {
+      return reply.code(404).send({ message: '团队不存在' })
+    }
+  }
+
+  async findAll(request: FastifyRequest<{ Querystring: ListTeamQuery }>, reply: FastifyReply) {
+    try {
+      const result = await this.teamService.findAll(request.query)
+      return reply.send(result)
+    } catch (error) {
+      return reply.code(500).send({ message: '获取团队列表失败', error })
+    }
+  }
+
+  async update(request: FastifyRequest<{ Params: TeamParams; Body: UpdateTeamBody }>, reply: FastifyReply) {
+    try {
+      const { id } = request.params
+      const updateData = { ...request.body, id }
+      
+      try {
+        await this.teamService.findById(id)
+      } catch (error) {
+        return reply.code(404).send({ message: '团队不存在' })
+      }
+
+      const updatedTeam = await this.teamService.update(updateData)
+      return reply.send(updatedTeam)
+    } catch (error) {
+      return reply.code(500).send({ message: '更新团队失败', error })
+    }
+  }
+
+  async delete(request: FastifyRequest<{ Params: TeamParams }>, reply: FastifyReply) {
+    try {
+      const { id } = request.params
+      
+      try {
+        await this.teamService.findById(id)
+      } catch (error) {
+        return reply.code(404).send({ message: '团队不存在' })
+      }
+
+      await this.teamService.delete(id)
+      return reply.send({ message: '团队已删除' })
+    } catch (error) {
+      return reply.code(500).send({ message: '删除团队失败', error })
+    }
+  }
+
+  async updateRevenue(request: FastifyRequest<{ Body: UpdateRevenueBody }>, reply: FastifyReply) {
+    try {
+      const { id, amount, type } = request.body
+      
+      try {
+        await this.teamService.findById(id)
+      } catch (error) {
+        return reply.code(404).send({ message: '团队不存在' })
+      }
+
+      const updatedTeam = await this.teamService.updateRevenue(id, amount, type)
+      return reply.send(updatedTeam)
+    } catch (error) {
+      return reply.code(500).send({ message: '更新团队收入失败', error })
+    }
+  }
+
+  async resetTodayRevenue(request: FastifyRequest, reply: FastifyReply) {
+    try {
+      await this.teamService.resetTodayRevenue()
+      return reply.send({ message: '今日收入已重置' })
+    } catch (error) {
+      return reply.code(500).send({ message: '重置今日收入失败', error })
+    }
+  }
+
+  async getStatistics(request: FastifyRequest, reply: FastifyReply) {
+    try {
+      const statistics = await this.teamService.getStatistics()
+      return reply.send(statistics)
+    } catch (error) {
+      return reply.code(500).send({ message: '获取统计数据失败', error })
+    }
+  }
+}

+ 41 - 0
src/dto/finance.dto.ts

@@ -0,0 +1,41 @@
+import { FastifyRequest } from 'fastify'
+import { FinanceStatus } from '../entities/finance.entity'
+import { Pagination } from './common.dto'
+
+export interface CreateFinanceBody {
+  reminderAmount: number
+  paymentQrCode?: string
+  paymentName?: string
+  paymentAccount?: string
+  bankName?: string
+  status?: FinanceStatus
+  rejectReason?: string
+  remark?: string
+  processedAt?: Date
+}
+
+export interface UpdateFinanceBody {
+  id: number
+  reminderAmount?: number
+  paymentQrCode?: string
+  paymentName?: string
+  paymentAccount?: string
+  bankName?: string
+  status?: FinanceStatus
+  rejectReason?: string
+  remark?: string
+  processedAt?: Date
+  delFlag?: boolean
+}
+
+export interface ListFinanceQuery extends Pagination {
+  status?: FinanceStatus
+  paymentName?: string
+  bankName?: string
+  startDate?: string
+  endDate?: string
+}
+
+export interface FinanceParams {
+  id: number
+}

+ 44 - 0
src/dto/income-records.dto.ts

@@ -0,0 +1,44 @@
+import { FastifyRequest } from 'fastify'
+import { IncomeType, OrderType } from '../entities/income-records.entity'
+import { Pagination } from './common.dto'
+
+export interface CreateIncomeRecordBody {
+  incomeAmount: number
+  agentName: string
+  incomeType: IncomeType
+  orderType: OrderType
+  video: string
+  price: number
+  tipOrderId: string
+  payChannel: string
+  payNo: string
+  source?: string
+}
+
+export interface UpdateIncomeRecordBody {
+  id: number
+  incomeAmount?: number
+  agentName?: string
+  incomeType?: IncomeType
+  orderType?: OrderType
+  video?: string
+  price?: number
+  tipOrderId?: string
+  payChannel?: string
+  payNo?: string
+  source?: string
+  delFlag?: boolean
+}
+
+export interface ListIncomeRecordsQuery extends Pagination {
+  agentName?: string
+  incomeType?: IncomeType
+  orderType?: OrderType
+  payChannel?: string
+  startDate?: string
+  endDate?: string
+}
+
+export interface IncomeRecordParams {
+  id: number
+}

+ 27 - 0
src/dto/promotion-link.dto.ts

@@ -0,0 +1,27 @@
+import { FastifyRequest } from 'fastify'
+import { LinkType } from '../entities/promotion-link.entity'
+import { Pagination } from './common.dto'
+
+export interface CreatePromotionLinkBody {
+  name: string
+  image: string
+  link: string
+  type?: LinkType
+}
+
+export interface UpdatePromotionLinkBody {
+  id: number
+  name?: string
+  image?: string
+  link?: string
+  type?: LinkType
+}
+
+export interface ListPromotionLinkQuery extends Pagination {
+  name?: string
+  type?: LinkType
+}
+
+export interface PromotionLinkParams {
+  id: number
+}

+ 31 - 0
src/dto/team.dto.ts

@@ -0,0 +1,31 @@
+import { FastifyRequest } from 'fastify'
+import { Pagination } from './common.dto'
+
+export interface CreateTeamBody {
+  name: string
+  totalRevenue?: number
+  todayRevenue?: number
+  commissionRate?: number
+}
+
+export interface UpdateTeamBody {
+  id: number
+  name?: string
+  totalRevenue?: number
+  todayRevenue?: number
+  commissionRate?: number
+}
+
+export interface ListTeamQuery extends Pagination {
+  name?: string
+}
+
+export interface TeamParams {
+  id: number
+}
+
+export interface UpdateRevenueBody {
+  id: number
+  amount: number
+  type: 'total' | 'today'
+}

+ 66 - 0
src/routes/finance.routes.ts

@@ -0,0 +1,66 @@
+import { FastifyInstance } from 'fastify'
+import { FinanceController } from '../controllers/finance.controller'
+import { authenticate, hasRole } from '../middlewares/auth.middleware'
+import { UserRole } from '../entities/user.entity'
+import { CreateFinanceBody, UpdateFinanceBody, ListFinanceQuery, FinanceParams } from '../dto/finance.dto'
+import { FinanceStatus } from '../entities/finance.entity'
+
+export default async function financeRoutes(fastify: FastifyInstance) {
+  const financeController = new FinanceController(fastify)
+
+  // 创建财务记录
+  fastify.post<{ Body: CreateFinanceBody }>(
+    '/',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    financeController.create.bind(financeController)
+  )
+
+  // 获取财务记录列表
+  fastify.get<{ Querystring: ListFinanceQuery }>(
+    '/',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    financeController.findAll.bind(financeController)
+  )
+
+  // 获取单个财务记录
+  fastify.get<{ Params: FinanceParams }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    financeController.findById.bind(financeController)
+  )
+
+  // 更新财务记录
+  fastify.put<{ Params: FinanceParams; Body: UpdateFinanceBody }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    financeController.update.bind(financeController)
+  )
+
+  // 更新财务状态
+  fastify.put<{ Params: FinanceParams; Body: { status: FinanceStatus; rejectReason?: string } }>(
+    '/:id/status',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    financeController.updateStatus.bind(financeController)
+  )
+
+  // 软删除财务记录
+  fastify.delete<{ Params: FinanceParams }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    financeController.delete.bind(financeController)
+  )
+
+  // 永久删除财务记录
+  fastify.delete<{ Params: FinanceParams }>(
+    '/:id/hard',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    financeController.hardDelete.bind(financeController)
+  )
+
+  // 获取统计数据
+  fastify.get<{ Querystring: { startDate?: string; endDate?: string } }>(
+    '/statistics/summary',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    financeController.getStatistics.bind(financeController)
+  )
+}

+ 58 - 0
src/routes/income-records.routes.ts

@@ -0,0 +1,58 @@
+import { FastifyInstance } from 'fastify'
+import { IncomeRecordsController } from '../controllers/income-records.controller'
+import { authenticate, hasRole } from '../middlewares/auth.middleware'
+import { UserRole } from '../entities/user.entity'
+import { CreateIncomeRecordBody, UpdateIncomeRecordBody, ListIncomeRecordsQuery, IncomeRecordParams } from '../dto/income-records.dto'
+
+export default async function incomeRecordsRoutes(fastify: FastifyInstance) {
+  const incomeRecordsController = new IncomeRecordsController(fastify)
+
+  // 创建收入记录
+  fastify.post<{ Body: CreateIncomeRecordBody }>(
+    '/',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    incomeRecordsController.create.bind(incomeRecordsController)
+  )
+
+  // 获取收入记录列表
+  fastify.get<{ Querystring: ListIncomeRecordsQuery }>(
+    '/',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    incomeRecordsController.findAll.bind(incomeRecordsController)
+  )
+
+  // 获取单个收入记录
+  fastify.get<{ Params: IncomeRecordParams }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    incomeRecordsController.findById.bind(incomeRecordsController)
+  )
+
+  // 更新收入记录
+  fastify.put<{ Params: IncomeRecordParams; Body: UpdateIncomeRecordBody }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    incomeRecordsController.update.bind(incomeRecordsController)
+  )
+
+  // 软删除收入记录
+  fastify.delete<{ Params: IncomeRecordParams }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    incomeRecordsController.delete.bind(incomeRecordsController)
+  )
+
+  // 永久删除收入记录
+  fastify.delete<{ Params: IncomeRecordParams }>(
+    '/:id/hard',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    incomeRecordsController.hardDelete.bind(incomeRecordsController)
+  )
+
+  // 获取统计数据
+  fastify.get<{ Querystring: { startDate?: string; endDate?: string } }>(
+    '/statistics/summary',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    incomeRecordsController.getStatistics.bind(incomeRecordsController)
+  )
+}

+ 59 - 0
src/routes/promotion-link.routes.ts

@@ -0,0 +1,59 @@
+import { FastifyInstance } from 'fastify'
+import { PromotionLinkController } from '../controllers/promotion-link.controller'
+import { authenticate, hasRole } from '../middlewares/auth.middleware'
+import { UserRole } from '../entities/user.entity'
+import { CreatePromotionLinkBody, UpdatePromotionLinkBody, ListPromotionLinkQuery, PromotionLinkParams } from '../dto/promotion-link.dto'
+import { LinkType } from '../entities/promotion-link.entity'
+
+export default async function promotionLinkRoutes(fastify: FastifyInstance) {
+  const promotionLinkController = new PromotionLinkController(fastify)
+
+  // 创建推广链接
+  fastify.post<{ Body: CreatePromotionLinkBody }>(
+    '/',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    promotionLinkController.create.bind(promotionLinkController)
+  )
+
+  // 获取推广链接列表
+  fastify.get<{ Querystring: ListPromotionLinkQuery }>(
+    '/',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    promotionLinkController.findAll.bind(promotionLinkController)
+  )
+
+  // 获取单个推广链接
+  fastify.get<{ Params: PromotionLinkParams }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    promotionLinkController.findById.bind(promotionLinkController)
+  )
+
+  // 更新推广链接
+  fastify.put<{ Params: PromotionLinkParams; Body: UpdatePromotionLinkBody }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    promotionLinkController.update.bind(promotionLinkController)
+  )
+
+  // 删除推广链接
+  fastify.delete<{ Params: PromotionLinkParams }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    promotionLinkController.delete.bind(promotionLinkController)
+  )
+
+  // 按类型获取推广链接
+  fastify.get<{ Querystring: { type: LinkType } }>(
+    '/type/:type',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    promotionLinkController.findByType.bind(promotionLinkController)
+  )
+
+  // 获取统计数据
+  fastify.get(
+    '/statistics/summary',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    promotionLinkController.getStatistics.bind(promotionLinkController)
+  )
+}

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

@@ -0,0 +1,65 @@
+import { FastifyInstance } from 'fastify'
+import { TeamController } from '../controllers/team.controller'
+import { authenticate, hasRole } from '../middlewares/auth.middleware'
+import { UserRole } from '../entities/user.entity'
+import { CreateTeamBody, UpdateTeamBody, ListTeamQuery, TeamParams, UpdateRevenueBody } from '../dto/team.dto'
+
+export default async function teamRoutes(fastify: FastifyInstance) {
+  const teamController = new TeamController(fastify)
+
+  // 创建团队
+  fastify.post<{ Body: CreateTeamBody }>(
+    '/',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    teamController.create.bind(teamController)
+  )
+
+  // 获取团队列表
+  fastify.get<{ Querystring: ListTeamQuery }>(
+    '/',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    teamController.findAll.bind(teamController)
+  )
+
+  // 获取单个团队
+  fastify.get<{ Params: TeamParams }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    teamController.findById.bind(teamController)
+  )
+
+  // 更新团队
+  fastify.put<{ Params: TeamParams; Body: UpdateTeamBody }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    teamController.update.bind(teamController)
+  )
+
+  // 更新团队收入
+  fastify.put<{ Body: UpdateRevenueBody }>(
+    '/:id/revenue',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    teamController.updateRevenue.bind(teamController)
+  )
+
+  // 删除团队
+  fastify.delete<{ Params: TeamParams }>(
+    '/:id',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    teamController.delete.bind(teamController)
+  )
+
+  // 重置今日收入
+  fastify.post(
+    '/reset-today-revenue',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    teamController.resetTodayRevenue.bind(teamController)
+  )
+
+  // 获取统计数据
+  fastify.get(
+    '/statistics/summary',
+    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    teamController.getStatistics.bind(teamController)
+  )
+}

+ 20 - 5
src/services/file.service.ts

@@ -8,6 +8,7 @@ export interface UploadResult {
   key: string
   size: number
   mimeType: string
+  signUrl?: string // 签名URL(当useSignedUrl为true时)
 }
 
 export interface FileUploadOptions {
@@ -89,16 +90,23 @@ export class FileService {
       const result = await this.ossClient.put(key, buffer, {
         mime: 'application/zip',
         headers: {
-          'Cache-Control': 'max-age=31536000' // 缓存1年
+          'Cache-Control': 'no-cache' // 不设置缓存,永久保存
         }
       })
 
-      return {
+      const uploadResult: UploadResult = {
         url: result.url,
         key: key,
         size: buffer.length,
         mimeType: 'application/zip'
       }
+
+      // 如果需要签名URL,则生成签名URL
+      if (options.useSignedUrl) {
+        uploadResult.signUrl = await this.getSignedUrl(key)
+      }
+
+      return uploadResult
     } catch (error) {
       throw new Error(`ZIP文件上传失败: ${error instanceof Error ? error.message : '未知错误'}`)
     }
@@ -145,16 +153,23 @@ export class FileService {
       const result = await this.ossClient.put(key, buffer, {
         mime: mimeType,
         headers: {
-          'Cache-Control': 'max-age=31536000' // 缓存1年
+          'Cache-Control': 'no-cache' // 不设置缓存,永久保存
         }
       })
 
-      return {
+      const uploadResult: UploadResult = {
         url: result.url,
         key: key,
         size: buffer.length,
         mimeType: mimeType
       }
+
+      // 如果需要签名URL,则生成签名URL
+      if (options.useSignedUrl) {
+        uploadResult.signUrl = await this.getSignedUrl(key)
+      }
+
+      return uploadResult
     } catch (error) {
       throw new Error(`文件上传失败: ${error instanceof Error ? error.message : '未知错误'}`)
     }
@@ -185,7 +200,7 @@ export class FileService {
       ...options,
       folder: options.folder || this.defaultFolder,
       allowedTypes: imageTypes,
-      maxSize: options.maxSize || 10 * 1024 * 1024 // 默认10MB
+      maxSize: options.maxSize || 10 * 1024 * 1024
     })
   }
 

+ 192 - 0
src/services/finance.service.ts

@@ -0,0 +1,192 @@
+import { Repository, Between, Like } from 'typeorm'
+import { FastifyInstance } from 'fastify'
+import { Finance, FinanceStatus } from '../entities/finance.entity'
+import { PaginationResponse } from '../dto/common.dto'
+import { CreateFinanceBody, UpdateFinanceBody, ListFinanceQuery } from '../dto/finance.dto'
+import { FileService } from './file.service'
+
+export class FinanceService {
+  private financeRepository: Repository<Finance>
+  private fileService: FileService
+
+  constructor(app: FastifyInstance) {
+    this.financeRepository = app.dataSource.getRepository(Finance)
+    this.fileService = new FileService(app)
+  }
+
+  async create(data: CreateFinanceBody): Promise<Finance> {
+    const finance = this.financeRepository.create(data)
+    return this.financeRepository.save(finance)
+  }
+
+  async findById(id: number): Promise<Finance> {
+    const finance = await this.financeRepository.findOneOrFail({ where: { id } })
+    // 处理支付二维码链接
+    finance.paymentQrCode = await this.processImageUrl(finance.paymentQrCode)
+    return finance
+  }
+
+  /**
+   * 处理图片链接,如果是OSS链接则生成签名URL
+   * @param imageUrl 原始图片URL
+   * @returns 处理后的图片URL
+   */
+  private async processImageUrl(imageUrl: string): Promise<string> {
+    if (!imageUrl) return imageUrl
+    
+    try {
+      // 检查是否为OSS链接
+      if (imageUrl.includes('.oss-') && imageUrl.includes('.aliyuncs.com')) {
+        // 从URL中提取key
+        const urlParts = imageUrl.split('.aliyuncs.com/')
+        if (urlParts.length > 1) {
+          const key = urlParts[1]
+          // 生成签名URL
+          return await this.fileService.getSignedUrl(key)
+        }
+      }
+      return imageUrl
+    } catch (error) {
+      // 如果签名失败,返回原始URL
+      console.warn('图片链接签名失败:', error)
+      return imageUrl
+    }
+  }
+
+  /**
+   * 批量处理图片链接
+   * @param finances 财务记录数组
+   * @returns 处理后的财务记录数组
+   */
+  private async processImageUrls(finances: Finance[]): Promise<Finance[]> {
+    const processedFinances = await Promise.all(
+      finances.map(async (finance) => {
+        const processedPaymentQrCode = await this.processImageUrl(finance.paymentQrCode)
+        return {
+          ...finance,
+          paymentQrCode: processedPaymentQrCode
+        }
+      })
+    )
+    return processedFinances
+  }
+
+  async findAll(query: ListFinanceQuery): Promise<PaginationResponse<Finance>> {
+    const { page, size, status, paymentName, bankName, startDate, endDate } = query
+    
+    const where: any = {}
+    
+    if (status) {
+      where.status = status
+    }
+    
+    if (paymentName) {
+      where.paymentName = Like(`%${paymentName}%`)
+    }
+    
+    if (bankName) {
+      where.bankName = Like(`%${bankName}%`)
+    }
+    
+    if (startDate && endDate) {
+      where.createdAt = Between(new Date(startDate), new Date(endDate))
+    }
+
+    const [records, total] = await this.financeRepository.findAndCount({
+      where,
+      skip: (Number(page) || 0) * (Number(size) || 20),
+      take: Number(size) || 20,
+      order: { createdAt: 'DESC' }
+    })
+
+    // 处理支付二维码链接,生成签名URL
+    const processedRecords = await this.processImageUrls(records)
+
+    return {
+      content: processedRecords,
+      metadata: {
+        total: Number(total),
+        page: Number(page) || 0,
+        size: Number(size) || 20
+      }
+    }
+  }
+
+  async update(data: UpdateFinanceBody): Promise<Finance> {
+    const { id, ...updateData } = data
+    await this.financeRepository.update(id, updateData)
+    return this.findById(id)
+  }
+
+  async delete(id: number): Promise<void> {
+    await this.financeRepository.update(id, { delFlag: true })
+  }
+
+  async hardDelete(id: number): Promise<void> {
+    await this.financeRepository.delete(id)
+  }
+
+  async updateStatus(id: number, status: FinanceStatus, rejectReason?: string): Promise<Finance> {
+    const updateData: any = { status }
+    
+    if (status === FinanceStatus.WITHDRAWN || status === FinanceStatus.REJECTED) {
+      updateData.processedAt = new Date()
+    }
+    
+    if (status === FinanceStatus.REJECTED && rejectReason) {
+      updateData.rejectReason = rejectReason
+    }
+
+    await this.financeRepository.update(id, updateData)
+    return this.findById(id)
+  }
+
+  async getStatistics(startDate?: string, endDate?: string): Promise<{
+    totalAmount: number
+    totalCount: number
+    byStatus: Record<FinanceStatus, { amount: number; count: number }>
+    pendingAmount: number
+    processedAmount: number
+  }> {
+    const where: any = {}
+    
+    if (startDate && endDate) {
+      where.createdAt = Between(new Date(startDate), new Date(endDate))
+    }
+
+    const records = await this.financeRepository.find({
+      where,
+      select: ['reminderAmount', 'status']
+    })
+
+    const statistics = {
+      totalAmount: 0,
+      totalCount: records.length,
+      byStatus: {} as Record<FinanceStatus, { amount: number; count: number }>,
+      pendingAmount: 0,
+      processedAmount: 0
+    }
+
+    // 初始化统计对象
+    Object.values(FinanceStatus).forEach(status => {
+      statistics.byStatus[status] = { amount: 0, count: 0 }
+    })
+
+    // 计算统计数据
+    records.forEach(record => {
+      const amount = Number(record.reminderAmount)
+      statistics.totalAmount += amount
+      
+      statistics.byStatus[record.status].amount += amount
+      statistics.byStatus[record.status].count += 1
+      
+      if (record.status === FinanceStatus.PROCESSING) {
+        statistics.pendingAmount += amount
+      } else {
+        statistics.processedAmount += amount
+      }
+    })
+
+    return statistics
+  }
+}

+ 125 - 0
src/services/income-records.service.ts

@@ -0,0 +1,125 @@
+import { Repository, Between, Like } from 'typeorm'
+import { FastifyInstance } from 'fastify'
+import { IncomeRecords, IncomeType, OrderType } from '../entities/income-records.entity'
+import { PaginationResponse } from '../dto/common.dto'
+import { CreateIncomeRecordBody, UpdateIncomeRecordBody, ListIncomeRecordsQuery } from '../dto/income-records.dto'
+
+export class IncomeRecordsService {
+  private incomeRecordsRepository: Repository<IncomeRecords>
+
+  constructor(app: FastifyInstance) {
+    this.incomeRecordsRepository = app.dataSource.getRepository(IncomeRecords)
+  }
+
+  async create(data: CreateIncomeRecordBody): Promise<IncomeRecords> {
+    const incomeRecord = this.incomeRecordsRepository.create(data)
+    return this.incomeRecordsRepository.save(incomeRecord)
+  }
+
+  async findById(id: number): Promise<IncomeRecords> {
+    return this.incomeRecordsRepository.findOneOrFail({ where: { id } })
+  }
+
+  async findAll(query: ListIncomeRecordsQuery): Promise<PaginationResponse<IncomeRecords>> {
+    const { page, size, agentName, incomeType, orderType, payChannel, startDate, endDate } = query
+    
+    const where: any = {}
+    
+    if (agentName) {
+      where.agentName = Like(`%${agentName}%`)
+    }
+    
+    if (incomeType) {
+      where.incomeType = incomeType
+    }
+    
+    if (orderType) {
+      where.orderType = orderType
+    }
+    
+    if (payChannel) {
+      where.payChannel = Like(`%${payChannel}%`)
+    }
+    
+    if (startDate && endDate) {
+      where.createdAt = Between(new Date(startDate), new Date(endDate))
+    }
+
+    const [records, total] = await this.incomeRecordsRepository.findAndCount({
+      where,
+      skip: (Number(page) || 0) * (Number(size) || 20),
+      take: Number(size) || 20,
+      order: { createdAt: 'DESC' }
+    })
+
+    return {
+      content: records,
+      metadata: {
+        total: Number(total),
+        page: Number(page) || 0,
+        size: Number(size) || 20
+      }
+    }
+  }
+
+  async update(data: UpdateIncomeRecordBody): Promise<IncomeRecords> {
+    const { id, ...updateData } = data
+    await this.incomeRecordsRepository.update(id, updateData)
+    return this.findById(id)
+  }
+
+  async delete(id: number): Promise<void> {
+    await this.incomeRecordsRepository.update(id, { delFlag: true })
+  }
+
+  async hardDelete(id: number): Promise<void> {
+    await this.incomeRecordsRepository.delete(id)
+  }
+
+  async getStatistics(startDate?: string, endDate?: string): Promise<{
+    totalAmount: number
+    totalCount: number
+    byType: Record<IncomeType, { amount: number; count: number }>
+    byOrderType: Record<OrderType, { amount: number; count: number }>
+  }> {
+    const where: any = {}
+    
+    if (startDate && endDate) {
+      where.createdAt = Between(new Date(startDate), new Date(endDate))
+    }
+
+    const records = await this.incomeRecordsRepository.find({
+      where,
+      select: ['incomeAmount', 'incomeType', 'orderType']
+    })
+
+    const statistics = {
+      totalAmount: 0,
+      totalCount: records.length,
+      byType: {} as Record<IncomeType, { amount: number; count: number }>,
+      byOrderType: {} as Record<OrderType, { amount: number; count: number }>
+    }
+
+    // 初始化统计对象
+    Object.values(IncomeType).forEach(type => {
+      statistics.byType[type] = { amount: 0, count: 0 }
+    })
+    
+    Object.values(OrderType).forEach(type => {
+      statistics.byOrderType[type] = { amount: 0, count: 0 }
+    })
+
+    // 计算统计数据
+    records.forEach(record => {
+      statistics.totalAmount += Number(record.incomeAmount)
+      
+      statistics.byType[record.incomeType].amount += Number(record.incomeAmount)
+      statistics.byType[record.incomeType].count += 1
+      
+      statistics.byOrderType[record.orderType].amount += Number(record.incomeAmount)
+      statistics.byOrderType[record.orderType].count += 1
+    })
+
+    return statistics
+  }
+}

+ 159 - 0
src/services/promotion-link.service.ts

@@ -0,0 +1,159 @@
+import { Repository, Like } from 'typeorm'
+import { FastifyInstance } from 'fastify'
+import { PromotionLink, LinkType } from '../entities/promotion-link.entity'
+import { PaginationResponse } from '../dto/common.dto'
+import { CreatePromotionLinkBody, UpdatePromotionLinkBody, ListPromotionLinkQuery } from '../dto/promotion-link.dto'
+import { FileService } from './file.service'
+
+export class PromotionLinkService {
+  private promotionLinkRepository: Repository<PromotionLink>
+  private fileService: FileService
+
+  constructor(app: FastifyInstance) {
+    this.promotionLinkRepository = app.dataSource.getRepository(PromotionLink)
+    this.fileService = new FileService(app)
+  }
+
+  async create(data: CreatePromotionLinkBody): Promise<PromotionLink> {
+    const promotionLink = this.promotionLinkRepository.create(data)
+    return this.promotionLinkRepository.save(promotionLink)
+  }
+
+  async findById(id: number): Promise<PromotionLink> {
+    const link = await this.promotionLinkRepository.findOneOrFail({ where: { id } })
+    // 处理图片链接
+    link.image = await this.processImageUrl(link.image)
+    return link
+  }
+
+  /**
+   * 处理图片链接,如果是OSS链接则生成签名URL
+   * @param imageUrl 原始图片URL
+   * @returns 处理后的图片URL
+   */
+  private async processImageUrl(imageUrl: string): Promise<string> {
+    if (!imageUrl) return imageUrl
+    
+    try {
+      // 检查是否为OSS链接
+      if (imageUrl.includes('.oss-') && imageUrl.includes('.aliyuncs.com')) {
+        // 从URL中提取key
+        const urlParts = imageUrl.split('.aliyuncs.com/')
+        if (urlParts.length > 1) {
+          const key = urlParts[1]
+          // 生成签名URL
+          return await this.fileService.getSignedUrl(key)
+        }
+      }
+      return imageUrl
+    } catch (error) {
+      // 如果签名失败,返回原始URL
+      console.warn('图片链接签名失败:', error)
+      return imageUrl
+    }
+  }
+
+  /**
+   * 批量处理图片链接
+   * @param links 推广链接数组
+   * @returns 处理后的推广链接数组
+   */
+  private async processImageUrls(links: PromotionLink[]): Promise<PromotionLink[]> {
+    const processedLinks = await Promise.all(
+      links.map(async (link) => {
+        const processedImage = await this.processImageUrl(link.image)
+        return {
+          ...link,
+          image: processedImage
+        }
+      })
+    )
+    return processedLinks
+  }
+
+  async findAll(query: ListPromotionLinkQuery): Promise<PaginationResponse<PromotionLink>> {
+    const { page, size, name, type } = query
+    
+    const where: any = {}
+    
+    if (name) {
+      where.name = Like(`%${name}%`)
+    }
+    
+    if (type) {
+      where.type = type
+    }
+
+    const [links, total] = await this.promotionLinkRepository.findAndCount({
+      where,
+      skip: (Number(page) || 0) * (Number(size) || 20),
+      take: Number(size) || 20,
+      order: { createdAt: 'DESC' }
+    })
+
+    // 处理图片链接,生成签名URL
+    const processedLinks = await this.processImageUrls(links)
+
+    return {
+      content: processedLinks,
+      metadata: {
+        total: Number(total),
+        page: Number(page) || 0,
+        size: Number(size) || 20
+      }
+    }
+  }
+
+  async update(data: UpdatePromotionLinkBody): Promise<PromotionLink> {
+    const { id, ...updateData } = data
+    await this.promotionLinkRepository.update(id, updateData)
+    return this.findById(id)
+  }
+
+  async delete(id: number): Promise<void> {
+    await this.promotionLinkRepository.delete(id)
+  }
+
+  async findByType(type: LinkType): Promise<PromotionLink[]> {
+    const links = await this.promotionLinkRepository.find({
+      where: { type },
+      order: { createdAt: 'DESC' }
+    })
+    // 处理图片链接
+    return await this.processImageUrls(links)
+  }
+
+  async getStatistics(): Promise<{
+    totalLinks: number
+    byType: Record<LinkType, number>
+    recentLinks: Array<{ id: number; name: string; type: LinkType; createdAt: Date }>
+  }> {
+    const links = await this.promotionLinkRepository.find({
+      select: ['id', 'name', 'type', 'createdAt'],
+      order: { createdAt: 'DESC' }
+    })
+
+    const statistics = {
+      totalLinks: links.length,
+      byType: {} as Record<LinkType, number>,
+      recentLinks: links.slice(0, 10).map(link => ({
+        id: link.id,
+        name: link.name,
+        type: link.type,
+        createdAt: link.createdAt
+      }))
+    }
+
+    // 初始化统计对象
+    Object.values(LinkType).forEach(type => {
+      statistics.byType[type] = 0
+    })
+
+    // 计算统计数据
+    links.forEach(link => {
+      statistics.byType[link.type] += 1
+    })
+
+    return statistics
+  }
+}

+ 117 - 0
src/services/team.service.ts

@@ -0,0 +1,117 @@
+import { Repository, Like } from 'typeorm'
+import { FastifyInstance } from 'fastify'
+import { Team } from '../entities/team.entity'
+import { PaginationResponse } from '../dto/common.dto'
+import { CreateTeamBody, UpdateTeamBody, ListTeamQuery } from '../dto/team.dto'
+
+export class TeamService {
+  private teamRepository: Repository<Team>
+
+  constructor(app: FastifyInstance) {
+    this.teamRepository = app.dataSource.getRepository(Team)
+  }
+
+  async create(data: CreateTeamBody): Promise<Team> {
+    const team = this.teamRepository.create(data)
+    return this.teamRepository.save(team)
+  }
+
+  async findById(id: number): Promise<Team> {
+    return this.teamRepository.findOneOrFail({ where: { id } })
+  }
+
+  async findAll(query: ListTeamQuery): Promise<PaginationResponse<Team>> {
+    const { page, size, name } = query
+    
+    const where: any = {}
+    
+    if (name) {
+      where.name = Like(`%${name}%`)
+    }
+
+    const [teams, total] = await this.teamRepository.findAndCount({
+      where,
+      skip: (Number(page) || 0) * (Number(size) || 20),
+      take: Number(size) || 20,
+      order: { createdAt: 'DESC' }
+    })
+
+    return {
+      content: teams,
+      metadata: {
+        total: Number(total),
+        page: Number(page) || 0,
+        size: Number(size) || 20
+      }
+    }
+  }
+
+  async update(data: UpdateTeamBody): Promise<Team> {
+    const { id, ...updateData } = data
+    await this.teamRepository.update(id, updateData)
+    return this.findById(id)
+  }
+
+  async delete(id: number): Promise<void> {
+    await this.teamRepository.delete(id)
+  }
+
+  async updateRevenue(id: number, amount: number, type: 'total' | 'today'): Promise<Team> {
+    const team = await this.findById(id)
+    
+    if (type === 'total') {
+      team.totalRevenue = Number(team.totalRevenue) + amount
+    } else {
+      team.todayRevenue = Number(team.todayRevenue) + amount
+    }
+    
+    return this.teamRepository.save(team)
+  }
+
+  async resetTodayRevenue(): Promise<void> {
+    await this.teamRepository.update({}, { todayRevenue: 0 })
+  }
+
+  async getStatistics(): Promise<{
+    totalTeams: number
+    totalRevenue: number
+    todayRevenue: number
+    averageCommissionRate: number
+    topTeams: Array<{ id: number; name: string; totalRevenue: number; todayRevenue: number }>
+  }> {
+    const teams = await this.teamRepository.find({
+      select: ['id', 'name', 'totalRevenue', 'todayRevenue', 'commissionRate']
+    })
+
+    const statistics = {
+      totalTeams: teams.length,
+      totalRevenue: 0,
+      todayRevenue: 0,
+      averageCommissionRate: 0,
+      topTeams: [] as Array<{ id: number; name: string; totalRevenue: number; todayRevenue: number }>
+    }
+
+    let totalCommissionRate = 0
+
+    teams.forEach(team => {
+      statistics.totalRevenue += Number(team.totalRevenue)
+      statistics.todayRevenue += Number(team.todayRevenue)
+      totalCommissionRate += Number(team.commissionRate)
+    })
+
+    statistics.averageCommissionRate = teams.length > 0 ? totalCommissionRate / teams.length : 0
+
+    // 获取收入前5的团队
+    statistics.topTeams = teams
+      .sort((a, b) => Number(b.totalRevenue) - Number(a.totalRevenue))
+      .slice(0, 5)
+      .map(team => ({
+        id: team.id,
+        name: team.name,
+        totalRevenue: Number(team.totalRevenue),
+        todayRevenue: Number(team.todayRevenue)
+      }))
+
+    return statistics
+  }
+}