qr-code.controller.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'
  2. import { QrCodeService } from '../services/qr-code.service'
  3. import { ScanRecordService } from '../services/scan-record.service'
  4. import {
  5. GenerateQrCodeDto,
  6. QueryQrCodeDto,
  7. VerifyMaintenanceCodeDto,
  8. GetQrCodeInfoDto,
  9. ResetMaintenanceCodeDto,
  10. AddUserQrCodeDto,
  11. DeleteUserQrCodeDto,
  12. QueryUserQrCodeDto,
  13. UpdateQrCodeDto
  14. } from '../dto/qr-code.dto'
  15. export class QrCodeController {
  16. private qrCodeService: QrCodeService
  17. private scanRecordService: ScanRecordService
  18. constructor(app: FastifyInstance) {
  19. this.qrCodeService = new QrCodeService(app)
  20. this.scanRecordService = new ScanRecordService(app)
  21. }
  22. /**
  23. * 生成二维码
  24. */
  25. async generate(request: FastifyRequest<{ Body: GenerateQrCodeDto }>, reply: FastifyReply) {
  26. try {
  27. const { qrType, quantity } = request.body
  28. const result = await this.qrCodeService.generateQrCodes(qrType, quantity || 1)
  29. return reply.code(201).send({
  30. message: '二维码生成成功',
  31. data: result
  32. })
  33. } catch (error) {
  34. const message = error instanceof Error ? error.message : '生成失败'
  35. return reply.code(500).send({ message })
  36. }
  37. }
  38. /**
  39. * 分页列表
  40. */
  41. async list(request: FastifyRequest<{ Querystring: QueryQrCodeDto }>, reply: FastifyReply) {
  42. try {
  43. const { qrCode, qrType, isActivated, startDate, endDate, ownerId, ownerName, page, pageSize } = request.query
  44. const result = await this.qrCodeService.queryQrCodes(
  45. qrCode,
  46. qrType,
  47. isActivated,
  48. startDate,
  49. endDate,
  50. ownerId,
  51. ownerName,
  52. page,
  53. pageSize
  54. )
  55. return reply.send(result)
  56. } catch (error) {
  57. const message = error instanceof Error ? error.message : '查询失败'
  58. return reply.code(500).send({ message })
  59. }
  60. }
  61. /**
  62. * 更新二维码信息及对应 info
  63. */
  64. async update(request: FastifyRequest<{ Body: UpdateQrCodeDto }>, reply: FastifyReply) {
  65. try {
  66. const { id, maintenanceCode, remark, info } = request.body
  67. await this.qrCodeService.updateQrCode(id, maintenanceCode, remark, info)
  68. return reply.send({ message: '更新成功' })
  69. } catch (error) {
  70. const message = error instanceof Error ? error.message : '更新失败'
  71. const clientErrorKeywords = ['维护码错误', '二维码不存在', '维护码最少', '维护码最多', '维护码只能']
  72. const isClientError = clientErrorKeywords.some(keyword => message.includes(keyword))
  73. const statusCode = isClientError ? 400 : 500
  74. return reply.code(statusCode).send({ message })
  75. }
  76. }
  77. /**
  78. * 按日期下载
  79. */
  80. async downloadByDate(request: FastifyRequest<{ Querystring: { date: string } }>, reply: FastifyReply) {
  81. try {
  82. const { date } = request.query
  83. if (!date) {
  84. return reply.code(400).send({ message: '请提供日期参数' })
  85. }
  86. const qrCodes = await this.qrCodeService.getQrCodesByDate(date)
  87. return reply.send({
  88. date,
  89. count: qrCodes.length,
  90. data: qrCodes
  91. })
  92. } catch (error) {
  93. const message = error instanceof Error ? error.message : '下载失败'
  94. return reply.code(500).send({ message })
  95. }
  96. }
  97. /**
  98. * 验证维护码
  99. */
  100. async verifyMaintenanceCode(request: FastifyRequest<{ Body: VerifyMaintenanceCodeDto }>, reply: FastifyReply) {
  101. try {
  102. const { qrCode, maintenanceCode } = request.body
  103. const isValid = await this.qrCodeService.verifyMaintenanceCode(qrCode, maintenanceCode)
  104. if (!isValid) {
  105. return reply.code(401).send({ message: '维护码错误' })
  106. }
  107. return reply.send({ message: '验证成功', valid: true })
  108. } catch (error) {
  109. const message = error instanceof Error ? error.message : '验证失败'
  110. return reply.code(500).send({ message })
  111. }
  112. }
  113. /**
  114. * 验证维护码并返回二维码信息
  115. */
  116. async verifyMaintenanceCodeInfo(request: FastifyRequest<{ Body: VerifyMaintenanceCodeDto }>, reply: FastifyReply) {
  117. try {
  118. const { qrCode, maintenanceCode } = request.body
  119. const isValid = await this.qrCodeService.verifyMaintenanceCode(qrCode, maintenanceCode)
  120. if (!isValid) {
  121. return reply.code(401).send({ message: '维护码错误' })
  122. }
  123. const qrCodeInfo = await this.qrCodeService.getQrCodeInfo(qrCode, undefined, true)
  124. return reply.send({
  125. message: '验证成功',
  126. valid: true,
  127. ...qrCodeInfo
  128. })
  129. } catch (error) {
  130. const message = error instanceof Error ? error.message : '验证失败'
  131. return reply.code(500).send({ message })
  132. }
  133. }
  134. /**
  135. * 前台获取二维码信息
  136. */
  137. async getInfo(request: FastifyRequest<{ Querystring: GetQrCodeInfoDto }>, reply: FastifyReply) {
  138. try {
  139. const { qrCode, id } = request.query
  140. if (!qrCode && !id) {
  141. return reply.code(400).send({ message: '请提供参数' })
  142. }
  143. // 记录扫描 - 获取真实IP地址
  144. let ipAddress = request.headers['x-forwarded-for'] as string
  145. if (ipAddress) {
  146. // x-forwarded-for 可能包含多个IP,取第一个(真实客户端IP)
  147. ipAddress = ipAddress.split(',')[0].trim()
  148. } else {
  149. // 如果没有 x-forwarded-for,尝试其他常见的代理头
  150. ipAddress =
  151. (request.headers['x-real-ip'] as string) ||
  152. (request.headers['cf-connecting-ip'] as string) ||
  153. request.ip ||
  154. request.socket.remoteAddress ||
  155. 'unknown'
  156. }
  157. const userAgent = request.headers['user-agent']
  158. // 如果通过 qrCode 查询,记录扫描
  159. if (qrCode) {
  160. await this.scanRecordService.create(qrCode, undefined, undefined, undefined, ipAddress, userAgent)
  161. }
  162. // 获取二维码信息
  163. const info = await this.qrCodeService.getQrCodeInfo(qrCode, id)
  164. return reply.send(info)
  165. } catch (error) {
  166. const message = error instanceof Error ? error.message : '获取信息失败'
  167. return reply.code(500).send({ message })
  168. }
  169. }
  170. /**
  171. * 后台获取二维码信息
  172. */
  173. async adminGetInfo(request: FastifyRequest<{ Querystring: GetQrCodeInfoDto }>, reply: FastifyReply) {
  174. try {
  175. const { qrCode, id } = request.query
  176. if (!qrCode && !id) {
  177. return reply.code(400).send({ message: '请提供参数' })
  178. }
  179. // 后台查询,不判断 isVisible,返回所有信息
  180. const info = await this.qrCodeService.getQrCodeInfo(qrCode, id, true)
  181. return reply.send(info)
  182. } catch (error) {
  183. const message = error instanceof Error ? error.message : '获取信息失败'
  184. return reply.code(500).send({ message })
  185. }
  186. }
  187. /**
  188. * 获取扫描记录
  189. */
  190. async getScanRecords(
  191. request: FastifyRequest<{ Querystring: { qrCode: string; limit?: number } }>,
  192. reply: FastifyReply
  193. ) {
  194. try {
  195. const { qrCode, limit } = request.query
  196. if (!qrCode) {
  197. return reply.code(400).send({ message: '请提供二维码参数' })
  198. }
  199. const records = await this.scanRecordService.getRecentRecords(qrCode, limit || 10)
  200. return reply.send({
  201. qrCode,
  202. count: records.length,
  203. records
  204. })
  205. } catch (error) {
  206. const message = error instanceof Error ? error.message : '获取记录失败'
  207. return reply.code(500).send({ message })
  208. }
  209. }
  210. /**
  211. * 重置维护码
  212. */
  213. async resetMaintenanceCode(request: FastifyRequest<{ Body: ResetMaintenanceCodeDto }>, reply: FastifyReply) {
  214. try {
  215. const { qrCode, maintenanceCode } = request.body
  216. const newMaintenanceCode = await this.qrCodeService.resetMaintenanceCode(qrCode, maintenanceCode)
  217. return reply.send({
  218. message: '维护码重置成功',
  219. data: {
  220. qrCode,
  221. maintenanceCode: newMaintenanceCode
  222. }
  223. })
  224. } catch (error) {
  225. const message = error instanceof Error ? error.message : '重置失败'
  226. return reply.code(500).send({ message })
  227. }
  228. }
  229. /**
  230. * 绑定二维码
  231. */
  232. async bindQrCode(request: FastifyRequest<{ Body: AddUserQrCodeDto }>, reply: FastifyReply) {
  233. try {
  234. const userId = request.user.id
  235. const { qrCodeId, qrCode, maintenanceCode } = request.body
  236. await this.qrCodeService.bindQrCode(userId, qrCodeId, qrCode, maintenanceCode)
  237. return reply.code(201).send({
  238. message: 'Bound successfully'
  239. })
  240. } catch (error) {
  241. const message = error instanceof Error ? error.message : 'Binding failed'
  242. const clientErrorKeywords = [
  243. 'not found',
  244. 'already bound',
  245. 'Please provide',
  246. 'Maintenance code cannot be empty',
  247. 'Maintenance code error'
  248. ]
  249. const isClientError = clientErrorKeywords.some(keyword => message.includes(keyword))
  250. const statusCode = isClientError ? 400 : 500
  251. return reply.code(statusCode).send({ message })
  252. }
  253. }
  254. /**
  255. * 取消绑定二维码
  256. */
  257. async unbindQrCode(request: FastifyRequest<{ Body: DeleteUserQrCodeDto }>, reply: FastifyReply) {
  258. try {
  259. const userId = request.user.id
  260. const { id, qrCodeId } = request.body
  261. await this.qrCodeService.unbindQrCode(userId, id, qrCodeId)
  262. return reply.send({
  263. message: 'Unbound successfully'
  264. })
  265. } catch (error) {
  266. const message = error instanceof Error ? error.message : 'Unbinding failed'
  267. const statusCode =
  268. message.includes('not found') || message.includes('already deleted') || message.includes('Please provide')
  269. ? 400
  270. : 500
  271. return reply.code(statusCode).send({ message })
  272. }
  273. }
  274. /**
  275. * 查询用户的关联二维码列表
  276. */
  277. async getUserQrCodes(request: FastifyRequest<{ Querystring: QueryUserQrCodeDto }>, reply: FastifyReply) {
  278. try {
  279. const userId = request.user.id
  280. const { page = 0, pageSize = 20 } = request.query
  281. const result = await this.qrCodeService.getUserQrCodes(userId, page, pageSize)
  282. return reply.send(result)
  283. } catch (error) {
  284. const message = error instanceof Error ? error.message : 'Query failed'
  285. return reply.code(500).send({ message })
  286. }
  287. }
  288. }