vehicle.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. package vehicle
  2. import (
  3. "errors"
  4. "fmt"
  5. "time"
  6. "wails-app/internal/dao"
  7. "wails-app/internal/global"
  8. "wails-app/internal/model/vehicle/request"
  9. )
  10. type VehicleService struct{}
  11. // CreateVehicle 创建车辆信息
  12. func (s *VehicleService) CreateVehicle(req request.VehicleCreate) error {
  13. // 检查车牌号是否已存在
  14. var existingVehicle dao.Vehicle
  15. result := global.GVA_DB.Where("plate_number = ? OR rfid_tag = ?", req.PlateNumber, req.RFIDTag).First(&existingVehicle)
  16. if result.RowsAffected > 0 {
  17. return errors.New("车牌号已存在")
  18. }
  19. // 检查车辆类型是否存在
  20. var vehicleType dao.VehicleType
  21. result = global.GVA_DB.First(&vehicleType, req.VehicleTypeID)
  22. if result.RowsAffected == 0 {
  23. return errors.New("车辆类型不存在")
  24. }
  25. // 处理临时车辆的情况
  26. var ownerId *uint
  27. if req.OwnerId > 0 {
  28. ownerId = &req.OwnerId
  29. }
  30. vehicle := dao.Vehicle{
  31. PlateNumber: req.PlateNumber,
  32. RFIDTag: req.RFIDTag,
  33. VehicleTypeID: req.VehicleTypeID,
  34. VehicleBrand: req.VehicleBrand,
  35. VehicleColor: req.VehicleColor,
  36. OwnerId: ownerId,
  37. }
  38. return global.GVA_DB.Create(&vehicle).Error
  39. }
  40. // UpdateVehicle 更新车辆信息
  41. func (s *VehicleService) UpdateVehicle(req request.VehicleUpdate) error {
  42. var vehicle dao.Vehicle
  43. result := global.GVA_DB.First(&vehicle, req.ID)
  44. if result.RowsAffected == 0 {
  45. return errors.New("车辆不存在")
  46. }
  47. // 检查车牌号是否被其他车辆使用
  48. var existingVehicle dao.Vehicle
  49. result = global.GVA_DB.Where("(plate_number = ? OR rfid_tag = ?) AND id != ?", req.PlateNumber, req.RFIDTag, req.ID).First(&existingVehicle)
  50. fmt.Println("result", result.RowsAffected)
  51. if result.RowsAffected > 0 {
  52. return errors.New("车牌号已被其他车辆使用")
  53. }
  54. // 检查车辆类型是否存在
  55. var vehicleType dao.VehicleType
  56. result = global.GVA_DB.First(&vehicleType, req.VehicleTypeID)
  57. if result.RowsAffected == 0 {
  58. return errors.New("车辆类型不存在")
  59. }
  60. // 处理临时车辆的情况
  61. var ownerId *uint
  62. if req.OwnerId > 0 {
  63. ownerId = &req.OwnerId
  64. } else {
  65. ownerId = nil
  66. }
  67. err := global.GVA_DB.Model(&vehicle).Updates(map[string]interface{}{
  68. "plate_number": req.PlateNumber,
  69. "rfid_tag": req.RFIDTag,
  70. "vehicle_type_id": req.VehicleTypeID,
  71. "vehicle_brand": req.VehicleBrand,
  72. "vehicle_color": req.VehicleColor,
  73. "owner_id": ownerId,
  74. }).Error
  75. return err
  76. }
  77. // GetVehicleByID 根据ID获取车辆信息
  78. func (s *VehicleService) GetVehicleByID(id uint) (dao.Vehicle, error) {
  79. var vehicle dao.Vehicle
  80. result := global.GVA_DB.First(&vehicle, id)
  81. if result.RowsAffected == 0 {
  82. return dao.Vehicle{}, errors.New("车辆不存在")
  83. }
  84. return vehicle, nil
  85. }
  86. // GetVehicleByPlateNumber 根据车牌号或RFID获取车辆信息
  87. func (s *VehicleService) GetVehicleByPlateNumber(plateNumber, RFIDTag string) (dao.Vehicle, error) {
  88. var vehicle dao.Vehicle
  89. query := global.GVA_DB.Preload("VehicleType")
  90. if RFIDTag != "" {
  91. query = query.Where("plate_number = ? OR rfid_tag = ?", plateNumber, RFIDTag)
  92. } else {
  93. query = query.Where("plate_number = ?", plateNumber)
  94. }
  95. result := query.First(&vehicle)
  96. if result.RowsAffected == 0 {
  97. return dao.Vehicle{}, errors.New("车辆不存在")
  98. }
  99. return vehicle, nil
  100. }
  101. func (s *VehicleService) QueryAllVehicles() ([]dao.Vehicle, error) {
  102. var vehicles []dao.Vehicle
  103. err := global.GVA_DB.Find(&vehicles).Error
  104. if err != nil {
  105. return vehicles, err
  106. }
  107. return vehicles, nil
  108. }
  109. // ListVehicles 获取车辆列表
  110. func (s *VehicleService) ListVehicles(req request.VehicleQuery) (int64, []dao.Vehicle, error) {
  111. var vehicles []dao.Vehicle
  112. var total int64
  113. query := global.GVA_DB.Model(&dao.Vehicle{})
  114. if req.PlateNumber != "" {
  115. query = query.Where("plate_number LIKE ?", "%"+req.PlateNumber+"%")
  116. }
  117. if req.VehicleTypeID > 0 {
  118. query = query.Where("vehicle_type_id = ?", req.VehicleTypeID)
  119. }
  120. if req.OwnerId > 0 {
  121. query = query.Where("owner_id = ?", req.OwnerId)
  122. }
  123. // 计算总数
  124. query.Count(&total)
  125. // 分页查询
  126. offset := (req.Page - 1) * req.PageSize
  127. result := query.Offset(offset).Limit(req.PageSize).Preload("Owner").Preload("VehicleType").Find(&vehicles)
  128. if result.Error != nil {
  129. return 0, nil, result.Error
  130. }
  131. return total, vehicles, nil
  132. }
  133. // DeleteVehicle 删除车辆
  134. func (s *VehicleService) DeleteVehicle(id uint) error {
  135. result := global.GVA_DB.Delete(&dao.Vehicle{}, id)
  136. if result.RowsAffected == 0 {
  137. return errors.New("车辆不存在")
  138. }
  139. return result.Error
  140. }
  141. // VehicleEntry 车辆入场
  142. func (s *VehicleService) VehicleEntry(req request.VehicleEntry) (request.VehicleEntryResponse, error) {
  143. // 检查车辆是否存在
  144. vehicle, err := s.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag)
  145. if err != nil {
  146. // 如果车辆不存在,创建新车辆,自动归类为"临时车"
  147. var tempType dao.VehicleType
  148. if global.GVA_DB.Where("is_system = ?", true).First(&tempType).Error != nil {
  149. return request.VehicleEntryResponse{}, errors.New("系统错误:临时车类型未配置")
  150. }
  151. createReq := request.VehicleCreate{
  152. PlateNumber: req.PlateNumber,
  153. VehicleTypeID: tempType.ID,
  154. OwnerId: 0,
  155. }
  156. err = s.CreateVehicle(createReq)
  157. if err != nil {
  158. return request.VehicleEntryResponse{}, err
  159. }
  160. vehicle, _ = s.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag)
  161. }
  162. // 检查是否有未出场的记录
  163. var existingRecord dao.VehicleRecord
  164. result := global.GVA_DB.Where("(plate_number = ? OR rfid_tag = ?) AND exit_time IS NULL", req.PlateNumber, req.RFIDTag).First(&existingRecord)
  165. if result.RowsAffected > 0 {
  166. return request.VehicleEntryResponse{}, errors.New("车辆已入场,未出场")
  167. }
  168. // 创建入场记录
  169. entryTime := time.Now()
  170. record := dao.VehicleRecord{
  171. PlateNumber: req.PlateNumber,
  172. RFIDTag: req.RFIDTag,
  173. EntryTime: entryTime,
  174. ParkingLotID: req.ParkingLotID,
  175. ParkingSpaceID: req.ParkingSpaceID,
  176. EntryImage: req.EntryImage,
  177. PaymentStatus: "unpaid",
  178. }
  179. err = global.GVA_DB.Create(&record).Error
  180. if err != nil {
  181. return request.VehicleEntryResponse{}, err
  182. }
  183. // 更新车辆最近入场时间
  184. vehicle.LastEntryTime = &entryTime
  185. global.GVA_DB.Save(&vehicle)
  186. response := request.VehicleEntryResponse{
  187. Success: true,
  188. Message: "车辆入场成功",
  189. }
  190. response.Data.PlateNumber = req.PlateNumber
  191. response.Data.EntryTime = entryTime.Unix()
  192. response.Data.ParkingLotID = req.ParkingLotID
  193. response.Data.ParkingSpaceID = req.ParkingSpaceID
  194. return response, nil
  195. }
  196. // VehicleExit 车辆出场
  197. func (s *VehicleService) VehicleExit(req request.VehicleExit) (request.VehicleExitResponse, error) {
  198. // 1. 查询未出场记录
  199. var record dao.VehicleRecord
  200. if err := global.GVA_DB.Where("(plate_number = ? OR rfid_tag = ?) AND exit_time IS NULL", req.PlateNumber, req.RFIDTag).First(&record).Error; err != nil {
  201. return request.VehicleExitResponse{}, errors.New("车辆未入场或已出场")
  202. }
  203. // 2. 获取车辆信息
  204. vehicle, err := s.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag)
  205. if err != nil {
  206. return request.VehicleExitResponse{}, err
  207. }
  208. // 3. 计算停留时长 & 费用
  209. exitTime := time.Now()
  210. stayTime := int64(exitTime.Sub(record.EntryTime).Minutes())
  211. if stayTime < 0 {
  212. stayTime = 0
  213. }
  214. fee := s.calculateFee(&vehicle, stayTime)
  215. // 4. 更新出场记录
  216. record.ExitTime = &exitTime
  217. record.StayTime = stayTime
  218. record.Fee = fee
  219. record.ExitImage = req.ExitImage
  220. record.PaymentStatus = "paid"
  221. if err := global.GVA_DB.Save(&record).Error; err != nil {
  222. return request.VehicleExitResponse{}, err
  223. }
  224. // 5. 更新车辆统计信息
  225. vehicle.LastExitTime = &exitTime
  226. vehicle.TotalStayTime += stayTime
  227. vehicle.TotalFee += fee
  228. global.GVA_DB.Save(&vehicle)
  229. // 6. 完全匹配你的匿名结构体返回(正确写法)
  230. var response request.VehicleExitResponse
  231. response.Success = true
  232. response.Message = "车辆出场成功"
  233. response.Data.PlateNumber = record.PlateNumber
  234. response.Data.EntryTime = record.EntryTime.Unix()
  235. response.Data.ExitTime = exitTime.Unix()
  236. response.Data.StayTime = stayTime
  237. response.Data.Fee = fee
  238. response.Data.PaymentStatus = record.PaymentStatus
  239. return response, nil
  240. }
  241. // calculateFee 计算停车费用
  242. func (s *VehicleService) calculateFee(vehicle *dao.Vehicle, stayTime int64) float64 {
  243. // 1. 根据车辆类型ID获取收费配置(唯一)
  244. var feeConfig dao.FeeConfig
  245. if err := global.GVA_DB.Where("vehicle_type_id = ?", vehicle.VehicleTypeID).First(&feeConfig).Error; err != nil {
  246. // 无配置 → 默认计费
  247. return float64(stayTime) * 0.1
  248. }
  249. // 2. 判断VIP有效性
  250. isVIP := false
  251. if vehicle.OwnerId != nil {
  252. var owner dao.Owner
  253. if err := global.GVA_DB.First(&owner, *vehicle.OwnerId).Error; err == nil {
  254. isVIP = owner.IsVip && time.Now().Before(owner.VipExpireTime)
  255. }
  256. }
  257. // 3. VIP 免费
  258. if isVIP && feeConfig.IsVIPFree {
  259. return 0
  260. }
  261. // ===================== 修复这里 =====================
  262. // 4. 在免费时长内 → 直接 0 元
  263. if stayTime <= int64(feeConfig.StartTime) {
  264. return 0
  265. }
  266. // 5. 超过免费时长 → 开始计费
  267. extraTime := stayTime - int64(feeConfig.StartTime)
  268. extraUnits := extraTime / int64(feeConfig.UnitTime)
  269. if extraTime%int64(feeConfig.UnitTime) > 0 {
  270. extraUnits++
  271. }
  272. // 起步费 + 超时费用
  273. fee := feeConfig.StartFee + float64(extraUnits)*feeConfig.UnitFee
  274. // 6. 每日封顶
  275. if feeConfig.DailyMaxFee > 0 && fee > feeConfig.DailyMaxFee {
  276. fee = feeConfig.DailyMaxFee
  277. }
  278. // 7. VIP 折扣
  279. if isVIP {
  280. fee *= feeConfig.VIPDiscount
  281. }
  282. return fee
  283. }