package vehicle import ( "errors" "fmt" "time" "wails-app/internal/dao" "wails-app/internal/global" "wails-app/internal/model/vehicle/request" vehicleResponse "wails-app/internal/model/vehicle/response" paymentService "wails-app/internal/modules/payment/service" "gorm.io/gorm" ) type VehicleService struct{} // buildPlateOrRfidQuery 按车牌或RFID查询,RFID为空时仅用车牌(避免 rfid_tag='' 匹配所有无标签车辆) func buildPlateOrRfidQuery(db *gorm.DB, plateNumber, rfidTag string) *gorm.DB { if rfidTag != "" { return db.Where("plate_number = ? OR rfid_tag = ?", plateNumber, rfidTag) } return db.Where("plate_number = ?", plateNumber) } // CreateVehicle 创建车辆信息 func (s *VehicleService) CreateVehicle(req request.VehicleCreate) error { // 检查车牌号是否已存在 var existingVehicle dao.Vehicle result := global.GVA_DB.Where("plate_number = ? OR rfid_tag = ?", req.PlateNumber, req.RFIDTag).First(&existingVehicle) if result.RowsAffected > 0 { return errors.New("车牌号已存在") } // 检查车辆类型是否存在 var vehicleType dao.VehicleType result = global.GVA_DB.First(&vehicleType, req.VehicleTypeID) if result.RowsAffected == 0 { return errors.New("车辆类型不存在") } // 处理临时车辆的情况 var ownerId *uint if req.OwnerId > 0 { ownerId = &req.OwnerId } vehicle := dao.Vehicle{ PlateNumber: req.PlateNumber, RFIDTag: req.RFIDTag, VehicleTypeID: req.VehicleTypeID, VehicleBrand: req.VehicleBrand, VehicleColor: req.VehicleColor, OwnerId: ownerId, } return global.GVA_DB.Create(&vehicle).Error } // UpdateVehicle 更新车辆信息 func (s *VehicleService) UpdateVehicle(req request.VehicleUpdate) error { var vehicle dao.Vehicle result := global.GVA_DB.First(&vehicle, req.ID) if result.RowsAffected == 0 { return errors.New("车辆不存在") } // 检查车牌号是否被其他车辆使用 var existingVehicle dao.Vehicle result = global.GVA_DB.Where("(plate_number = ? OR rfid_tag = ?) AND id != ?", req.PlateNumber, req.RFIDTag, req.ID).First(&existingVehicle) fmt.Println("result", result.RowsAffected) if result.RowsAffected > 0 { return errors.New("车牌号已被其他车辆使用") } // 检查车辆类型是否存在 var vehicleType dao.VehicleType result = global.GVA_DB.First(&vehicleType, req.VehicleTypeID) if result.RowsAffected == 0 { return errors.New("车辆类型不存在") } // 处理临时车辆的情况 var ownerId *uint if req.OwnerId > 0 { ownerId = &req.OwnerId } else { ownerId = nil } err := global.GVA_DB.Model(&vehicle).Updates(map[string]interface{}{ "plate_number": req.PlateNumber, "rfid_tag": req.RFIDTag, "vehicle_type_id": req.VehicleTypeID, "vehicle_brand": req.VehicleBrand, "vehicle_color": req.VehicleColor, "owner_id": ownerId, }).Error return err } // GetVehicleByID 根据ID获取车辆信息 func (s *VehicleService) GetVehicleByID(id uint) (dao.Vehicle, error) { var vehicle dao.Vehicle result := global.GVA_DB.First(&vehicle, id) if result.RowsAffected == 0 { return dao.Vehicle{}, errors.New("车辆不存在") } return vehicle, nil } // GetVehicleByPlateNumber 根据车牌号或RFID获取车辆信息 func (s *VehicleService) GetVehicleByPlateNumber(plateNumber, RFIDTag string) (dao.Vehicle, error) { var vehicle dao.Vehicle query := global.GVA_DB.Preload("VehicleType") if RFIDTag != "" { query = query.Where("plate_number = ? OR rfid_tag = ?", plateNumber, RFIDTag) } else { query = query.Where("plate_number = ?", plateNumber) } result := query.First(&vehicle) if result.RowsAffected == 0 { return dao.Vehicle{}, errors.New("车辆不存在") } return vehicle, nil } func (s *VehicleService) QueryAllVehicles() ([]dao.Vehicle, error) { var vehicles []dao.Vehicle err := global.GVA_DB.Find(&vehicles).Error if err != nil { return vehicles, err } return vehicles, nil } // ListVehicles 获取车辆列表 func (s *VehicleService) ListVehicles(req request.VehicleQuery) (int64, []dao.Vehicle, error) { var vehicles []dao.Vehicle var total int64 query := global.GVA_DB.Model(&dao.Vehicle{}) if req.PlateNumber != "" { query = query.Where("plate_number LIKE ?", "%"+req.PlateNumber+"%") } if req.VehicleTypeID > 0 { query = query.Where("vehicle_type_id = ?", req.VehicleTypeID) } if req.OwnerId > 0 { query = query.Where("owner_id = ?", req.OwnerId) } // 计算总数 query.Count(&total) // 分页查询 offset := (req.Page - 1) * req.PageSize result := query.Offset(offset).Limit(req.PageSize).Preload("Owner").Preload("VehicleType").Find(&vehicles) if result.Error != nil { return 0, nil, result.Error } return total, vehicles, nil } // DeleteVehicle 删除车辆 func (s *VehicleService) DeleteVehicle(id uint) error { result := global.GVA_DB.Delete(&dao.Vehicle{}, id) if result.RowsAffected == 0 { return errors.New("车辆不存在") } return result.Error } // VehicleEntry 车辆入场 func (s *VehicleService) VehicleEntry(req request.VehicleEntry) (request.VehicleEntryResponse, error) { // 检查车辆是否存在 vehicle, err := s.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag) if err != nil { // 如果车辆不存在,创建新车辆,自动归类为"临时车" var tempType dao.VehicleType if global.GVA_DB.Where("is_system = ?", true).First(&tempType).Error != nil { return request.VehicleEntryResponse{}, errors.New("系统错误:临时车类型未配置") } createReq := request.VehicleCreate{ PlateNumber: req.PlateNumber, VehicleTypeID: tempType.ID, OwnerId: 0, } err = s.CreateVehicle(createReq) if err != nil { return request.VehicleEntryResponse{}, err } vehicle, _ = s.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag) } // 检查是否有未出场的记录 var existingRecord dao.VehicleRecord result := buildPlateOrRfidQuery(global.GVA_DB, req.PlateNumber, req.RFIDTag).Where("exit_time IS NULL").First(&existingRecord) if result.RowsAffected > 0 { return request.VehicleEntryResponse{}, errors.New("车辆已入场,未出场") } // 创建入场记录 entryTime := time.Now() record := dao.VehicleRecord{ PlateNumber: req.PlateNumber, RFIDTag: req.RFIDTag, EntryTime: entryTime, ParkingLotID: req.ParkingLotID, ParkingSpaceID: req.ParkingSpaceID, EntryImage: req.EntryImage, PaymentStatus: "unpaid", } err = global.GVA_DB.Create(&record).Error if err != nil { return request.VehicleEntryResponse{}, err } // 更新车辆最近入场时间 vehicle.LastEntryTime = &entryTime global.GVA_DB.Save(&vehicle) response := request.VehicleEntryResponse{ Success: true, Message: "车辆入场成功", } response.Data.PlateNumber = req.PlateNumber response.Data.EntryTime = entryTime.Unix() response.Data.ParkingLotID = req.ParkingLotID response.Data.ParkingSpaceID = req.ParkingSpaceID return response, nil } // VehicleExit 车辆出场 func (s *VehicleService) VehicleExit(req request.VehicleExit) (request.VehicleExitResponse, error) { // 1. 查询未出场记录 var record dao.VehicleRecord if err := buildPlateOrRfidQuery(global.GVA_DB, req.PlateNumber, req.RFIDTag).Where("exit_time IS NULL").First(&record).Error; err != nil { return request.VehicleExitResponse{}, errors.New("车辆未入场或已出场") } // 2. 获取车辆信息 vehicle, err := s.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag) if err != nil { return request.VehicleExitResponse{}, err } // 3. 计算停留时长 & 费用 exitTime := time.Now() stayTime := int64(exitTime.Sub(record.EntryTime).Minutes()) if stayTime < 0 { stayTime = 0 } fee := s.calculateFee(&vehicle, stayTime) // 4. 更新出场记录 record.ExitTime = &exitTime record.StayTime = stayTime record.Fee = fee record.ExitImage = req.ExitImage record.PaymentStatus = "paid" if err := global.GVA_DB.Save(&record).Error; err != nil { return request.VehicleExitResponse{}, err } // 5. 更新车辆统计信息 vehicle.LastExitTime = &exitTime vehicle.TotalStayTime += stayTime vehicle.TotalFee += fee global.GVA_DB.Save(&vehicle) // 6. 完全匹配你的匿名结构体返回(正确写法) var response request.VehicleExitResponse response.Success = true response.Message = "车辆出场成功" response.Data.PlateNumber = record.PlateNumber response.Data.EntryTime = record.EntryTime.Unix() response.Data.ExitTime = exitTime.Unix() response.Data.StayTime = stayTime response.Data.Fee = fee response.Data.PaymentStatus = record.PaymentStatus return response, nil } // ExitPreview 费用预览(不修改数据库,仅查询+计算费用) func (s *VehicleService) ExitPreview(req request.VehicleEntry) (vehicleResponse.ExitPreviewResponse, error) { var record dao.VehicleRecord err := buildPlateOrRfidQuery(global.GVA_DB, req.PlateNumber, req.RFIDTag).Where("exit_time IS NULL").First(&record).Error if err != nil { return vehicleResponse.ExitPreviewResponse{}, errors.New("车辆未入场或已出场") } vehicle, err := s.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag) if err != nil { return vehicleResponse.ExitPreviewResponse{}, err } now := time.Now() stayTime := int64(now.Sub(record.EntryTime).Minutes()) if stayTime < 0 { stayTime = 0 } fee := s.calculateFee(&vehicle, stayTime) typeName := "" isTemp := false if vehicle.VehicleType != nil { typeName = vehicle.VehicleType.Name isTemp = vehicle.VehicleType.IsSystem } return vehicleResponse.ExitPreviewResponse{ PlateNumber: vehicle.PlateNumber, EntryTime: record.EntryTime.Unix(), StayTime: stayTime, Fee: fee, VehicleType: typeName, IsTemp: isTemp, }, nil } // ExitConfirm 确认出场 + 支付(更新车辆记录 + 创建支付记录) func (s *VehicleService) ExitConfirm(req request.ExitConfirmRequest, operatorID uint) (vehicleResponse.ExitConfirmResponse, error) { var record dao.VehicleRecord err := buildPlateOrRfidQuery(global.GVA_DB, req.PlateNumber, req.RFIDTag).Where("exit_time IS NULL").First(&record).Error if err != nil { return vehicleResponse.ExitConfirmResponse{}, errors.New("车辆未入场或已出场") } vehicle, err := s.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag) if err != nil { return vehicleResponse.ExitConfirmResponse{}, err } exitTime := time.Now() stayTime := int64(exitTime.Sub(record.EntryTime).Minutes()) if stayTime < 0 { stayTime = 0 } fee := s.calculateFee(&vehicle, stayTime) record.ExitTime = &exitTime record.StayTime = stayTime record.Fee = fee record.PaymentMethod = req.PaymentMethod global.GVA_DB.Save(&record) vehicle.LastExitTime = &exitTime vehicle.TotalStayTime += stayTime vehicle.TotalFee += fee global.GVA_DB.Save(&vehicle) paymentSvc := paymentService.NewPaymentService() pr, err := paymentSvc.ProcessPayment(record.ID, req.PaymentMethod, fee, req.PaidAmount, operatorID) if err != nil { return vehicleResponse.ExitConfirmResponse{}, err } changeAmount := 0.0 if pr != nil { changeAmount = pr.ChangeAmount } return vehicleResponse.ExitConfirmResponse{ PlateNumber: vehicle.PlateNumber, EntryTime: record.EntryTime.Unix(), ExitTime: exitTime.Unix(), StayTime: stayTime, Fee: fee, ChangeAmount: changeAmount, PaymentMethod: req.PaymentMethod, }, nil } // calculateFee 计算停车费用 func (s *VehicleService) calculateFee(vehicle *dao.Vehicle, stayTime int64) float64 { // 1. 根据车辆类型ID获取收费配置(唯一) var feeConfig dao.FeeConfig if err := global.GVA_DB.Where("vehicle_type_id = ?", vehicle.VehicleTypeID).First(&feeConfig).Error; err != nil { // 无配置 → 默认计费 return float64(stayTime) * 0.1 } // 2. 判断VIP有效性 isVIP := false if vehicle.OwnerId != nil { var owner dao.Owner if err := global.GVA_DB.First(&owner, *vehicle.OwnerId).Error; err == nil { isVIP = owner.IsVip && time.Now().Before(owner.VipExpireTime) } } // 3. VIP 免费 if isVIP && feeConfig.IsVIPFree { return 0 } // ===================== 修复这里 ===================== // 4. 在免费时长内 → 直接 0 元 if stayTime <= int64(feeConfig.StartTime) { return 0 } // 5. 超过免费时长 → 开始计费 extraTime := stayTime - int64(feeConfig.StartTime) extraUnits := extraTime / int64(feeConfig.UnitTime) if extraTime%int64(feeConfig.UnitTime) > 0 { extraUnits++ } // 起步费 + 超时费用 fee := feeConfig.StartFee + float64(extraUnits)*feeConfig.UnitFee // 6. 每日封顶 if feeConfig.DailyMaxFee > 0 && fee > feeConfig.DailyMaxFee { fee = feeConfig.DailyMaxFee } // 7. VIP 折扣 if isVIP { fee *= feeConfig.VIPDiscount } return fee }