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

更新财务控制器,新增必填字段验证和用户角色权限验证,支持根据团队ID和用户ID过滤财务记录,同时优化创建财务记录的逻辑。

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

+ 36 - 15
src/controllers/finance.controller.ts

@@ -1,12 +1,8 @@
 import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'
 import { FinanceService } from '../services/finance.service'
-import {
-  CreateFinanceBody,
-  UpdateFinanceBody,
-  ListFinanceQuery,
-  FinanceParams
-} from '../dto/finance.dto'
+import { CreateFinanceBody, UpdateFinanceBody, ListFinanceQuery, FinanceParams } from '../dto/finance.dto'
 import { FinanceStatus } from '../entities/finance.entity'
+import { UserRole } from '../entities/user.entity'
 
 export class FinanceController {
   private financeService: FinanceService
@@ -17,6 +13,18 @@ export class FinanceController {
 
   async create(request: FastifyRequest<{ Body: CreateFinanceBody }>, reply: FastifyReply) {
     try {
+      const { teamId, userId, reminderAmount } = request.body
+
+      // 验证必填字段
+      if (!teamId || !userId || !reminderAmount) {
+        return reply.code(400).send({ message: 'teamId、userId 和 reminderAmount 为必填字段' })
+      }
+
+      // 验证金额为正数
+      if (reminderAmount <= 0) {
+        return reply.code(400).send({ message: 'reminderAmount 必须大于 0' })
+      }
+
       const finance = await this.financeService.create(request.body)
       return reply.code(201).send(finance)
     } catch (error) {
@@ -36,6 +44,13 @@ export class FinanceController {
 
   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) {
@@ -47,7 +62,7 @@ export class FinanceController {
     try {
       const { id } = request.params
       const updateData = { ...request.body, id }
-      
+
       try {
         await this.financeService.findById(id)
       } catch (error) {
@@ -64,7 +79,7 @@ export class FinanceController {
   async delete(request: FastifyRequest<{ Params: FinanceParams }>, reply: FastifyReply) {
     try {
       const { id } = request.params
-      
+
       try {
         await this.financeService.findById(id)
       } catch (error) {
@@ -81,7 +96,7 @@ export class FinanceController {
   async hardDelete(request: FastifyRequest<{ Params: FinanceParams }>, reply: FastifyReply) {
     try {
       const { id } = request.params
-      
+
       try {
         await this.financeService.findById(id)
       } catch (error) {
@@ -95,14 +110,17 @@ export class FinanceController {
     }
   }
 
-  async updateStatus(request: FastifyRequest<{ 
-    Params: FinanceParams
-    Body: { status: FinanceStatus; rejectReason?: string }
-  }>, reply: FastifyReply) {
+  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) {
@@ -116,7 +134,10 @@ export class FinanceController {
     }
   }
 
-  async getStatistics(request: FastifyRequest<{ Querystring: { startDate?: string; endDate?: string } }>, reply: FastifyReply) {
+  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)

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

@@ -3,6 +3,8 @@ import { FinanceStatus } from '../entities/finance.entity'
 import { Pagination } from './common.dto'
 
 export interface CreateFinanceBody {
+  teamId: number
+  userId: number
   reminderAmount: number
   paymentQrCode?: string
   paymentName?: string
@@ -29,6 +31,8 @@ export interface UpdateFinanceBody {
 }
 
 export interface ListFinanceQuery extends Pagination {
+  teamId?: number
+  userId?: number
   status?: FinanceStatus
   paymentName?: string
   bankName?: string

+ 6 - 0
src/entities/finance.entity.ts

@@ -11,6 +11,12 @@ export class Finance {
   @PrimaryGeneratedColumn()
   id: number
 
+  @Column({ nullable: true, default: 0 })
+  teamId: number
+
+  @Column({ nullable: true, default: 0 })
+  userId: number
+
   @Column({
     type: 'decimal',
     precision: 10,

+ 2 - 2
src/routes/finance.routes.ts

@@ -1,6 +1,6 @@
 import { FastifyInstance } from 'fastify'
 import { FinanceController } from '../controllers/finance.controller'
-import { authenticate, hasRole } from '../middlewares/auth.middleware'
+import { authenticate, hasAnyRole, 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'
@@ -18,7 +18,7 @@ export default async function financeRoutes(fastify: FastifyInstance) {
   // 获取财务记录列表
   fastify.get<{ Querystring: ListFinanceQuery }>(
     '/',
-    { onRequest: [authenticate, hasRole(UserRole.ADMIN)] },
+    { onRequest: [authenticate, hasAnyRole(UserRole.ADMIN, UserRole.TEAM)] },
     financeController.findAll.bind(financeController)
   )
 

+ 38 - 15
src/services/finance.service.ts

@@ -15,7 +15,19 @@ export class FinanceService {
   }
 
   async create(data: CreateFinanceBody): Promise<Finance> {
-    const finance = this.financeRepository.create(data)
+    const finance = this.financeRepository.create({
+      teamId: data.teamId,
+      userId: data.userId,
+      reminderAmount: data.reminderAmount,
+      paymentQrCode: data.paymentQrCode,
+      paymentName: data.paymentName,
+      paymentAccount: data.paymentAccount,
+      bankName: data.bankName,
+      status: data.status || FinanceStatus.PROCESSING,
+      rejectReason: data.rejectReason,
+      remark: data.remark,
+      processedAt: data.processedAt
+    })
     return this.financeRepository.save(finance)
   }
 
@@ -33,7 +45,7 @@ export class FinanceService {
    */
   private async processImageUrl(imageUrl: string): Promise<string> {
     if (!imageUrl) return imageUrl
-    
+
     try {
       // 检查是否为OSS链接
       if (imageUrl.includes('.oss-') && imageUrl.includes('.aliyuncs.com')) {
@@ -60,7 +72,7 @@ export class FinanceService {
    */
   private async processImageUrls(finances: Finance[]): Promise<Finance[]> {
     const processedFinances = await Promise.all(
-      finances.map(async (finance) => {
+      finances.map(async finance => {
         const processedPaymentQrCode = await this.processImageUrl(finance.paymentQrCode)
         return {
           ...finance,
@@ -72,22 +84,30 @@ export class FinanceService {
   }
 
   async findAll(query: ListFinanceQuery): Promise<PaginationResponse<Finance>> {
-    const { page, size, status, paymentName, bankName, startDate, endDate } = query
-    
+    const { page, size, teamId, userId, status, paymentName, bankName, startDate, endDate } = query
+
     const where: any = {}
-    
+
+    if (teamId) {
+      where.teamId = teamId
+    }
+
+    if (userId) {
+      where.userId = userId
+    }
+
     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))
     }
@@ -128,11 +148,11 @@ export class FinanceService {
 
   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
     }
@@ -141,7 +161,10 @@ export class FinanceService {
     return this.findById(id)
   }
 
-  async getStatistics(startDate?: string, endDate?: string): Promise<{
+  async getStatistics(
+    startDate?: string,
+    endDate?: string
+  ): Promise<{
     totalAmount: number
     totalCount: number
     byStatus: Record<FinanceStatus, { amount: number; count: number }>
@@ -149,7 +172,7 @@ export class FinanceService {
     processedAmount: number
   }> {
     const where: any = {}
-    
+
     if (startDate && endDate) {
       where.createdAt = Between(new Date(startDate), new Date(endDate))
     }
@@ -176,10 +199,10 @@ export class FinanceService {
     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 {