import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify' import { QrCodeService } from '../services/qr-code.service' import { ScanRecordService } from '../services/scan-record.service' import { GenerateQrCodeDto, QueryQrCodeDto, VerifyMaintenanceCodeDto, GetQrCodeInfoDto, ResetMaintenanceCodeDto, AddUserQrCodeDto, DeleteUserQrCodeDto, QueryUserQrCodeDto, UpdateQrCodeDto } from '../dto/qr-code.dto' export class QrCodeController { private qrCodeService: QrCodeService private scanRecordService: ScanRecordService constructor(app: FastifyInstance) { this.qrCodeService = new QrCodeService(app) this.scanRecordService = new ScanRecordService(app) } /** * 生成二维码 */ async generate(request: FastifyRequest<{ Body: GenerateQrCodeDto }>, reply: FastifyReply) { try { const { qrType, quantity } = request.body const result = await this.qrCodeService.generateQrCodes(qrType, quantity || 1) return reply.code(201).send({ message: '二维码生成成功', data: result }) } catch (error) { const message = error instanceof Error ? error.message : '生成失败' return reply.code(500).send({ message }) } } /** * 分页列表 */ async list(request: FastifyRequest<{ Querystring: QueryQrCodeDto }>, reply: FastifyReply) { try { const { qrCode, qrType, isActivated, startDate, endDate, page, pageSize } = request.query const result = await this.qrCodeService.queryQrCodes( qrCode, qrType, isActivated, startDate, endDate, page, pageSize ) return reply.send(result) } catch (error) { const message = error instanceof Error ? error.message : '查询失败' return reply.code(500).send({ message }) } } /** * 按日期下载 */ async downloadByDate(request: FastifyRequest<{ Querystring: { date: string } }>, reply: FastifyReply) { try { const { date } = request.query if (!date) { return reply.code(400).send({ message: '请提供日期参数' }) } const qrCodes = await this.qrCodeService.getQrCodesByDate(date) return reply.send({ date, count: qrCodes.length, data: qrCodes }) } catch (error) { const message = error instanceof Error ? error.message : '下载失败' return reply.code(500).send({ message }) } } /** * 验证维护码 */ async verifyMaintenanceCode(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: '维护码错误' }) } return reply.send({ message: '验证成功', valid: true }) } catch (error) { const message = error instanceof Error ? error.message : '验证失败' return reply.code(500).send({ message }) } } /** * 验证维护码并返回二维码信息 */ 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 { const { qrCode, id } = request.query if (!qrCode && !id) { return reply.code(400).send({ message: '请提供参数' }) } // 记录扫描 - 获取真实IP地址 let ipAddress = request.headers['x-forwarded-for'] as string if (ipAddress) { // x-forwarded-for 可能包含多个IP,取第一个(真实客户端IP) ipAddress = ipAddress.split(',')[0].trim() } else { // 如果没有 x-forwarded-for,尝试其他常见的代理头 ipAddress = (request.headers['x-real-ip'] as string) || (request.headers['cf-connecting-ip'] as string) || request.ip || request.socket.remoteAddress || 'unknown' } const userAgent = request.headers['user-agent'] // 如果通过 qrCode 查询,记录扫描 if (qrCode) { await this.scanRecordService.create(qrCode, undefined, undefined, undefined, ipAddress, userAgent) } // 获取二维码信息 const info = await this.qrCodeService.getQrCodeInfo(qrCode, id) return reply.send(info) } catch (error) { const message = error instanceof Error ? error.message : '获取信息失败' return reply.code(500).send({ message }) } } /** * 后台获取二维码信息 */ 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 }) } } /** * 获取扫描记录 */ async getScanRecords( request: FastifyRequest<{ Querystring: { qrCode: string; limit?: number } }>, reply: FastifyReply ) { try { const { qrCode, limit } = request.query if (!qrCode) { return reply.code(400).send({ message: '请提供二维码参数' }) } const records = await this.scanRecordService.getRecentRecords(qrCode, limit || 10) return reply.send({ qrCode, count: records.length, records }) } catch (error) { const message = error instanceof Error ? error.message : '获取记录失败' return reply.code(500).send({ message }) } } /** * 重置维护码 */ async resetMaintenanceCode(request: FastifyRequest<{ Body: ResetMaintenanceCodeDto }>, reply: FastifyReply) { try { const { qrCode, maintenanceCode } = request.body const newMaintenanceCode = await this.qrCodeService.resetMaintenanceCode(qrCode, maintenanceCode) return reply.send({ message: '维护码重置成功', data: { qrCode, maintenanceCode: newMaintenanceCode } }) } catch (error) { const message = error instanceof Error ? error.message : '重置失败' return reply.code(500).send({ message }) } } /** * 绑定二维码 */ async bindQrCode(request: FastifyRequest<{ Body: AddUserQrCodeDto }>, reply: FastifyReply) { try { const userId = request.user.id const { qrCodeId, qrCode, maintenanceCode } = request.body const userQrCode = await this.qrCodeService.bindQrCode(userId, qrCodeId, qrCode, maintenanceCode) return reply.code(201).send({ message: 'Bound successfully' }) } catch (error) { const message = error instanceof Error ? error.message : 'Binding failed' const clientErrorKeywords = [ 'not found', 'already bound', 'Please provide', 'Maintenance code cannot be empty', 'Maintenance code error' ] const isClientError = clientErrorKeywords.some(keyword => message.includes(keyword)) const statusCode = isClientError ? 400 : 500 return reply.code(statusCode).send({ message }) } } /** * 取消绑定二维码 */ async unbindQrCode(request: FastifyRequest<{ Body: DeleteUserQrCodeDto }>, reply: FastifyReply) { try { const userId = request.user.id const { id, qrCodeId } = request.body await this.qrCodeService.unbindQrCode(userId, id, qrCodeId) return reply.send({ message: 'Unbound successfully' }) } catch (error) { const message = error instanceof Error ? error.message : 'Unbinding failed' const statusCode = message.includes('not found') || message.includes('already deleted') || message.includes('Please provide') ? 400 : 500 return reply.code(statusCode).send({ message }) } } /** * 查询用户的关联二维码列表 */ async getUserQrCodes(request: FastifyRequest<{ Querystring: QueryUserQrCodeDto }>, reply: FastifyReply) { try { const userId = request.user.id const { page = 0, pageSize = 20 } = request.query const result = await this.qrCodeService.getUserQrCodes(userId, page, pageSize) return reply.send(result) } catch (error) { const message = error instanceof Error ? error.message : 'Query failed' return reply.code(500).send({ message }) } } /** * 更新二维码信息 */ async update(request: FastifyRequest<{ Body: UpdateQrCodeDto }>, reply: FastifyReply) { try { const { qrCode, maintenanceCode, remark } = request.body const updated = await this.qrCodeService.updateQrCode(qrCode, maintenanceCode, { remark }) return reply.send({ message: '更新成功', data: { qrCode: updated.qrCode, remark: updated.remark } }) } catch (error) { const message = error instanceof Error ? error.message : '更新失败' const clientErrorKeywords = ['维护码错误', '二维码不存在'] const isClientError = clientErrorKeywords.some(keyword => message.includes(keyword)) const statusCode = isClientError ? 400 : 500 return reply.code(statusCode).send({ message }) } } }