فهرست منبع

refactor(ocr): 重构 OCR 设备和记录的统计逻辑- 优化了日期处理逻辑,支持自定义起始和结束日期
- 重构了查询方式,使用 SQL 直接获取每日统计数据
- 改进了 API 用户和普通用户的渠道处理方式
-优化了代码结构,提高了可读性和可维护性

wui 6 ماه پیش
والد
کامیت
66f8883870
1فایلهای تغییر یافته به همراه148 افزوده شده و 52 حذف شده
  1. 148 52
      app/Controllers/Http/OcrDevicesController.ts

+ 148 - 52
app/Controllers/Http/OcrDevicesController.ts

@@ -127,37 +127,125 @@ export default class OcrDevicesController {
         try {
             const user = auth.user
             const isApiUser = user?.$attributes?.role === 'api'
-            const deviceQuery = OcrDevice.query()
-            const recordQuery = OcrRecord.query()
 
-            if (isApiUser) {
-                deviceQuery.where('channel', user.username)
-                recordQuery.where('channel', user.username)
+            // 获取开始日期和结束日期,默认为不包括今天的最近七天
+            let startDate = request.input(
+                'startDate',
+                DateTime.now().minus({ days: 7 }).startOf('day').toFormat('yyyy-MM-dd HH:mm:ss')
+            )
+            let endDate = request.input(
+                'endDate',
+                DateTime.now().minus({ days: 1 }).endOf('day').toFormat('yyyy-MM-dd HH:mm:ss')
+            )
+
+            // 处理可能的不同日期格式
+            let startDateTime: DateTime
+            let endDateTime: DateTime
+
+            // 尝试解析开始日期
+            if (startDate.includes(' ')) {
+                // 如果包含空格,假设是完整的日期时间格式
+                startDateTime = DateTime.fromFormat(startDate, 'yyyy-MM-dd HH:mm:ss')
+                if (!startDateTime.isValid) {
+                    // 尝试其他可能的格式
+                    startDateTime = DateTime.fromISO(startDate)
+                }
             } else {
-                const channel = request.input('channel')
-                if (channel) {
-                    deviceQuery.where('channel', channel)
-                    recordQuery.where('channel', channel)
+                // 只有日期部分
+                startDateTime = DateTime.fromFormat(startDate, 'yyyy-MM-dd').startOf('day')
+                startDate = startDateTime.toFormat('yyyy-MM-dd HH:mm:ss')
+            }
+
+            // 尝试解析结束日期
+            if (endDate.includes(' ')) {
+                // 如果包含空格,假设是完整的日期时间格式
+                endDateTime = DateTime.fromFormat(endDate, 'yyyy-MM-dd HH:mm:ss')
+                if (!endDateTime.isValid) {
+                    // 尝试其他可能的格式
+                    endDateTime = DateTime.fromISO(endDate)
                 }
+            } else {
+                // 只有日期部分,设置为当天结束
+                endDateTime = DateTime.fromFormat(endDate, 'yyyy-MM-dd').endOf('day')
+                endDate = endDateTime.toFormat('yyyy-MM-dd HH:mm:ss')
+            }
+
+            // 验证日期是否有效
+            if (!startDateTime.isValid || !endDateTime.isValid) {
+                return response.badRequest({
+                    message: '无效的日期格式',
+                    details: {
+                        startDate: startDateTime.isValid ? '有效' : '无效',
+                        endDate: endDateTime.isValid ? '有效' : '无效'
+                    }
+                })
             }
 
-            // 获取近7天的数据
-            const sevenDaysAgo = DateTime.now().minus({ days: 7 }).startOf('day').toSQL()
-            const devices = await deviceQuery
-                .where('createdAt', '>=', sevenDaysAgo)
-                .select('id', 'total', 'scanned', 'createdAt', 'channel')
+            // 计算日期之间的天数差
+            const diffInDays = Math.max(1, Math.ceil(endDateTime.diff(startDateTime, 'days').days))
 
-            // 过去7天的日期数组
+            console.log('日期调试信息:', {
+                startDate,
+                endDate,
+                startDateTime: startDateTime.toISO(),
+                endDateTime: endDateTime.toISO(),
+                diffInDays
+            })
+
+            // 生成日期数组
             const dates: string[] = []
-            for (let i = 6; i >= 0; i--) {
-                const date = DateTime.now().minus({ days: i }).toFormat('yyyy-MM-dd')
+            for (let i = 0; i < diffInDays; i++) {
+                const date = startDateTime.plus({ days: i }).toFormat('yyyy-MM-dd')
                 dates.push(date)
             }
-            const dailyStats: Record<
-                string,
-                { total: number; scanned: number; deviceCount: number }
-            > = {}
 
+            // 准备查询条件
+            let channelCondition = {}
+            if (isApiUser) {
+                channelCondition = { channel: user.username }
+            } else if (request.input('channel')) {
+                channelCondition = { channel: request.input('channel') }
+            }
+
+            // 使用SQL直接获取每日设备统计数据
+            const deviceStatsQuery = Database.from('ocr_devices')
+                .select(
+                    Database.raw("DATE_FORMAT(created_at, '%Y-%m-%d') as date"),
+                    Database.raw('COUNT(id) as device_count'),
+                    Database.raw('SUM(scanned) as scanned_count')
+                )
+                .whereBetween('created_at', [startDate, endDate])
+                .groupBy('date')
+                .orderBy('date', 'asc')
+
+            // 添加渠道条件
+            if (Object.keys(channelCondition).length > 0) {
+                deviceStatsQuery.where(channelCondition)
+            }
+
+            const deviceStats = await deviceStatsQuery
+
+            // 使用SQL直接获取每日记录统计数据
+            const recordStatsQuery = Database.from('ocr_records')
+                .select(
+                    Database.raw("DATE_FORMAT(created_at, '%Y-%m-%d') as date"),
+                    Database.raw('COUNT(id) as record_count')
+                )
+                .whereBetween('created_at', [startDate, endDate])
+                .groupBy('date')
+                .orderBy('date', 'asc')
+
+            // 添加渠道条件
+            if (Object.keys(channelCondition).length > 0) {
+                recordStatsQuery.where(channelCondition)
+            }
+
+            const recordStats = await recordStatsQuery
+
+            // 合并结果
+            const dailyStats = {}
+
+            // 初始化所有日期的统计数据
             dates.forEach((date) => {
                 dailyStats[date] = {
                     total: 0,
@@ -166,25 +254,20 @@ export default class OcrDevicesController {
                 }
             })
 
-            // 按设备创建日期统计数据 scanned和deviceCount
-            for (const device of devices) {
-                const dateStr = device.createdAt.toFormat('yyyy-MM-dd')
-                if (dailyStats[dateStr]) {
-                    dailyStats[dateStr].scanned += device.scanned
-                    dailyStats[dateStr].deviceCount += 1
+            // 填充设备统计数据
+            deviceStats.forEach((stat) => {
+                if (dailyStats[stat.date]) {
+                    dailyStats[stat.date].scanned = Number(stat.scanned_count) || 0
+                    dailyStats[stat.date].deviceCount = Number(stat.device_count) || 0
                 }
-            }
+            })
 
-            // 获取OcrRecord的每日数量
-            for (const date of dates) {
-                const startDate = DateTime.fromFormat(date, 'yyyy-MM-dd').startOf('day').toSQL()
-                const endDate = DateTime.fromFormat(date, 'yyyy-MM-dd').endOf('day').toSQL()
-                const recordCount = await Database.from('ocr_records')
-                    .where('created_at', '>=', startDate)
-                    .where('created_at', '<=', endDate)
-                    .count('* as total')
-                dailyStats[date].total = Number(recordCount[0].total) || 0
-            }
+            // 填充记录统计数据
+            recordStats.forEach((stat) => {
+                if (dailyStats[stat.date]) {
+                    dailyStats[stat.date].total = Number(stat.record_count) || 0
+                }
+            })
 
             // 转换为前端需要的数组格式
             const totals = dates.map((date) => dailyStats[date].total)
@@ -210,46 +293,59 @@ export default class OcrDevicesController {
             const user = auth.user
             const isApiUser = user?.$attributes?.role === 'api'
             const deviceQuery = OcrDevice.query()
-            const recordQuery = OcrRecord.query()
 
-            // 如果是 API 用户,强制使用其 username 作为 channel
+            // 如果是API用户,强制使用其username作为channel
             if (isApiUser) {
                 deviceQuery.where('channel', user.username)
-                recordQuery.where('channel', user.username)
             } else {
-                // 如果不是 API 用户,则使用请求中的 channel 参数
+                // 如果不是API用户,则使用请求中的channel参数
                 const channel = request.input('channel')
                 if (channel) {
                     deviceQuery.where('channel', channel)
-                    recordQuery.where('channel', channel)
                 }
             }
 
-            // 获取今天的数据
-            const today = DateTime.now().startOf('day').toSQL()
-            const todayEnd = DateTime.now().endOf('day').toSQL()
+            // 获取指定日期的数据,默认为今天
+            const targetDate = request.input('date', DateTime.now().toFormat('yyyy-MM-dd'))
+            const dayStart = DateTime.fromFormat(targetDate, 'yyyy-MM-dd').startOf('day').toSQL()
+            const dayEnd = DateTime.fromFormat(targetDate, 'yyyy-MM-dd').endOf('day').toSQL()
+
             // 获取设备数据
-            const deviceData = await deviceQuery.where('createdAt', '>=', today).select('scanned')
+            const deviceData = await deviceQuery
+                .where('createdAt', '>=', dayStart)
+                .where('createdAt', '<=', dayEnd)
+                .select('scanned')
+
             // 获取OcrRecord数据
             const recordCount = await Database.from('ocr_records')
-                .where('created_at', '>=', today)
-                .where('created_at', '<=', todayEnd)
+                .where('created_at', '>=', dayStart)
+                .where('created_at', '<=', dayEnd)
+                .where(function (query) {
+                    if (isApiUser) {
+                        query.where('channel', user.username)
+                    } else {
+                        const channel = request.input('channel')
+                        if (channel) {
+                            query.where('channel', channel)
+                        }
+                    }
+                })
                 .count('* as total')
 
-            // 计算今日统计数据
+            // 计算统计数据
             const scanned = deviceData.reduce((acc, item) => acc + item.scanned, 0)
             const total = Number(recordCount[0].total) || 0
             const deviceCount = deviceData.length
 
             return response.ok({
-                date: DateTime.now().toFormat('yyyy-MM-dd'),
+                date: targetDate,
                 total,
                 scanned,
                 deviceCount
             })
         } catch (error) {
             return response.internalServerError({
-                message: '获取今日统计数据时发生错误',
+                message: '获取统计数据时发生错误',
                 error: error.message
             })
         }