vehicle.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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 根据车牌号获取车辆信息
  87. func (s *VehicleService) GetVehicleByPlateNumber(plateNumber, RFIDTag string) (dao.Vehicle, error) {
  88. var vehicle dao.Vehicle
  89. result := global.GVA_DB.Where("plate_number = ? or rfid_tag = ?", plateNumber, RFIDTag).Preload("VehicleType").First(&vehicle)
  90. if result.RowsAffected == 0 {
  91. return dao.Vehicle{}, errors.New("车辆不存在")
  92. }
  93. return vehicle, nil
  94. }
  95. func (s *VehicleService) QueryAllVehicles() ([]dao.Vehicle, error) {
  96. var vehicles []dao.Vehicle
  97. err := global.GVA_DB.Find(&vehicles).Error
  98. if err != nil {
  99. return vehicles, err
  100. }
  101. return vehicles, nil
  102. }
  103. // ListVehicles 获取车辆列表
  104. func (s *VehicleService) ListVehicles(req request.VehicleQuery) (int64, []dao.Vehicle, error) {
  105. var vehicles []dao.Vehicle
  106. var total int64
  107. query := global.GVA_DB.Model(&dao.Vehicle{})
  108. if req.PlateNumber != "" {
  109. query = query.Where("plate_number LIKE ?", "%"+req.PlateNumber+"%")
  110. }
  111. if req.VehicleTypeID > 0 {
  112. query = query.Where("vehicle_type_id = ?", req.VehicleTypeID)
  113. }
  114. if req.OwnerId > 0 {
  115. query = query.Where("owner_id = ?", req.OwnerId)
  116. }
  117. // 计算总数
  118. query.Count(&total)
  119. // 分页查询
  120. offset := (req.Page - 1) * req.PageSize
  121. result := query.Offset(offset).Limit(req.PageSize).Preload("Owner").Preload("VehicleType").Find(&vehicles)
  122. if result.Error != nil {
  123. return 0, nil, result.Error
  124. }
  125. return total, vehicles, nil
  126. }
  127. // DeleteVehicle 删除车辆
  128. func (s *VehicleService) DeleteVehicle(id uint) error {
  129. result := global.GVA_DB.Delete(&dao.Vehicle{}, id)
  130. if result.RowsAffected == 0 {
  131. return errors.New("车辆不存在")
  132. }
  133. return result.Error
  134. }
  135. // VehicleEntry 车辆入场
  136. func (s *VehicleService) VehicleEntry(req request.VehicleEntry) (request.VehicleEntryResponse, error) {
  137. // 检查车辆是否存在
  138. vehicle, err := s.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag)
  139. if err != nil {
  140. // 如果车辆不存在,创建新车辆,自动归类为"临时车"
  141. var tempType dao.VehicleType
  142. if global.GVA_DB.Where("is_system = ?", true).First(&tempType).Error != nil {
  143. return request.VehicleEntryResponse{}, errors.New("系统错误:临时车类型未配置")
  144. }
  145. createReq := request.VehicleCreate{
  146. PlateNumber: req.PlateNumber,
  147. VehicleTypeID: tempType.ID,
  148. OwnerId: 0,
  149. }
  150. err = s.CreateVehicle(createReq)
  151. if err != nil {
  152. return request.VehicleEntryResponse{}, err
  153. }
  154. vehicle, _ = s.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag)
  155. }
  156. // 检查是否有未出场的记录
  157. var existingRecord dao.VehicleRecord
  158. result := global.GVA_DB.Where("(plate_number = ? OR rfid_tag = ?) AND exit_time IS NULL", req.PlateNumber, req.RFIDTag).First(&existingRecord)
  159. if result.RowsAffected > 0 {
  160. return request.VehicleEntryResponse{}, errors.New("车辆已入场,未出场")
  161. }
  162. // 创建入场记录
  163. entryTime := time.Now()
  164. record := dao.VehicleRecord{
  165. PlateNumber: req.PlateNumber,
  166. RFIDTag: req.RFIDTag,
  167. EntryTime: entryTime,
  168. ParkingLotID: req.ParkingLotID,
  169. ParkingSpaceID: req.ParkingSpaceID,
  170. EntryImage: req.EntryImage,
  171. PaymentStatus: "unpaid",
  172. }
  173. err = global.GVA_DB.Create(&record).Error
  174. if err != nil {
  175. return request.VehicleEntryResponse{}, err
  176. }
  177. // 更新车辆最近入场时间
  178. vehicle.LastEntryTime = &entryTime
  179. global.GVA_DB.Save(&vehicle)
  180. response := request.VehicleEntryResponse{
  181. Success: true,
  182. Message: "车辆入场成功",
  183. }
  184. response.Data.PlateNumber = req.PlateNumber
  185. response.Data.EntryTime = entryTime.Unix()
  186. response.Data.ParkingLotID = req.ParkingLotID
  187. response.Data.ParkingSpaceID = req.ParkingSpaceID
  188. return response, nil
  189. }
  190. // VehicleExit 车辆出场
  191. func (s *VehicleService) VehicleExit(req request.VehicleExit) (request.VehicleExitResponse, error) {
  192. // 1. 查询未出场记录
  193. var record dao.VehicleRecord
  194. 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 {
  195. return request.VehicleExitResponse{}, errors.New("车辆未入场或已出场")
  196. }
  197. // 2. 获取车辆信息
  198. vehicle, err := s.GetVehicleByPlateNumber(req.PlateNumber, req.RFIDTag)
  199. if err != nil {
  200. return request.VehicleExitResponse{}, err
  201. }
  202. // 3. 计算停留时长 & 费用
  203. exitTime := time.Now()
  204. stayTime := int64(exitTime.Sub(record.EntryTime).Minutes())
  205. if stayTime < 0 {
  206. stayTime = 0
  207. }
  208. fee := s.calculateFee(&vehicle, stayTime)
  209. // 4. 更新出场记录
  210. record.ExitTime = &exitTime
  211. record.StayTime = stayTime
  212. record.Fee = fee
  213. record.ExitImage = req.ExitImage
  214. record.PaymentStatus = "paid"
  215. if err := global.GVA_DB.Save(&record).Error; err != nil {
  216. return request.VehicleExitResponse{}, err
  217. }
  218. // 5. 更新车辆统计信息
  219. vehicle.LastExitTime = &exitTime
  220. vehicle.TotalStayTime += stayTime
  221. vehicle.TotalFee += fee
  222. global.GVA_DB.Save(&vehicle)
  223. // 6. 完全匹配你的匿名结构体返回(正确写法)
  224. var response request.VehicleExitResponse
  225. response.Success = true
  226. response.Message = "车辆出场成功"
  227. response.Data.PlateNumber = record.PlateNumber
  228. response.Data.EntryTime = record.EntryTime.Unix()
  229. response.Data.ExitTime = exitTime.Unix()
  230. response.Data.StayTime = stayTime
  231. response.Data.Fee = fee
  232. response.Data.PaymentStatus = record.PaymentStatus
  233. return response, nil
  234. }
  235. // calculateFee 计算停车费用
  236. func (s *VehicleService) calculateFee(vehicle *dao.Vehicle, stayTime int64) float64 {
  237. // 1. 根据车辆类型ID获取收费配置(唯一)
  238. var feeConfig dao.FeeConfig
  239. if err := global.GVA_DB.Where("vehicle_type_id = ?", vehicle.VehicleTypeID).First(&feeConfig).Error; err != nil {
  240. // 无配置 → 默认计费
  241. return float64(stayTime) * 0.1
  242. }
  243. // 2. 判断VIP有效性
  244. isVIP := false
  245. if vehicle.OwnerId != nil {
  246. var owner dao.Owner
  247. if err := global.GVA_DB.First(&owner, *vehicle.OwnerId).Error; err == nil {
  248. isVIP = owner.IsVip && time.Now().Before(owner.VipExpireTime)
  249. }
  250. }
  251. // 3. VIP 免费
  252. if isVIP && feeConfig.IsVIPFree {
  253. return 0
  254. }
  255. // ===================== 修复这里 =====================
  256. // 4. 在免费时长内 → 直接 0 元
  257. if stayTime <= int64(feeConfig.StartTime) {
  258. return 0
  259. }
  260. // 5. 超过免费时长 → 开始计费
  261. extraTime := stayTime - int64(feeConfig.StartTime)
  262. extraUnits := extraTime / int64(feeConfig.UnitTime)
  263. if extraTime%int64(feeConfig.UnitTime) > 0 {
  264. extraUnits++
  265. }
  266. // 起步费 + 超时费用
  267. fee := feeConfig.StartFee + float64(extraUnits)*feeConfig.UnitFee
  268. // 6. 每日封顶
  269. if feeConfig.DailyMaxFee > 0 && fee > feeConfig.DailyMaxFee {
  270. fee = feeConfig.DailyMaxFee
  271. }
  272. // 7. VIP 折扣
  273. if isVIP {
  274. fee *= feeConfig.VIPDiscount
  275. }
  276. return fee
  277. }