Răsfoiți Sursa

添加维护码验证接口和管理员获取二维码信息接口,更新相关服务和路由配置,支持更灵活的二维码信息查询。

wuyi 1 lună în urmă
părinte
comite
347c6189d3

+ 48 - 1
src/controllers/qr-code.controller.ts

@@ -98,7 +98,33 @@ export class QrCodeController {
   }
 
   /**
-   * 获取二维码信息
+   * 验证维护码并返回二维码信息
+   */
+  async verifyMaintenanceCodeInfo(request: FastifyRequest<{ Body: VerifyMaintenanceCodeDto }>, reply: FastifyReply) {
+    try {
+      const { qrCode, maintenanceCode } = request.body
+
+      const isValid = await this.qrCodeService.verifyMaintenanceCode(qrCode, maintenanceCode)
+
+      if (!isValid) {
+        return reply.code(401).send({ message: '维护码错误' })
+      }
+
+      const qrCodeInfo = await this.qrCodeService.getQrCodeInfo(qrCode, undefined, true)
+
+      return reply.send({
+        message: '验证成功',
+        valid: true,
+        ...qrCodeInfo
+      })
+    } catch (error) {
+      const message = error instanceof Error ? error.message : '验证失败'
+      return reply.code(500).send({ message })
+    }
+  }
+
+  /**
+   * 前台获取二维码信息
    */
   async getInfo(request: FastifyRequest<{ Querystring: GetQrCodeInfoDto }>, reply: FastifyReply) {
     try {
@@ -127,6 +153,27 @@ export class QrCodeController {
     }
   }
 
+  /**
+   * 后台获取二维码信息
+   */
+  async adminGetInfo(request: FastifyRequest<{ Querystring: GetQrCodeInfoDto }>, reply: FastifyReply) {
+    try {
+      const { qrCode, id } = request.query
+
+      if (!qrCode && !id) {
+        return reply.code(400).send({ message: '请提供参数' })
+      }
+
+      // 后台查询,不判断 isVisible,返回所有信息
+      const info = await this.qrCodeService.getQrCodeInfo(qrCode, id, true)
+
+      return reply.send(info)
+    } catch (error) {
+      const message = error instanceof Error ? error.message : '获取信息失败'
+      return reply.code(500).send({ message })
+    }
+  }
+
   /**
    * 获取扫描记录
    */

+ 2 - 2
src/controllers/scan-record.controller.ts

@@ -52,9 +52,9 @@ export class ScanRecordController {
    */
   async getRecent(request: FastifyRequest<{ Querystring: QueryScanRecordDto }>, reply: FastifyReply) {
     try {
-      const { qrCode, limit } = request.query
+      const { qrCode } = request.query
 
-      const records = await this.scanRecordService.getRecentRecords(qrCode, limit || 10)
+      const records = await this.scanRecordService.getRecentRecords(qrCode, 10)
 
       return reply.send({
         qrCode,

+ 13 - 1
src/dto/goods-info.dto.ts

@@ -1,4 +1,4 @@
-import { IsString, IsOptional, IsEmail, Length, MaxLength, IsNumber, IsDateString, Min } from 'class-validator'
+import { IsString, IsOptional, IsEmail, Length, MaxLength, IsNumber, IsDateString, Min, IsBoolean } from 'class-validator'
 
 export class CreateGoodsInfoDto {
   @IsString()
@@ -30,6 +30,10 @@ export class CreateGoodsInfoDto {
   @IsString()
   @MaxLength(500)
   location?: string
+
+  @IsOptional()
+  @IsBoolean()
+  isVisible?: boolean
 }
 
 export class UpdateGoodsInfoDto {
@@ -68,6 +72,10 @@ export class UpdateGoodsInfoDto {
   @IsString()
   @MaxLength(500)
   location?: string
+
+  @IsOptional()
+  @IsBoolean()
+  isVisible?: boolean
 }
 
 export class QueryGoodsInfoDto {
@@ -135,5 +143,9 @@ export class AdminUpdateGoodsInfoDto {
   @IsString()
   @MaxLength(500)
   location?: string
+
+  @IsOptional()
+  @IsBoolean()
+  isVisible?: boolean
 }
 

+ 13 - 1
src/dto/person-info.dto.ts

@@ -1,4 +1,4 @@
-import { IsString, IsEnum, IsOptional, IsEmail, IsPhoneNumber, Length, MaxLength, IsNumber, IsDateString, Min } from 'class-validator'
+import { IsString, IsEnum, IsOptional, IsEmail, IsPhoneNumber, Length, MaxLength, IsNumber, IsDateString, Min, IsBoolean } from 'class-validator'
 import { Gender } from '../entities/person-info.entity'
 
 export class CreatePersonInfoDto {
@@ -42,6 +42,10 @@ export class CreatePersonInfoDto {
   @IsString()
   @MaxLength(500)
   location?: string
+
+  @IsOptional()
+  @IsBoolean()
+  isVisible?: boolean
 }
 
 export class UpdatePersonInfoDto {
@@ -93,6 +97,10 @@ export class UpdatePersonInfoDto {
   @IsString()
   @MaxLength(500)
   location?: string
+
+  @IsOptional()
+  @IsBoolean()
+  isVisible?: boolean
 }
 
 export class QueryPersonInfoDto {
@@ -173,5 +181,9 @@ export class AdminUpdatePersonInfoDto {
   @IsString()
   @MaxLength(500)
   location?: string
+
+  @IsOptional()
+  @IsBoolean()
+  isVisible?: boolean
 }
 

+ 13 - 1
src/dto/pet-info.dto.ts

@@ -1,4 +1,4 @@
-import { IsString, IsOptional, IsEmail, Length, MaxLength, IsNumber, IsDateString, Min } from 'class-validator'
+import { IsString, IsOptional, IsEmail, Length, MaxLength, IsNumber, IsDateString, Min, IsBoolean } from 'class-validator'
 
 export class CreatePetInfoDto {
   @IsString()
@@ -30,6 +30,10 @@ export class CreatePetInfoDto {
   @IsString()
   @MaxLength(500)
   location?: string
+
+  @IsOptional()
+  @IsBoolean()
+  isVisible?: boolean
 }
 
 export class UpdatePetInfoDto {
@@ -68,6 +72,10 @@ export class UpdatePetInfoDto {
   @IsString()
   @MaxLength(500)
   location?: string
+
+  @IsOptional()
+  @IsBoolean()
+  isVisible?: boolean
 }
 
 export class QueryPetInfoDto {
@@ -135,5 +143,9 @@ export class AdminUpdatePetInfoDto {
   @IsString()
   @MaxLength(500)
   location?: string
+
+  @IsOptional()
+  @IsBoolean()
+  isVisible?: boolean
 }
 

+ 3 - 0
src/entities/goods-info.entity.ts

@@ -38,6 +38,9 @@ export class GoodsInfo {
   @Column({ length: 500, nullable: true })
   location: string
 
+  @Column({ default: true })
+  isVisible: boolean
+
   @CreateDateColumn()
   createdAt: Date
 

+ 3 - 0
src/entities/person-info.entity.ts

@@ -56,6 +56,9 @@ export class PersonInfo {
   @Column({ length: 500, nullable: true })
   location: string
 
+  @Column({ default: true })
+  isVisible: boolean
+
   @CreateDateColumn()
   createdAt: Date
 

+ 3 - 0
src/entities/pet-info.entity.ts

@@ -38,6 +38,9 @@ export class PetInfo {
   @Column({ length: 500, nullable: true })
   location: string
 
+  @Column({ default: true })
+  isVisible: boolean
+
   @CreateDateColumn()
   createdAt: Date
 

+ 11 - 0
src/routes/qr-code.routes.ts

@@ -15,11 +15,22 @@ export default async function qrCodeRoutes(fastify: FastifyInstance) {
 
   fastify.get<{ Querystring: GetQrCodeInfoDto }>('/info', qrCodeController.getInfo.bind(qrCodeController))
 
+  fastify.get<{ Querystring: GetQrCodeInfoDto }>(
+    '/admin/info',
+    { onRequest: [hasRole(UserRole.ADMIN)] },
+    qrCodeController.adminGetInfo.bind(qrCodeController)
+  )
+
   fastify.post<{ Body: VerifyMaintenanceCodeDto }>(
     '/verify',
     qrCodeController.verifyMaintenanceCode.bind(qrCodeController)
   )
 
+  fastify.post<{ Body: VerifyMaintenanceCodeDto }>(
+    '/verify/info',
+    qrCodeController.verifyMaintenanceCodeInfo.bind(qrCodeController)
+  )
+
   fastify.post<{ Body: GenerateQrCodeDto }>(
     '/generate',
     { onRequest: [hasRole(UserRole.ADMIN)] },

+ 19 - 2
src/services/qr-code.service.ts

@@ -124,8 +124,11 @@ export class QrCodeService {
 
   /**
    * 根据二维码或ID获取信息
+   * @param qrCode 二维码
+   * @param id 二维码ID
+   * @param isAdmin 是否为后台查询,后台查询不判断 isVisible
    */
-  async getQrCodeInfo(qrCode?: string, id?: number): Promise<any> {
+  async getQrCodeInfo(qrCode?: string, id?: number, isAdmin: boolean = false): Promise<any> {
     let entity: QrCode | null = null
 
     if (qrCode) {
@@ -138,7 +141,11 @@ export class QrCodeService {
       throw new Error('二维码不存在')
     }
 
+    let scanCount = entity.scanCount
     let info = null
+    let isVisible = true
+
+    // 根据类型获取信息
     if (entity.qrType === QrType.PERSON) {
       info = await this.personInfoRepository.findOne({ where: { qrCodeId: entity.id } })
     } else if (entity.qrType === QrType.PET) {
@@ -147,6 +154,14 @@ export class QrCodeService {
       info = await this.goodsInfoRepository.findOne({ where: { qrCodeId: entity.id } })
     }
 
+    if (!isAdmin && info && info.isVisible === false) {
+      scanCount = 0
+      info = null
+      isVisible = false
+    } else if (info) {
+      isVisible = info.isVisible ?? true
+    }
+
     // 处理图片签名URL
     if (info && info.photoUrl) {
       try {
@@ -167,8 +182,10 @@ export class QrCodeService {
       id: entity.id,
       qrCode: entity.qrCode,
       qrType: entity.qrType,
+      ...(isAdmin && { createdAt: entity.createdAt, updatedAt: entity.updatedAt }),
       isActivated: entity.isActivated,
-      scanCount: entity.scanCount,
+      scanCount,
+      isVisible,
       info
     }
   }