qr-code.service.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import { Repository, Between, LessThanOrEqual, MoreThanOrEqual } from 'typeorm'
  2. import { FastifyInstance } from 'fastify'
  3. import { QrCode, QrType } from '../entities/qr-code.entity'
  4. import { PersonInfo } from '../entities/person-info.entity'
  5. import { PetInfo } from '../entities/pet-info.entity'
  6. import { PaginationResponse } from '../dto/common.dto'
  7. import bcrypt from 'bcryptjs'
  8. import { randomBytes } from 'crypto'
  9. export class QrCodeService {
  10. private qrCodeRepository: Repository<QrCode>
  11. private personInfoRepository: Repository<PersonInfo>
  12. private petInfoRepository: Repository<PetInfo>
  13. constructor(app: FastifyInstance) {
  14. this.qrCodeRepository = app.dataSource.getRepository(QrCode)
  15. this.personInfoRepository = app.dataSource.getRepository(PersonInfo)
  16. this.petInfoRepository = app.dataSource.getRepository(PetInfo)
  17. }
  18. /**
  19. * 生成唯一的二维码编号
  20. */
  21. private generateQrCode(): string {
  22. const timestamp = Date.now().toString(36)
  23. const random = randomBytes(8).toString('hex')
  24. return `QR${timestamp}${random}`.toUpperCase()
  25. }
  26. /**
  27. * 生成维护码
  28. */
  29. private generateMaintenanceCode(): string {
  30. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
  31. let code = ''
  32. for (let i = 0; i < 8; i++) {
  33. code += chars.charAt(Math.floor(Math.random() * chars.length))
  34. }
  35. return code
  36. }
  37. /**
  38. * 生成二维码
  39. */
  40. async generateQrCodes(
  41. qrType: QrType,
  42. quantity: number = 1
  43. ): Promise<Array<{ qrCode: string; maintenanceCode: string }>> {
  44. const result = []
  45. for (let i = 0; i < quantity; i++) {
  46. const qrCode = this.generateQrCode()
  47. const maintenanceCode = this.generateMaintenanceCode()
  48. const hashedMaintenanceCode = await bcrypt.hash(maintenanceCode, 10)
  49. const entity = this.qrCodeRepository.create({
  50. qrCode,
  51. maintenanceCode: hashedMaintenanceCode,
  52. qrType,
  53. isActivated: false,
  54. scanCount: 0
  55. })
  56. await this.qrCodeRepository.save(entity)
  57. result.push({
  58. qrCode,
  59. maintenanceCode
  60. })
  61. }
  62. return result
  63. }
  64. /**
  65. * 验证维护码
  66. */
  67. async verifyMaintenanceCode(qrCode: string, maintenanceCode: string): Promise<boolean> {
  68. const entity = await this.qrCodeRepository.findOne({ where: { qrCode } })
  69. if (!entity) {
  70. return false
  71. }
  72. return bcrypt.compare(maintenanceCode, entity.maintenanceCode)
  73. }
  74. /**
  75. * 根据二维码获取信息
  76. */
  77. async getQrCodeInfo(qrCode: string): Promise<any> {
  78. const entity = await this.qrCodeRepository.findOne({ where: { qrCode } })
  79. if (!entity) {
  80. throw new Error('二维码不存在')
  81. }
  82. let info = null
  83. if (entity.qrType === QrType.PERSON) {
  84. info = await this.personInfoRepository.findOne({ where: { qrCodeId: entity.id } })
  85. } else if (entity.qrType === QrType.PET) {
  86. info = await this.petInfoRepository.findOne({ where: { qrCodeId: entity.id } })
  87. }
  88. return {
  89. id: entity.id,
  90. qrCode: entity.qrCode,
  91. qrType: entity.qrType,
  92. isActivated: entity.isActivated,
  93. scanCount: entity.scanCount,
  94. info
  95. }
  96. }
  97. /**
  98. * 增加扫描次数
  99. */
  100. async incrementScanCount(qrCodeId: number): Promise<void> {
  101. await this.qrCodeRepository.increment({ id: qrCodeId }, 'scanCount', 1)
  102. }
  103. /**
  104. * 激活二维码
  105. */
  106. async activateQrCode(qrCodeId: number): Promise<void> {
  107. await this.qrCodeRepository.update(qrCodeId, { isActivated: true })
  108. }
  109. /**
  110. * 查询二维码列表
  111. */
  112. async queryQrCodes(
  113. qrType?: QrType,
  114. isActivated?: boolean,
  115. startDate?: string,
  116. endDate?: string,
  117. page: number = 0,
  118. pageSize: number = 20
  119. ): Promise<PaginationResponse<QrCode>> {
  120. const queryBuilder = this.qrCodeRepository.createQueryBuilder('qrCode')
  121. if (qrType) {
  122. queryBuilder.andWhere('qrCode.qrType = :qrType', { qrType })
  123. }
  124. if (isActivated !== undefined) {
  125. queryBuilder.andWhere('qrCode.isActivated = :isActivated', { isActivated })
  126. }
  127. if (startDate && endDate) {
  128. queryBuilder.andWhere('DATE(qrCode.createdAt) BETWEEN :startDate AND :endDate', { startDate, endDate })
  129. } else if (startDate) {
  130. queryBuilder.andWhere('DATE(qrCode.createdAt) >= :startDate', { startDate })
  131. } else if (endDate) {
  132. queryBuilder.andWhere('DATE(qrCode.createdAt) <= :endDate', { endDate })
  133. }
  134. const [content, total] = await queryBuilder
  135. .select([
  136. 'qrCode.id',
  137. 'qrCode.qrCode',
  138. 'qrCode.qrType',
  139. 'qrCode.isActivated',
  140. 'qrCode.scanCount',
  141. 'qrCode.createdAt',
  142. 'qrCode.updatedAt'
  143. ])
  144. .skip(page * pageSize)
  145. .take(pageSize)
  146. .orderBy('qrCode.createdAt', 'DESC')
  147. .getManyAndCount()
  148. return {
  149. content,
  150. metadata: {
  151. total: Number(total),
  152. page: Number(page),
  153. size: Number(pageSize)
  154. }
  155. }
  156. }
  157. /**
  158. * 根据日期获取二维码列表(用于下载)
  159. */
  160. async getQrCodesByDate(date: string): Promise<QrCode[]> {
  161. return this.qrCodeRepository
  162. .createQueryBuilder('qrCode')
  163. .where('DATE(qrCode.createdAt) = :date', { date })
  164. .orderBy('qrCode.createdAt', 'ASC')
  165. .getMany()
  166. }
  167. /**
  168. * 根据ID获取二维码
  169. */
  170. async findById(id: number): Promise<QrCode | null> {
  171. return this.qrCodeRepository.findOne({ where: { id } })
  172. }
  173. /**
  174. * 根据qrCode获取实体
  175. */
  176. async findByQrCode(qrCode: string): Promise<QrCode | null> {
  177. return this.qrCodeRepository.findOne({ where: { qrCode } })
  178. }
  179. }