passage.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. package parking
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. "wails-app/internal/dao"
  7. "wails-app/internal/global"
  8. common "wails-app/internal/model/common"
  9. "wails-app/internal/model/vehicle/request"
  10. "wails-app/internal/service/vehicle"
  11. )
  12. // GateOpener 道闸控制函数(由 UHF 模块在初始化时注入,避免循环依赖)
  13. var GateOpener func(deviceCode string, validTime byte) error
  14. // PassageService 统一的进出场服务(所有触发方式共用:RFID、摄像头、手动录入)
  15. type PassageService struct{}
  16. // ===================== 防抖与状态 =====================
  17. var (
  18. passageLastTime = make(map[string]int64)
  19. passageStatus = make(map[string]bool) // true=在场内
  20. passageMutex sync.Mutex
  21. passageDebounceSec = int64(3) // 3秒防抖
  22. )
  23. // getIdentifier 从请求中提取唯一标识(优先 RFID,其次车牌)
  24. func getIdentifier(req common.PassageRequest) string {
  25. if req.RFIDTag != "" {
  26. return req.RFIDTag
  27. }
  28. return req.PlateNumber
  29. }
  30. // HandlePassage 统一的进出场处理入口
  31. // 调用方只需传入车辆标识 + 设备编码,其余逻辑全部在此处理:
  32. //
  33. // 查询设备/通道 → 查询车辆 → 黑名单检查 → 临时车规则 → 进场/出场 → 开闸
  34. func (s *PassageService) HandlePassage(req common.PassageRequest) (*common.PassageResult, error) {
  35. identifier := getIdentifier(req)
  36. if identifier == "" {
  37. return nil, fmt.Errorf("缺少车辆标识(RFIDTag 或 PlateNumber)")
  38. }
  39. // ===================== 1. 防抖 =====================
  40. passageMutex.Lock()
  41. if last, ok := passageLastTime[identifier]; ok && time.Now().Unix()-last < passageDebounceSec {
  42. passageMutex.Unlock()
  43. return nil, fmt.Errorf("防抖中: %s", identifier)
  44. }
  45. passageLastTime[identifier] = time.Now().Unix()
  46. passageMutex.Unlock()
  47. // ===================== 2. 查询设备 + 通道 =====================
  48. var device dao.UHFReader
  49. if err := global.GVA_DB.Preload("Channel").First(&device, "device_code = ?", req.DeviceCode).Error; err != nil {
  50. return nil, fmt.Errorf("设备不存在: %s", req.DeviceCode)
  51. }
  52. channel := device.Channel
  53. direction := channel.Direction
  54. allowTemporary := channel.AllowTemporary
  55. // ===================== 3. 查询车辆 =====================
  56. vehicleSvc := &vehicle.VehicleService{}
  57. shortlistSvc := &vehicle.ShortlistService{}
  58. v, err := vehicleSvc.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag)
  59. isTempVehicle := false
  60. if err != nil || v.ID == 0 {
  61. isTempVehicle = true
  62. } else if v.VehicleType != nil {
  63. isTempVehicle = v.VehicleType.IsSystem
  64. } else {
  65. isTempVehicle = true
  66. }
  67. // ===================== 4. 黑名单检查 =====================
  68. isBlack, _ := shortlistSvc.CheckVehicleShortlist(req.RFIDTag)
  69. if isBlack {
  70. fmt.Printf("🚫 禁止通行 | 车辆在黑名单中 | 设备:%s 车牌:%s RFID:%s\n", req.DeviceCode, req.PlateNumber, req.RFIDTag)
  71. return nil, fmt.Errorf("车辆在黑名单中")
  72. }
  73. // ===================== 5. 临时车规则 =====================
  74. if isTempVehicle && !allowTemporary {
  75. fmt.Printf("🚫 禁止通行 | 临时车不允许此通道 | 设备:%s 车牌:%s RFID:%s\n", req.DeviceCode, req.PlateNumber, req.RFIDTag)
  76. return nil, fmt.Errorf("临时车不允许此通道")
  77. }
  78. // ===================== 6. 根据方向处理 =====================
  79. switch direction {
  80. case "in":
  81. return s.handleEntry(req, identifier, channel.ParkingLotID)
  82. case "out":
  83. return s.handleExit(req, identifier)
  84. default:
  85. return nil, fmt.Errorf("未知通道方向: %s", direction)
  86. }
  87. }
  88. // handleEntry 处理入场
  89. func (s *PassageService) handleEntry(req common.PassageRequest, identifier string, parkingLotID uint) (*common.PassageResult, error) {
  90. passageMutex.Lock()
  91. canIn := !passageStatus[identifier]
  92. passageMutex.Unlock()
  93. if !canIn {
  94. return nil, fmt.Errorf("车辆已在场内: %s", identifier)
  95. }
  96. fmt.Printf("🟢 入口开闸 | 设备:%s 车牌:%s RFID:%s\n", req.DeviceCode, req.PlateNumber, req.RFIDTag)
  97. // 开闸
  98. if GateOpener != nil {
  99. if err := GateOpener(req.DeviceCode, 2); err != nil {
  100. fmt.Printf("⚠️ 开闸失败: %v\n", err)
  101. }
  102. }
  103. // 执行入场
  104. vehicleSvc := &vehicle.VehicleService{}
  105. resp, err := vehicleSvc.VehicleEntry(request.VehicleEntry{
  106. PlateNumber: req.PlateNumber,
  107. RFIDTag: req.RFIDTag,
  108. ParkingLotID: parkingLotID,
  109. EntryImage: req.Image,
  110. })
  111. if err != nil {
  112. return nil, err
  113. }
  114. passageMutex.Lock()
  115. passageStatus[identifier] = true
  116. passageMutex.Unlock()
  117. return &common.PassageResult{
  118. Success: true,
  119. Message: "入场成功",
  120. Direction: "in",
  121. PlateNumber: resp.Data.PlateNumber,
  122. RFIDTag: req.RFIDTag,
  123. EntryTime: resp.Data.EntryTime,
  124. }, nil
  125. }
  126. // handleExit 处理出场
  127. func (s *PassageService) handleExit(req common.PassageRequest, identifier string) (*common.PassageResult, error) {
  128. fmt.Printf("🔴 出口开闸 | 设备:%s 车牌:%s RFID:%s\n", req.DeviceCode, req.PlateNumber, req.RFIDTag)
  129. // 开闸
  130. if GateOpener != nil {
  131. if err := GateOpener(req.DeviceCode, 2); err != nil {
  132. fmt.Printf("⚠️ 开闸失败: %v\n", err)
  133. }
  134. }
  135. // 执行出场
  136. vehicleSvc := &vehicle.VehicleService{}
  137. resp, err := vehicleSvc.VehicleExit(request.VehicleExit{
  138. PlateNumber: req.PlateNumber,
  139. RFIDTag: req.RFIDTag,
  140. ExitImage: req.Image,
  141. })
  142. if err != nil {
  143. return nil, err
  144. }
  145. passageMutex.Lock()
  146. passageStatus[identifier] = false
  147. passageMutex.Unlock()
  148. return &common.PassageResult{
  149. Success: true,
  150. Message: "出场成功",
  151. Direction: "out",
  152. PlateNumber: req.PlateNumber,
  153. RFIDTag: req.RFIDTag,
  154. Fee: resp.Data.Fee,
  155. StayTime: resp.Data.StayTime,
  156. EntryTime: resp.Data.EntryTime,
  157. ExitTime: resp.Data.ExitTime,
  158. }, nil
  159. }
  160. // SetEpcExit 手动重置离场状态
  161. func SetEpcExit(identifier string) {
  162. passageMutex.Lock()
  163. defer passageMutex.Unlock()
  164. passageStatus[identifier] = false
  165. fmt.Printf("🚙 手动重置离场 | 标识: %s\n", identifier)
  166. }