|
@@ -11,6 +11,7 @@ import (
|
|
|
common "wails-app/internal/model/common"
|
|
common "wails-app/internal/model/common"
|
|
|
"wails-app/internal/model/vehicle/request"
|
|
"wails-app/internal/model/vehicle/request"
|
|
|
incidentService "wails-app/internal/modules/incident/service"
|
|
incidentService "wails-app/internal/modules/incident/service"
|
|
|
|
|
+ sessionRequest "wails-app/internal/modules/parking-session/model/request"
|
|
|
sessionService "wails-app/internal/modules/parking-session/service"
|
|
sessionService "wails-app/internal/modules/parking-session/service"
|
|
|
"wails-app/internal/service/vehicle"
|
|
"wails-app/internal/service/vehicle"
|
|
|
)
|
|
)
|
|
@@ -67,10 +68,14 @@ func (s *PassageService) HandlePassage(req common.PassageRequest) (*common.Passa
|
|
|
channel := device.Channel
|
|
channel := device.Channel
|
|
|
direction := channel.Direction
|
|
direction := channel.Direction
|
|
|
if direction == "inout" {
|
|
if direction == "inout" {
|
|
|
- if req.Direction != "in" && req.Direction != "out" {
|
|
|
|
|
- return nil, errors.New("双向通道必须明确本次通行方向")
|
|
|
|
|
|
|
+ if req.Direction == "in" || req.Direction == "out" {
|
|
|
|
|
+ direction = req.Direction
|
|
|
|
|
+ } else if inferred, inferErr := s.inferInoutDirection(req); inferErr != nil {
|
|
|
|
|
+ // 单一通道方向推断失败(如重复读卡):按失败事件处理,出口屏/工作台可见原因
|
|
|
|
|
+ return nil, inferErr
|
|
|
|
|
+ } else {
|
|
|
|
|
+ direction = inferred
|
|
|
}
|
|
}
|
|
|
- direction = req.Direction
|
|
|
|
|
} else if req.Direction != "" && req.Direction != direction {
|
|
} else if req.Direction != "" && req.Direction != direction {
|
|
|
return nil, fmt.Errorf("所选设备属于%s通道,不能执行%s操作", direction, req.Direction)
|
|
return nil, fmt.Errorf("所选设备属于%s通道,不能执行%s操作", direction, req.Direction)
|
|
|
}
|
|
}
|
|
@@ -95,16 +100,16 @@ func (s *PassageService) HandlePassage(req common.PassageRequest) (*common.Passa
|
|
|
if isBlack {
|
|
if isBlack {
|
|
|
fmt.Printf("🚫 禁止通行 | 车辆在黑名单中 | 设备:%s 车牌:%s RFID:%s\n", req.DeviceCode, req.PlateNumber, req.RFIDTag)
|
|
fmt.Printf("🚫 禁止通行 | 车辆在黑名单中 | 设备:%s 车牌:%s RFID:%s\n", req.DeviceCode, req.PlateNumber, req.RFIDTag)
|
|
|
incidentSvc.RecordIncident(incidentService.RecordIncidentRequest{
|
|
incidentSvc.RecordIncident(incidentService.RecordIncidentRequest{
|
|
|
- Category: incidentService.CategoryBlacklist,
|
|
|
|
|
- Source: incidentService.SourcePassage,
|
|
|
|
|
- PlateNumber: req.PlateNumber,
|
|
|
|
|
- RFIDTag: req.RFIDTag,
|
|
|
|
|
|
|
+ Category: incidentService.CategoryBlacklist,
|
|
|
|
|
+ Source: incidentService.SourcePassage,
|
|
|
|
|
+ PlateNumber: req.PlateNumber,
|
|
|
|
|
+ RFIDTag: req.RFIDTag,
|
|
|
ParkingLotID: channel.ParkingLotID,
|
|
ParkingLotID: channel.ParkingLotID,
|
|
|
- ChannelID: channel.ID,
|
|
|
|
|
- ChannelCode: channel.ChannelCode,
|
|
|
|
|
- DeviceCode: req.DeviceCode,
|
|
|
|
|
- OperatorID: req.OperatorID,
|
|
|
|
|
- Description: "黑名单车辆通行拦截",
|
|
|
|
|
|
|
+ ChannelID: channel.ID,
|
|
|
|
|
+ ChannelCode: channel.ChannelCode,
|
|
|
|
|
+ DeviceCode: req.DeviceCode,
|
|
|
|
|
+ OperatorID: req.OperatorID,
|
|
|
|
|
+ Description: "黑名单车辆通行拦截",
|
|
|
})
|
|
})
|
|
|
return nil, fmt.Errorf("车辆在黑名单中")
|
|
return nil, fmt.Errorf("车辆在黑名单中")
|
|
|
}
|
|
}
|
|
@@ -136,6 +141,35 @@ func (s *PassageService) HandlePassage(req common.PassageRequest) (*common.Passa
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// inferInoutDirection 单一通道(inout)方向推断:单一通道只装一个读卡器,管进也管出,
|
|
|
|
|
+// 事件本身不带方向,由业务状态决定——无在场会话即进场,已有在场会话即出场。
|
|
|
|
|
+// 防误判:入场后不足 system.inout-min-stay-minutes 的再次读卡按重复读卡忽略,
|
|
|
|
|
+// 避免车还没驶离读卡区就被推断成出场。显式携带方向的请求不会走到这里。
|
|
|
|
|
+func (s *PassageService) inferInoutDirection(req common.PassageRequest) (string, error) {
|
|
|
|
|
+ sessionSvc := sessionService.NewParkingSessionService()
|
|
|
|
|
+ record, err := sessionSvc.ResolveActiveSession(sessionRequest.ResolveActiveSessionRequest{
|
|
|
|
|
+ PlateNumber: req.PlateNumber,
|
|
|
|
|
+ RFIDTag: req.RFIDTag,
|
|
|
|
|
+ })
|
|
|
|
|
+ if errors.Is(err, sessionService.ErrSessionNotFound) {
|
|
|
|
|
+ return "in", nil
|
|
|
|
|
+ }
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return "", fmt.Errorf("单一通道方向推断失败: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ minStay := time.Duration(global.GVA_CONFIG.System.InoutMinStayMinutes) * time.Minute
|
|
|
|
|
+ if minStay <= 0 {
|
|
|
|
|
+ minStay = time.Minute
|
|
|
|
|
+ }
|
|
|
|
|
+ stay := time.Since(record.EntryTime)
|
|
|
|
|
+ if stay < minStay {
|
|
|
|
|
+ return "", fmt.Errorf("单一通道重复读卡忽略: 入场后需满 %v 才触发出场(当前 %v)",
|
|
|
|
|
+ minStay, stay.Round(time.Second))
|
|
|
|
|
+ }
|
|
|
|
|
+ return "out", nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// handleEntry 处理入场
|
|
// handleEntry 处理入场
|
|
|
func (s *PassageService) handleEntry(req common.PassageRequest, identifier string, parkingLotID uint, channel *dao.Channel, device *dao.UHFReader) (*common.PassageResult, error) {
|
|
func (s *PassageService) handleEntry(req common.PassageRequest, identifier string, parkingLotID uint, channel *dao.Channel, device *dao.UHFReader) (*common.PassageResult, error) {
|
|
|
passageMutex.Lock()
|
|
passageMutex.Lock()
|
|
@@ -144,16 +178,16 @@ func (s *PassageService) handleEntry(req common.PassageRequest, identifier strin
|
|
|
|
|
|
|
|
if !canIn {
|
|
if !canIn {
|
|
|
incidentSvc.RecordIncident(incidentService.RecordIncidentRequest{
|
|
incidentSvc.RecordIncident(incidentService.RecordIncidentRequest{
|
|
|
- Category: incidentService.CategoryDuplicateEntry,
|
|
|
|
|
- Source: incidentService.SourcePassage,
|
|
|
|
|
- PlateNumber: req.PlateNumber,
|
|
|
|
|
- RFIDTag: req.RFIDTag,
|
|
|
|
|
|
|
+ Category: incidentService.CategoryDuplicateEntry,
|
|
|
|
|
+ Source: incidentService.SourcePassage,
|
|
|
|
|
+ PlateNumber: req.PlateNumber,
|
|
|
|
|
+ RFIDTag: req.RFIDTag,
|
|
|
ParkingLotID: parkingLotID,
|
|
ParkingLotID: parkingLotID,
|
|
|
- ChannelID: channel.ID,
|
|
|
|
|
- ChannelCode: channel.ChannelCode,
|
|
|
|
|
- DeviceCode: req.DeviceCode,
|
|
|
|
|
- OperatorID: req.OperatorID,
|
|
|
|
|
- Description: "车辆已在场内,重复入场拦截",
|
|
|
|
|
|
|
+ ChannelID: channel.ID,
|
|
|
|
|
+ ChannelCode: channel.ChannelCode,
|
|
|
|
|
+ DeviceCode: req.DeviceCode,
|
|
|
|
|
+ OperatorID: req.OperatorID,
|
|
|
|
|
+ Description: "车辆已在场内,重复入场拦截",
|
|
|
})
|
|
})
|
|
|
return nil, fmt.Errorf("车辆已在场内: %s", identifier)
|
|
return nil, fmt.Errorf("车辆已在场内: %s", identifier)
|
|
|
}
|
|
}
|
|
@@ -271,18 +305,18 @@ func (s *PassageService) handleExit(req common.PassageRequest, identifier string
|
|
|
// 无入场记录/丢票出场(resolveExitRecordTx 将所有定位失败统一包装为"车辆未入场")
|
|
// 无入场记录/丢票出场(resolveExitRecordTx 将所有定位失败统一包装为"车辆未入场")
|
|
|
if strings.Contains(err.Error(), "车辆未入场") {
|
|
if strings.Contains(err.Error(), "车辆未入场") {
|
|
|
incidentSvc.RecordIncident(incidentService.RecordIncidentRequest{
|
|
incidentSvc.RecordIncident(incidentService.RecordIncidentRequest{
|
|
|
- Category: incidentService.CategoryNoEntryExit,
|
|
|
|
|
- Source: incidentService.SourcePassage,
|
|
|
|
|
- TicketNo: req.TicketNo,
|
|
|
|
|
- PlateNumber: req.PlateNumber,
|
|
|
|
|
- RFIDTag: req.RFIDTag,
|
|
|
|
|
|
|
+ Category: incidentService.CategoryNoEntryExit,
|
|
|
|
|
+ Source: incidentService.SourcePassage,
|
|
|
|
|
+ TicketNo: req.TicketNo,
|
|
|
|
|
+ PlateNumber: req.PlateNumber,
|
|
|
|
|
+ RFIDTag: req.RFIDTag,
|
|
|
ParkingLotID: channel.ParkingLotID,
|
|
ParkingLotID: channel.ParkingLotID,
|
|
|
- ChannelID: channel.ID,
|
|
|
|
|
- ChannelCode: channel.ChannelCode,
|
|
|
|
|
- DeviceCode: req.DeviceCode,
|
|
|
|
|
- OperatorID: req.OperatorID,
|
|
|
|
|
- Description: "无入场记录出场或票据不存在",
|
|
|
|
|
- Detail: err.Error(),
|
|
|
|
|
|
|
+ ChannelID: channel.ID,
|
|
|
|
|
+ ChannelCode: channel.ChannelCode,
|
|
|
|
|
+ DeviceCode: req.DeviceCode,
|
|
|
|
|
+ OperatorID: req.OperatorID,
|
|
|
|
|
+ Description: "无入场记录出场或票据不存在",
|
|
|
|
|
+ Detail: err.Error(),
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
return nil, err
|
|
return nil, err
|