OcrDevicesController.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
  2. import PaginationService from 'App/Services/PaginationService'
  3. import { schema } from '@ioc:Adonis/Core/Validator'
  4. import OcrDevice from 'App/Models/OcrDevice'
  5. import OcrRecord from 'App/Models/OcrRecord'
  6. import { DateTime } from 'luxon'
  7. import * as console from 'node:console'
  8. import Database from '@ioc:Adonis/Lucid/Database'
  9. export default class OcrDevicesController {
  10. private paginationService = new PaginationService(OcrDevice)
  11. public async index({ request, auth }: HttpContextContract) {
  12. const user = auth.user
  13. const isApiUser = user?.$attributes?.role === 'api'
  14. const requestData = request.all()
  15. if (isApiUser) {
  16. requestData.channel = user.username
  17. }
  18. return await this.paginationService.paginate(request.all())
  19. }
  20. public async store({ request, bouncer }: HttpContextContract) {
  21. // await bouncer.authorize('admin')
  22. const data = await request.validate({
  23. schema: schema.create({
  24. id: schema.string(),
  25. platform: schema.string(),
  26. channel: schema.string(),
  27. deviceInfo: schema.string.optional(),
  28. total: schema.number(),
  29. scanned: schema.number(),
  30. ipAddress: schema.string.optional()
  31. })
  32. })
  33. const clientIp = request.ip()
  34. if (!data.ipAddress) {
  35. data.ipAddress = clientIp
  36. }
  37. const device = await OcrDevice.findBy('id', data.id)
  38. if (device) {
  39. device.merge(data)
  40. return await device.save()
  41. } else {
  42. return await OcrDevice.create(data)
  43. }
  44. }
  45. public async show({ params, bouncer }: HttpContextContract) {
  46. await bouncer.authorize('admin')
  47. return await OcrDevice.findOrFail(params.id)
  48. }
  49. public async plusTotal({ request, response }: HttpContextContract) {
  50. try {
  51. const device = await OcrDevice.findBy('id', request.param('id'))
  52. if (!device) {
  53. return response.notFound({ message: `未找到ID为 ${request.param('id')} 的OCR设备` })
  54. }
  55. device.total += 1
  56. await device.save()
  57. return response.ok(device)
  58. } catch (error) {
  59. return response.internalServerError({
  60. message: '更新设备记录数量时发生错误',
  61. error: error.message
  62. })
  63. }
  64. }
  65. public async plusScanned({ request, response }: HttpContextContract) {
  66. const scanCount = Number(request.param('scanCount'))
  67. if (isNaN(scanCount)) {
  68. return response.badRequest({ message: 'scanCount 参数必须是有效数字' })
  69. }
  70. try {
  71. const device = await OcrDevice.findBy('id', request.param('id'))
  72. if (!device) {
  73. return response.notFound({
  74. message: `未找到 ID 为 ${request.param('id')} 的 OCR 设备`
  75. })
  76. }
  77. device.scanned += scanCount
  78. await device.save()
  79. return response.ok(device)
  80. } catch (error) {
  81. return response.internalServerError({
  82. message: '更新设备扫描数量时发生错误',
  83. error: error.message
  84. })
  85. }
  86. }
  87. public async getStatistics({ request, response, auth }: HttpContextContract) {
  88. try {
  89. const user = auth.user
  90. const isApiUser = user?.$attributes?.role === 'api'
  91. const deviceQuery = OcrDevice.query()
  92. const recordQuery = OcrRecord.query()
  93. if (isApiUser) {
  94. deviceQuery.where('channel', user.username)
  95. recordQuery.where('channel', user.username)
  96. } else {
  97. const channel = request.input('channel')
  98. if (channel) {
  99. deviceQuery.where('channel', channel)
  100. recordQuery.where('channel', channel)
  101. }
  102. }
  103. // 获取近7天的数据
  104. const sevenDaysAgo = DateTime.now().minus({ days: 7 }).startOf('day').toSQL()
  105. const devices = await deviceQuery
  106. .where('createdAt', '>=', sevenDaysAgo)
  107. .select('id', 'total', 'scanned', 'createdAt', 'channel')
  108. // 过去7天的日期数组
  109. const dates: string[] = []
  110. for (let i = 6; i >= 0; i--) {
  111. const date = DateTime.now().minus({ days: i }).toFormat('yyyy-MM-dd')
  112. dates.push(date)
  113. }
  114. const dailyStats: Record<
  115. string,
  116. { total: number; scanned: number; deviceCount: number }
  117. > = {}
  118. dates.forEach((date) => {
  119. dailyStats[date] = {
  120. total: 0,
  121. scanned: 0,
  122. deviceCount: 0
  123. }
  124. })
  125. // 按设备创建日期统计数据 scanned和deviceCount
  126. for (const device of devices) {
  127. const dateStr = device.createdAt.toFormat('yyyy-MM-dd')
  128. if (dailyStats[dateStr]) {
  129. dailyStats[dateStr].scanned += device.scanned
  130. dailyStats[dateStr].deviceCount += 1
  131. }
  132. }
  133. // 获取OcrRecord的每日数量
  134. for (const date of dates) {
  135. const startDate = DateTime.fromFormat(date, 'yyyy-MM-dd').startOf('day').toSQL()
  136. const endDate = DateTime.fromFormat(date, 'yyyy-MM-dd').endOf('day').toSQL()
  137. const recordCount = await Database.from('ocr_records')
  138. .where('created_at', '>=', startDate)
  139. .where('created_at', '<=', endDate)
  140. .count('* as total')
  141. dailyStats[date].total = Number(recordCount[0].total) || 0
  142. }
  143. // 转换为前端需要的数组格式
  144. const totals = dates.map((date) => dailyStats[date].total)
  145. const scanned = dates.map((date) => dailyStats[date].scanned)
  146. const deviceCounts = dates.map((date) => dailyStats[date].deviceCount)
  147. return response.ok({
  148. dates,
  149. total: totals,
  150. scanned: scanned,
  151. deviceCount: deviceCounts
  152. })
  153. } catch (error) {
  154. return response.internalServerError({
  155. message: '获取设备统计数据时发生错误',
  156. error: error.message
  157. })
  158. }
  159. }
  160. public async getTodayStatistics({ request, response, auth }: HttpContextContract) {
  161. try {
  162. const user = auth.user
  163. const isApiUser = user?.$attributes?.role === 'api'
  164. const deviceQuery = OcrDevice.query()
  165. const recordQuery = OcrRecord.query()
  166. // 如果是 API 用户,强制使用其 username 作为 channel
  167. if (isApiUser) {
  168. deviceQuery.where('channel', user.username)
  169. recordQuery.where('channel', user.username)
  170. } else {
  171. // 如果不是 API 用户,则使用请求中的 channel 参数
  172. const channel = request.input('channel')
  173. if (channel) {
  174. deviceQuery.where('channel', channel)
  175. recordQuery.where('channel', channel)
  176. }
  177. }
  178. // 获取今天的数据
  179. const today = DateTime.now().startOf('day').toSQL()
  180. const todayEnd = DateTime.now().endOf('day').toSQL()
  181. // 获取设备数据
  182. const deviceData = await deviceQuery.where('createdAt', '>=', today).select('scanned')
  183. // 获取OcrRecord数据
  184. const recordCount = await Database.from('ocr_records')
  185. .where('created_at', '>=', today)
  186. .where('created_at', '<=', todayEnd)
  187. .count('* as total')
  188. // 计算今日统计数据
  189. const scanned = deviceData.reduce((acc, item) => acc + item.scanned, 0)
  190. const total = Number(recordCount[0].total) || 0
  191. const deviceCount = deviceData.length
  192. return response.ok({
  193. date: DateTime.now().toFormat('yyyy-MM-dd'),
  194. total,
  195. scanned,
  196. deviceCount
  197. })
  198. } catch (error) {
  199. return response.internalServerError({
  200. message: '获取今日统计数据时发生错误',
  201. error: error.message
  202. })
  203. }
  204. }
  205. }