qr-code.controller.ts 10 KB

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