Explorar el Código

优化二维码生成逻辑,支持批量生成并处理数据库唯一性冲突;更新二维码实体以包含创建时间索引和修改二维码字段类型。

wuyi hace 1 mes
padre
commit
f1e73b713a
Se han modificado 2 ficheros con 57 adiciones y 25 borrados
  1. 2 3
      src/entities/qr-code.entity.ts
  2. 55 22
      src/services/qr-code.service.ts

+ 2 - 3
src/entities/qr-code.entity.ts

@@ -20,14 +20,13 @@ export enum QrType {
 }
 
 @Entity()
-@Index(['qrType'])
-@Index(['isActivated'])
+@Index(['qrType', 'isActivated', 'createdAt'])
 @Index(['createdAt'])
 export class QrCode {
   @PrimaryGeneratedColumn()
   id: number
 
-  @Column({ unique: true, length: 100 })
+  @Column({ type: 'char', length: 12, unique: true, nullable: false })
   qrCode: string
 
   @Column({ length: 100 })

+ 55 - 22
src/services/qr-code.service.ts

@@ -5,7 +5,6 @@ import { PersonInfo } from '../entities/person-info.entity'
 import { PetInfo } from '../entities/pet-info.entity'
 import { GoodsInfo } from '../entities/goods-info.entity'
 import { PaginationResponse } from '../dto/common.dto'
-import { randomBytes } from 'crypto'
 import { FileService } from './file.service'
 
 export class QrCodeService {
@@ -26,12 +25,17 @@ export class QrCodeService {
   }
 
   /**
-   * 生成唯一二维码编号
+   * 生成唯一二维码
    */
-  private generateQrCode(): string {
-    const timestamp = Date.now().toString(36)
-    const random = randomBytes(8).toString('hex')
-    return `QR${timestamp}${random}`.toUpperCase()
+  private generateRandomQrCode(length = 12): string {
+    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
+    let code = ''
+
+    for (let i = 0; i < length; i++) {
+      code += chars.charAt(Math.floor(Math.random() * chars.length))
+    }
+
+    return code
   }
 
   /**
@@ -49,30 +53,59 @@ export class QrCodeService {
   /**
    * 生成二维码
    */
+  /**
+   * 批量生成二维码(自动处理数据库重复)
+   */
   async generateQrCodes(
     qrType: QrType,
-    quantity: number = 1
+    quantity: number = 1,
+    batchSize: number = 1000
   ): Promise<Array<{ qrCode: string; maintenanceCode: string }>> {
     const result = []
 
-    for (let i = 0; i < quantity; i++) {
-      const qrCode = this.generateQrCode()
-      const maintenanceCode = this.generateMaintenanceCode()
+    let remaining = quantity
+    let round = 0
+
+    while (remaining > 0) {
+      round++
 
-      const entity = this.qrCodeRepository.create({
-        qrCode,
-        maintenanceCode,
-        qrType,
-        isActivated: false,
-        scanCount: 0
-      })
+      const rows = []
+      const localSet = new Set<string>()
 
-      await this.qrCodeRepository.save(entity)
+      while (rows.length < remaining) {
+        const qrCode = this.generateRandomQrCode(12)
+        if (localSet.has(qrCode)) continue
+        localSet.add(qrCode)
+
+        const maintenanceCode = this.generateMaintenanceCode()
+
+        rows.push({
+          qrCode,
+          maintenanceCode,
+          qrType,
+          isActivated: false,
+          scanCount: 0
+        })
+
+        result.push({ qrCode, maintenanceCode })
+      }
+
+      let insertedCount = 0
+
+      for (let i = 0; i < rows.length; i += batchSize) {
+        const chunk = rows.slice(i, i + batchSize)
+
+        try {
+          const result = await this.qrCodeRepository.createQueryBuilder().insert().into(QrCode).values(chunk).execute()
+
+          insertedCount += result.raw?.affectedRows ?? chunk.length
+        } catch (err) {
+          this.app.log.warn('数据库唯一性冲突,已忽略,自动补足剩余数量')
+        }
+      }
 
-      result.push({
-        qrCode,
-        maintenanceCode
-      })
+      const failed = remaining - insertedCount
+      remaining = failed
     }
 
     return result