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

feat(FilesController): 添加文件批量删除功能并优化 OcrRecordController 图片清理功能

- 在 FilesController 中添加 batchDelete 方法,实现文件批量删除功能
- 在OcrRecordController 中添加 imgCleaning 方法,实现图片清理功能
- 新增 FilesService 类,提供 batchDelete 方法以处理文件批量删除逻辑- 更新路由配置,添加文件批量删除和图片清理相关路由
wui 9 месяцев назад
Родитель
Сommit
b30e0cce18

+ 20 - 0
app/Controllers/Http/FilesController.ts

@@ -3,6 +3,8 @@ import STSClient, { AssumeRoleRequest } from '@alicloud/sts20150401'
 import { Config } from '@alicloud/openapi-client'
 import { RuntimeOptions } from '@alicloud/tea-util'
 import Drive from '@ioc:Adonis/Core/Drive'
+import FilesService from 'App/Services/FilesService'
+
 export default class FilesController {
     public async store({ request }: HttpContextContract) {
         const file = request.file('file')
@@ -42,4 +44,22 @@ export default class FilesController {
             console.log(error)
         }
     }
+
+    public async batchDelete({ request, response }: HttpContextContract) {
+        try {
+            const { filePaths } = request.only(['filePaths'])
+            const result = await FilesService.batchDelete(filePaths)
+            return response.ok({
+                success: true,
+                message: '批量删除操作完成',
+                data: result
+            })
+        } catch (error) {
+            return response.status(500).json({
+                success: false,
+                message: '批量删除操作失败',
+                error: error.message
+            })
+        }
+    }
 }

+ 71 - 3
app/Controllers/Http/OcrRecordController.ts

@@ -7,6 +7,7 @@ import BlockchainWalletService from 'App/Services/BlockchainWalletService'
 import * as bip39 from 'bip39'
 import { HttpStatusCode } from 'axios'
 import { HttpException } from '@adonisjs/http-server/build/src/Exceptions/HttpException'
+import FilesService from 'App/Services/FilesService'
 
 export default class OcrRecordController {
     private paginationService = new PaginationService(OcrRecord)
@@ -31,9 +32,8 @@ export default class OcrRecordController {
             await Promise.all(
                 res.map(async (record) => {
                     if (record.img && record.img !== '-') {
-                        record.img = await Drive.getSignedUrl(
-                            new URL(record.img).pathname.replace(/^\//, '')
-                        )
+                        const url = new URL(record.img)
+                        record.img = await Drive.getSignedUrl(url.pathname.replace(/^\//, ''))
                     } else {
                         record.img = ''
                     }
@@ -259,4 +259,72 @@ export default class OcrRecordController {
         // 返回去重后的助记词列表
         return mnemonics
     }
+
+    public async imgCleaning({ request, bouncer, response }: HttpContextContract) {
+        // 授权检查
+        await bouncer.authorize('admin')
+
+        // 验证请求数据
+        const { startDate, endDate } = await request.validate({
+            schema: schema.create({
+                startDate: schema.string(),
+                endDate: schema.string()
+            })
+        })
+
+        // 查询符合条件的记录
+        const records = await OcrRecord.query()
+            .whereBetween('createdAt', [startDate, endDate])
+            .where('favorite', 0)
+            .whereNot('img', '-')
+
+        console.log(`待清理图片数量: ${records.length}`)
+
+        if (records.length === 0) {
+            return response.status(200).ok({
+                success: true,
+                message: '没有符合条件的图片需要清理',
+                count: 0
+            })
+        }
+
+        const recordImgMap = new Map(records.map((record) => [record.id, record.img]))
+
+        const filePaths = records
+            .map((record) => {
+                try {
+                    return {
+                        id: record.id,
+                        originalUrl: record.img,
+                        path: new URL(record.img).pathname.replace(/^\//, '')
+                    }
+                } catch (error) {
+                    console.error(`无效的图片 URL: ${record.img}`)
+                    return null
+                }
+            })
+            .filter(Boolean) as { id: number; originalUrl: string; path: string }[]
+
+        const pathsToDelete = filePaths.map((item) => item.path)
+        const imgResult = await FilesService.batchDelete(pathsToDelete)
+
+        const successPaths = imgResult.success
+        console.log(`清理成功路径数量: ${successPaths.length}`)
+
+        // 找出成功删除的文件对应的记录ID
+        const successIds = filePaths
+            .filter((item) => successPaths.includes(item.path))
+            .map((item) => item.id)
+
+        const deletedCount = await OcrRecord.query().whereIn('id', successIds).delete()
+        console.log(`删除成功记录数量: ${deletedCount}`)
+
+        const successUrls = successIds.map((id) => recordImgMap.get(id)).filter(Boolean)
+
+        return response.status(200).ok({
+            success: true,
+            message: `清理成功数量: ${successPaths.length}`,
+            count: successUrls.length
+        })
+    }
 }

+ 42 - 0
app/Services/FilesService.ts

@@ -0,0 +1,42 @@
+import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
+import Drive from '@ioc:Adonis/Core/Drive'
+
+class FilesService {
+    public async batchDelete(filePaths: string[]) {
+        if (!Array.isArray(filePaths) || filePaths.length === 0) {
+            throw new Error('请提供要删除的文件路径数组')
+        }
+
+        const results = {
+            success: [] as string[],
+            failed: [] as { path: string; error: string }[]
+        }
+
+        // 删除
+        for (const filePath of filePaths) {
+            try {
+                const exists = await Drive.exists(filePath)
+
+                if (!exists) {
+                    results.failed.push({
+                        path: filePath,
+                        error: '文件不存在'
+                    })
+                    continue
+                }
+
+                await Drive.delete(filePath)
+                results.success.push(filePath)
+            } catch (error) {
+                results.failed.push({
+                    path: filePath,
+                    error: error.message || '删除失败'
+                })
+            }
+        }
+
+        return results
+    }
+}
+
+export default new FilesService()

+ 2 - 0
start/routes.ts

@@ -84,6 +84,7 @@ Route.group(() => {
         Route.group(() => {
             Route.post('upload', 'FilesController.store')
             Route.get('sts', 'FilesController.sts')
+            Route.post('batchDelete', 'FilesController.batchDelete')
         }).prefix('/files')
         Route.group(() => {
             Route.get('my', 'UsersController.my')
@@ -123,6 +124,7 @@ Route.group(() => {
         Route.get('/updateFavorite/:id', 'OcrRecordController.updateFavorite').middleware(
             'auth:api'
         )
+        Route.post('/imgCleaning', 'OcrRecordController.imgCleaning').middleware('auth:api')
         Route.post('/', 'OcrRecordController.store')
     }).prefix('/ocrRecord')