OcrDevicesController.ts 8.4 KB

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