Parcourir la source

车辆 车主

xu il y a 4 mois
Parent
commit
236c3c60b1
48 fichiers modifiés avec 2713 ajouts et 0 suppressions
  1. 11 0
      server/api/v1/owner/enter.go
  2. 120 0
      server/api/v1/owner/owner.go
  3. 120 0
      server/api/v1/parking/booth.go
  4. 120 0
      server/api/v1/parking/channel.go
  5. 148 0
      server/api/v1/parking/device.go
  6. 30 0
      server/api/v1/parking/enter.go
  7. 120 0
      server/api/v1/parking/parking_lot.go
  8. 21 0
      server/api/v1/vehicle/enter.go
  9. 103 0
      server/api/v1/vehicle/fee_config.go
  10. 154 0
      server/api/v1/vehicle/vehicle.go
  11. 81 0
      server/api/v1/vehicle/vehicle_record.go
  12. 15 0
      server/dao/booth.go
  13. 16 0
      server/dao/channel.go
  14. 18 0
      server/dao/device.go
  15. 20 0
      server/dao/fee_config.go
  16. 16 0
      server/dao/parking_lot.go
  17. 24 0
      server/dao/vehicle_record.go
  18. 28 0
      server/model/owner/request/owner.go
  19. 12 0
      server/model/owner/response/owner.go
  20. 23 0
      server/model/parking/request/booth.go
  21. 26 0
      server/model/parking/request/channel.go
  22. 29 0
      server/model/parking/request/device.go
  23. 23 0
      server/model/parking/request/parking_lot.go
  24. 12 0
      server/model/parking/response/booth.go
  25. 12 0
      server/model/parking/response/channel.go
  26. 12 0
      server/model/parking/response/device.go
  27. 12 0
      server/model/parking/response/parking_lot.go
  28. 32 0
      server/model/vehicle/request/fee_config.go
  29. 62 0
      server/model/vehicle/request/vehicle.go
  30. 30 0
      server/model/vehicle/response/vehicle.go
  31. 6 0
      server/router/owner/enter.go
  32. 20 0
      server/router/owner/owner.go
  33. 6 0
      server/router/parking/enter.go
  34. 58 0
      server/router/parking/parking.go
  35. 6 0
      server/router/vehicle/enter.go
  36. 37 0
      server/router/vehicle/vehicle.go
  37. BIN
      server/server
  38. 5 0
      server/service/owner/enter.go
  39. 129 0
      server/service/owner/owner.go
  40. 113 0
      server/service/parking/booth.go
  41. 119 0
      server/service/parking/channel.go
  42. 138 0
      server/service/parking/device.go
  43. 8 0
      server/service/parking/enter.go
  44. 138 0
      server/service/parking/parking_lot.go
  45. 7 0
      server/service/vehicle/enter.go
  46. 119 0
      server/service/vehicle/fee_config.go
  47. 292 0
      server/service/vehicle/vehicle.go
  48. 62 0
      server/service/vehicle/vehicle_record.go

+ 11 - 0
server/api/v1/owner/enter.go

@@ -0,0 +1,11 @@
+package owner
+
+// 车主相关API
+var ApiGroup = []interface{}{
+	CreateOwner,
+	UpdateOwner,
+	GetOwnerByID,
+	GetOwnerByPhone,
+	ListOwners,
+	DeleteOwner,
+}

+ 120 - 0
server/api/v1/owner/owner.go

