| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
- import PaginationService from 'App/Services/PaginationService'
- import { schema } from '@ioc:Adonis/Core/Validator'
- import OcrDevice from 'App/Models/OcrDevice'
- import OcrRecord from 'App/Models/OcrRecord'
- import OcrChannel from 'App/Models/OcrChannel'
- import { DateTime } from 'luxon'
- import * as console from 'node:console'
- import Database from '@ioc:Adonis/Lucid/Database'
- import { UserRoles } from 'App/Models/User'
- import UserService from 'App/Services/UserService'
- export default class OcrDevicesController {
- private paginationService = new PaginationService(OcrDevice)
- public async index({ request, response, auth }: HttpContextContract) {
- try {
- const { page = 1, size = 20, id, deviceId, channel } = request.qs()
- const user = auth.user
- const role = user?.$attributes?.role
- if (!role) {
- return response.forbidden({
- error: 'Forbidden',
- message: 'Unauthorized access'
- })
- }
- let userChannel: string | undefined
- if (role === UserRoles.Api) {
- userChannel = user!.username
- } else if (['admin', 'operator'].includes(role)) {
- const apiUsers = await UserService.findReferredUsers(user!.id)
- const allowedChannels = apiUsers.map((user) => user.username as string)
- // 如果请求中指定了channel,检查是否在允许的channel列表中
- if (channel) {
- if (!allowedChannels.includes(channel)) {
- return response.ok({
- data: [],
- meta: {
- total: 0,
- per_page: Number(size),
- current_page: Number(page),
- last_page: 0,
- first_page: 1,
- first_page_url: '/?page=1',
- last_page_url: '/?page=0',
- next_page_url: null,
- previous_page_url: null
- }
- })
- }
- userChannel = channel
- } else {
- userChannel = allowedChannels.join(',')
- }
- } else {
- return response.forbidden({
- error: 'Forbidden',
- message: 'You are not authorized to access this resource'
- })
- }
- // 构建查询条件
- const query = OcrDevice.query()
- if (id) {
- query.where('id', id)
- }
- if (deviceId) {
- query.where('id', deviceId)
- }
- if (userChannel) {
- query.whereIn('channel', userChannel.split(','))
- }
- // 执行分页查询
- const result = await query.orderBy('created_at', 'desc').paginate(page, size)
- return response.ok(result)
- } catch (error) {
- return response.internalServerError({
- message: '获取OCR设备列表时发生错误',
- error: error.message
- })
- }
- }
- public async store({ request, bouncer }: HttpContextContract) {
- // await bouncer.authorize('admin')
- const data = await request.validate({
- schema: schema.create({
- id: schema.string(),
- platform: schema.string(),
- channel: schema.string(),
- deviceInfo: schema.string.optional(),
- total: schema.number(),
- scanned: schema.number(),
- ipAddress: schema.string.optional()
- })
- })
- const clientIp = request.ip()
- if (!data.ipAddress) {
- data.ipAddress = clientIp
- }
- const device = await OcrDevice.findBy('id', data.id)
- let ocrDevice: OcrDevice
- if (device) {
- device.merge(data)
- ocrDevice = await device.save()
- } else {
- ocrDevice = await OcrDevice.create(data)
- }
- // 更新渠道统计数据
- await this.updateNums(data.channel)
- return ocrDevice
- }
- private async updateNums(channel: string) {
- if (channel) {
- const ocrChannel = await OcrChannel.findBy('name', channel)
- if (ocrChannel) {
- const deviceCount = await OcrDevice.query()
- .where('channel', channel)
- .count('* as total')
- const recordCount = await OcrRecord.query()
- .where('channel', channel)
- .count('* as total')
- const scanSum = await OcrDevice.query()
- .where('channel', channel)
- .sum('scanned as total')
- ocrChannel.deviceNum = Number(deviceCount[0].$extras.total || 0)
- ocrChannel.recordNum = Number(recordCount[0].$extras.total || 0)
- ocrChannel.scanNum = Number(scanSum[0].$extras.total || 0)
- await ocrChannel.save()
- }
- }
- }
- public async show({ params, bouncer }: HttpContextContract) {
- await bouncer.authorize('admin')
- return await OcrDevice.findOrFail(params.id)
- }
- public async plusTotal({ request, response }: HttpContextContract) {
- try {
- const device = await OcrDevice.findBy('id', request.param('id'))
- if (!device) {
- return response.notFound({ message: `未找到ID为 ${request.param('id')} 的OCR设备` })
- }
- device.total += 1
- await device.save()
- return response.ok(device)
- } catch (error) {
- return response.internalServerError({
- message: '更新设备记录数量时发生错误',
- error: error.message
- })
- }
- }
- public async plusScanned({ request, response }: HttpContextContract) {
- const scanCount = Number(request.param('scanCount'))
- if (isNaN(scanCount)) {
- return response.badRequest({ message: 'scanCount 参数必须是有效数字' })
- }
- try {
- const device = await OcrDevice.findBy('id', request.param('id'))
- if (!device) {
- return response.notFound({
- message: `未找到 ID 为 ${request.param('id')} 的 OCR 设备`
- })
- }
- device.scanned += scanCount
- await device.save()
- return response.ok(device)
- } catch (error) {
- return response.internalServerError({
- message: '更新设备扫描数量时发生错误',
- error: error.message
- })
- }
- }
- public async getStatistics({ request, response, auth }: HttpContextContract) {
- try {
- const user = auth.user
- const role = user?.$attributes?.role
- if (!role) {
- return response.forbidden({
- error: 'Forbidden',
- message: 'Unauthorized access'
- })
- }
- let userChannel: string | undefined
- if (role === UserRoles.Api) {
- userChannel = user!.username
- } else if (['admin', 'operator'].includes(role)) {
- const apiUsers = await UserService.findReferredUsers(user!.id)
- const allowedChannels = apiUsers.map((user) => user.username as string)
- // 如果请求中指定了channel,检查是否在允许的channel列表中
- const requestChannel = request.input('channel')
- if (requestChannel) {
- if (!allowedChannels.includes(requestChannel)) {
- return response.ok({
- dates: [],
- total: [],
- scanned: [],
- deviceCount: []
- })
- }
- userChannel = requestChannel
- } else {
- userChannel = allowedChannels.join(',')
- }
- } else {
- return response.forbidden({
- error: 'Forbidden',
- message: 'You are not authorized to access this resource'
- })
- }
- // 获取开始日期和结束日期,默认为不包括今天的最近七天
- 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 {
- // 只有日期部分
- 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 ? '有效' : '无效'
- }
- })
- }
- // 计算日期之间的天数差
- const diffInDays = Math.max(1, Math.ceil(endDateTime.diff(startDateTime, 'days').days))
- console.log('日期调试信息:', {
- startDate,
- endDate,
- startDateTime: startDateTime.toISO(),
- endDateTime: endDateTime.toISO(),
- diffInDays
- })
- // 生成日期数组
- const dates: string[] = []
- for (let i = 0; i < diffInDays; i++) {
- const date = startDateTime.plus({ days: i }).toFormat('yyyy-MM-dd')
- dates.push(date)
- }
- // 准备查询条件
- 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])
- .whereIn('channel', userChannel!.split(','))
- .groupBy('date')
- .orderBy('date', 'asc')
- 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])
- .whereIn('channel', userChannel!.split(','))
- .groupBy('date')
- .orderBy('date', 'asc')
- const recordStats = await recordStatsQuery
- // 合并结果
- const dailyStats = {}
- // 初始化所有日期的统计数据
- dates.forEach((date) => {
- dailyStats[date] = {
- total: 0,
- scanned: 0,
- deviceCount: 0
- }
- })
- // 填充设备统计数据
- 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
- }
- })
- // 填充记录统计数据
- recordStats.forEach((stat) => {
- if (dailyStats[stat.date]) {
- dailyStats[stat.date].total = Number(stat.record_count) || 0
- }
- })
- // 转换为前端需要的数组格式
- const totals = dates.map((date) => dailyStats[date].total)
- const scanned = dates.map((date) => dailyStats[date].scanned)
- const deviceCounts = dates.map((date) => dailyStats[date].deviceCount)
- return response.ok({
- dates,
- total: totals,
- scanned: scanned,
- deviceCount: deviceCounts
- })
- } catch (error) {
- return response.internalServerError({
- message: '获取设备统计数据时发生错误',
- error: error.message
- })
- }
- }
- public async getTodayStatistics({ request, response, auth }: HttpContextContract) {
- try {
- const user = auth.user
- const role = user?.$attributes?.role
- if (!role) {
- return response.forbidden({
- error: 'Forbidden',
- message: 'Unauthorized access'
- })
- }
- let userChannel: string | undefined
- if (role === UserRoles.Api) {
- userChannel = user!.username
- } else if (['admin', 'operator'].includes(role)) {
- const apiUsers = await UserService.findReferredUsers(user!.id)
- const allowedChannels = apiUsers.map((user) => user.username as string)
- // 如果请求中指定了channel,检查是否在允许的channel列表中
- const requestChannel = request.input('channel')
- if (requestChannel) {
- if (!allowedChannels.includes(requestChannel)) {
- return response.ok({
- date: request.input('date', DateTime.now().toFormat('yyyy-MM-dd')),
- total: 0,
- scanned: 0,
- deviceCount: 0
- })
- }
- userChannel = requestChannel
- } else {
- userChannel = allowedChannels.join(',')
- }
- } else {
- return response.forbidden({
- error: 'Forbidden',
- message: 'You are not authorized to access this resource'
- })
- }
- // 获取指定日期的数据,默认为今天
- 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 Database.from('ocr_devices')
- .where('created_at', '>=', dayStart)
- .where('created_at', '<=', dayEnd)
- .whereIn('channel', userChannel!.split(','))
- .select('scanned')
- // 获取OcrRecord数据
- const recordCount = await Database.from('ocr_records')
- .where('created_at', '>=', dayStart)
- .where('created_at', '<=', dayEnd)
- .whereIn('channel', userChannel!.split(','))
- .count('* as total')
- // 计算统计数据
- const scanned = deviceData.reduce((acc, item) => acc + Number(item.scanned || 0), 0)
- const total = Number(recordCount[0].total) || 0
- const deviceCount = deviceData.length
- return response.ok({
- date: targetDate,
- total,
- scanned,
- deviceCount
- })
- } catch (error) {
- return response.internalServerError({
- message: '获取统计数据时发生错误',
- error: error.message
- })
- }
- }
- }
|