@@ -0,0 +1,120 @@
+package owner
+
+import (
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"server/model/common/response"
+	"server/model/owner/request"
+	ownerResponse "server/model/owner/response"
+	"server/service/owner"
+)
+
+// CreateOwner 创建车主信息
+func CreateOwner(c *gin.Context) {
+	var req request.OwnerCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := owner.OwnerService.CreateOwner(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+// UpdateOwner 更新车主信息
+func UpdateOwner(c *gin.Context) {
+	var req request.OwnerUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := owner.OwnerService.UpdateOwner(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+// GetOwnerByID 根据ID获取车主信息
+func GetOwnerByID(c *gin.Context) {
+	id := c.Query("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	ownerInfo, err := owner.OwnerService.GetOwnerByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(ownerInfo, c)
+}
+
+// GetOwnerByPhone 根据手机号获取车主信息
+func GetOwnerByPhone(c *gin.Context) {
+	phone := c.Query("phone")
+	if phone == "" {
+		response.FailWithMessage("手机号不能为空", c)
+		return
+	}
+
+	ownerInfo, err := owner.OwnerService.GetOwnerByPhone(phone)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(ownerInfo, c)
+}
+
+// ListOwners 获取车主列表
+func ListOwners(c *gin.Context) {
+	var req request.OwnerQuery
+	if err := c.ShouldBindQuery(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	total, list, err := owner.OwnerService.ListOwners(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(ownerResponse.OwnerList{
+		Total: total,
+		List:  list,
+	}, c)
+}
+
+// DeleteOwner 删除车主
+func DeleteOwner(c *gin.Context) {
+	id := c.Query("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	err = owner.OwnerService.DeleteOwner(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}

+ 120 - 0
server/api/v1/parking/booth.go

@@ -0,0 +1,120 @@
+package parking
+
+import (
+	"fmt"
+	"server/model/common/response"
+	"server/model/parking/request"
+	parkingResponse "server/model/parking/response"
+	"server/service/parking"
+
+	"github.com/gin-gonic/gin"
+)
+
+// CreateBooth 创建岗亭
+func CreateBooth(c *gin.Context) {
+	var req request.BoothCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parking.BoothService.CreateBooth(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+// UpdateBooth 更新岗亭
+func UpdateBooth(c *gin.Context) {
+	var req request.BoothUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parking.BoothService.UpdateBooth(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+// GetBoothByID 根据ID获取岗亭
+func GetBoothByID(c *gin.Context) {
+	id := c.Param("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	booth, err := parking.BoothService.GetBoothByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(booth, c)
+}
+
+// GetBoothByCode 根据编码获取岗亭
+func GetBoothByCode(c *gin.Context) {
+	code := c.Query("code")
+	if code == "" {
+		response.FailWithMessage("岗亭编码不能为空", c)
+		return
+	}
+
+	booth, err := parking.BoothService.GetBoothByCode(code)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(booth, c)
+}
+
+// ListBooths 获取岗亭列表
+func ListBooths(c *gin.Context) {
+	var req request.BoothQuery
+	if err := c.ShouldBindQuery(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	total, list, err := parking.BoothService.ListBooths(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(parkingResponse.BoothList{
+		Total: total,
+		List:  list,
+	}, c)
+}
+
+// DeleteBooth 删除岗亭
+func DeleteBooth(c *gin.Context) {
+	id := c.Param("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	err = parking.BoothService.DeleteBooth(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}

+ 120 - 0
server/api/v1/parking/channel.go

@@ -0,0 +1,120 @@
+package parking
+
+import (
+	"fmt"
+	"server/model/common/response"
+	"server/model/parking/request"
+	parkingResponse "server/model/parking/response"
+	"server/service/parking"
+
+	"github.com/gin-gonic/gin"
+)
+
+// CreateChannel 创建通道
+func CreateChannel(c *gin.Context) {
+	var req request.ChannelCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parking.ChannelService.CreateChannel(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+// UpdateChannel 更新通道
+func UpdateChannel(c *gin.Context) {
+	var req request.ChannelUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parking.ChannelService.UpdateChannel(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+// GetChannelByID 根据ID获取通道
+func GetChannelByID(c *gin.Context) {
+	id := c.Param("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	channel, err := parking.ChannelService.GetChannelByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(channel, c)
+}
+
+// GetChannelByCode 根据编码获取通道
+func GetChannelByCode(c *gin.Context) {
+	code := c.Query("code")
+	if code == "" {
+		response.FailWithMessage("通道编码不能为空", c)
+		return
+	}
+
+	channel, err := parking.ChannelService.GetChannelByCode(code)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(channel, c)
+}
+
+// ListChannels 获取通道列表
+func ListChannels(c *gin.Context) {
+	var req request.ChannelQuery
+	if err := c.ShouldBindQuery(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	total, list, err := parking.ChannelService.ListChannels(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(parkingResponse.ChannelList{
+		Total: total,
+		List:  list,
+	}, c)
+}
+
+// DeleteChannel 删除通道
+func DeleteChannel(c *gin.Context) {
+	id := c.Param("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	err = parking.ChannelService.DeleteChannel(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}

+ 148 - 0
server/api/v1/parking/device.go

@@ -0,0 +1,148 @@
+package parking
+
+import (
+	"fmt"
+	"server/model/common/response"
+	"server/model/parking/request"
+	parkingResponse "server/model/parking/response"
+	"server/service/parking"
+
+	"github.com/gin-gonic/gin"
+)
+
+// CreateDevice 创建设备
+func CreateDevice(c *gin.Context) {
+	var req request.DeviceCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parking.DeviceService.CreateDevice(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+// UpdateDevice 更新设备
+func UpdateDevice(c *gin.Context) {
+	var req request.DeviceUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parking.DeviceService.UpdateDevice(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+// GetDeviceByID 根据ID获取设备
+func GetDeviceByID(c *gin.Context) {
+	id := c.Param("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	device, err := parking.DeviceService.GetDeviceByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(device, c)
+}
+
+// GetDeviceByCode 根据编码获取设备
+func GetDeviceByCode(c *gin.Context) {
+	code := c.Query("code")
+	if code == "" {
+		response.FailWithMessage("设备编码不能为空", c)
+		return
+	}
+
+	device, err := parking.DeviceService.GetDeviceByCode(code)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(device, c)
+}
+
+// ListDevices 获取设备列表
+func ListDevices(c *gin.Context) {
+	var req request.DeviceQuery
+	if err := c.ShouldBindQuery(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	total, list, err := parking.DeviceService.ListDevices(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(parkingResponse.DeviceList{
+		Total: total,
+		List:  list,
+	}, c)
+}
+
+// DeleteDevice 删除设备
+func DeleteDevice(c *gin.Context) {
+	id := c.Param("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	err = parking.DeviceService.DeleteDevice(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}
+
+// UpdateDeviceStatus 更新设备状态
+func UpdateDeviceStatus(c *gin.Context) {
+	id := c.Param("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	var req struct {
+		Status string `json:"status" binding:"required"`
+	}
+
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err = parking.DeviceService.UpdateDeviceStatus(uintID, req.Status)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("状态更新成功", c)
+}

+ 30 - 0
server/api/v1/parking/enter.go

@@ -0,0 +1,30 @@
+package parking
+
+// 停车相关API
+var ApiGroup = []interface{}{
+	CreateParkingLot,
+	UpdateParkingLot,
+	GetParkingLotByID,
+	GetParkingLotByCode,
+	ListParkingLots,
+	DeleteParkingLot,
+	CreateBooth,
+	UpdateBooth,
+	GetBoothByID,
+	GetBoothByCode,
+	ListBooths,
+	DeleteBooth,
+	CreateChannel,
+	UpdateChannel,
+	GetChannelByID,
+	GetChannelByCode,
+	ListChannels,
+	DeleteChannel,
+	CreateDevice,
+	UpdateDevice,
+	GetDeviceByID,
+	GetDeviceByCode,
+	ListDevices,
+	DeleteDevice,
+	UpdateDeviceStatus,
+}

+ 120 - 0
server/api/v1/parking/parking_lot.go

@@ -0,0 +1,120 @@
+package parking
+
+import (
+	"fmt"
+	"server/model/common/response"
+	"server/model/parking/request"
+	parkingResponse "server/model/parking/response"
+	"server/service/parking"
+
+	"github.com/gin-gonic/gin"
+)
+
+// CreateParkingLot 创建停车区域
+func CreateParkingLot(c *gin.Context) {
+	var req request.ParkingLotCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parking.ParkingLotService.CreateParkingLot(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+// UpdateParkingLot 更新停车区域
+func UpdateParkingLot(c *gin.Context) {
+	var req request.ParkingLotUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parking.ParkingLotService.UpdateParkingLot(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+// GetParkingLotByID 根据ID获取停车区域
+func GetParkingLotByID(c *gin.Context) {
+	id := c.Param("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	lot, err := parking.ParkingLotService.GetParkingLotByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(lot, c)
+}
+
+// GetParkingLotByCode 根据编码获取停车区域
+func GetParkingLotByCode(c *gin.Context) {
+	code := c.Query("code")
+	if code == "" {
+		response.FailWithMessage("区域编码不能为空", c)
+		return
+	}
+
+	lot, err := parking.ParkingLotService.GetParkingLotByCode(code)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(lot, c)
+}
+
+// ListParkingLots 获取停车区域列表
+func ListParkingLots(c *gin.Context) {
+	var req request.ParkingLotQuery
+	if err := c.ShouldBindQuery(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	total, list, err := parking.ParkingLotService.ListParkingLots(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(parkingResponse.ParkingLotList{
+		Total: total,
+		List:  list,
+	}, c)
+}
+
+// DeleteParkingLot 删除停车区域
+func DeleteParkingLot(c *gin.Context) {
+	id := c.Param("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	err = parking.ParkingLotService.DeleteParkingLot(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}

+ 21 - 0
server/api/v1/vehicle/enter.go

@@ -0,0 +1,21 @@
+package vehicle
+
+// 车辆相关API
+var ApiGroup = []interface{}{
+	CreateVehicle,
+	UpdateVehicle,
+	GetVehicleByID,
+	GetVehicleByPlateNumber,
+	ListVehicles,
+	DeleteVehicle,
+	VehicleEntry,
+	VehicleExit,
+	CreateFeeConfig,
+	UpdateFeeConfig,
+	GetFeeConfigByID,
+	ListFeeConfigs,
+	DeleteFeeConfig,
+	GetVehicleRecordByID,
+	ListVehicleRecordsByPlateNumber,
+	ListVehicleRecords,
+}

+ 103 - 0
server/api/v1/vehicle/fee_config.go

@@ -0,0 +1,103 @@
+package vehicle
+
+import (
+	"fmt"
+	"server/model/common/response"
+	"server/model/vehicle/request"
+	vehicleResponse "server/model/vehicle/response"
+	"server/service/vehicle"
+
+	"github.com/gin-gonic/gin"
+)
+
+// CreateFeeConfig 创建收费配置
+func CreateFeeConfig(c *gin.Context) {
+	var req request.FeeConfigCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := vehicle.FeeConfigService.CreateFeeConfig(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+// UpdateFeeConfig 更新收费配置
+func UpdateFeeConfig(c *gin.Context) {
+	var req request.FeeConfigUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := vehicle.FeeConfigService.UpdateFeeConfig(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+// GetFeeConfigByID 根据ID获取收费配置
+func GetFeeConfigByID(c *gin.Context) {
+	id := c.Query("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	config, err := vehicle.FeeConfigService.GetFeeConfigByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(config, c)
+}
+
+// ListFeeConfigs 获取收费配置列表
+func ListFeeConfigs(c *gin.Context) {
+	var req request.FeeConfigQuery
+	if err := c.ShouldBindQuery(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	total, list, err := vehicle.FeeConfigService.ListFeeConfigs(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(vehicleResponse.FeeConfigList{
+		Total: total,
+		List:  list,
+	}, c)
+}
+
+// DeleteFeeConfig 删除收费配置
+func DeleteFeeConfig(c *gin.Context) {
+	id := c.Param("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	err = vehicle.FeeConfigService.DeleteFeeConfig(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}

+ 154 - 0
server/api/v1/vehicle/vehicle.go

@@ -0,0 +1,154 @@
+package vehicle
+
+import (
+	"fmt"
+	"server/model/common/response"
+	"server/model/vehicle/request"
+	vehicleResponse "server/model/vehicle/response"
+	"server/service/vehicle"
+
+	"github.com/gin-gonic/gin"
+)
+
+// CreateVehicle 创建车辆信息
+func CreateVehicle(c *gin.Context) {
+	var req request.VehicleCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := vehicle.VehicleService.CreateVehicle(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+// UpdateVehicle 更新车辆信息
+func UpdateVehicle(c *gin.Context) {
+	var req request.VehicleUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := vehicle.VehicleService.UpdateVehicle(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+// GetVehicleByID 根据ID获取车辆信息
+func GetVehicleByID(c *gin.Context) {
+	id := c.Query("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	vehicleInfo, err := vehicle.VehicleService.GetVehicleByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(vehicleInfo, c)
+}
+
+// GetVehicleByPlateNumber 根据车牌号获取车辆信息
+func GetVehicleByPlateNumber(c *gin.Context) {
+	plateNumber := c.Query("plate_number")
+	if plateNumber == "" {
+		response.FailWithMessage("车牌号不能为空", c)
+		return
+	}
+
+	vehicleInfo, err := vehicle.VehicleService.GetVehicleByPlateNumber(plateNumber)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(vehicleInfo, c)
+}
+
+// ListVehicles 获取车辆列表
+func ListVehicles(c *gin.Context) {
+	var req request.VehicleQuery
+	if err := c.ShouldBindQuery(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	total, list, err := vehicle.VehicleService.ListVehicles(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(vehicleResponse.VehicleList{
+		Total: total,
+		List:  list,
+	}, c)
+}
+
+// DeleteVehicle 删除车辆
+func DeleteVehicle(c *gin.Context) {
+	id := c.Query("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	err = vehicle.VehicleService.DeleteVehicle(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}
+
+// VehicleEntry 车辆入场
+func VehicleEntry(c *gin.Context) {
+	var req request.VehicleEntry
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	resp, err := vehicle.VehicleService.VehicleEntry(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(resp, c)
+}
+
+// VehicleExit 车辆出场
+func VehicleExit(c *gin.Context) {
+	var req request.VehicleExit
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	resp, err := vehicle.VehicleService.VehicleExit(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(resp, c)
+}

+ 81 - 0
server/api/v1/vehicle/vehicle_record.go

@@ -0,0 +1,81 @@
+package vehicle
+
+import (
+	"fmt"
+	"server/model/common/response"
+	vehicleResponse "server/model/vehicle/response"
+	"server/service/vehicle"
+
+	"github.com/gin-gonic/gin"
+)
+
+// GetVehicleRecordByID 根据ID获取车辆记录
+func GetVehicleRecordByID(c *gin.Context) {
+	id := c.Query("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	record, err := vehicle.VehicleRecordService.GetVehicleRecordByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(record, c)
+}
+
+// ListVehicleRecordsByPlateNumber 根据车牌号获取车辆记录列表
+func ListVehicleRecordsByPlateNumber(c *gin.Context) {
+	plateNumber := c.Query("plate_number")
+	if plateNumber == "" {
+		response.FailWithMessage("车牌号不能为空", c)
+		return
+	}
+
+	page := 1
+	pageSize := 10
+	if p := c.Query("page"); p != "" {
+		fmt.Sscanf(p, "%d", &page)
+	}
+	if ps := c.Query("page_size"); ps != "" {
+		fmt.Sscanf(ps, "%d", &pageSize)
+	}
+
+	total, list, err := vehicle.VehicleRecordService.ListVehicleRecordsByPlateNumber(plateNumber, page, pageSize)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(vehicleResponse.VehicleRecordList{
+		Total: total,
+		List:  list,
+	}, c)
+}
+
+// ListVehicleRecords 获取所有车辆记录列表
+func ListVehicleRecords(c *gin.Context) {
+	page := 1
+	pageSize := 10
+	if p := c.Query("page"); p != "" {
+		fmt.Sscanf(p, "%d", &page)
+	}
+	if ps := c.Query("page_size"); ps != "" {
+		fmt.Sscanf(ps, "%d", &pageSize)
+	}
+
+	total, list, err := vehicle.VehicleRecordService.ListVehicleRecords(page, pageSize)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(vehicleResponse.VehicleRecordList{
+		Total: total,
+		List:  list,
+	}, c)
+}

+ 15 - 0
server/dao/booth.go

@@ -0,0 +1,15 @@
+package dao
+
+import "server/global"
+
+type Booth struct {
+	global.GVA_MODEL
+	BoothCode   string `gorm:"size:50;not null;uniqueIndex" json:"booth_code"` // 岗亭编码
+	BoothName   string `gorm:"size:100;not null" json:"booth_name"`            // 岗亭名称
+	IPAddress   string `gorm:"size:50" json:"ip_address"`                      // IP地址
+	Description string `gorm:"size:200" json:"description"`                    // 备注
+}
+
+func (Booth) TableName() string {
+	return "booth"
+}

+ 16 - 0
server/dao/channel.go

@@ -0,0 +1,16 @@
+package dao
+
+import "server/global"
+
+type Channel struct {
+	global.GVA_MODEL
+	ChannelCode    string `gorm:"size:50;not null;uniqueIndex" json:"channel_code"` // 通道编码
+	ChannelName    string `gorm:"size:100;not null" json:"channel_name"`            // 通道名称
+	Direction      string `gorm:"size:20;not null" json:"direction"`                // 进出方向
+	AllowTemporary bool   `gorm:"not null" json:"allow_temporary"`                  // 是否允许临时车进出
+	Description    string `gorm:"size:200" json:"description"`                      // 备注
+}
+
+func (Channel) TableName() string {
+	return "channel"
+}

+ 18 - 0
server/dao/device.go

@@ -0,0 +1,18 @@
+package dao
+
+import "server/global"
+
+type Device struct {
+	global.GVA_MODEL
+	DeviceCode  string `gorm:"size:50;not null;uniqueIndex" json:"device_code"` // 设备编码
+	DeviceName  string `gorm:"size:100;not null" json:"device_name"`            // 设备名称
+	DeviceType  string `gorm:"size:50;not null" json:"device_type"`             // 设备类别
+	IPAddress   string `gorm:"size:50" json:"ip_address"`                       // IP地址
+	Port        int    `json:"port"`                                            // 端口
+	Status      string `gorm:"size:20;default:'offline'" json:"status"`         // 设备状态
+	Description string `gorm:"size:200" json:"description"`                     // 备注
+}
+
+func (Device) TableName() string {
+	return "device"
+}

+ 20 - 0
server/dao/fee_config.go

@@ -0,0 +1,20 @@
+package dao
+
+import "server/global"
+
+type FeeConfig struct {
+	global.GVA_MODEL
+	VehicleType string  `gorm:"size:20;not null;uniqueIndex" json:"vehicle_type"` // 车辆类型
+	StartTime   int     `json:"start_time"`                                       // 起始时间(分钟)
+	StartFee    float64 `json:"start_fee"`                                        // 起始费用
+	UnitTime    int     `json:"unit_time"`                                        // 单位时间(分钟)
+	UnitFee     float64 `json:"unit_fee"`                                         // 单位费用
+	DailyMaxFee float64 `json:"daily_max_fee"`                                    // 每日最高费用
+	IsVIPFree   bool    `gorm:"default:false" json:"is_vip_free"`                 // VIP是否免费
+	VIPDiscount float64 `gorm:"default:1.0" json:"vip_discount"`                  // VIP折扣
+	Description string  `gorm:"size:200" json:"description"`                      // 描述
+}
+
+func (FeeConfig) TableName() string {
+	return "fee_config"
+}

+ 16 - 0
server/dao/parking_lot.go

@@ -0,0 +1,16 @@
+package dao
+
+import "server/global"
+
+type ParkingLot struct {
+	global.GVA_MODEL
+	LotCode     string `gorm:"size:50;not null;uniqueIndex" json:"lot_code"` // 区域编码
+	LotName     string `gorm:"size:100;not null" json:"lot_name"`            // 区域名称
+	Capacity    int    `gorm:"not null" json:"capacity"`                     // 车位数量
+	Available   int    `gorm:"not null" json:"available"`                    // 可用车位
+	Description string `gorm:"size:200" json:"description"`                  // 备注
+}
+
+func (ParkingLot) TableName() string {
+	return "parking_lot"
+}

+ 24 - 0
server/dao/vehicle_record.go

@@ -0,0 +1,24 @@
+package dao
+
+import (
+	"server/global"
+	"time"
+)
+
+type VehicleRecord struct {
+	global.GVA_MODEL
+	PlateNumber    string    `gorm:"size:20;not null;index" json:"plate_number"`     // 车牌号
+	EntryTime      time.Time `json:"entry_time"`                                     // 入场时间
+	ExitTime       time.Time `json:"exit_time"`                                      // 出场时间
+	StayTime       int64     `json:"stay_time"`                                      // 停留时间(分钟)
+	Fee            float64   `json:"fee"`                                            // 费用
+	PaymentStatus  string    `gorm:"size:20;default:'unpaid'" json:"payment_status"` // 支付状态
+	EntryImage     string    `gorm:"size:255" json:"entry_image"`                    // 入场图片
+	ExitImage      string    `gorm:"size:255" json:"exit_image"`                     // 出场图片
+	ParkingLotID   uint      `json:"parking_lot_id"`                                 // 停车场ID
+	ParkingSpaceID uint      `json:"parking_space_id"`                               // 停车位ID
+}
+
+func (VehicleRecord) TableName() string {
+	return "vehicle_record"
+}

+ 28 - 0
server/model/owner/request/owner.go

@@ -0,0 +1,28 @@
+package request
+
+import "time"
+
+type OwnerCreate struct {
+	OwnerName     string    `json:"owner_name" binding:"required"`
+	OwnerSurname  string    `json:"owner_surname" binding:"required"`
+	OwnerPhone    string    `json:"owner_phone" binding:"required"`
+	IsVip         bool      `json:"is_vip"`
+	VipExpireTime time.Time `json:"vip_expire_time"`
+}
+
+type OwnerUpdate struct {
+	ID            uint      `json:"id" binding:"required"`
+	OwnerName     string    `json:"owner_name" binding:"required"`
+	OwnerSurname  string    `json:"owner_surname" binding:"required"`
+	OwnerPhone    string    `json:"owner_phone" binding:"required"`
+	IsVip         bool      `json:"is_vip"`
+	VipExpireTime time.Time `json:"vip_expire_time"`
+}
+
+type OwnerQuery struct {
+	OwnerName  string `form:"owner_name"`
+	OwnerPhone string `form:"owner_phone"`
+	IsVip      *bool  `form:"is_vip"`
+	Page       int    `form:"page,default=1"`
+	PageSize   int    `form:"page_size,default=10"`
+}

+ 12 - 0
server/model/owner/response/owner.go

@@ -0,0 +1,12 @@
+package response
+
+import "server/dao"
+
+type Owner struct {
+	dao.Owner
+}
+
+type OwnerList struct {
+	Total int64       `json:"total"`
+	List  []dao.Owner `json:"list"`
+}

+ 23 - 0
server/model/parking/request/booth.go

@@ -0,0 +1,23 @@
+package request
+
+type BoothCreate struct {
+	BoothCode   string `json:"booth_code" binding:"required"`
+	BoothName   string `json:"booth_name" binding:"required"`
+	IPAddress   string `json:"ip_address"`
+	Description string `json:"description"`
+}
+
+type BoothUpdate struct {
+	ID          uint   `json:"id" binding:"required"`
+	BoothCode   string `json:"booth_code" binding:"required"`
+	BoothName   string `json:"booth_name" binding:"required"`
+	IPAddress   string `json:"ip_address"`
+	Description string `json:"description"`
+}
+
+type BoothQuery struct {
+	BoothCode string `form:"booth_code"`
+	BoothName string `form:"booth_name"`
+	Page      int    `form:"page,default=1"`
+	PageSize  int    `form:"page_size,default=10"`
+}

+ 26 - 0
server/model/parking/request/channel.go

@@ -0,0 +1,26 @@
+package request
+
+type ChannelCreate struct {
+	ChannelCode    string `json:"channel_code" binding:"required"`
+	ChannelName    string `json:"channel_name" binding:"required"`
+	Direction      string `json:"direction" binding:"required"`
+	AllowTemporary bool   `json:"allow_temporary"`
+	Description    string `json:"description"`
+}
+
+type ChannelUpdate struct {
+	ID             uint   `json:"id" binding:"required"`
+	ChannelCode    string `json:"channel_code" binding:"required"`
+	ChannelName    string `json:"channel_name" binding:"required"`
+	Direction      string `json:"direction" binding:"required"`
+	AllowTemporary bool   `json:"allow_temporary"`
+	Description    string `json:"description"`
+}
+
+type ChannelQuery struct {
+	ChannelCode string `form:"channel_code"`
+	ChannelName string `form:"channel_name"`
+	Direction   string `form:"direction"`
+	Page        int    `form:"page,default=1"`
+	PageSize    int    `form:"page_size,default=10"`
+}

+ 29 - 0
server/model/parking/request/device.go

@@ -0,0 +1,29 @@
+package request
+
+type DeviceCreate struct {
+	DeviceCode  string `json:"device_code" binding:"required"`
+	DeviceName  string `json:"device_name" binding:"required"`
+	DeviceType  string `json:"device_type" binding:"required"`
+	IPAddress   string `json:"ip_address"`
+	Port        int    `json:"port"`
+	Description string `json:"description"`
+}
+
+type DeviceUpdate struct {
+	ID          uint   `json:"id" binding:"required"`
+	DeviceCode  string `json:"device_code" binding:"required"`
+	DeviceName  string `json:"device_name" binding:"required"`
+	DeviceType  string `json:"device_type" binding:"required"`
+	IPAddress   string `json:"ip_address"`
+	Port        int    `json:"port"`
+	Description string `json:"description"`
+}
+
+type DeviceQuery struct {
+	DeviceCode string `form:"device_code"`
+	DeviceName string `form:"device_name"`
+	DeviceType string `form:"device_type"`
+	Status     string `form:"status"`
+	Page       int    `form:"page,default=1"`
+	PageSize   int    `form:"page_size,default=10"`
+}

+ 23 - 0
server/model/parking/request/parking_lot.go

@@ -0,0 +1,23 @@
+package request
+
+type ParkingLotCreate struct {
+	LotCode     string `json:"lot_code" binding:"required"`
+	LotName     string `json:"lot_name" binding:"required"`
+	Capacity    int    `json:"capacity" binding:"required,min=0"`
+	Description string `json:"description"`
+}
+
+type ParkingLotUpdate struct {
+	ID          uint   `json:"id" binding:"required"`
+	LotCode     string `json:"lot_code" binding:"required"`
+	LotName     string `json:"lot_name" binding:"required"`
+	Capacity    int    `json:"capacity" binding:"required,min=0"`
+	Description string `json:"description"`
+}
+
+type ParkingLotQuery struct {
+	LotCode  string `form:"lot_code"`
+	LotName  string `form:"lot_name"`
+	Page     int    `form:"page,default=1"`
+	PageSize int    `form:"page_size,default=10"`
+}

+ 12 - 0
server/model/parking/response/booth.go

@@ -0,0 +1,12 @@
+package response
+
+import "server/dao"
+
+type Booth struct {
+	dao.Booth
+}
+
+type BoothList struct {
+	Total int64       `json:"total"`
+	List  []dao.Booth `json:"list"`
+}

+ 12 - 0
server/model/parking/response/channel.go

@@ -0,0 +1,12 @@
+package response
+
+import "server/dao"
+
+type Channel struct {
+	dao.Channel
+}
+
+type ChannelList struct {
+	Total int64         `json:"total"`
+	List  []dao.Channel `json:"list"`
+}

+ 12 - 0
server/model/parking/response/device.go

@@ -0,0 +1,12 @@
+package response
+
+import "server/dao"
+
+type Device struct {
+	dao.Device
+}
+
+type DeviceList struct {
+	Total int64        `json:"total"`
+	List  []dao.Device `json:"list"`
+}

+ 12 - 0
server/model/parking/response/parking_lot.go

@@ -0,0 +1,12 @@
+package response
+
+import "server/dao"
+
+type ParkingLot struct {
+	dao.ParkingLot
+}
+
+type ParkingLotList struct {
+	Total int64            `json:"total"`
+	List  []dao.ParkingLot `json:"list"`
+}

+ 32 - 0
server/model/vehicle/request/fee_config.go

@@ -0,0 +1,32 @@
+package request
+
+type FeeConfigCreate struct {
+	VehicleType string  `json:"vehicle_type" binding:"required"`
+	StartTime   int     `json:"start_time" binding:"required,min=0"`
+	StartFee    float64 `json:"start_fee" binding:"required,min=0"`
+	UnitTime    int     `json:"unit_time" binding:"required,min=1"`
+	UnitFee     float64 `json:"unit_fee" binding:"required,min=0"`
+	DailyMaxFee float64 `json:"daily_max_fee" binding:"required,min=0"`
+	IsVIPFree   bool    `json:"is_vip_free"`
+	VIPDiscount float64 `json:"vip_discount" binding:"required,min=0,max=1"`
+	Description string  `json:"description"`
+}
+
+type FeeConfigUpdate struct {
+	ID          uint    `json:"id" binding:"required"`
+	VehicleType string  `json:"vehicle_type" binding:"required"`
+	StartTime   int     `json:"start_time" binding:"required,min=0"`
+	StartFee    float64 `json:"start_fee" binding:"required,min=0"`
+	UnitTime    int     `json:"unit_time" binding:"required,min=1"`
+	UnitFee     float64 `json:"unit_fee" binding:"required,min=0"`
+	DailyMaxFee float64 `json:"daily_max_fee" binding:"required,min=0"`
+	IsVIPFree   bool    `json:"is_vip_free"`
+	VIPDiscount float64 `json:"vip_discount" binding:"required,min=0,max=1"`
+	Description string  `json:"description"`
+}
+
+type FeeConfigQuery struct {
+	VehicleType string `form:"vehicle_type"`
+	Page        int    `form:"page,default=1"`
+	PageSize    int    `form:"page_size,default=10"`
+}

+ 62 - 0
server/model/vehicle/request/vehicle.go

@@ -0,0 +1,62 @@
+package request
+
+type VehicleCreate struct {
+	PlateNumber  string `json:"plate_number" binding:"required"`
+	VehicleType  string `json:"vehicle_type" binding:"required"`
+	VehicleBrand string `json:"vehicle_brand"`
+	VehicleColor string `json:"vehicle_color"`
+	OwnerId      uint   `json:"owner_id"`
+}
+
+type VehicleUpdate struct {
+	ID           uint   `json:"id" binding:"required"`
+	PlateNumber  string `json:"plate_number" binding:"required"`
+	VehicleType  string `json:"vehicle_type" binding:"required"`
+	VehicleBrand string `json:"vehicle_brand"`
+	VehicleColor string `json:"vehicle_color"`
+	OwnerId      uint   `json:"owner_id"`
+}
+
+type VehicleQuery struct {
+	PlateNumber string `form:"plate_number"`
+	VehicleType string `form:"vehicle_type"`
+	OwnerId     uint   `form:"owner_id"`
+	Page        int    `form:"page,default=1"`
+	PageSize    int    `form:"page_size,default=10"`
+}
+
+type VehicleEntry struct {
+	PlateNumber    string `json:"plate_number" binding:"required"`
+	ParkingLotID   uint   `json:"parking_lot_id" binding:"required"`
+	ParkingSpaceID uint   `json:"parking_space_id" binding:"required"`
+	EntryImage     string `json:"entry_image"`
+}
+
+type VehicleExit struct {
+	PlateNumber string `json:"plate_number" binding:"required"`
+	ExitImage   string `json:"exit_image"`
+}
+
+type VehicleEntryResponse struct {
+	Success bool   `json:"success"`
+	Message string `json:"message"`
+	Data    struct {
+		PlateNumber    string `json:"plate_number"`
+		EntryTime      int64  `json:"entry_time"`
+		ParkingLotID   uint   `json:"parking_lot_id"`
+		ParkingSpaceID uint   `json:"parking_space_id"`
+	} `json:"data"`
+}
+
+type VehicleExitResponse struct {
+	Success bool   `json:"success"`
+	Message string `json:"message"`
+	Data    struct {
+		PlateNumber   string  `json:"plate_number"`
+		EntryTime     int64   `json:"entry_time"`
+		ExitTime      int64   `json:"exit_time"`
+		StayTime      int64   `json:"stay_time"`
+		Fee           float64 `json:"fee"`
+		PaymentStatus string  `json:"payment_status"`
+	} `json:"data"`
+}

+ 30 - 0
server/model/vehicle/response/vehicle.go

@@ -0,0 +1,30 @@
+package response
+
+import "server/dao"
+
+type Vehicle struct {
+	dao.Vehicle
+}
+
+type VehicleList struct {
+	Total int64         `json:"total"`
+	List  []dao.Vehicle `json:"list"`
+}
+
+type VehicleRecord struct {
+	dao.VehicleRecord
+}
+
+type VehicleRecordList struct {
+	Total int64               `json:"total"`
+	List  []dao.VehicleRecord `json:"list"`
+}
+
+type FeeConfig struct {
+	dao.FeeConfig
+}
+
+type FeeConfigList struct {
+	Total int64           `json:"total"`
+	List  []dao.FeeConfig `json:"list"`
+}

+ 6 - 0
server/router/owner/enter.go

@@ -0,0 +1,6 @@
+package owner
+
+// 车主相关路由
+var RouterGroup = []interface{}{
+	SetupOwnerRouter,
+}

+ 20 - 0
server/router/owner/owner.go

@@ -0,0 +1,20 @@
+package owner
+
+import (
+	"server/api/v1/owner"
+
+	"github.com/gin-gonic/gin"
+)
+
+// SetupOwnerRouter 车主相关路由
+func SetupOwnerRouter(router *gin.RouterGroup) {
+	ownerGroup := router.Group("/owner")
+	{
+		ownerGroup.POST("/create", owner.CreateOwner)
+		ownerGroup.PUT("/update", owner.UpdateOwner)
+		ownerGroup.GET("/get", owner.GetOwnerByID)
+		ownerGroup.GET("/get-by-phone", owner.GetOwnerByPhone)
+		ownerGroup.GET("/list", owner.ListOwners)
+		ownerGroup.DELETE("/delete", owner.DeleteOwner)
+	}
+}

+ 6 - 0
server/router/parking/enter.go

@@ -0,0 +1,6 @@
+package parking
+
+// 停车相关路由
+var RouterGroup = []interface{}{
+	SetupParkingRouter,
+}

+ 58 - 0
server/router/parking/parking.go

@@ -0,0 +1,58 @@
+package parking
+
+import (
+	"server/api/v1/parking"
+
+	"github.com/gin-gonic/gin"
+)
+
+// SetupParkingRouter 停车相关路由
+func SetupParkingRouter(router *gin.RouterGroup) {
+	parkingGroup := router.Group("/parking")
+	{
+		// 停车区域路由
+		lotGroup := parkingGroup.Group("/lot")
+		{
+			lotGroup.POST("/create", parking.CreateParkingLot)
+			lotGroup.PUT("/update", parking.UpdateParkingLot)
+			lotGroup.GET("/get/:id", parking.GetParkingLotByID)
+			lotGroup.GET("/get-by-code", parking.GetParkingLotByCode)
+			lotGroup.GET("/list", parking.ListParkingLots)
+			lotGroup.DELETE("/delete/:id", parking.DeleteParkingLot)
+		}
+
+		// 岗亭路由
+		boothGroup := parkingGroup.Group("/booth")
+		{
+			boothGroup.POST("/create", parking.CreateBooth)
+			boothGroup.PUT("/update", parking.UpdateBooth)
+			boothGroup.GET("/get/:id", parking.GetBoothByID)
+			boothGroup.GET("/get-by-code", parking.GetBoothByCode)
+			boothGroup.GET("/list", parking.ListBooths)
+			boothGroup.DELETE("/delete/:id", parking.DeleteBooth)
+		}
+
+		// 通道路由
+		channelGroup := parkingGroup.Group("/channel")
+		{
+			channelGroup.POST("/create", parking.CreateChannel)
+			channelGroup.PUT("/update", parking.UpdateChannel)
+			channelGroup.GET("/get/:id", parking.GetChannelByID)
+			channelGroup.GET("/get-by-code", parking.GetChannelByCode)
+			channelGroup.GET("/list", parking.ListChannels)
+			channelGroup.DELETE("/delete/:id", parking.DeleteChannel)
+		}
+
+		// 设备路由
+		deviceGroup := parkingGroup.Group("/device")
+		{
+			deviceGroup.POST("/create", parking.CreateDevice)
+			deviceGroup.PUT("/update", parking.UpdateDevice)
+			deviceGroup.GET("/get/:id", parking.GetDeviceByID)
+			deviceGroup.GET("/get-by-code", parking.GetDeviceByCode)
+			deviceGroup.GET("/list", parking.ListDevices)
+			deviceGroup.DELETE("/delete/:id", parking.DeleteDevice)
+			deviceGroup.PUT("/update-status/:id", parking.UpdateDeviceStatus)
+		}
+	}
+}

+ 6 - 0
server/router/vehicle/enter.go

@@ -0,0 +1,6 @@
+package vehicle
+
+// 车辆相关路由
+var RouterGroup = []interface{}{
+	SetupVehicleRouter,
+}

+ 37 - 0
server/router/vehicle/vehicle.go

@@ -0,0 +1,37 @@
+package vehicle
+
+import (
+	"server/api/v1/vehicle"
+
+	"github.com/gin-gonic/gin"
+)
+
+// SetupVehicleRouter 车辆相关路由
+func SetupVehicleRouter(router *gin.RouterGroup) {
+	vehicleGroup := router.Group("/vehicle")
+	{
+		// 车辆管理
+		vehicleGroup.POST("/create", vehicle.CreateVehicle)
+		vehicleGroup.PUT("/update", vehicle.UpdateVehicle)
+		vehicleGroup.GET("/get", vehicle.GetVehicleByID)
+		vehicleGroup.GET("/get-by-plate", vehicle.GetVehicleByPlateNumber)
+		vehicleGroup.GET("/list", vehicle.ListVehicles)
+		vehicleGroup.DELETE("/delete", vehicle.DeleteVehicle)
+
+		// 车辆出入
+		vehicleGroup.POST("/entry", vehicle.VehicleEntry)
+		vehicleGroup.POST("/exit", vehicle.VehicleExit)
+
+		// 收费配置
+		vehicleGroup.POST("/fee-config/create", vehicle.CreateFeeConfig)
+		vehicleGroup.PUT("/fee-config/update", vehicle.UpdateFeeConfig)
+		vehicleGroup.GET("/fee-config/get", vehicle.GetFeeConfigByID)
+		vehicleGroup.GET("/fee-config/list", vehicle.ListFeeConfigs)
+		vehicleGroup.DELETE("/fee-config/delete", vehicle.DeleteFeeConfig)
+
+		// 车辆记录
+		vehicleGroup.GET("/record/get", vehicle.GetVehicleRecordByID)
+		vehicleGroup.GET("/record/list-by-plate", vehicle.ListVehicleRecordsByPlateNumber)
+		vehicleGroup.GET("/record/list", vehicle.ListVehicleRecords)
+	}
+}

BIN
server/server


+ 5 - 0
server/service/owner/enter.go

@@ -0,0 +1,5 @@
+package owner
+
+var (
+	OwnerService = NewOwnerService()
+)

+ 129 - 0
server/service/owner/owner.go

@@ -0,0 +1,129 @@
+package owner
+
+import (
+	"errors"
+	"server/dao"
+	"server/global"
+	"server/model/owner/request"
+)
+
+type ownerService struct{}
+
+func NewOwnerService() *ownerService {
+	return &ownerService{}
+}
+
+// CreateOwner 创建车主信息
+func (s *ownerService) CreateOwner(req request.OwnerCreate) error {
+	// 检查手机号是否已存在
+	var existingOwner dao.Owner
+	result := global.GVA_DB.Where("owner_phone = ?", req.OwnerPhone).First(&existingOwner)
+	if result.RowsAffected > 0 {
+		return errors.New("手机号已存在")
+	}
+
+	owner := dao.Owner{
+		OwnerName:     req.OwnerName,
+		OwnerSurname:  req.OwnerSurname,
+		OwnerPhone:    req.OwnerPhone,
+		IsVip:         req.IsVip,
+		VipExpireTime: req.VipExpireTime,
+	}
+
+	return global.GVA_DB.Create(&owner).Error
+}
+
+// UpdateOwner 更新车主信息
+func (s *ownerService) UpdateOwner(req request.OwnerUpdate) error {
+	var owner dao.Owner
+	result := global.GVA_DB.First(&owner, req.ID)
+	if result.RowsAffected == 0 {
+		return errors.New("车主不存在")
+	}
+
+	// 检查手机号是否被其他车主使用
+	var existingOwner dao.Owner
+	result = global.GVA_DB.Where("owner_phone = ? AND id != ?", req.OwnerPhone, req.ID).First(&existingOwner)
+	if result.RowsAffected > 0 {
+		return errors.New("手机号已被其他车主使用")
+	}
+
+	owner.OwnerName = req.OwnerName
+	owner.OwnerSurname = req.OwnerSurname
+	owner.OwnerPhone = req.OwnerPhone
+	owner.IsVip = req.IsVip
+	owner.VipExpireTime = req.VipExpireTime
+
+	return global.GVA_DB.Save(&owner).Error
+}
+
+// GetOwnerByID 根据ID获取车主信息
+func (s *ownerService) GetOwnerByID(id uint) (dao.Owner, error) {
+	var owner dao.Owner
+	result := global.GVA_DB.First(&owner, id)
+	if result.RowsAffected == 0 {
+		return dao.Owner{}, errors.New("车主不存在")
+	}
+	return owner, nil
+}
+
+// GetOwnerByPhone 根据手机号获取车主信息
+func (s *ownerService) GetOwnerByPhone(phone string) (dao.Owner, error) {
+	var owner dao.Owner
+	result := global.GVA_DB.Where("owner_phone = ?", phone).First(&owner)
+	if result.RowsAffected == 0 {
+		return dao.Owner{}, errors.New("车主不存在")
+	}
+	return owner, nil
+}
+
+// ListOwners 获取车主列表
+func (s *ownerService) ListOwners(req request.OwnerQuery) (int64, []dao.Owner, error) {
+	var owners []dao.Owner
+	var total int64
+
+	query := global.GVA_DB.Model(&dao.Owner{})
+
+	if req.OwnerName != "" {
+		query = query.Where("owner_name LIKE ?", "%"+req.OwnerName+"%")
+	}
+
+	if req.OwnerPhone != "" {
+		query = query.Where("owner_phone LIKE ?", "%"+req.OwnerPhone+"%")
+	}
+
+	if req.IsVip != nil {
+		query = query.Where("is_vip = ?", *req.IsVip)
+	}
+
+	// 计算总数
+	query.Count(&total)
+
+	// 分页查询
+	offset := (req.Page - 1) * req.PageSize
+	result := query.Offset(offset).Limit(req.PageSize).Find(&owners)
+	if result.Error != nil {
+		return 0, nil, result.Error
+	}
+
+	return total, owners, nil
+}
+
+// DeleteOwner 删除车主
+func (s *ownerService) DeleteOwner(id uint) error {
+	// 检查车主是否存在
+	var owner dao.Owner
+	result := global.GVA_DB.First(&owner, id)
+	if result.RowsAffected == 0 {
+		return errors.New("车主不存在")
+	}
+
+	// 检查车主是否有车辆
+	var vehicleCount int64
+	global.GVA_DB.Model(&dao.Vehicle{}).Where("owner_id = ?", id).Count(&vehicleCount)
+	if vehicleCount > 0 {
+		return errors.New("车主有车辆,无法删除")
+	}
+
+	return global.GVA_DB.Delete(&owner, id).Error
+}

+ 113 - 0
server/service/parking/booth.go

@@ -0,0 +1,113 @@
+package parking
+
+import (
+	"errors"
+	"server/dao"
+	"server/global"
+	"server/model/parking/request"
+)
+
+type boothService struct{}
+
+func NewBoothService() *boothService {
+	return &boothService{}
+}
+
+// CreateBooth 创建岗亭
+func (s *boothService) CreateBooth(req request.BoothCreate) error {
+	// 检查岗亭编码是否已存在
+	var existingBooth dao.Booth
+	result := global.GVA_DB.Where("booth_code = ?", req.BoothCode).First(&existingBooth)
+	if result.RowsAffected > 0 {
+		return errors.New("岗亭编码已存在")
+	}
+
+	booth := dao.Booth{
+		BoothCode:   req.BoothCode,
+		BoothName:   req.BoothName,
+		IPAddress:   req.IPAddress,
+		Description: req.Description,
+	}
+
+	return global.GVA_DB.Create(&booth).Error
+}
+
+// UpdateBooth 更新岗亭
+func (s *boothService) UpdateBooth(req request.BoothUpdate) error {
+	var booth dao.Booth
+	result := global.GVA_DB.First(&booth, req.ID)
+	if result.RowsAffected == 0 {
+		return errors.New("岗亭不存在")
+	}
+
+	// 检查岗亭编码是否被其他岗亭使用
+	var existingBooth dao.Booth
+	result = global.GVA_DB.Where("booth_code = ? AND id != ?", req.BoothCode, req.ID).First(&existingBooth)
+	if result.RowsAffected > 0 {
+		return errors.New("岗亭编码已被其他岗亭使用")
+	}
+
+	booth.BoothCode = req.BoothCode
+	booth.BoothName = req.BoothName
+	booth.IPAddress = req.IPAddress
+	booth.Description = req.Description
+
+	return global.GVA_DB.Save(&booth).Error
+}
+
+// GetBoothByID 根据ID获取岗亭
+func (s *boothService) GetBoothByID(id uint) (dao.Booth, error) {
+	var booth dao.Booth
+	result := global.GVA_DB.First(&booth, id)
+	if result.RowsAffected == 0 {
+		return dao.Booth{}, errors.New("岗亭不存在")
+	}
+	return booth, nil
+}
+
+// GetBoothByCode 根据编码获取岗亭
+func (s *boothService) GetBoothByCode(code string) (dao.Booth, error) {
+	var booth dao.Booth
+	result := global.GVA_DB.Where("booth_code = ?", code).First(&booth)
+	if result.RowsAffected == 0 {
+		return dao.Booth{}, errors.New("岗亭不存在")
+	}
+	return booth, nil
+}
+
+// ListBooths 获取岗亭列表
+func (s *boothService) ListBooths(req request.BoothQuery) (int64, []dao.Booth, error) {
+	var booths []dao.Booth
+	var total int64
+
+	query := global.GVA_DB.Model(&dao.Booth{})
+
+	if req.BoothCode != "" {
+		query = query.Where("booth_code LIKE ?", "%"+req.BoothCode+"%")
+	}
+
+	if req.BoothName != "" {
+		query = query.Where("booth_name LIKE ?", "%"+req.BoothName+"%")
+	}
+
+	// 计算总数
+	query.Count(&total)
+
+	// 分页查询
+	offset := (req.Page - 1) * req.PageSize
+	result := query.Offset(offset).Limit(req.PageSize).Find(&booths)
+	if result.Error != nil {
+		return 0, nil, result.Error
+	}
+
+	return total, booths, nil
+}
+
+// DeleteBooth 删除岗亭
+func (s *boothService) DeleteBooth(id uint) error {
+	result := global.GVA_DB.Delete(&dao.Booth{}, id)
+	if result.RowsAffected == 0 {
+		return errors.New("岗亭不存在")
+	}
+	return result.Error
+}

+ 119 - 0
server/service/parking/channel.go

@@ -0,0 +1,119 @@
+package parking
+
+import (
+	"errors"
+	"server/dao"
+	"server/global"
+	"server/model/parking/request"
+)
+
+type channelService struct{}
+
+func NewChannelService() *channelService {
+	return &channelService{}
+}
+
+// CreateChannel 创建通道
+func (s *channelService) CreateChannel(req request.ChannelCreate) error {
+	// 检查通道编码是否已存在
+	var existingChannel dao.Channel
+	result := global.GVA_DB.Where("channel_code = ?", req.ChannelCode).First(&existingChannel)
+	if result.RowsAffected > 0 {
+		return errors.New("通道编码已存在")
+	}
+
+	channel := dao.Channel{
+		ChannelCode:    req.ChannelCode,
+		ChannelName:    req.ChannelName,
+		Direction:      req.Direction,
+		AllowTemporary: req.AllowTemporary,
+		Description:    req.Description,
+	}
+
+	return global.GVA_DB.Create(&channel).Error
+}
+
+// UpdateChannel 更新通道
+func (s *channelService) UpdateChannel(req request.ChannelUpdate) error {
+	var channel dao.Channel
+	result := global.GVA_DB.First(&channel, req.ID)
+	if result.RowsAffected == 0 {
+		return errors.New("通道不存在")
+	}
+
+	// 检查通道编码是否被其他通道使用
+	var existingChannel dao.Channel
+	result = global.GVA_DB.Where("channel_code = ? AND id != ?", req.ChannelCode, req.ID).First(&existingChannel)
+	if result.RowsAffected > 0 {
+		return errors.New("通道编码已被其他通道使用")
+	}
+
+	channel.ChannelCode = req.ChannelCode
+	channel.ChannelName = req.ChannelName
+	channel.Direction = req.Direction
+	channel.AllowTemporary = req.AllowTemporary
+	channel.Description = req.Description
+
+	return global.GVA_DB.Save(&channel).Error
+}
+
+// GetChannelByID 根据ID获取通道
+func (s *channelService) GetChannelByID(id uint) (dao.Channel, error) {
+	var channel dao.Channel
+	result := global.GVA_DB.First(&channel, id)
+	if result.RowsAffected == 0 {
+		return dao.Channel{}, errors.New("通道不存在")
+	}
+	return channel, nil
+}
+
+// GetChannelByCode 根据编码获取通道
+func (s *channelService) GetChannelByCode(code string) (dao.Channel, error) {
+	var channel dao.Channel
+	result := global.GVA_DB.Where("channel_code = ?", code).First(&channel)
+	if result.RowsAffected == 0 {
+		return dao.Channel{}, errors.New("通道不存在")
+	}
+	return channel, nil
+}
+
+// ListChannels 获取通道列表
+func (s *channelService) ListChannels(req request.ChannelQuery) (int64, []dao.Channel, error) {
+	var channels []dao.Channel
+	var total int64
+
+	query := global.GVA_DB.Model(&dao.Channel{})
+
+	if req.ChannelCode != "" {
+		query = query.Where("channel_code LIKE ?", "%"+req.ChannelCode+"%")
+	}
+
+	if req.ChannelName != "" {
+		query = query.Where("channel_name LIKE ?", "%"+req.ChannelName+"%")
+	}
+
+	if req.Direction != "" {
+		query = query.Where("direction = ?", req.Direction)
+	}
+
+	// 计算总数
+	query.Count(&total)
+
+	// 分页查询
+	offset := (req.Page - 1) * req.PageSize
+	result := query.Offset(offset).Limit(req.PageSize).Find(&channels)
+	if result.Error != nil {
+		return 0, nil, result.Error
+	}
+
+	return total, channels, nil
+}
+
+// DeleteChannel 删除通道
+func (s *channelService) DeleteChannel(id uint) error {
+	result := global.GVA_DB.Delete(&dao.Channel{}, id)
+	if result.RowsAffected == 0 {
+		return errors.New("通道不存在")
+	}
+	return result.Error
+}

+ 138 - 0
server/service/parking/device.go

@@ -0,0 +1,138 @@
+package parking
+
+import (
+	"errors"
+	"server/dao"
+	"server/global"
+	"server/model/parking/request"
+)
+
+type deviceService struct{}
+
+func NewDeviceService() *deviceService {
+	return &deviceService{}
+}
+
+// CreateDevice 创建设备
+func (s *deviceService) CreateDevice(req request.DeviceCreate) error {
+	// 检查设备编码是否已存在
+	var existingDevice dao.Device
+	result := global.GVA_DB.Where("device_code = ?", req.DeviceCode).First(&existingDevice)
+	if result.RowsAffected > 0 {
+		return errors.New("设备编码已存在")
+	}
+
+	device := dao.Device{
+		DeviceCode:  req.DeviceCode,
+		DeviceName:  req.DeviceName,
+		DeviceType:  req.DeviceType,
+		IPAddress:   req.IPAddress,
+		Port:        req.Port,
+		Status:      "offline", // 初始状态为离线
+		Description: req.Description,
+	}
+
+	return global.GVA_DB.Create(&device).Error
+}
+
+// UpdateDevice 更新设备
+func (s *deviceService) UpdateDevice(req request.DeviceUpdate) error {
+	var device dao.Device
+	result := global.GVA_DB.First(&device, req.ID)
+	if result.RowsAffected == 0 {
+		return errors.New("设备不存在")
+	}
+
+	// 检查设备编码是否被其他设备使用
+	var existingDevice dao.Device
+	result = global.GVA_DB.Where("device_code = ? AND id != ?", req.DeviceCode, req.ID).First(&existingDevice)
+	if result.RowsAffected > 0 {
+		return errors.New("设备编码已被其他设备使用")
+	}
+
+	device.DeviceCode = req.DeviceCode
+	device.DeviceName = req.DeviceName
+	device.DeviceType = req.DeviceType
+	device.IPAddress = req.IPAddress
+	device.Port = req.Port
+	device.Description = req.Description
+
+	return global.GVA_DB.Save(&device).Error
+}
+
+// GetDeviceByID 根据ID获取设备
+func (s *deviceService) GetDeviceByID(id uint) (dao.Device, error) {
+	var device dao.Device
+	result := global.GVA_DB.First(&device, id)
+	if result.RowsAffected == 0 {
+		return dao.Device{}, errors.New("设备不存在")
+	}
+	return device, nil
+}
+
+// GetDeviceByCode 根据编码获取设备
+func (s *deviceService) GetDeviceByCode(code string) (dao.Device, error) {
+	var device dao.Device
+	result := global.GVA_DB.Where("device_code = ?", code).First(&device)
+	if result.RowsAffected == 0 {
+		return dao.Device{}, errors.New("设备不存在")
+	}
+	return device, nil
+}
+
+// ListDevices 获取设备列表
+func (s *deviceService) ListDevices(req request.DeviceQuery) (int64, []dao.Device, error) {
+	var devices []dao.Device
+	var total int64
+
+	query := global.GVA_DB.Model(&dao.Device{})
+
+	if req.DeviceCode != "" {
+		query = query.Where("device_code LIKE ?", "%"+req.DeviceCode+"%")
+	}
+
+	if req.DeviceName != "" {
+		query = query.Where("device_name LIKE ?", "%"+req.DeviceName+"%")
+	}
+
+	if req.DeviceType != "" {
+		query = query.Where("device_type = ?", req.DeviceType)
+	}
+
+	if req.Status != "" {
+		query = query.Where("status = ?", req.Status)
+	}
+
+	// 计算总数
+	query.Count(&total)
+
+	// 分页查询
+	offset := (req.Page - 1) * req.PageSize
+	result := query.Offset(offset).Limit(req.PageSize).Find(&devices)
+	if result.Error != nil {
+		return 0, nil, result.Error
+	}
+
+	return total, devices, nil
+}
+
+// DeleteDevice 删除设备
+func (s *deviceService) DeleteDevice(id uint) error {
+	result := global.GVA_DB.Delete(&dao.Device{}, id)
+	if result.RowsAffected == 0 {
+		return errors.New("设备不存在")
+	}
+	return result.Error
+}
+
+// UpdateDeviceStatus 更新设备状态
+func (s *deviceService) UpdateDeviceStatus(id uint, status string) error {
+	var device dao.Device
+	result := global.GVA_DB.First(&device, id)
+	if result.RowsAffected == 0 {
+		return errors.New("设备不存在")
+	}
+
+	device.Status = status
+	return global.GVA_DB.Save(&device).Error
+}

+ 8 - 0
server/service/parking/enter.go

@@ -0,0 +1,8 @@
+package parking
+
+var (
+	ParkingLotService = NewParkingLotService()
+	BoothService      = NewBoothService()
+	ChannelService    = NewChannelService()
+	DeviceService     = NewDeviceService()
+)

+ 138 - 0
server/service/parking/parking_lot.go

@@ -0,0 +1,138 @@
+package parking
+
+import (
+	"errors"
+	"server/dao"
+	"server/global"
+	"server/model/parking/request"
+)
+
+type parkingLotService struct{}
+
+func NewParkingLotService() *parkingLotService {
+	return &parkingLotService{}
+}
+
+// CreateParkingLot 创建停车区域
+func (s *parkingLotService) CreateParkingLot(req request.ParkingLotCreate) error {
+	// 检查区域编码是否已存在
+	var existingLot dao.ParkingLot
+	result := global.GVA_DB.Where("lot_code = ?", req.LotCode).First(&existingLot)
+	if result.RowsAffected > 0 {
+		return errors.New("区域编码已存在")
+	}
+
+	lot := dao.ParkingLot{
+		LotCode:     req.LotCode,
+		LotName:     req.LotName,
+		Capacity:    req.Capacity,
+		Available:   req.Capacity, // 初始可用车位等于总车位
+		Description: req.Description,
+	}
+
+	return global.GVA_DB.Create(&lot).Error
+}
+
+// UpdateParkingLot 更新停车区域
+func (s *parkingLotService) UpdateParkingLot(req request.ParkingLotUpdate) error {
+	var lot dao.ParkingLot
+	result := global.GVA_DB.First(&lot, req.ID)
+	if result.RowsAffected == 0 {
+		return errors.New("停车区域不存在")
+	}
+
+	// 检查区域编码是否被其他区域使用
+	var existingLot dao.ParkingLot
+	result = global.GVA_DB.Where("lot_code = ? AND id != ?", req.LotCode, req.ID).First(&existingLot)
+	if result.RowsAffected > 0 {
+		return errors.New("区域编码已被其他区域使用")
+	}
+
+	// 计算可用车位的变化
+	capacityDiff := req.Capacity - lot.Capacity
+	lot.LotCode = req.LotCode
+	lot.LotName = req.LotName
+	lot.Capacity = req.Capacity
+	lot.Available += capacityDiff
+	if lot.Available < 0 {
+		lot.Available = 0
+	}
+	lot.Description = req.Description
+
+	return global.GVA_DB.Save(&lot).Error
+}
+
+// GetParkingLotByID 根据ID获取停车区域
+func (s *parkingLotService) GetParkingLotByID(id uint) (dao.ParkingLot, error) {
+	var lot dao.ParkingLot
+	result := global.GVA_DB.First(&lot, id)
+	if result.RowsAffected == 0 {
+		return dao.ParkingLot{}, errors.New("停车区域不存在")
+	}
+	return lot, nil
+}
+
+// GetParkingLotByCode 根据编码获取停车区域
+func (s *parkingLotService) GetParkingLotByCode(code string) (dao.ParkingLot, error) {
+	var lot dao.ParkingLot
+	result := global.GVA_DB.Where("lot_code = ?", code).First(&lot)
+	if result.RowsAffected == 0 {
+		return dao.ParkingLot{}, errors.New("停车区域不存在")
+	}
+	return lot, nil
+}
+
+// ListParkingLots 获取停车区域列表
+func (s *parkingLotService) ListParkingLots(req request.ParkingLotQuery) (int64, []dao.ParkingLot, error) {
+	var lots []dao.ParkingLot
+	var total int64
+
+	query := global.GVA_DB.Model(&dao.ParkingLot{})
+
+	if req.LotCode != "" {
+		query = query.Where("lot_code LIKE ?", "%"+req.LotCode+"%")
+	}
+
+	if req.LotName != "" {
+		query = query.Where("lot_name LIKE ?", "%"+req.LotName+"%")
+	}
+
+	// 计算总数
+	query.Count(&total)
+
+	// 分页查询
+	offset := (req.Page - 1) * req.PageSize
+	result := query.Offset(offset).Limit(req.PageSize).Find(&lots)
+	if result.Error != nil {
+		return 0, nil, result.Error
+	}
+
+	return total, lots, nil
+}
+
+// DeleteParkingLot 删除停车区域
+func (s *parkingLotService) DeleteParkingLot(id uint) error {
+	result := global.GVA_DB.Delete(&dao.ParkingLot{}, id)
+	if result.RowsAffected == 0 {
+		return errors.New("停车区域不存在")
+	}
+	return result.Error
+}
+
+// UpdateParkingLotAvailability 更新停车区域可用车位
+func (s *parkingLotService) UpdateParkingLotAvailability(lotID uint, delta int) error {
+	var lot dao.ParkingLot
+	result := global.GVA_DB.First(&lot, lotID)
+	if result.RowsAffected == 0 {
+		return errors.New("停车区域不存在")
+	}
+
+	lot.Available += delta
+	if lot.Available < 0 {
+		lot.Available = 0
+	} else if lot.Available > lot.Capacity {
+		lot.Available = lot.Capacity
+	}
+
+	return global.GVA_DB.Save(&lot).Error
+}

+ 7 - 0
server/service/vehicle/enter.go

@@ -0,0 +1,7 @@
+package vehicle
+
+var (
+	VehicleService       = NewVehicleService()
+	FeeConfigService     = NewFeeConfigService()
+	VehicleRecordService = NewVehicleRecordService()
+)

+ 119 - 0
server/service/vehicle/fee_config.go

@@ -0,0 +1,119 @@
+package vehicle
+
+import (
+	"errors"
+	"server/dao"
+	"server/global"
+	"server/model/vehicle/request"
+)
+
+type feeConfigService struct{}
+
+func NewFeeConfigService() *feeConfigService {
+	return &feeConfigService{}
+}
+
+// CreateFeeConfig 创建收费配置
+func (s *feeConfigService) CreateFeeConfig(req request.FeeConfigCreate) error {
+	// 检查车辆类型是否已存在
+	var existingConfig dao.FeeConfig
+	result := global.GVA_DB.Where("vehicle_type = ?", req.VehicleType).First(&existingConfig)
+	if result.RowsAffected > 0 {
+		return errors.New("该车辆类型的收费配置已存在")
+	}
+
+	config := dao.FeeConfig{
+		VehicleType: req.VehicleType,
+		StartTime:   req.StartTime,
+		StartFee:    req.StartFee,
+		UnitTime:    req.UnitTime,
+		UnitFee:     req.UnitFee,
+		DailyMaxFee: req.DailyMaxFee,
+		IsVIPFree:   req.IsVIPFree,
+		VIPDiscount: req.VIPDiscount,
+		Description: req.Description,
+	}
+
+	return global.GVA_DB.Create(&config).Error
+}
+
+// UpdateFeeConfig 更新收费配置
+func (s *feeConfigService) UpdateFeeConfig(req request.FeeConfigUpdate) error {
+	var config dao.FeeConfig
+	result := global.GVA_DB.First(&config, req.ID)
+	if result.RowsAffected == 0 {
+		return errors.New("收费配置不存在")
+	}
+
+	// 检查车辆类型是否被其他配置使用
+	var existingConfig dao.FeeConfig
+	result = global.GVA_DB.Where("vehicle_type = ? AND id != ?", req.VehicleType, req.ID).First(&existingConfig)
+	if result.RowsAffected > 0 {
+		return errors.New("该车辆类型的收费配置已存在")
+	}
+
+	config.VehicleType = req.VehicleType
+	config.StartTime = req.StartTime
+	config.StartFee = req.StartFee
+	config.UnitTime = req.UnitTime
+	config.UnitFee = req.UnitFee
+	config.DailyMaxFee = req.DailyMaxFee
+	config.IsVIPFree = req.IsVIPFree
+	config.VIPDiscount = req.VIPDiscount
+	config.Description = req.Description
+
+	return global.GVA_DB.Save(&config).Error
+}
+
+// GetFeeConfigByID 根据ID获取收费配置
+func (s *feeConfigService) GetFeeConfigByID(id uint) (dao.FeeConfig, error) {
+	var config dao.FeeConfig
+	result := global.GVA_DB.First(&config, id)
+	if result.RowsAffected == 0 {
+		return dao.FeeConfig{}, errors.New("收费配置不存在")
+	}
+	return config, nil
+}
+
+// GetFeeConfigByVehicleType 根据车辆类型获取收费配置
+func (s *feeConfigService) GetFeeConfigByVehicleType(vehicleType string) (dao.FeeConfig, error) {
+	var config dao.FeeConfig
+	result := global.GVA_DB.Where("vehicle_type = ?", vehicleType).First(&config)
+	if result.RowsAffected == 0 {
+		return dao.FeeConfig{}, errors.New("收费配置不存在")
+	}
+	return config, nil
+}
+
+// ListFeeConfigs 获取收费配置列表
+func (s *feeConfigService) ListFeeConfigs(req request.FeeConfigQuery) (int64, []dao.FeeConfig, error) {
+	var configs []dao.FeeConfig
+	var total int64
+
+	query := global.GVA_DB.Model(&dao.FeeConfig{})
+
+	if req.VehicleType != "" {
+		query = query.Where("vehicle_type LIKE ?", "%"+req.VehicleType+"%")
+	}
+
+	// 计算总数
+	query.Count(&total)
+
+	// 分页查询
+	offset := (req.Page - 1) * req.PageSize
+	result := query.Offset(offset).Limit(req.PageSize).Find(&configs)
+	if result.Error != nil {
+		return 0, nil, result.Error
+	}
+
+	return total, configs, nil
+}
+
+// DeleteFeeConfig 删除收费配置
+func (s *feeConfigService) DeleteFeeConfig(id uint) error {
+	result := global.GVA_DB.Delete(&dao.FeeConfig{}, id)
+	if result.RowsAffected == 0 {
+		return errors.New("收费配置不存在")
+	}
+	return result.Error
+}

+ 292 - 0
server/service/vehicle/vehicle.go

@@ -0,0 +1,292 @@
+package vehicle
+
+import (
+	"errors"
+	"server/dao"
+	"server/global"
+	"server/model/vehicle/request"
+	"time"
+)
+
+type vehicleService struct{}
+
+func NewVehicleService() *vehicleService {
+	return &vehicleService{}
+}
+
+// CreateVehicle 创建车辆信息
+func (s *vehicleService) CreateVehicle(req request.VehicleCreate) error {
+	// 检查车牌号是否已存在
+	var existingVehicle dao.Vehicle
+	result := global.GVA_DB.Where("plate_number = ?", req.PlateNumber).First(&existingVehicle)
+	if result.RowsAffected > 0 {
+		return errors.New("车牌号已存在")
+	}
+
+	// 处理临时车辆的情况
+	var ownerId *uint
+	if req.OwnerId > 0 {
+		ownerId = &req.OwnerId
+	}
+
+	vehicle := dao.Vehicle{
+		PlateNumber:  req.PlateNumber,
+		VehicleType:  req.VehicleType,
+		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 = ? AND id != ?", req.PlateNumber, req.ID).First(&existingVehicle)
+	if result.RowsAffected > 0 {
+		return errors.New("车牌号已被其他车辆使用")
+	}
+
+	// 处理临时车辆的情况
+	var ownerId *uint
+	if req.OwnerId > 0 {
+		ownerId = &req.OwnerId
+	} else {
+		ownerId = nil
+	}
+
+	vehicle.PlateNumber = req.PlateNumber
+	vehicle.VehicleType = req.VehicleType
+	vehicle.VehicleBrand = req.VehicleBrand
+	vehicle.VehicleColor = req.VehicleColor
+	vehicle.OwnerId = ownerId
+
+	return global.GVA_DB.Save(&vehicle).Error
+}
+
+// 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 根据车牌号获取车辆信息
+func (s *vehicleService) GetVehicleByPlateNumber(plateNumber string) (dao.Vehicle, error) {
+	var vehicle dao.Vehicle
+	result := global.GVA_DB.Where("plate_number = ?", plateNumber).First(&vehicle)
+	if result.RowsAffected == 0 {
+		return dao.Vehicle{}, errors.New("车辆不存在")
+	}
+	return vehicle, 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.VehicleType != "" {
+		query = query.Where("vehicle_type = ?", req.VehicleType)
+	}
+
+	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).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)
+	if err != nil {
+		// 如果车辆不存在,创建新车辆
+		createReq := request.VehicleCreate{
+			PlateNumber: req.PlateNumber,
+			VehicleType: "普通车辆", // 默认类型
+			OwnerId:     0,      // 默认无车主
+		}
+		err = s.CreateVehicle(createReq)
+		if err != nil {
+			return request.VehicleEntryResponse{}, err
+		}
+		vehicle, _ = s.GetVehicleByPlateNumber(req.PlateNumber)
+	}
+
+	// 检查是否有未出场的记录
+	var existingRecord dao.VehicleRecord
+	result := global.GVA_DB.Where("plate_number = ? AND exit_time IS NULL", req.PlateNumber).First(&existingRecord)
+	if result.RowsAffected > 0 {
+		return request.VehicleEntryResponse{}, errors.New("车辆已入场,未出场")
+	}
+
+	// 创建入场记录
+	entryTime := time.Now()
+	record := dao.VehicleRecord{
+		PlateNumber:    req.PlateNumber,
+		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) {
+	// 检查是否有未出场的记录
+	var record dao.VehicleRecord
+	result := global.GVA_DB.Where("plate_number = ? AND exit_time IS NULL", req.PlateNumber).First(&record)
+	if result.RowsAffected == 0 {
+		return request.VehicleExitResponse{}, errors.New("车辆未入场")
+	}
+
+	// 获取车辆信息
+	vehicle, err := s.GetVehicleByPlateNumber(req.PlateNumber)
+	if err != nil {
+		return request.VehicleExitResponse{}, err
+	}
+
+	// 计算费用
+	exitTime := time.Now()
+	stayTime := int64(exitTime.Sub(record.EntryTime).Minutes()) // 转换为分钟
+	fee := s.calculateFee(vehicle, stayTime)
+
+	// 更新出场记录
+	record.ExitTime = exitTime
+	record.StayTime = stayTime
+	record.Fee = fee
+	record.ExitImage = req.ExitImage
+	record.PaymentStatus = "paid" // 假设出场时已支付
+
+	err = global.GVA_DB.Save(&record).Error
+	if err != nil {
+		return request.VehicleExitResponse{}, err
+	}
+
+	// 更新车辆信息
+	vehicle.LastExitTime = exitTime
+	vehicle.TotalStayTime += stayTime
+	vehicle.TotalFee += fee
+	global.GVA_DB.Save(&vehicle)
+
+	response := request.VehicleExitResponse{
+		Success: true,
+		Message: "车辆出场成功",
+	}
+	response.Data.PlateNumber = req.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
+}
+
+// calculateFee 计算停车费用
+func (s *vehicleService) calculateFee(vehicle dao.Vehicle, stayTime int64) float64 {
+	// 获取收费配置
+	var feeConfig dao.FeeConfig
+	result := global.GVA_DB.Where("vehicle_type = ?", vehicle.VehicleType).First(&feeConfig)
+	if result.RowsAffected == 0 {
+		// 使用默认收费标准
+		return float64(stayTime) * 0.1 // 默认0.1元/分钟
+	}
+
+	// 检查是否是VIP且免费
+	isVIP := false
+	if vehicle.OwnerId != nil {
+		var owner dao.Owner
+		ownerResult := global.GVA_DB.First(&owner, *vehicle.OwnerId)
+		if ownerResult.RowsAffected > 0 {
+			// 检查是否是VIP且未过期
+			isVIP = owner.IsVip && time.Now().Before(owner.VipExpireTime)
+		}
+	}
+
+	if isVIP && feeConfig.IsVIPFree {
+		return 0
+	}
+
+	// 计算费用
+	fee := feeConfig.StartFee
+	if stayTime > int64(feeConfig.StartTime) {
+		extraTime := stayTime - int64(feeConfig.StartTime)
+		extraUnits := extraTime / int64(feeConfig.UnitTime)
+		if extraTime%int64(feeConfig.UnitTime) > 0 {
+			extraUnits++
+		}
+		fee += float64(extraUnits) * feeConfig.UnitFee
+	}
+
+	// 检查每日最高费用
+	if fee > feeConfig.DailyMaxFee {
+		fee = feeConfig.DailyMaxFee
+	}
+
+	// 应用VIP折扣
+	if isVIP {
+		fee *= feeConfig.VIPDiscount
+	}
+
+	return fee
+}

+ 62 - 0
server/service/vehicle/vehicle_record.go

@@ -0,0 +1,62 @@
+package vehicle
+
+import (
+	"server/dao"
+	"server/global"
+)
+
+type vehicleRecordService struct{}
+
+func NewVehicleRecordService() *vehicleRecordService {
+	return &vehicleRecordService{}
+}
+
+// GetVehicleRecordByID 根据ID获取车辆记录
+func (s *vehicleRecordService) GetVehicleRecordByID(id uint) (dao.VehicleRecord, error) {
+	var record dao.VehicleRecord
+	result := global.GVA_DB.First(&record, id)
+	if result.RowsAffected == 0 {
+		return dao.VehicleRecord{}, result.Error
+	}
+	return record, nil
+}
+
+// ListVehicleRecordsByPlateNumber 根据车牌号获取车辆记录列表
+func (s *vehicleRecordService) ListVehicleRecordsByPlateNumber(plateNumber string, page, pageSize int) (int64, []dao.VehicleRecord, error) {
+	var records []dao.VehicleRecord
+	var total int64
+
+	query := global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", plateNumber)
+
+	// 计算总数
+	query.Count(&total)
+
+	// 分页查询
+	offset := (page - 1) * pageSize
+	result := query.Order("entry_time DESC").Offset(offset).Limit(pageSize).Find(&records)
+	if result.Error != nil {
+		return 0, nil, result.Error
+	}
+
+	return total, records, nil
+}
+
+// ListVehicleRecords 获取所有车辆记录列表
+func (s *vehicleRecordService) ListVehicleRecords(page, pageSize int) (int64, []dao.VehicleRecord, error) {
+	var records []dao.VehicleRecord
+	var total int64
+
+	query := global.GVA_DB.Model(&dao.VehicleRecord{})
+
+	// 计算总数
+	query.Count(&total)
+
+	// 分页查询
+	offset := (page - 1) * pageSize
+	result := query.Order("entry_time DESC").Offset(offset).Limit(pageSize).Find(&records)
+	if result.Error != nil {
+		return 0, nil, result.Error
+	}
+
+	return total, records, nil
+}