Переглянути джерело

refactor: restructure code by modules — system/parking/vehicle under internal/modules/

lq 2 місяців тому
батько
коміт
53f7c169b0
100 змінених файлів з 6999 додано та 11 видалено
  1. 1 1
      internal/core/server.go
  2. 1 1
      internal/core/zap.go
  3. 1 1
      internal/global/global.go
  4. 1 1
      internal/initialize/mongo.go
  5. 1 1
      internal/initialize/other.go
  6. 1 1
      internal/initialize/plugin.go
  7. 1 1
      internal/initialize/validator.go
  8. 1 1
      internal/middleware/casbin_rbac.go
  9. 1 1
      internal/middleware/email.go
  10. 1 1
      internal/middleware/jwt.go
  11. 1 1
      internal/middleware/operation.go
  12. 121 0
      internal/modules/parking/api/booth.go
  13. 122 0
      internal/modules/parking/api/channel.go
  14. 7 0
      internal/modules/parking/api/enter.go
  15. 122 0
      internal/modules/parking/api/parking_lot.go
  16. 192 0
      internal/modules/parking/api/reader.go
  17. 29 0
      internal/modules/parking/model/request/booth.go
  18. 32 0
      internal/modules/parking/model/request/channel.go
  19. 27 0
      internal/modules/parking/model/request/parking_lot.go
  20. 66 0
      internal/modules/parking/model/request/reader.go
  21. 12 0
      internal/modules/parking/model/response/booth.go
  22. 12 0
      internal/modules/parking/model/response/channel.go
  23. 12 0
      internal/modules/parking/model/response/parking_lot.go
  24. 39 0
      internal/modules/parking/model/response/reader.go
  25. 6 0
      internal/modules/parking/router/enter.go
  26. 51 0
      internal/modules/parking/router/parking.go
  27. 136 0
      internal/modules/parking/service/booth.go
  28. 138 0
      internal/modules/parking/service/channel.go
  29. 94 0
      internal/modules/parking/service/conn_manager.go
  30. 153 0
      internal/modules/parking/service/parking_lot.go
  31. 408 0
      internal/modules/parking/service/reader.go
  32. 53 0
      internal/modules/parking/service/reader_interface.go
  33. 118 0
      internal/modules/parking/service/serial_reader.go
  34. 126 0
      internal/modules/parking/service/tcp_reader.go
  35. 285 0
      internal/modules/parking/service/uhf_service.go
  36. 11 0
      internal/modules/system/api/enter.go
  37. 231 0
      internal/modules/system/api/sys_api.go
  38. 177 0
      internal/modules/system/api/sys_authority.go
  39. 80 0
      internal/modules/system/api/sys_authority_btn.go
  40. 70 0
      internal/modules/system/api/sys_captcha.go
  41. 68 0
      internal/modules/system/api/sys_casbin.go
  42. 129 0
      internal/modules/system/api/sys_dictionary.go
  43. 148 0
      internal/modules/system/api/sys_dictionary_detail.go
  44. 33 0
      internal/modules/system/api/sys_jwt_blacklist.go
  45. 278 0
      internal/modules/system/api/sys_menu.go
  46. 149 0
      internal/modules/system/api/sys_operation_record.go
  47. 473 0
      internal/modules/system/api/sys_user.go
  48. 21 0
      internal/modules/system/model/request/jwt.go
  49. 14 0
      internal/modules/system/model/request/sys_api.go
  50. 7 0
      internal/modules/system/model/request/sys_authority_btn.go
  51. 13 0
      internal/modules/system/model/request/sys_auto_history.go
  52. 26 0
      internal/modules/system/model/request/sys_casbin.go
  53. 11 0
      internal/modules/system/model/request/sys_dictionary_detail.go
  54. 27 0
      internal/modules/system/model/request/sys_menu.go
  55. 11 0
      internal/modules/system/model/request/sys_operation_record.go
  56. 55 0
      internal/modules/system/model/request/sys_user.go
  57. 13 0
      internal/modules/system/model/response/sys_api.go
  58. 14 0
      internal/modules/system/model/response/sys_authority.go
  59. 5 0
      internal/modules/system/model/response/sys_authority_btn.go
  60. 8 0
      internal/modules/system/model/response/sys_captcha.go
  61. 9 0
      internal/modules/system/model/response/sys_casbin.go
  62. 17 0
      internal/modules/system/model/response/sys_menu.go
  63. 15 0
      internal/modules/system/model/response/sys_user.go
  64. 5 0
      internal/modules/system/router/enter.go
  65. 23 0
      internal/modules/system/router/exa_file_upload_and_download.go
  66. 31 0
      internal/modules/system/router/sys_api.go
  67. 24 0
      internal/modules/system/router/sys_authority.go
  68. 19 0
      internal/modules/system/router/sys_authority_btn.go
  69. 19 0
      internal/modules/system/router/sys_base.go
  70. 21 0
      internal/modules/system/router/sys_casbin.go
  71. 24 0
      internal/modules/system/router/sys_dictionary.go
  72. 24 0
      internal/modules/system/router/sys_dictionary_detail.go
  73. 16 0
      internal/modules/system/router/sys_jwt.go
  74. 29 0
      internal/modules/system/router/sys_menu.go
  75. 21 0
      internal/modules/system/router/sys_operation_record.go
  76. 29 0
      internal/modules/system/router/sys_user.go
  77. 7 0
      internal/modules/system/service/enter.go
  78. 82 0
      internal/modules/system/service/jwt_black_list.go
  79. 140 0
      internal/modules/system/service/sys_api.go
  80. 175 0
      internal/modules/system/service/sys_authority.go
  81. 36 0
      internal/modules/system/service/sys_authority_btn.go
  82. 73 0
      internal/modules/system/service/sys_base_menu.go
  83. 191 0
      internal/modules/system/service/sys_casbin.go
  84. 109 0
      internal/modules/system/service/sys_dictionary.go
  85. 80 0
      internal/modules/system/service/sys_dictionary_detail.go
  86. 228 0
      internal/modules/system/service/sys_menu.go
  87. 64 0
      internal/modules/system/service/sys_operation_record.go
  88. 212 0
      internal/modules/system/service/sys_user.go
  89. 15 0
      internal/modules/vehicle/api/enter.go
  90. 101 0
      internal/modules/vehicle/api/fee_config.go
  91. 119 0
      internal/modules/vehicle/api/owner.go
  92. 86 0
      internal/modules/vehicle/api/shortlist.go
  93. 160 0
      internal/modules/vehicle/api/vehicle.go
  94. 78 0
      internal/modules/vehicle/api/vehicle_record.go
  95. 113 0
      internal/modules/vehicle/api/vehicle_type.go
  96. 32 0
      internal/modules/vehicle/model/request/fee_config.go
  97. 28 0
      internal/modules/vehicle/model/request/owner.go
  98. 9 0
      internal/modules/vehicle/model/request/shortlist.go
  99. 91 0
      internal/modules/vehicle/model/request/vehicle.go
  100. 0 0
      internal/modules/vehicle/model/response/owner.go

+ 1 - 1
internal/core/server.go

@@ -5,7 +5,7 @@ import (
 	"go.uber.org/zap"
 	"wails-app/internal/global"
 	"wails-app/internal/initialize"
-	"wails-app/internal/service/system"
+	"wails-app/internal/modules/system/service"
 )
 
 type server interface {

+ 1 - 1
internal/core/zap.go

@@ -7,7 +7,7 @@ import (
 	"os"
 	"wails-app/internal/core/internal"
 	"wails-app/internal/global"
-	"wails-app/internal/utils"
+	"wails-app/internal/pkg"
 )
 
 // Zap 获取 zap.Logger

+ 1 - 1
internal/global/global.go

@@ -5,7 +5,7 @@ import (
 	"sync"
 
 	"github.com/songzhibin97/gkit/cache/local_cache"
-	"wails-app/internal/utils/timer"
+	"wails-app/internal/pkg/timer"
 
 	"golang.org/x/sync/singleflight"
 

+ 1 - 1
internal/initialize/mongo.go

@@ -10,7 +10,7 @@ import (
 	option "go.mongodb.org/mongo-driver/mongo/options"
 	"wails-app/internal/global"
 	"wails-app/internal/initialize/internal"
-	"wails-app/internal/utils"
+	"wails-app/internal/pkg"
 	"sort"
 	"strings"
 )

+ 1 - 1
internal/initialize/other.go

@@ -4,7 +4,7 @@ import (
 	"github.com/songzhibin97/gkit/cache/local_cache"
 
 	"wails-app/internal/global"
-	"wails-app/internal/utils"
+	"wails-app/internal/pkg"
 )
 
 func OtherInit() {

+ 1 - 1
internal/initialize/plugin.go

@@ -7,7 +7,7 @@ import (
 	"wails-app/internal/global"
 	"wails-app/internal/middleware"
 	"wails-app/internal/plugin/email"
-	"wails-app/internal/utils/plugin"
+	"wails-app/internal/pkg/plugin"
 )
 
 func PluginInit(group *gin.RouterGroup, Plugin ...plugin.Plugin) {

+ 1 - 1
internal/initialize/validator.go

@@ -1,6 +1,6 @@
 package initialize
 
-import "wails-app/internal/utils"
+import "wails-app/internal/pkg"
 
 func init() {
 	_ = utils.RegisterRule("PageVerify",

+ 1 - 1
internal/middleware/casbin_rbac.go

@@ -8,7 +8,7 @@ import (
 	"wails-app/internal/global"
 	"wails-app/internal/model/common/response"
 	"wails-app/internal/service"
-	"wails-app/internal/utils"
+	"wails-app/internal/pkg"
 )
 
 var casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService

+ 1 - 1
internal/middleware/email.go

@@ -8,7 +8,7 @@ import (
 	"time"
 
 	"wails-app/internal/plugin/email/utils"
-	utils2 "wails-app/internal/utils"
+	utils2 "wails-app/internal/pkg"
 
 	"github.com/gin-gonic/gin"
 	"go.uber.org/zap"

+ 1 - 1
internal/middleware/jwt.go

@@ -6,7 +6,7 @@ import (
 	"go.uber.org/zap"
 	"wails-app/internal/dao"
 	"wails-app/internal/global"
-	"wails-app/internal/utils"
+	"wails-app/internal/pkg"
 	"strconv"
 	"time"
 

+ 1 - 1
internal/middleware/operation.go

@@ -12,7 +12,7 @@ import (
 	"sync"
 	"time"
 
-	"wails-app/internal/utils"
+	"wails-app/internal/pkg"
 
 	"github.com/gin-gonic/gin"
 	"go.uber.org/zap"

+ 121 - 0
internal/modules/parking/api/booth.go

@@ -0,0 +1,121 @@
+package parking
+
+import (
+	"fmt"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/modules/parking/model/request"
+	parkingResponse "wails-app/internal/modules/parking/model/response"
+
+	"github.com/gin-gonic/gin"
+)
+
+func CreateBooth(c *gin.Context) {
+	var req request.BoothCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := BoothService.CreateBooth(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+func UpdateBooth(c *gin.Context) {
+	var req request.BoothUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := BoothService.UpdateBooth(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+func GetBoothByID(c *gin.Context) {
+	id := c.Query("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	booth, err := BoothService.GetBoothByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(booth, c)
+}
+
+func GetBoothByCode(c *gin.Context) {
+	code := c.Query("code")
+	if code == "" {
+		response.FailWithMessage("岗亭编码不能为空", c)
+		return
+	}
+
+	booth, err := BoothService.GetBoothByCode(code)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(booth, c)
+}
+
+func QueryAllBooths(c *gin.Context) {
+	booths, err := BoothService.QueryAllBooths()
+	if err != nil {
+		response.FailWithMessage("THE QUERY FAILED", c)
+	}
+	response.OkWithData(booths, c)
+}
+
+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 := BoothService.ListBooths(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(parkingResponse.BoothList{
+		Total: total,
+		List:  list,
+	}, c)
+}
+
+func DeleteBooth(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 = BoothService.DeleteBooth(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}

+ 122 - 0
internal/modules/parking/api/channel.go

@@ -0,0 +1,122 @@
+package parking
+
+import (
+	"fmt"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/modules/parking/model/request"
+	parkingResponse "wails-app/internal/modules/parking/model/response"
+
+	"github.com/gin-gonic/gin"
+)
+
+func CreateChannel(c *gin.Context) {
+	var req request.ChannelCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := ChannelService.CreateChannel(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+func UpdateChannel(c *gin.Context) {
+	var req request.ChannelUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := ChannelService.UpdateChannel(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+func GetChannelByID(c *gin.Context) {
+	id := c.Query("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	channel, err := ChannelService.GetChannelByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(channel, c)
+}
+
+func GetChannelByCode(c *gin.Context) {
+	code := c.Query("code")
+	if code == "" {
+		response.FailWithMessage("通道编码不能为空", c)
+		return
+	}
+
+	channel, err := ChannelService.GetChannelByCode(code)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(channel, c)
+}
+
+func QueryAllChannels(c *gin.Context) {
+	channels, err := ChannelService.QueryAllChannels()
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	response.OkWithData(channels, c)
+}
+
+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 := ChannelService.ListChannels(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(parkingResponse.ChannelList{
+		Total: total,
+		List:  list,
+	}, c)
+}
+
+func DeleteChannel(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 = ChannelService.DeleteChannel(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}

+ 7 - 0
internal/modules/parking/api/enter.go

@@ -0,0 +1,7 @@
+package parking
+
+import parkingService "wails-app/internal/modules/parking/service"
+
+var BoothService = parkingService.NewBoothService()
+var ChannelService = parkingService.NewChannelService()
+var ParkingLotService = parkingService.NewParkingLotService()

+ 122 - 0
internal/modules/parking/api/parking_lot.go

@@ -0,0 +1,122 @@
+package parking
+
+import (
+	"fmt"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/modules/parking/model/request"
+	parkingResponse "wails-app/internal/modules/parking/model/response"
+
+	"github.com/gin-gonic/gin"
+)
+
+func CreateParkingLot(c *gin.Context) {
+	var req request.ParkingLotCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := ParkingLotService.CreateParkingLot(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+func UpdateParkingLot(c *gin.Context) {
+	var req request.ParkingLotUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := ParkingLotService.UpdateParkingLot(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+func GetParkingLotByID(c *gin.Context) {
+	id := c.Query("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	lot, err := ParkingLotService.GetParkingLotByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(lot, c)
+}
+
+func GetParkingLotByCode(c *gin.Context) {
+	code := c.Query("code")
+	if code == "" {
+		response.FailWithMessage("区域编码不能为空", c)
+		return
+	}
+
+	lot, err := ParkingLotService.GetParkingLotByCode(code)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(lot, c)
+}
+
+func QueryAllParkingLots(c *gin.Context) {
+	lots, err := ParkingLotService.QueryAllParkingLots()
+	if err != nil {
+		response.FailWithMessage("THE QUERY FAILED", c)
+		return
+	}
+	response.OkWithData(lots, c)
+}
+
+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 := ParkingLotService.ListParkingLots(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(parkingResponse.ParkingLotList{
+		Total: total,
+		List:  list,
+	}, c)
+}
+
+func DeleteParkingLot(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 = ParkingLotService.DeleteParkingLot(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}

+ 192 - 0
internal/modules/parking/api/reader.go

@@ -0,0 +1,192 @@
+package parking
+
+import (
+	"fmt"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/modules/parking/model/request"
+	parkingService "wails-app/internal/modules/parking/service"
+
+	"github.com/gin-gonic/gin"
+)
+
+func CreateReader(c *gin.Context) {
+	var req request.UHFReaderCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parkingService.UHFService.CreateReader(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+func UpdateReader(c *gin.Context) {
+	var req request.UHFReaderUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parkingService.UHFService.UpdateReader(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+func ConnectedDevice(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 = parkingService.UHFService.ConnectedDevice(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.Ok(c)
+}
+
+func GetReaderByCode(c *gin.Context) {
+	code := c.Query("code")
+	if code == "" {
+		response.FailWithMessage("设备编码不能为空", c)
+		return
+	}
+
+	reader, err := parkingService.UHFService.GetReaderByCode(code)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(reader, c)
+}
+
+func QueryAllReaders(c *gin.Context) {
+	readers, err := parkingService.UHFService.QueryAllReaders()
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	response.OkWithData(readers, c)
+}
+
+func ListReaders(c *gin.Context) {
+	var req request.UHFReaderQuery
+	if err := c.ShouldBindQuery(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	list, total, err := parkingService.UHFService.ListReaders(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     req.Page,
+		PageSize: req.PageSize,
+	}, "获取成功", c)
+}
+
+func DeleteReader(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 = parkingService.UHFService.DeleteReader(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}
+
+func ConnectReader(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 = parkingService.UHFService.ConnectReader(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("连接成功", c)
+}
+
+func DisconnectReader(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 = parkingService.UHFService.DisconnectReader(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("断开连接成功", c)
+}
+
+func SetReaderPower(c *gin.Context) {
+	var req request.UHFReaderPower
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parkingService.UHFService.SetReaderPower(req.ID, int(req.Power))
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("功率设置成功", c)
+}
+
+func ReportTag(c *gin.Context) {
+	var req request.UHFReaderTagReport
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := parkingService.UHFService.ReportTag(req.DeviceCode, req.Tag)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("标签上报成功", c)
+}

+ 29 - 0
internal/modules/parking/model/request/booth.go

@@ -0,0 +1,29 @@
+package request
+
+// BoothCreate 岗亭创建请求
+type BoothCreate struct {
+	BoothCode    string `json:"booth_code"`     // 岗亭编码
+	BoothName    string `json:"booth_name"`     // 岗亭名称
+	IPAddress    string `json:"ip_address"`     // IP地址
+	Description  string `json:"description"`    // 备注
+	ParkingLotID uint   `json:"parking_lot_id"` // 所属区域ID
+}
+
+// BoothUpdate 岗亭更新请求
+type BoothUpdate struct {
+	ID           uint   `json:"id"`             // 岗亭ID
+	BoothCode    string `json:"booth_code"`     // 岗亭编码
+	BoothName    string `json:"booth_name"`     // 岗亭名称
+	IPAddress    string `json:"ip_address"`     // IP地址
+	Description  string `json:"description"`    // 备注
+	ParkingLotID uint   `json:"parking_lot_id"` // 所属区域ID
+}
+
+// BoothQuery 岗亭查询请求
+type BoothQuery struct {
+	BoothCode    string `form:"booth_code"`           // 岗亭编码
+	BoothName    string `form:"booth_name"`           // 岗亭名称
+	ParkingLotID uint   `form:"parking_lot_id"`       // 所属区域ID
+	Page         int    `form:"page,default=1"`       // 页码
+	PageSize     int    `form:"page_size,default=10"` // 每页数量
+}

+ 32 - 0
internal/modules/parking/model/request/channel.go

@@ -0,0 +1,32 @@
+package request
+
+// ChannelCreate 通道创建请求
+type ChannelCreate struct {
+	ChannelCode    string `json:"channel_code"`    // 通道编码
+	ChannelName    string `json:"channel_name"`    // 通道名称
+	Direction      string `json:"direction"`       // 进出方向 in/out/inout
+	AllowTemporary bool   `json:"allow_temporary"` // 是否允许临时车进出
+	Description    string `json:"description"`     // 备注
+	BoothId        uint   `json:"booth_id"`        //岗亭id
+}
+
+// ChannelUpdate 通道更新请求
+type ChannelUpdate struct {
+	ID             uint   `json:"id"`              // 通道ID
+	ChannelCode    string `json:"channel_code"`    // 通道编码
+	ChannelName    string `json:"channel_name"`    // 通道名称
+	Direction      string `json:"direction"`       // 进出方向 in/out/inout
+	AllowTemporary bool   `json:"allow_temporary"` // 是否允许临时车进出
+	Description    string `json:"description"`     // 备注
+	BoothId        uint   `json:"booth_id"`        //岗亭id
+}
+
+// ChannelQuery 通道查询请求
+type ChannelQuery struct {
+	ChannelCode  string `form:"channel_code"`         // 通道编码
+	ChannelName  string `form:"channel_name"`         // 通道名称
+	Direction    string `form:"direction"`            // 进出方向
+	ParkingLotID uint   `form:"parking_lot_id"`       // 所属区域ID
+	Page         int    `form:"page,default=1"`       // 页码
+	PageSize     int    `form:"page_size,default=10"` // 每页数量
+}

+ 27 - 0
internal/modules/parking/model/request/parking_lot.go

@@ -0,0 +1,27 @@
+package request
+
+// ParkingLotCreate 停车区域创建请求
+type ParkingLotCreate struct {
+	LotCode     string `json:"lot_code"`                 // 区域编码
+	LotName     string `json:"lot_name"`                 // 区域名称
+	Capacity    int    `json:"capacity" binding:"min=0"` // 车位数量
+	Description string `json:"description"`              // 备注
+}
+
+// ParkingLotUpdate 停车区域更新请求
+type ParkingLotUpdate struct {
+	ID          uint   `json:"id"`                       // 区域ID
+	LotCode     string `json:"lot_code"`                 // 区域编码
+	LotName     string `json:"lot_name"`                 // 区域名称
+	Capacity    int    `json:"capacity" binding:"min=0"` // 车位数量
+	Description string `json:"description"`              // 备注
+}
+
+// ParkingLotQuery 停车区域查询请求
+type ParkingLotQuery struct {
+	LotCode  string `form:"lot_code"`             // 区域编码
+	LotName  string `form:"lot_name"`             // 区域名称
+	LotId    int    `form:"lot_id"`               // 区域id
+	Page     int    `form:"page,default=1"`       // 页码
+	PageSize int    `form:"page_size,default=10"` // 每页数量
+}

+ 66 - 0
internal/modules/parking/model/request/reader.go

@@ -0,0 +1,66 @@
+package request
+
+// UHFReaderCreate UHF读写器创建请求
+type UHFReaderCreate struct {
+	DeviceCode  string `json:"device_code"`  // 设备编码
+	DeviceName  string `json:"device_name"`  // 设备名称
+	DeviceType  string `json:"device_type"`  // 设备类型
+	ConnectType string `json:"connect_type"` // 连接类型 tcp/serial
+	IPAddress   string `json:"ip_address"`   // IP地址
+	Port        int    `json:"port"`         // 端口
+	COMPort     string `json:"com_port"`     // 串口名称
+	BaudRate    int    `json:"baud_rate"`    // 串口波特率
+	IsActive    bool   `json:"is_active"`    // 是否启用
+	Description string `json:"description"`  // 备注
+	ChannelId   int    `json:"channel_id"`   // 通道id
+}
+
+// UHFReaderUpdate UHF读写器更新请求
+type UHFReaderUpdate struct {
+	ID          uint   `json:"id"`           // 设备ID
+	DeviceCode  string `json:"device_code"`  // 设备编码
+	DeviceName  string `json:"device_name"`  // 设备名称
+	DeviceType  string `json:"device_type"`  // 设备类型
+	ConnectType string `json:"connect_type"` // 连接类型 tcp/serial
+	IPAddress   string `json:"ip_address"`   // IP地址
+	Port        int    `json:"port"`         // 端口
+	COMPort     string `json:"com_port"`     // 串口名称
+	BaudRate    int    `json:"baud_rate"`    // 串口波特率
+	IsActive    bool   `json:"is_active"`    // 是否启用
+	Description string `json:"description"`  // 备注
+	ChannelId   int    `json:"channel_id"`   // 通道id
+}
+
+// UHFReaderQuery UHF读写器查询请求
+type UHFReaderQuery struct {
+	DeviceCode   string `form:"device_code"`          // 设备编码
+	DeviceName   string `form:"device_name"`          // 设备名称
+	ConnectType  string `form:"connect_type"`         // 连接类型
+	IPAddress    string `form:"ip_address"`           // IP地址
+	Status       string `form:"status"`               // 状态
+	ParkingLotID uint   `form:"parking_lot_id"`       // 所属区域ID
+	Page         int    `form:"page,default=1"`       // 页码
+	PageSize     int    `form:"page_size,default=10"` // 每页数量
+}
+
+// UHFReaderConnect UHF读写器连接请求
+type UHFReaderConnect struct {
+	ID uint `json:"id"` // 设备ID
+}
+
+// UHFReaderDisconnect UHF读写器断开连接请求
+type UHFReaderDisconnect struct {
+	ID uint `json:"id"` // 设备ID
+}
+
+// UHFReaderPower UHF读写器功率设置请求
+type UHFReaderPower struct {
+	ID    uint `json:"id"`                                    // 设备ID
+	Power byte `json:"power" binding:"required,min=0,max=26"` // 功率值 0-26dBm
+}
+
+// UHFReaderTagReport UHF读写器标签上报请求
+type UHFReaderTagReport struct {
+	DeviceCode string `json:"device_code" binding:"required"` // 设备编码
+	Tag        string `json:"tag" binding:"required"`         // 标签UII
+}

+ 12 - 0
internal/modules/parking/model/response/booth.go

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

+ 12 - 0
internal/modules/parking/model/response/channel.go

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

+ 12 - 0
internal/modules/parking/model/response/parking_lot.go

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

+ 39 - 0
internal/modules/parking/model/response/reader.go

@@ -0,0 +1,39 @@
+package response
+
+import (
+	"wails-app/internal/dao"
+	"time"
+)
+
+type UHFReader struct {
+	dao.UHFReader
+}
+
+type UHFReaderResponse struct {
+	ID           uint      `json:"id"`
+	DeviceCode   string    `json:"device_code"`
+	DeviceName   string    `json:"device_name"`
+	ConnectType  string    `json:"connect_type"`
+	IPAddress    string    `json:"ip_address"`
+	Port         int       `json:"port"`
+	COMPort      string    `json:"com_port"`
+	BaudRate     int       `json:"baud_rate"`
+	Status       string    `json:"status"`
+	IsActive     bool      `json:"is_active"`
+	Description  string    `json:"description"`
+	ParkingLotID uint      `json:"parking_lot_id"`
+	CreatedAt    time.Time `json:"created_at"`
+	UpdatedAt    time.Time `json:"updated_at"`
+}
+
+type UHFReaderList struct {
+	Total int64               `json:"total"`
+	List  []UHFReaderResponse `json:"list"`
+}
+
+type TagReadResult struct {
+	ReaderID   uint   `json:"reader_id"`
+	DeviceCode string `json:"device_code"`
+	TagUII     string `json:"tag_uii"`
+	ReadTime   int64  `json:"read_time"`
+}

+ 6 - 0
internal/modules/parking/router/enter.go

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

+ 51 - 0
internal/modules/parking/router/parking.go

@@ -0,0 +1,51 @@
+package parking
+
+import (
+	"wails-app/internal/api/v1/parking"
+	"wails-app/internal/api/v1/uhf"
+
+	"github.com/gin-gonic/gin"
+)
+
+func SetupParkingRouter(router *gin.RouterGroup) {
+	parkLotGroup := router.Group("/lot")
+	{
+		parkLotGroup.POST("/create", parking.CreateParkingLot)
+		parkLotGroup.PUT("/update", parking.UpdateParkingLot)
+		parkLotGroup.GET("/get", parking.GetParkingLotByID)
+		parkLotGroup.GET("/get-by-code", parking.GetParkingLotByCode)
+		parkLotGroup.GET("/list", parking.ListParkingLots)
+		parkLotGroup.GET("/all", parking.QueryAllParkingLots)
+		parkLotGroup.DELETE("/delete", parking.DeleteParkingLot)
+	}
+	boothGroup := router.Group("/booth")
+	{
+		boothGroup.POST("/create", parking.CreateBooth)
+		boothGroup.PUT("/update", parking.UpdateBooth)
+		boothGroup.GET("/get", parking.GetBoothByID)
+		boothGroup.GET("/get-by-code", parking.GetBoothByCode)
+		boothGroup.GET("/list", parking.ListBooths)
+		boothGroup.GET("/all", parking.QueryAllBooths)
+		boothGroup.DELETE("/delete", parking.DeleteBooth)
+	}
+	channelGroup := router.Group("/channel")
+	{
+		channelGroup.POST("/create", parking.CreateChannel)
+		channelGroup.PUT("/update", parking.UpdateChannel)
+		channelGroup.GET("/get", parking.GetChannelByID)
+		channelGroup.GET("/get-by-code", parking.GetChannelByCode)
+		channelGroup.GET("/list", parking.ListChannels)
+		channelGroup.GET("/all", parking.QueryAllChannels)
+		channelGroup.DELETE("/delete", parking.DeleteChannel)
+	}
+	deviceGroup := router.Group("/device")
+	{
+		deviceGroup.POST("/create", uhf.CreateReader)
+		deviceGroup.PUT("/update", uhf.UpdateReader)
+		deviceGroup.GET("/connectedDevice", uhf.ConnectedDevice)
+		deviceGroup.GET("/get-by-code", uhf.GetReaderByCode)
+		deviceGroup.GET("/list", uhf.ListReaders)
+		deviceGroup.GET("/all", uhf.QueryAllReaders)
+		deviceGroup.DELETE("/delete", uhf.DeleteReader)
+	}
+}

+ 136 - 0
internal/modules/parking/service/booth.go

@@ -0,0 +1,136 @@
+package parking
+
+import (
+	"errors"
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/modules/parking/model/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,
+		ParkingLotID: req.ParkingLotID,
+	}
+
+	return global.GVA_DB.Create(&booth).Error
+}
+
+// UpdateBooth 更新岗亭
+func (s *boothService) UpdateBooth(req request.BoothUpdate) error {
+	var booth dao.Booth
+	result := global.GVA_DB.Preload("Channels").Preload("Channels.UHFReaders").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
+	booth.ParkingLotID = req.ParkingLotID
+
+	// 修改岗亭的区域id,那么它下面的子级关系的区域id都要改
+	for _, channel := range booth.Channels {
+		global.GVA_DB.Model(&channel).Where("id = ?", channel.ID).Update("parking_lot_id", req.ParkingLotID)
+		for _, reader := range channel.UHFReaders {
+			global.GVA_DB.Model(&reader).Where("id = ?", channel.ID).Update("parking_lot_id", req.ParkingLotID)
+		}
+	}
+
+	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
+}
+
+func (s *boothService) QueryAllBooths() ([]dao.Booth, error) {
+	var booths []dao.Booth
+	err := global.GVA_DB.Find(&booths).Error
+	if err != nil {
+		return []dao.Booth{}, err
+	}
+	return booths, 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+"%")
+	}
+
+	if req.ParkingLotID != 0 {
+		query = query.Where("parking_lot_id = ?", req.ParkingLotID)
+	}
+
+	// 计算总数
+	query.Count(&total)
+
+	// 分页查询
+	offset := (req.Page - 1) * req.PageSize
+	result := query.Offset(offset).Limit(req.PageSize).Preload("ParkingLot").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
+}

+ 138 - 0
internal/modules/parking/service/channel.go

@@ -0,0 +1,138 @@
+package parking
+
+import (
+	"errors"
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/modules/parking/model/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("通道编码已存在")
+	}
+
+	var booth dao.Booth
+	global.GVA_DB.Model(&booth).Where("id = ?", req.BoothId).First(&booth)
+
+	channel := dao.Channel{
+		ChannelCode:    req.ChannelCode,
+		ChannelName:    req.ChannelName,
+		Direction:      req.Direction,
+		AllowTemporary: req.AllowTemporary,
+		Description:    req.Description,
+		BoothID:        req.BoothId,
+		ParkingLotID:   booth.ParkingLotID,
+	}
+
+	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
+	channel.BoothID = req.BoothId
+
+	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
+}
+
+func (s *channelService) QueryAllChannels() ([]dao.Channel, error) {
+	var channels []dao.Channel
+	err := global.GVA_DB.Find(&channels).Error
+	if err != nil {
+		return channels, err
+	}
+	return channels, 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)
+	}
+
+	if req.ParkingLotID != 0 {
+		query = query.Where("parking_lot_id = ?", req.ParkingLotID)
+	}
+
+	// 计算总数
+	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
+}

+ 94 - 0
internal/modules/parking/service/conn_manager.go

@@ -0,0 +1,94 @@
+package parking
+
+import (
+	"log"
+	"wails-app/internal/global"
+	"sync"
+)
+
+// ConnManager 全局多设备连接管理器(单例)
+var ConnManager = &connManager{
+	connMap: make(map[string]UHFReaderConn), // key: DeviceCode
+}
+
+type connManager struct {
+	mu      sync.RWMutex // 读写锁,保证多协程安全
+	connMap map[string]UHFReaderConn
+	// 消息回调:设备收到数据后触发的处理函数(可自定义)
+	recvCallback func(deviceCode string, data []byte)
+}
+
+// SetRecvCallback 设置全局消息接收回调
+func (m *connManager) SetRecvCallback(callback func(deviceCode string, data []byte)) {
+	m.recvCallback = callback
+}
+
+// AddConn 添加设备连接(多设备核心:按DeviceCode存储)
+func (m *connManager) AddConn(deviceCode string, conn UHFReaderConn) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	m.connMap[deviceCode] = conn
+	// 启动消息监听协程(异步接收设备数据)
+	go m.listenDeviceData(deviceCode, conn)
+	log.Printf("设备[%s]已加入连接池", deviceCode)
+}
+
+// GetConn 获取指定设备连接(多设备核心:按DeviceCode检索)
+func (m *connManager) GetConn(deviceCode string) (UHFReaderConn, bool) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	conn, ok := m.connMap[deviceCode]
+	return conn, ok
+}
+
+// GetAllConns 获取所有设备连接(批量操作)
+func (m *connManager) GetAllConns() map[string]UHFReaderConn {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	// 返回副本,避免外部修改原map
+	copyMap := make(map[string]UHFReaderConn)
+	for k, v := range m.connMap {
+		copyMap[k] = v
+	}
+	return copyMap
+}
+
+// RemoveConn 移除设备连接
+func (m *connManager) RemoveConn(deviceCode string) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	delete(m.connMap, deviceCode)
+	log.Printf("设备[%s]已移出连接池", deviceCode)
+}
+
+// listenDeviceData 异步监听设备数据(多设备消息接收核心)
+func (m *connManager) listenDeviceData(deviceCode string, conn UHFReaderConn) {
+	for {
+		// 检查连接是否有效
+		if !conn.IsConnected() {
+			log.Printf("设备[%s]连接已断开,停止监听", deviceCode)
+			// 更新数据库状态
+			//var device dao.UHFReader
+			global.GVA_DB.Where("device_code = ?", deviceCode).Update("status", "offline")
+			return
+		}
+
+		// 阻塞读取设备数据(TCP/串口统一通过ReadData读取)
+		data, err := conn.ReadData() // 新增ReadData接口方法
+		if err != nil {
+			log.Printf("设备[%s]读取数据失败:%v", deviceCode, err)
+			continue
+		}
+
+		// 空数据跳过
+		if len(data) == 0 {
+			continue
+		}
+
+		log.Printf("设备[%s]收到数据:%x", deviceCode, data)
+		// 触发回调处理数据(业务层自定义逻辑)
+		if m.recvCallback != nil {
+			m.recvCallback(deviceCode, data)
+		}
+	}
+}

+ 153 - 0
internal/modules/parking/service/parking_lot.go

@@ -0,0 +1,153 @@
+package parking
+
+import (
+	"errors"
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/modules/parking/model/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
+}
+
+func (s *parkingLotService) QueryAllParkingLots() ([]dao.ParkingLot, error) {
+	var parkingLots []dao.ParkingLot
+	err := global.GVA_DB.Find(&parkingLots).Error
+
+	if err != nil {
+		return parkingLots, err
+	}
+
+	return parkingLots, 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.LotCode != "" {
+		query = query.Where("id = ?", req.LotId)
+	}
+
+	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).Preload("Booths").Preload("Channels").Preload("UHFReaders").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
+}

+ 408 - 0
internal/modules/parking/service/reader.go

@@ -0,0 +1,408 @@
+package parking
+
+import (
+	"context"
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/model/vehicle/request"
+	"wails-app/internal/service"
+	"sync"
+	"time"
+)
+
+// Reader 通用读写接口(串口和TCP都实现它)
+type Reader interface {
+	Connect() error
+	Disconnect() error
+	SendData([]byte) error
+	ReadData() ([]byte, error)
+	IsConnected() bool
+
+	// ===================== 加入继电器接口 =====================
+	CloseRelay1(validTime byte) error
+	ReleaseRelay1() error
+	CloseRelay2(validTime byte) error
+	ReleaseRelay2() error
+}
+
+// 上报数据模型
+type ReportData struct {
+	DeviceCode string   `json:"device_code"` // 设备编码
+	Hex        string   `json:"hex"`         // 原始报文
+	Epcs       []string `json:"epcs"`        // 解析出的EPC列表
+	RSSI       int      `json:"rssi"`        // 信号强度
+	Antenna    int      `json:"antenna"`     // 天线号
+	Timestamp  int64    `json:"timestamp"`   // 上报时间
+}
+
+// 全局设备管理器(管理所有已连接的设备)
+var DeviceManager = &Manager{
+	devices: make(map[string]*DeviceHandler),
+}
+
+type Manager struct {
+	mu      sync.RWMutex
+	devices map[string]*DeviceHandler
+}
+
+// DeviceHandler 每个设备的处理实例(包含读写器和协程)
+type DeviceHandler struct {
+	reader    Reader
+	device    *dao.UHFReader
+	ctx       context.Context
+	cancel    context.CancelFunc
+	dataChan  chan *ReportData
+	isRunning bool
+}
+
+// Register 注册设备
+func (m *Manager) Register(code string, h *DeviceHandler) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	m.devices[code] = h
+}
+
+// Unregister 注销设备
+func (m *Manager) Unregister(code string) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	delete(m.devices, code)
+}
+
+// Get 获取设备
+func (m *Manager) Get(code string) (*DeviceHandler, bool) {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	h, ok := m.devices[code]
+	return h, ok
+}
+
+// ===================== 复用原有CRC和解析函数(无需修改)=====================
+func uiCrc16Cal(pucY []byte, ucX uint8) uint16 {
+	const PRESET_VALUE = 0xFFFF
+	const POLYNOMIAL = 0x8408
+	var uiCrcValue uint16 = PRESET_VALUE
+	for ucI := uint8(0); ucI < ucX; ucI++ {
+		uiCrcValue = uiCrcValue ^ uint16(pucY[ucI])
+		for ucJ := uint8(0); ucJ < 8; ucJ++ {
+			if uiCrcValue&0x0001 != 0 {
+				uiCrcValue = (uiCrcValue >> 1) ^ POLYNOMIAL
+			} else {
+				uiCrcValue = uiCrcValue >> 1
+			}
+		}
+	}
+	return (uiCrcValue << 8) | (uiCrcValue >> 8)
+}
+
+// StartDeviceHandler 启动单个设备的读写协程
+func StartDeviceHandler(device *dao.UHFReader) error {
+	// 1. 根据连接类型创建读写器
+	var r Reader
+	switch device.ConnectType {
+	case "serial":
+		r = NewSerialReader(device.COMPort, device.BaudRate)
+	case "tcp":
+		r = NewTCPReader(device.IPAddress, device.Port)
+	default:
+		return fmt.Errorf("不支持的连接类型: %s", device.ConnectType)
+	}
+
+	// 2. 连接设备
+	if err := r.Connect(); err != nil {
+		return err
+	}
+
+	// 3. 创建上下文和handler
+	ctx, cancel := context.WithCancel(context.Background())
+	h := &DeviceHandler{
+		reader:    r,
+		device:    device,
+		ctx:       ctx,
+		cancel:    cancel,
+		dataChan:  make(chan *ReportData, 200), // 适当加大
+		isRunning: true,
+	}
+
+	// 4. 注册到管理器
+	DeviceManager.Register(device.DeviceCode, h)
+
+	// 5. 启动读写协程
+	go func() {
+		defer func() {
+			h.isRunning = false
+			_ = r.Disconnect()
+			close(h.dataChan)
+			DeviceManager.Unregister(device.DeviceCode)
+			global.GVA_LOG.Info("设备已停止 code:" + device.DeviceCode)
+		}()
+
+		for {
+			select {
+			case <-ctx.Done():
+				return
+			default:
+				buf, err := r.ReadData()
+				if err != nil {
+					global.GVA_DB.Model(device).Update("status", "offline")
+					_ = r.Disconnect() // 先断开
+					time.Sleep(2 * time.Second)
+					if err := r.Connect(); err != nil {
+						continue
+					}
+					global.GVA_DB.Model(device).Update("status", "online")
+					continue
+				}
+
+				if len(buf) == 0 {
+					continue
+				}
+
+				// 解析上报数据
+				report, err := parseReportData(device.DeviceCode, buf)
+				if err != nil {
+					continue
+				}
+
+				// 推送(不丢死,也不阻塞)
+				select {
+				case h.dataChan <- report:
+				default:
+					global.GVA_LOG.Warn("通道已满,丢弃数据 device" + device.DeviceCode)
+				}
+			}
+		}
+	}()
+
+	// 6. 启动业务处理协程
+	go func() {
+		for {
+			select {
+			case <-ctx.Done():
+				return
+			case report, ok := <-h.dataChan:
+				if !ok {
+					return
+				}
+				handleReportData(report)
+			}
+		}
+	}()
+
+	global.GVA_DB.Model(device).Update("status", "online")
+	return nil
+}
+
+// parseReportData 解析上报数据
+func parseReportData(deviceCode string, buf []byte) (*ReportData, error) {
+	if len(buf) < 25 || buf[0] != 0xCF {
+		return nil, errors.New("无效帧")
+	}
+
+	dataWithoutCRC := buf[:len(buf)-2]
+	recvCRC := binary.LittleEndian.Uint16(buf[len(buf)-2:])
+	calcCRC := uiCrc16Cal(dataWithoutCRC, uint8(len(dataWithoutCRC)))
+	if recvCRC != calcCRC {
+		return nil, errors.New("CRC校验失败")
+	}
+
+	rssi := int(buf[6])
+	antenna := int(buf[22])
+	epc := hex.EncodeToString(buf[11:23])
+
+	fmt.Printf(hex.EncodeToString(buf))
+
+	return &ReportData{
+		DeviceCode: deviceCode,
+		Hex:        hex.EncodeToString(buf),
+		Epcs:       []string{epc},
+		RSSI:       rssi,
+		Antenna:    antenna,
+		Timestamp:  time.Now().Unix(),
+	}, nil
+}
+
+// 防抖与状态
+var (
+	epcLastTime    = make(map[string]int64)
+	epcStatus      = make(map[string]bool)
+	epcMutex       sync.Mutex
+	debounceSecond = int64(3)
+)
+
+// handleReportData 处理UHF读取到的标签 + 完整权限判断
+func handleReportData(report *ReportData) {
+	if len(report.Epcs) == 0 {
+		return
+	}
+
+	epc := report.Epcs[0]
+	fmt.Println("<UNK>", epc)
+	now := time.Now().Unix()
+
+	// ===================== 【修复】锁只包防抖,绝不包业务!=====================
+	epcMutex.Lock()
+	// 3秒防抖
+	if lastTime, ok := epcLastTime[epc]; ok && now-lastTime < debounceSecond {
+		epcMutex.Unlock()
+		return
+	}
+	epcLastTime[epc] = now
+	epcMutex.Unlock()
+	// ===================== 锁已释放,后面逻辑不会阻塞 =====================
+
+	// 获取设备处理器
+	_, exists := DeviceManager.Get(report.DeviceCode)
+	if !exists {
+		return
+	}
+
+	// ===================== 查询设备 + 通道 =====================
+	var device dao.UHFReader
+	err := global.GVA_DB.Preload("Channel").First(&device, "device_code = ?", report.DeviceCode).Error
+	if err != nil {
+		return
+	}
+	channel := device.Channel
+	direction := channel.Direction
+	allowTemporary := channel.AllowTemporary
+
+	vehicleService := service.ServiceGroupApp.VehicleServiceGroup.VehicleService
+	shortlistService := service.ServiceGroupApp.VehicleServiceGroup.ShortlistService
+
+	// ===================== 1. 根据EPC查询车辆 =====================
+	vehicle, err := vehicleService.GetVehicleByPlateNumber("", epc)
+	isTempVehicle := false
+	if err != nil || vehicle.ID == 0 {
+		isTempVehicle = true
+	} else {
+		if vehicle.VehicleType != nil {
+			isTempVehicle = (vehicle.VehicleTypeID == 1)
+		} else {
+			isTempVehicle = true
+		}
+	}
+
+	// ===================== 2. 查询黑白名单 =====================
+	isBlack, isWhite := shortlistService.CheckVehicleShortlist(epc)
+
+	// ===================== 3. 黑名单优先判断 =====================
+	if isBlack {
+		fmt.Printf("🚫 禁止通行 | 车辆在黑名单中 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
+		return
+	}
+
+	// ===================== 4. 临时车规则 =====================
+	if isTempVehicle {
+		if !allowTemporary && !isWhite {
+			fmt.Printf("🚫 禁止通行 | 临时车不允许此通道且不在白名单 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
+			return
+		}
+		if !isWhite {
+			fmt.Printf("🚫 禁止通行 | 临时车不在白名单 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
+			return
+		}
+	}
+
+	// ===================== 5. 入口 =====================
+	if direction == "in" {
+		epcMutex.Lock()
+		canIn := !epcStatus[epc]
+		epcMutex.Unlock()
+
+		if canIn {
+			fmt.Printf("🟢 入口开闸 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
+			//handler.reader.CloseRelay1(2)
+
+			var vehicleEntry request.VehicleEntry
+			vehicleEntry.RFIDTag = epc
+			vehicleEntry.ParkingLotID = channel.ParkingLotID
+			_, err := vehicleService.VehicleEntry(vehicleEntry)
+
+			if err == nil {
+				epcMutex.Lock()
+				epcStatus[epc] = true
+				epcMutex.Unlock()
+			}
+		}
+	}
+
+	// ===================== 6. 出口 =====================
+	if direction == "out" {
+		fmt.Printf("🔴 出口开闸 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
+		//handler.reader.CloseRelay1(2)
+
+		var vehicleExit request.VehicleExit
+		vehicleExit.RFIDTag = epc
+		_, err := vehicleService.VehicleExit(vehicleExit)
+
+		if err == nil {
+			epcMutex.Lock()
+			epcStatus[epc] = false
+			epcMutex.Unlock()
+		}
+	}
+}
+
+// ===================== 手动重置离场 =====================
+func SetEpcExit(epc string) {
+	epcMutex.Lock()
+	defer epcMutex.Unlock()
+	epcStatus[epc] = false
+	fmt.Printf("🚙 手动重置离场 | EPC: %s\n", epc)
+}
+
+// ==============================
+// 继电器控制(完整支持 Relay1 & Relay2)
+// ==============================
+const (
+	RELAY_OP_RELEASE = 0x01
+	RELAY_OP_CLOSE   = 0x02
+)
+
+// CloseRelay1 开闸
+func (s *SerialReader) CloseRelay1(validTime byte) error {
+	frame := buildRelayFrame(0x0077, 1, RELAY_OP_CLOSE, validTime)
+	_, err := s.SendAndRecv(frame)
+	return err
+}
+
+// ReleaseRelay1
+func (s *SerialReader) ReleaseRelay1() error {
+	frame := buildRelayFrame(0x0077, 1, RELAY_OP_RELEASE, 0)
+	_, err := s.SendAndRecv(frame)
+	return err
+}
+
+// CloseRelay2 关闸
+func (s *SerialReader) CloseRelay2(validTime byte) error {
+	frame := buildRelayFrame(0x0078, 2, RELAY_OP_CLOSE, validTime)
+	_, err := s.SendAndRecv(frame)
+	return err
+}
+
+// ReleaseRelay2
+func (s *SerialReader) ReleaseRelay2() error {
+	frame := buildRelayFrame(0x0078, 2, RELAY_OP_RELEASE, 0)
+	_, err := s.SendAndRecv(frame)
+	return err
+}
+
+// buildRelayFrame 构建指令
+func buildRelayFrame(cmd uint16, relayNum byte, option byte, validTime byte) []byte {
+	frame := []byte{
+		0xCF, 0xFF,
+		byte(cmd >> 8), byte(cmd & 0xFF),
+		0x03,     // len
+		relayNum, // 1=继电器1  2=继电器2
+		option,
+		validTime,
+	}
+	crc := uiCrc16Cal(frame, uint8(len(frame)))
+	frame = append(frame, byte(crc&0xFF), byte(crc>>8))
+	return frame
+}

+ 53 - 0
internal/modules/parking/service/reader_interface.go

@@ -0,0 +1,53 @@
+package parking
+
+import (
+	"errors"
+	"wails-app/internal/dao"
+)
+
+// UHFReaderConn 统一的设备连接接口(适配多设备+消息收发)
+type UHFReaderConn interface {
+	Connect() error                          // 建立连接
+	Disconnect() error                       // 断开连接
+	IsConnected() bool                       // 检查连接状态
+	SendData(data []byte) error              // 发送数据(仅发送,无返回)
+	ReadData() ([]byte, error)               // 读取数据(阻塞式)
+	SendAndRecv(data []byte) ([]byte, error) // 发送并等待响应(一问一答)
+}
+
+// NewReaderConn 批量创建连接(多设备扩展)
+func NewReaderConn(device *dao.UHFReader) (UHFReaderConn, error) {
+	switch device.ConnectType {
+	case "tcp":
+		return &TCPReader{
+			IP:   device.IPAddress,
+			Port: device.Port,
+		}, nil
+	case "serial":
+		return &SerialReader{
+			COMPort:  device.COMPort,
+			BaudRate: device.BaudRate,
+		}, nil
+	default:
+		return nil, errors.New("不支持的连接类型:" + device.ConnectType)
+	}
+}
+
+// BatchConnect 批量连接设备(多设备初始化)
+func BatchConnect(devices []*dao.UHFReader) map[string]error {
+	result := make(map[string]error)
+	for _, device := range devices {
+		conn, err := NewReaderConn(device)
+		if err != nil {
+			result[device.DeviceCode] = err
+			continue
+		}
+		if err := conn.Connect(); err != nil {
+			result[device.DeviceCode] = err
+			continue
+		}
+		// 加入连接池
+		ConnManager.AddConn(device.DeviceCode, conn)
+	}
+	return result
+}

+ 118 - 0
internal/modules/parking/service/serial_reader.go

@@ -0,0 +1,118 @@
+package parking
+
+import (
+	"errors"
+	"github.com/tarm/serial"
+	"io"
+	"os"
+	"time"
+)
+
+type SerialReader struct {
+	COMPort     string
+	BaudRate    int
+	port        *serial.Port
+	isConnected bool
+	buffer      []byte // 拼包缓冲区
+}
+
+func NewSerialReader(comPort string, baudRate int) *SerialReader {
+	return &SerialReader{
+		COMPort:  comPort,
+		BaudRate: baudRate,
+	}
+}
+
+func (s *SerialReader) Connect() error {
+	cfg := &serial.Config{
+		Name:        s.COMPort,
+		Baud:        s.BaudRate,
+		ReadTimeout: 1 * time.Second,
+		Size:        8,
+		Parity:      serial.ParityNone,
+		StopBits:    serial.Stop1,
+	}
+	port, err := serial.OpenPort(cfg)
+	if err != nil {
+		s.isConnected = false
+		return err
+	}
+	s.port = port
+	s.isConnected = true
+	return nil
+}
+
+func (s *SerialReader) Disconnect() error {
+	if s.port != nil {
+		_ = s.port.Close()
+	}
+	s.isConnected = false
+	s.buffer = nil
+	return nil
+}
+
+func (s *SerialReader) IsConnected() bool {
+	return s.isConnected
+}
+
+func (s *SerialReader) SendData(data []byte) error {
+	if !s.isConnected {
+		return errors.New("串口未连接")
+	}
+	n, err := s.port.Write(data)
+	if err != nil {
+		return err
+	}
+	if n != len(data) {
+		return errors.New("发送不完整")
+	}
+	return nil
+}
+
+// ==============================
+// 🔥 关键修复:串口返回完整25字节帧
+// ==============================
+func (s *SerialReader) ReadData() ([]byte, error) {
+	if !s.isConnected {
+		return nil, errors.New("串口未连接")
+	}
+
+	buf := make([]byte, 1024)
+	for {
+		n, err := s.port.Read(buf)
+		if err != nil {
+			if err == io.EOF || os.IsTimeout(err) {
+				break
+			}
+			s.isConnected = false
+			return nil, err
+		}
+		if n > 0 {
+			s.buffer = append(s.buffer, buf[:n]...)
+		}
+
+		// 找25字节完整帧
+		for len(s.buffer) >= 25 {
+			if s.buffer[0] == 0xCF {
+				frame := s.buffer[:25]
+				s.buffer = s.buffer[25:]
+				return frame, nil
+			} else {
+				s.buffer = s.buffer[1:]
+			}
+		}
+
+		// 超时退出
+		if len(s.buffer) == 0 {
+			break
+		}
+	}
+
+	return nil, errors.New("等待完整帧")
+}
+
+func (s *SerialReader) SendAndRecv(data []byte) ([]byte, error) {
+	_ = s.SendData(data)
+	time.Sleep(100 * time.Millisecond)
+	return s.ReadData()
+}

+ 126 - 0
internal/modules/parking/service/tcp_reader.go

@@ -0,0 +1,126 @@
+package parking
+
+import (
+	"errors"
+	"net"
+	"time"
+)
+
+type TCPReader struct {
+	IP          string
+	Port        int
+	conn        net.Conn
+	isConnected bool
+	buffer      []byte // 内部拼包缓冲区(关键修复)
+}
+
+// 实现 Reader 接口的继电器方法(不再panic)
+func (t *TCPReader) CloseRelay1(validTime byte) error {
+	frame := buildRelayFrame(0x0077, 1, RELAY_OP_CLOSE, validTime)
+	_, err := t.SendAndRecv(frame)
+	return err
+}
+
+func (t *TCPReader) ReleaseRelay1() error {
+	frame := buildRelayFrame(0x0077, 1, RELAY_OP_RELEASE, 0)
+	_, err := t.SendAndRecv(frame)
+	return err
+}
+
+func (t *TCPReader) CloseRelay2(validTime byte) error {
+	frame := buildRelayFrame(0x0078, 2, RELAY_OP_CLOSE, validTime)
+	_, err := t.SendAndRecv(frame)
+	return err
+}
+
+func (t *TCPReader) ReleaseRelay2() error {
+	frame := buildRelayFrame(0x0078, 2, RELAY_OP_RELEASE, 0)
+	_, err := t.SendAndRecv(frame)
+	return err
+}
+
+func NewTCPReader(ip string, port int) *TCPReader {
+	return &TCPReader{
+		IP:   ip,
+		Port: port,
+	}
+}
+
+func (t *TCPReader) Connect() error {
+	addr := net.JoinHostPort(t.IP, string(rune(t.Port)))
+	conn, err := net.DialTimeout("tcp", addr, 5*time.Second)
+	if err != nil {
+		t.isConnected = false
+		return err
+	}
+	t.conn = conn
+	t.isConnected = true
+	return nil
+}
+
+func (t *TCPReader) Disconnect() error {
+	if t.conn != nil {
+		_ = t.conn.Close()
+	}
+	t.isConnected = false
+	t.buffer = nil
+	return nil
+}
+
+func (t *TCPReader) IsConnected() bool {
+	return t.isConnected
+}
+
+func (t *TCPReader) SendData(data []byte) error {
+	if !t.isConnected {
+		return errors.New("TCP未连接")
+	}
+	_, err := t.conn.Write(data)
+	if err != nil {
+		t.isConnected = false
+	}
+	return err
+}
+
+// ==============================
+// 🔥 关键修复:TCP 自动拼包,返回完整25字节帧
+// ==============================
+func (t *TCPReader) ReadData() ([]byte, error) {
+	if !t.isConnected {
+		return nil, errors.New("TCP未连接")
+	}
+
+	// 读超时,防止永久阻塞
+	t.conn.SetReadDeadline(time.Now().Add(3 * time.Second))
+
+	buf := make([]byte, 1024)
+	n, err := t.conn.Read(buf)
+	if err != nil {
+		t.isConnected = false
+		return nil, err
+	}
+
+	// 加入缓冲区
+	t.buffer = append(t.buffer, buf[:n]...)
+
+	// 找完整帧:0xCF 开头 + 25字节
+	for len(t.buffer) >= 25 {
+		if t.buffer[0] == 0xCF {
+			frame := t.buffer[:25]
+			t.buffer = t.buffer[25:]
+			return frame, nil
+		} else {
+			t.buffer = t.buffer[1:]
+		}
+	}
+
+	return nil, errors.New("等待完整帧")
+}
+
+func (t *TCPReader) SendAndRecv(data []byte) ([]byte, error) {
+	if err := t.SendData(data); err != nil {
+		return nil, err
+	}
+	t.conn.SetReadDeadline(time.Now().Add(3 * time.Second))
+	return t.ReadData()
+}

+ 285 - 0
internal/modules/parking/service/uhf_service.go

@@ -0,0 +1,285 @@
+package parking
+
+import (
+	"fmt"
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/modules/parking/model/request"
+	"wails-app/internal/modules/parking/model/response"
+
+	"go.uber.org/zap"
+)
+
+type uhfService struct{}
+
+var UHFService = new(uhfService)
+
+// CreateReader 创建UHF读写器
+func (s *uhfService) CreateReader(req request.UHFReaderCreate) error {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("device_code = ?", req.DeviceCode).First(&reader)
+	if result.RowsAffected > 0 {
+		return fmt.Errorf("设备编码已存在")
+	}
+
+	var channel dao.Channel
+	global.GVA_DB.Model(&channel).Where("id = ?", req.ChannelId).First(&channel)
+
+	var booth dao.Booth
+	global.GVA_DB.Model(&booth).Where("id = ?", channel.BoothID).First(&booth)
+
+	reader = dao.UHFReader{
+		DeviceCode:   req.DeviceCode,
+		DeviceName:   req.DeviceName,
+		DeviceType:   req.DeviceType,
+		ConnectType:  req.ConnectType,
+		IPAddress:    req.IPAddress,
+		Port:         req.Port,
+		COMPort:      req.COMPort,
+		BaudRate:     req.BaudRate,
+		IsActive:     req.IsActive,
+		Description:  req.Description,
+		ParkingLotID: booth.ParkingLotID,
+		ChannelID:    uint(req.ChannelId),
+	}
+
+	err := global.GVA_DB.Create(&reader).Error
+
+	if req.IsActive {
+		if err := StartDeviceHandler(&reader); err != nil {
+			global.GVA_LOG.Error("启动设备失败", zap.String("code", reader.DeviceCode), zap.Error(err))
+		}
+	}
+
+	return err
+}
+
+// UpdateReader 更新UHF读写器
+func (s *uhfService) UpdateReader(req request.UHFReaderUpdate) error {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("id = ?", req.ID).First(&reader)
+	if result.RowsAffected == 0 {
+		return fmt.Errorf("设备不存在")
+	}
+
+	updateData := map[string]interface{}{
+		"device_name":  req.DeviceName,
+		"device_type":  req.DeviceType,
+		"connect_type": req.ConnectType,
+		"ip_address":   req.IPAddress,
+		"port":         req.Port,
+		"com_port":     req.COMPort,
+		"baud_rate":    req.BaudRate,
+		"is_active":    req.IsActive,
+		"description":  req.Description,
+		"channel_id":   req.ChannelId,
+	}
+
+	return global.GVA_DB.Model(&reader).Updates(updateData).Error
+}
+
+// ConnectedDevice 连接设备
+func (s *uhfService) ConnectedDevice(id uint) error {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("id = ?", id).First(&reader)
+	if result.RowsAffected == 0 {
+		return fmt.Errorf("设备不存在")
+	}
+
+	return StartDeviceHandler(&reader)
+}
+
+// GetReaderByCode 根据设备编码获取设备
+func (s *uhfService) GetReaderByCode(code string) (response.UHFReaderResponse, error) {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("device_code = ?", code).First(&reader)
+	if result.RowsAffected == 0 {
+		return response.UHFReaderResponse{}, fmt.Errorf("设备不存在")
+	}
+
+	return response.UHFReaderResponse{
+		ID:           reader.ID,
+		DeviceCode:   reader.DeviceCode,
+		DeviceName:   reader.DeviceName,
+		ConnectType:  reader.ConnectType,
+		IPAddress:    reader.IPAddress,
+		Port:         reader.Port,
+		COMPort:      reader.COMPort,
+		BaudRate:     reader.BaudRate,
+		Status:       reader.Status,
+		IsActive:     reader.IsActive,
+		Description:  reader.Description,
+		ParkingLotID: reader.ParkingLotID,
+		CreatedAt:    reader.CreatedAt,
+		UpdatedAt:    reader.UpdatedAt,
+	}, nil
+}
+
+func (s *uhfService) QueryAllReaders() ([]dao.UHFReader, error) {
+	var readers []dao.UHFReader
+	err := global.GVA_DB.Find(&readers).Error
+	if err != nil {
+		return readers, err
+	}
+	return readers, nil
+}
+
+// ListReaders 分页查询设备列表
+func (s *uhfService) ListReaders(req request.UHFReaderQuery) ([]dao.UHFReader, int64, error) {
+	var readers []dao.UHFReader
+	var total int64
+
+	query := global.GVA_DB.Model(&dao.UHFReader{}).Preload("Channel").Preload("ParkingLot")
+
+	if req.DeviceCode != "" {
+		query = query.Where("device_code LIKE ?", "%"+req.DeviceCode+"%")
+	}
+	if req.DeviceName != "" {
+		query = query.Where("device_name LIKE ?", "%"+req.DeviceName+"%")
+	}
+	if req.Status != "" {
+		query = query.Where("status = ?", req.Status)
+	}
+	if req.ConnectType != "" {
+		query = query.Where("connect_type = ?", req.ConnectType)
+	}
+
+	if req.ParkingLotID != 0 {
+		query = query.Where("parking_lot_id = ?", req.ParkingLotID)
+	}
+
+	err := query.Count(&total).Error
+	if err != nil {
+		return nil, 0, err
+	}
+
+	limit := req.PageSize
+	offset := (req.Page - 1) * req.PageSize
+	err = query.Limit(limit).Offset(offset).Order("created_at DESC").Find(&readers).Error
+	if err != nil {
+		return nil, 0, err
+	}
+
+	return readers, total, nil
+}
+
+// DeleteReader 删除设备
+func (s *uhfService) DeleteReader(id uint) error {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("id = ?", id).First(&reader)
+	if result.RowsAffected == 0 {
+		return fmt.Errorf("设备不存在")
+	}
+
+	return global.GVA_DB.Delete(&reader).Error
+}
+
+// ConnectReader 连接设备
+func (s *uhfService) ConnectReader(id uint) error {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("id = ?", id).First(&reader)
+	if result.RowsAffected == 0 {
+		return fmt.Errorf("设备不存在")
+	}
+
+	if !reader.IsActive {
+		return fmt.Errorf("设备未启用")
+	}
+
+	err := global.GVA_DB.Model(&reader).Update("status", "online").Error
+	if err != nil {
+		return err
+	}
+
+	fmt.Printf("设备[%s]连接成功\n", reader.DeviceCode)
+	return nil
+}
+
+// DisconnectReader 断开设备连接
+func (s *uhfService) DisconnectReader(id uint) error {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("id = ?", id).First(&reader)
+	if result.RowsAffected == 0 {
+		return fmt.Errorf("设备不存在")
+	}
+
+	err := global.GVA_DB.Model(&reader).Update("status", "offline").Error
+	if err != nil {
+		return err
+	}
+
+	fmt.Printf("设备[%s]断开连接\n", reader.DeviceCode)
+	return nil
+}
+
+// ReportTag 客户端上报标签数据
+func (s *uhfService) ReportTag(deviceCode string, tag string) error {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("device_code = ?", deviceCode).First(&reader)
+	if result.RowsAffected == 0 {
+		return fmt.Errorf("设备不存在")
+	}
+
+	fmt.Printf("📶 收到设备 %s 上报的标签: %s\n", deviceCode, tag)
+
+	return nil
+}
+
+// SetReaderPower 设置功率(模拟实现)
+func (s *uhfService) SetReaderPower(id uint, power int) error {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("id = ?", id).First(&reader)
+	if result.RowsAffected == 0 {
+		return fmt.Errorf("设备不存在")
+	}
+
+	if reader.Status != "online" {
+		return fmt.Errorf("设备未连接")
+	}
+
+	fmt.Printf("设备[%s]功率设置为: %ddBm\n", reader.DeviceCode, power)
+	return nil
+}
+
+// StartInventory 启动读卡(模拟实现)
+func (s *uhfService) StartInventory(id uint) error {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("id = ?", id).First(&reader)
+	if result.RowsAffected == 0 {
+		return fmt.Errorf("设备不存在")
+	}
+
+	if reader.Status != "online" {
+		return fmt.Errorf("设备未连接")
+	}
+
+	fmt.Printf("设备[%s]已启动读卡模式\n", reader.DeviceCode)
+	return nil
+}
+
+// StopInventory 停止读卡(模拟实现)
+func (s *uhfService) StopInventory(id uint) error {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("id = ?", id).First(&reader)
+	if result.RowsAffected == 0 {
+		return fmt.Errorf("设备不存在")
+	}
+
+	fmt.Printf("设备[%s]已停止读卡\n", reader.DeviceCode)
+	return nil
+}
+
+// ReadTag 读取标签(模拟实现)
+func (s *uhfService) ReadTag(id uint) (string, error) {
+	var reader dao.UHFReader
+	result := global.GVA_DB.Where("id = ?", id).First(&reader)
+	if result.RowsAffected == 0 {
+		return "", fmt.Errorf("设备不存在")
+	}
+
+	if reader.Status != "online" {
+		return "", fmt.Errorf("设备未连接")
+	}
+
+	return "", nil
+}

+ 11 - 0
internal/modules/system/api/enter.go

@@ -0,0 +1,11 @@
+package system
+
+import "wails-app/internal/service"
+
+type ApiGroup struct {
+	FileUploadAndDownloadApi
+}
+
+var (
+	fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
+)

+ 231 - 0
internal/modules/system/api/sys_api.go

@@ -0,0 +1,231 @@
+package system
+
+import (
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/request"
+	"wails-app/internal/model/common/response"
+	systemReq "wails-app/internal/modules/system/model/request"
+	systemRes "wails-app/internal/modules/system/model/response"
+	"wails-app/internal/pkg"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+type SystemApiApi struct{}
+
+// CreateApi
+// @Tags      SysApi
+// @Summary   创建基础api
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysApi                  true  "api路径, api中文描述, api组, 方法"
+// @Success   200   {object}  response.Response{msg=string}  "创建基础api"
+// @Router    /api/createApi [post]
+func (s *SystemApiApi) CreateApi(c *gin.Context) {
+	var api dao.SysApi
+	err := c.ShouldBindJSON(&api)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(api, utils.ApiVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = apiService.CreateApi(api)
+	if err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+		return
+	}
+	response.OkWithMessage("创建成功", c)
+}
+
+// DeleteApi
+// @Tags      SysApi
+// @Summary   删除api
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysApi                  true  "ID"
+// @Success   200   {object}  response.Response{msg=string}  "删除api"
+// @Router    /api/deleteApi [post]
+func (s *SystemApiApi) DeleteApi(c *gin.Context) {
+	var api dao.SysApi
+	err := c.ShouldBindJSON(&api)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(api.GVA_MODEL, utils.IdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = apiService.DeleteApi(api)
+	if err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}
+
+// GetApiList
+// @Tags      SysApi
+// @Summary   分页获取API列表
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      systemReq.SearchApiParams                               true  "分页获取API列表"
+// @Success   200   {object}  response.Response{data=response.PageResult,msg=string}  "分页获取API列表,返回包括列表,总数,页码,每页数量"
+// @Router    /api/getApiList [post]
+func (s *SystemApiApi) GetApiList(c *gin.Context) {
+	var pageInfo systemReq.SearchApiParams
+	err := c.ShouldBindJSON(&pageInfo)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(pageInfo.PageInfo, utils.PageInfoVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	list, total, err := apiService.GetAPIInfoList(pageInfo.SysApi, pageInfo.PageInfo, pageInfo.OrderKey, pageInfo.Desc)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     pageInfo.Page,
+		PageSize: pageInfo.PageSize,
+	}, "获取成功", c)
+}
+
+// GetApiById
+// @Tags      SysApi
+// @Summary   根据id获取api
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.GetById                                   true  "根据id获取api"
+// @Success   200   {object}  response.Response{data=systemRes.SysAPIResponse}  "根据id获取api,返回包括api详情"
+// @Router    /api/getApiById [post]
+func (s *SystemApiApi) GetApiById(c *gin.Context) {
+	var idInfo request.GetById
+	err := c.ShouldBindJSON(&idInfo)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(idInfo, utils.IdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	api, err := apiService.GetApiById(idInfo.ID)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(systemRes.SysAPIResponse{Api: api}, "获取成功", c)
+}
+
+// UpdateApi
+// @Tags      SysApi
+// @Summary   修改基础api
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysApi                  true  "api路径, api中文描述, api组, 方法"
+// @Success   200   {object}  response.Response{msg=string}  "修改基础api"
+// @Router    /api/updateApi [post]
+func (s *SystemApiApi) UpdateApi(c *gin.Context) {
+	var api dao.SysApi
+	err := c.ShouldBindJSON(&api)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(api, utils.ApiVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = apiService.UpdateApi(api)
+	if err != nil {
+		global.GVA_LOG.Error("修改失败!", zap.Error(err))
+		response.FailWithMessage("修改失败", c)
+		return
+	}
+	response.OkWithMessage("修改成功", c)
+}
+
+// GetAllApis
+// @Tags      SysApi
+// @Summary   获取所有的Api 不分页
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Success   200  {object}  response.Response{data=systemRes.SysAPIListResponse,msg=string}  "获取所有的Api 不分页,返回包括api列表"
+// @Router    /api/getAllApis [post]
+func (s *SystemApiApi) GetAllApis(c *gin.Context) {
+	apis, err := apiService.GetAllApis()
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(systemRes.SysAPIListResponse{Apis: apis}, "获取成功", c)
+}
+
+// DeleteApisByIds
+// @Tags      SysApi
+// @Summary   删除选中Api
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.IdsReq                 true  "ID"
+// @Success   200   {object}  response.Response{msg=string}  "删除选中Api"
+// @Router    /api/deleteApisByIds [delete]
+func (s *SystemApiApi) DeleteApisByIds(c *gin.Context) {
+	var ids request.IdsReq
+	err := c.ShouldBindJSON(&ids)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = apiService.DeleteApisByIds(ids)
+	if err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}
+
+// FreshCasbin
+// @Tags      SysApi
+// @Summary   刷新casbin缓存
+// @accept    application/json
+// @Produce   application/json
+// @Success   200   {object}  response.Response{msg=string}  "刷新成功"
+// @Router    /api/freshCasbin [get]
+func (s *SystemApiApi) FreshCasbin(c *gin.Context) {
+	err := casbinService.FreshCasbin()
+	if err != nil {
+		global.GVA_LOG.Error("刷新失败!", zap.Error(err))
+		response.FailWithMessage("刷新失败", c)
+		return
+	}
+	response.OkWithMessage("刷新成功", c)
+}

+ 177 - 0
internal/modules/system/api/sys_authority.go

@@ -0,0 +1,177 @@
+package system
+
+import (
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/request"
+	"wails-app/internal/model/common/response"
+	systemRes "wails-app/internal/modules/system/model/response"
+	"wails-app/internal/pkg"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+type AuthorityApi struct{}
+
+// CreateAuthority
+// @Tags      Authority
+// @Summary   创建角色
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysAuthority                                                true  "权限id, 权限名, 父角色id"
+// @Success   200   {object}  response.Response{data=systemRes.SysAuthorityResponse,msg=string}  "创建角色,返回包括系统角色详情"
+// @Router    /authority/createAuthority [post]
+func (a *AuthorityApi) CreateAuthority(c *gin.Context) {
+	var authority, authBack dao.SysAuthority
+	var err error
+
+	if err = c.ShouldBindJSON(&authority); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	if err = utils.Verify(authority, utils.AuthorityVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	if authBack, err = authorityService.CreateAuthority(authority); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败"+err.Error(), c)
+		return
+	}
+	err = casbinService.FreshCasbin()
+	if err != nil {
+		global.GVA_LOG.Error("创建成功,权限刷新失败。", zap.Error(err))
+		response.FailWithMessage("创建成功,权限刷新失败。"+err.Error(), c)
+		return
+	}
+	response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "创建成功", c)
+}
+
+// DeleteAuthority
+// @Tags      Authority
+// @Summary   删除角色
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysAuthority            true  "删除角色"
+// @Success   200   {object}  response.Response{msg=string}  "删除角色"
+// @Router    /authority/deleteAuthority [post]
+func (a *AuthorityApi) DeleteAuthority(c *gin.Context) {
+	var authority dao.SysAuthority
+	var err error
+	if err = c.ShouldBindJSON(&authority); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err = utils.Verify(authority, utils.AuthorityIdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	// 删除角色之前需要判断是否有用户正在使用此角色
+	if err = authorityService.DeleteAuthority(&authority); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败"+err.Error(), c)
+		return
+	}
+	_ = casbinService.FreshCasbin()
+	response.OkWithMessage("删除成功", c)
+}
+
+// UpdateAuthority
+// @Tags      Authority
+// @Summary   更新角色信息
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysAuthority                                                true  "权限id, 权限名, 父角色id"
+// @Success   200   {object}  response.Response{data=systemRes.SysAuthorityResponse,msg=string}  "更新角色信息,返回包括系统角色详情"
+// @Router    /authority/updateAuthority [post]
+func (a *AuthorityApi) UpdateAuthority(c *gin.Context) {
+	var auth dao.SysAuthority
+	err := c.ShouldBindJSON(&auth)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(auth, utils.AuthorityVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	authority, err := authorityService.UpdateAuthority(auth)
+	if err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Error(err))
+		response.FailWithMessage("更新失败"+err.Error(), c)
+		return
+	}
+	response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authority}, "更新成功", c)
+}
+
+// GetAuthorityList
+// @Tags      Authority
+// @Summary   分页获取角色列表
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.PageInfo                                        true  "页码, 每页大小"
+// @Success   200   {object}  response.Response{data=response.PageResult,msg=string}  "分页获取角色列表,返回包括列表,总数,页码,每页数量"
+// @Router    /authority/getAuthorityList [post]
+func (a *AuthorityApi) GetAuthorityList(c *gin.Context) {
+	var pageInfo request.PageInfo
+	err := c.ShouldBindJSON(&pageInfo)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(pageInfo, utils.PageInfoVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	list, total, err := authorityService.GetAuthorityInfoList(pageInfo)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败"+err.Error(), c)
+		return
+	}
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     pageInfo.Page,
+		PageSize: pageInfo.PageSize,
+	}, "获取成功", c)
+}
+
+// SetDataAuthority
+// @Tags      Authority
+// @Summary   设置角色资源权限
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysAuthority            true  "设置角色资源权限"
+// @Success   200   {object}  response.Response{msg=string}  "设置角色资源权限"
+// @Router    /authority/setDataAuthority [post]
+func (a *AuthorityApi) SetDataAuthority(c *gin.Context) {
+	var auth dao.SysAuthority
+	err := c.ShouldBindJSON(&auth)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(auth, utils.AuthorityIdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = authorityService.SetDataAuthority(auth)
+	if err != nil {
+		global.GVA_LOG.Error("设置失败!", zap.Error(err))
+		response.FailWithMessage("设置失败"+err.Error(), c)
+		return
+	}
+	response.OkWithMessage("设置成功", c)
+}

+ 80 - 0
internal/modules/system/api/sys_authority_btn.go

@@ -0,0 +1,80 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/modules/system/model/request"
+)
+
+type AuthorityBtnApi struct{}
+
+// GetAuthorityBtn
+// @Tags      AuthorityBtn
+// @Summary   获取权限按钮
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.SysAuthorityBtnReq                                      true  "菜单id, 角色id, 选中的按钮id"
+// @Success   200   {object}  response.Response{data=response.SysAuthorityBtnRes,msg=string}  "返回列表成功"
+// @Router    /authorityBtn/getAuthorityBtn [post]
+func (a *AuthorityBtnApi) GetAuthorityBtn(c *gin.Context) {
+	var req request.SysAuthorityBtnReq
+	err := c.ShouldBindJSON(&req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	res, err := authorityBtnService.GetAuthorityBtn(req)
+	if err != nil {
+		global.GVA_LOG.Error("查询失败!", zap.Error(err))
+		response.FailWithMessage("查询失败", c)
+		return
+	}
+	response.OkWithDetailed(res, "查询成功", c)
+}
+
+// SetAuthorityBtn
+// @Tags      AuthorityBtn
+// @Summary   设置权限按钮
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.SysAuthorityBtnReq     true  "菜单id, 角色id, 选中的按钮id"
+// @Success   200   {object}  response.Response{msg=string}  "返回列表成功"
+// @Router    /authorityBtn/setAuthorityBtn [post]
+func (a *AuthorityBtnApi) SetAuthorityBtn(c *gin.Context) {
+	var req request.SysAuthorityBtnReq
+	err := c.ShouldBindJSON(&req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = authorityBtnService.SetAuthorityBtn(req)
+	if err != nil {
+		global.GVA_LOG.Error("分配失败!", zap.Error(err))
+		response.FailWithMessage("分配失败", c)
+		return
+	}
+	response.OkWithMessage("分配成功", c)
+}
+
+// CanRemoveAuthorityBtn
+// @Tags      AuthorityBtn
+// @Summary   设置权限按钮
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Success   200  {object}  response.Response{msg=string}  "删除成功"
+// @Router    /authorityBtn/canRemoveAuthorityBtn [post]
+func (a *AuthorityBtnApi) CanRemoveAuthorityBtn(c *gin.Context) {
+	id := c.Query("id")
+	err := authorityBtnService.CanRemoveAuthorityBtn(id)
+	if err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}

+ 70 - 0
internal/modules/system/api/sys_captcha.go

@@ -0,0 +1,70 @@
+package system
+
+import (
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"github.com/mojocn/base64Captcha"
+	"go.uber.org/zap"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/response"
+	systemRes "wails-app/internal/modules/system/model/response"
+)
+
+// 当开启多服务器部署时,替换下面的配置,使用redis共享存储验证码
+// var store = captcha.NewDefaultRedisStore()
+var store = base64Captcha.DefaultMemStore
+
+type BaseApi struct{}
+
+// Captcha
+// @Tags      Base
+// @Summary   生成验证码
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Success   200  {object}  response.Response{data=systemRes.SysCaptchaResponse,msg=string}  "生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码"
+// @Router    /base/captcha [post]
+func (b *BaseApi) Captcha(c *gin.Context) {
+	// 判断验证码是否开启
+	openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha               // 是否开启防爆次数
+	openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
+	key := c.ClientIP()
+	v, ok := global.BlackCache.Get(key)
+	if !ok {
+		global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
+	}
+
+	var oc bool
+	if openCaptcha == 0 || openCaptcha < interfaceToInt(v) {
+		oc = true
+	}
+	// 字符,公式,验证码配置
+	// 生成默认数字的driver
+	driver := base64Captcha.NewDriverDigit(global.GVA_CONFIG.Captcha.ImgHeight, global.GVA_CONFIG.Captcha.ImgWidth, global.GVA_CONFIG.Captcha.KeyLong, 0.7, 80)
+	// cp := base64Captcha.NewCaptcha(driver, store.UseWithCtx(c))   // v8下使用redis
+	cp := base64Captcha.NewCaptcha(driver, store)
+	id, b64s, _, err := cp.Generate()
+	if err != nil {
+		global.GVA_LOG.Error("验证码获取失败!", zap.Error(err))
+		response.FailWithMessage("验证码获取失败", c)
+		return
+	}
+	response.OkWithDetailed(systemRes.SysCaptchaResponse{
+		CaptchaId:     id,
+		PicPath:       b64s,
+		CaptchaLength: global.GVA_CONFIG.Captcha.KeyLong,
+		OpenCaptcha:   oc,
+	}, "验证码获取成功", c)
+}
+
+// 类型转换
+func interfaceToInt(v interface{}) (i int) {
+	switch v := v.(type) {
+	case int:
+		i = v
+	default:
+		i = 0
+	}
+	return
+}

+ 68 - 0
internal/modules/system/api/sys_casbin.go

@@ -0,0 +1,68 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/modules/system/model/request"
+	systemRes "wails-app/internal/modules/system/model/response"
+	"wails-app/internal/pkg"
+)
+
+type CasbinApi struct{}
+
+// UpdateCasbin
+// @Tags      Casbin
+// @Summary   更新角色api权限
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.CasbinInReceive        true  "权限id, 权限模型列表"
+// @Success   200   {object}  response.Response{msg=string}  "更新角色api权限"
+// @Router    /casbin/UpdateCasbin [post]
+func (cas *CasbinApi) UpdateCasbin(c *gin.Context) {
+	var cmr request.CasbinInReceive
+	err := c.ShouldBindJSON(&cmr)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(cmr, utils.AuthorityIdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = casbinService.UpdateCasbin(cmr.AuthorityId, cmr.CasbinInfos)
+	if err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Error(err))
+		response.FailWithMessage("更新失败", c)
+		return
+	}
+	response.OkWithMessage("更新成功", c)
+}
+
+// GetPolicyPathByAuthorityId
+// @Tags      Casbin
+// @Summary   获取权限列表
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.CasbinInReceive                                          true  "权限id, 权限模型列表"
+// @Success   200   {object}  response.Response{data=systemRes.PolicyPathResponse,msg=string}  "获取权限列表,返回包括casbin详情列表"
+// @Router    /casbin/getPolicyPathByAuthorityId [post]
+func (cas *CasbinApi) GetPolicyPathByAuthorityId(c *gin.Context) {
+	var casbin request.CasbinInReceive
+	err := c.ShouldBindJSON(&casbin)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(casbin, utils.AuthorityIdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	paths := casbinService.GetPolicyPathByAuthorityId(casbin.AuthorityId)
+	response.OkWithDetailed(systemRes.PolicyPathResponse{Paths: paths}, "获取成功", c)
+}

+ 129 - 0
internal/modules/system/api/sys_dictionary.go

@@ -0,0 +1,129 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/response"
+)
+
+type DictionaryApi struct{}
+
+// CreateSysDictionary
+// @Tags      SysDictionary
+// @Summary   创建SysDictionary
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysDictionary           true  "SysDictionary模型"
+// @Success   200   {object}  response.Response{msg=string}  "创建SysDictionary"
+// @Router    /sysDictionary/createSysDictionary [post]
+func (s *DictionaryApi) CreateSysDictionary(c *gin.Context) {
+	var dictionary dao.SysDictionary
+	err := c.ShouldBindJSON(&dictionary)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = dictionaryService.CreateSysDictionary(dictionary)
+	if err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+		return
+	}
+	response.OkWithMessage("创建成功", c)
+}
+
+// DeleteSysDictionary
+// @Tags      SysDictionary
+// @Summary   删除SysDictionary
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysDictionary           true  "SysDictionary模型"
+// @Success   200   {object}  response.Response{msg=string}  "删除SysDictionary"
+// @Router    /sysDictionary/deleteSysDictionary [delete]
+func (s *DictionaryApi) DeleteSysDictionary(c *gin.Context) {
+	var dictionary dao.SysDictionary
+	err := c.ShouldBindJSON(&dictionary)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = dictionaryService.DeleteSysDictionary(dictionary)
+	if err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}
+
+// UpdateSysDictionary
+// @Tags      SysDictionary
+// @Summary   更新SysDictionary
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysDictionary           true  "SysDictionary模型"
+// @Success   200   {object}  response.Response{msg=string}  "更新SysDictionary"
+// @Router    /sysDictionary/updateSysDictionary [put]
+func (s *DictionaryApi) UpdateSysDictionary(c *gin.Context) {
+	var dictionary dao.SysDictionary
+	err := c.ShouldBindJSON(&dictionary)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = dictionaryService.UpdateSysDictionary(&dictionary)
+	if err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Error(err))
+		response.FailWithMessage("更新失败", c)
+		return
+	}
+	response.OkWithMessage("更新成功", c)
+}
+
+// FindSysDictionary
+// @Tags      SysDictionary
+// @Summary   用id查询SysDictionary
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  query     system.SysDictionary                                       true  "ID或字典英名"
+// @Success   200   {object}  response.Response{data=map[string]interface{},msg=string}  "用id查询SysDictionary"
+// @Router    /sysDictionary/findSysDictionary [get]
+func (s *DictionaryApi) FindSysDictionary(c *gin.Context) {
+	var dictionary dao.SysDictionary
+	err := c.ShouldBindJSON(&dictionary)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	sysDictionary, err := dictionaryService.GetSysDictionary(dictionary.Type, dictionary.ID, dictionary.Status)
+	if err != nil {
+		global.GVA_LOG.Error("字典未创建或未开启!", zap.Error(err))
+		response.FailWithMessage("字典未创建或未开启", c)
+		return
+	}
+	response.OkWithDetailed(gin.H{"resysDictionary": sysDictionary}, "查询成功", c)
+}
+
+// GetSysDictionaryList
+// @Tags      SysDictionary
+// @Summary   分页获取SysDictionary列表
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Success   200   {object}  response.Response{data=response.PageResult,msg=string}  "分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量"
+// @Router    /sysDictionary/getSysDictionaryList [get]
+func (s *DictionaryApi) GetSysDictionaryList(c *gin.Context) {
+	list, err := dictionaryService.GetSysDictionaryInfoList()
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(list, "获取成功", c)
+}

+ 148 - 0
internal/modules/system/api/sys_dictionary_detail.go

@@ -0,0 +1,148 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/modules/system/model/request"
+	"wails-app/internal/pkg"
+)
+
+type DictionaryDetailApi struct{}
+
+// CreateSysDictionaryDetail
+// @Tags      SysDictionaryDetail
+// @Summary   创建SysDictionaryDetail
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysDictionaryDetail     true  "SysDictionaryDetail模型"
+// @Success   200   {object}  response.Response{msg=string}  "创建SysDictionaryDetail"
+// @Router    /sysDictionaryDetail/createSysDictionaryDetail [post]
+func (s *DictionaryDetailApi) CreateSysDictionaryDetail(c *gin.Context) {
+	var detail dao.SysDictionaryDetail
+	err := c.ShouldBindJSON(&detail)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = dictionaryDetailService.CreateSysDictionaryDetail(detail)
+	if err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+		return
+	}
+	response.OkWithMessage("创建成功", c)
+}
+
+// DeleteSysDictionaryDetail
+// @Tags      SysDictionaryDetail
+// @Summary   删除SysDictionaryDetail
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysDictionaryDetail     true  "SysDictionaryDetail模型"
+// @Success   200   {object}  response.Response{msg=string}  "删除SysDictionaryDetail"
+// @Router    /sysDictionaryDetail/deleteSysDictionaryDetail [delete]
+func (s *DictionaryDetailApi) DeleteSysDictionaryDetail(c *gin.Context) {
+	var detail dao.SysDictionaryDetail
+	err := c.ShouldBindJSON(&detail)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = dictionaryDetailService.DeleteSysDictionaryDetail(detail)
+	if err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}
+
+// UpdateSysDictionaryDetail
+// @Tags      SysDictionaryDetail
+// @Summary   更新SysDictionaryDetail
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysDictionaryDetail     true  "更新SysDictionaryDetail"
+// @Success   200   {object}  response.Response{msg=string}  "更新SysDictionaryDetail"
+// @Router    /sysDictionaryDetail/updateSysDictionaryDetail [put]
+func (s *DictionaryDetailApi) UpdateSysDictionaryDetail(c *gin.Context) {
+	var detail dao.SysDictionaryDetail
+	err := c.ShouldBindJSON(&detail)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = dictionaryDetailService.UpdateSysDictionaryDetail(&detail)
+	if err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Error(err))
+		response.FailWithMessage("更新失败", c)
+		return
+	}
+	response.OkWithMessage("更新成功", c)
+}
+
+// FindSysDictionaryDetail
+// @Tags      SysDictionaryDetail
+// @Summary   用id查询SysDictionaryDetail
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  query     system.SysDictionaryDetail                                 true  "用id查询SysDictionaryDetail"
+// @Success   200   {object}  response.Response{data=map[string]interface{},msg=string}  "用id查询SysDictionaryDetail"
+// @Router    /sysDictionaryDetail/findSysDictionaryDetail [get]
+func (s *DictionaryDetailApi) FindSysDictionaryDetail(c *gin.Context) {
+	var detail dao.SysDictionaryDetail
+	err := c.ShouldBindJSON(&detail)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(detail, utils.IdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	reSysDictionaryDetail, err := dictionaryDetailService.GetSysDictionaryDetail(detail.ID)
+	if err != nil {
+		global.GVA_LOG.Error("查询失败!", zap.Error(err))
+		response.FailWithMessage("查询失败", c)
+		return
+	}
+	response.OkWithDetailed(gin.H{"reSysDictionaryDetail": reSysDictionaryDetail}, "查询成功", c)
+}
+
+// GetSysDictionaryDetailList
+// @Tags      SysDictionaryDetail
+// @Summary   分页获取SysDictionaryDetail列表
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  query     request.SysDictionaryDetailSearch                       true  "页码, 每页大小, 搜索条件"
+// @Success   200   {object}  response.Response{data=response.PageResult,msg=string}  "分页获取SysDictionaryDetail列表,返回包括列表,总数,页码,每页数量"
+// @Router    /sysDictionaryDetail/getSysDictionaryDetailList [get]
+func (s *DictionaryDetailApi) GetSysDictionaryDetailList(c *gin.Context) {
+	var pageInfo request.SysDictionaryDetailSearch
+	err := c.ShouldBindJSON(&pageInfo)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	list, total, err := dictionaryDetailService.GetSysDictionaryDetailInfoList(pageInfo)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     pageInfo.Page,
+		PageSize: pageInfo.PageSize,
+	}, "获取成功", c)
+}

+ 33 - 0
internal/modules/system/api/sys_jwt_blacklist.go

@@ -0,0 +1,33 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/pkg"
+)
+
+type JwtApi struct{}
+
+// JsonInBlacklist
+// @Tags      Jwt
+// @Summary   jwt加入黑名单
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Success   200  {object}  response.Response{msg=string}  "jwt加入黑名单"
+// @Router    /jwt/jsonInBlacklist [post]
+func (j *JwtApi) JsonInBlacklist(c *gin.Context) {
+	token := utils.GetToken(c)
+	jwt := dao.JwtBlacklist{Jwt: token}
+	err := jwtService.JsonInBlacklist(jwt)
+	if err != nil {
+		global.GVA_LOG.Error("jwt作废失败!", zap.Error(err))
+		response.FailWithMessage("jwt作废失败", c)
+		return
+	}
+	utils.ClearToken(c)
+	response.OkWithMessage("jwt作废成功", c)
+}

+ 278 - 0
internal/modules/system/api/sys_menu.go

@@ -0,0 +1,278 @@
+package system
+
+import (
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/request"
+	"wails-app/internal/model/common/response"
+	systemReq "wails-app/internal/modules/system/model/request"
+	systemRes "wails-app/internal/modules/system/model/response"
+	"wails-app/internal/pkg"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+type AuthorityMenuApi struct{}
+
+// GetMenu
+// @Tags      AuthorityMenu
+// @Summary   获取用户动态路由
+// @Security  ApiKeyAuth
+// @Produce   application/json
+// @Param     data  body      request.Empty                                                  true  "空"
+// @Success   200   {object}  response.Response{data=systemRes.SysMenusResponse,msg=string}  "获取用户动态路由,返回包括系统菜单详情列表"
+// @Router    /menu/getMenu [post]
+func (a *AuthorityMenuApi) GetMenu(c *gin.Context) {
+	menus, err := menuService.GetMenuTree(utils.GetUserAuthorityId(c))
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	if menus == nil {
+		menus = []dao.SysMenu{}
+	}
+	response.OkWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取成功", c)
+}
+
+// GetBaseMenuTree
+// @Tags      AuthorityMenu
+// @Summary   获取用户动态路由
+// @Security  ApiKeyAuth
+// @Produce   application/json
+// @Param     data  body      request.Empty                                                      true  "空"
+// @Success   200   {object}  response.Response{data=systemRes.SysBaseMenusResponse,msg=string}  "获取用户动态路由,返回包括系统菜单列表"
+// @Router    /menu/getBaseMenuTree [post]
+func (a *AuthorityMenuApi) GetBaseMenuTree(c *gin.Context) {
+	menus, err := menuService.GetBaseMenuTree()
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(systemRes.SysBaseMenusResponse{Menus: menus}, "获取成功", c)
+}
+
+// AddMenuAuthority
+// @Tags      AuthorityMenu
+// @Summary   增加menu和角色关联关系
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      systemReq.AddMenuAuthorityInfo  true  "角色ID"
+// @Success   200   {object}  response.Response{msg=string}   "增加menu和角色关联关系"
+// @Router    /menu/addMenuAuthority [post]
+func (a *AuthorityMenuApi) AddMenuAuthority(c *gin.Context) {
+	var authorityMenu systemReq.AddMenuAuthorityInfo
+	err := c.ShouldBindJSON(&authorityMenu)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := utils.Verify(authorityMenu, utils.AuthorityIdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := menuService.AddMenuAuthority(authorityMenu.Menus, authorityMenu.AuthorityId); err != nil {
+		global.GVA_LOG.Error("添加失败!", zap.Error(err))
+		response.FailWithMessage("添加失败", c)
+	} else {
+		response.OkWithMessage("添加成功", c)
+	}
+}
+
+// GetMenuAuthority
+// @Tags      AuthorityMenu
+// @Summary   获取指定角色menu
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.GetAuthorityId                                     true  "角色ID"
+// @Success   200   {object}  response.Response{data=map[string]interface{},msg=string}  "获取指定角色menu"
+// @Router    /menu/getMenuAuthority [post]
+func (a *AuthorityMenuApi) GetMenuAuthority(c *gin.Context) {
+	var param request.GetAuthorityId
+	err := c.ShouldBindJSON(&param)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(param, utils.AuthorityIdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	menus, err := menuService.GetMenuAuthority(&param)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取失败", c)
+		return
+	}
+	response.OkWithDetailed(gin.H{"menus": menus}, "获取成功", c)
+}
+
+// AddBaseMenu
+// @Tags      Menu
+// @Summary   新增菜单
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysBaseMenu             true  "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
+// @Success   200   {object}  response.Response{msg=string}  "新增菜单"
+// @Router    /menu/addBaseMenu [post]
+func (a *AuthorityMenuApi) AddBaseMenu(c *gin.Context) {
+	var menu dao.SysBaseMenu
+	err := c.ShouldBindJSON(&menu)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(menu, utils.MenuVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(menu.Meta, utils.MenuMetaVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = menuService.AddBaseMenu(menu)
+	if err != nil {
+		global.GVA_LOG.Error("添加失败!", zap.Error(err))
+		response.FailWithMessage("添加失败", c)
+		return
+	}
+	response.OkWithMessage("添加成功", c)
+}
+
+// DeleteBaseMenu
+// @Tags      Menu
+// @Summary   删除菜单
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.GetById                true  "菜单id"
+// @Success   200   {object}  response.Response{msg=string}  "删除菜单"
+// @Router    /menu/deleteBaseMenu [post]
+func (a *AuthorityMenuApi) DeleteBaseMenu(c *gin.Context) {
+	var menu request.GetById
+	err := c.ShouldBindJSON(&menu)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(menu, utils.IdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = baseMenuService.DeleteBaseMenu(menu.ID)
+	if err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}
+
+// UpdateBaseMenu
+// @Tags      Menu
+// @Summary   更新菜单
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysBaseMenu             true  "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
+// @Success   200   {object}  response.Response{msg=string}  "更新菜单"
+// @Router    /menu/updateBaseMenu [post]
+func (a *AuthorityMenuApi) UpdateBaseMenu(c *gin.Context) {
+	var menu dao.SysBaseMenu
+	err := c.ShouldBindJSON(&menu)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(menu, utils.MenuVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(menu.Meta, utils.MenuMetaVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = baseMenuService.UpdateBaseMenu(menu)
+	if err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Error(err))
+		response.FailWithMessage("更新失败", c)
+		return
+	}
+	response.OkWithMessage("更新成功", c)
+}
+
+// GetBaseMenuById
+// @Tags      Menu
+// @Summary   根据id获取菜单
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.GetById                                                   true  "菜单id"
+// @Success   200   {object}  response.Response{data=systemRes.SysBaseMenuResponse,msg=string}  "根据id获取菜单,返回包括系统菜单列表"
+// @Router    /menu/getBaseMenuById [post]
+func (a *AuthorityMenuApi) GetBaseMenuById(c *gin.Context) {
+	var idInfo request.GetById
+	err := c.ShouldBindJSON(&idInfo)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(idInfo, utils.IdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	menu, err := baseMenuService.GetBaseMenuById(idInfo.ID)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(systemRes.SysBaseMenuResponse{Menu: menu}, "获取成功", c)
+}
+
+// GetMenuList
+// @Tags      Menu
+// @Summary   分页获取基础menu列表
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.PageInfo                                        true  "页码, 每页大小"
+// @Success   200   {object}  response.Response{data=response.PageResult,msg=string}  "分页获取基础menu列表,返回包括列表,总数,页码,每页数量"
+// @Router    /menu/getMenuList [post]
+func (a *AuthorityMenuApi) GetMenuList(c *gin.Context) {
+	var pageInfo request.PageInfo
+	err := c.ShouldBindJSON(&pageInfo)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(pageInfo, utils.PageInfoVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	menuList, total, err := menuService.GetInfoList()
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(response.PageResult{
+		List:     menuList,
+		Total:    total,
+		Page:     pageInfo.Page,
+		PageSize: pageInfo.PageSize,
+	}, "获取成功", c)
+}

+ 149 - 0
internal/modules/system/api/sys_operation_record.go

@@ -0,0 +1,149 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/request"
+	"wails-app/internal/model/common/response"
+	systemReq "wails-app/internal/modules/system/model/request"
+	"wails-app/internal/pkg"
+)
+
+type OperationRecordApi struct{}
+
+// CreateSysOperationRecord
+// @Tags      SysOperationRecord
+// @Summary   创建SysOperationRecord
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysOperationRecord      true  "创建SysOperationRecord"
+// @Success   200   {object}  response.Response{msg=string}  "创建SysOperationRecord"
+// @Router    /sysOperationRecord/createSysOperationRecord [post]
+func (s *OperationRecordApi) CreateSysOperationRecord(c *gin.Context) {
+	var sysOperationRecord dao.SysOperationRecord
+	err := c.ShouldBindJSON(&sysOperationRecord)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = operationRecordService.CreateSysOperationRecord(sysOperationRecord)
+	if err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+		return
+	}
+	response.OkWithMessage("创建成功", c)
+}
+
+// DeleteSysOperationRecord
+// @Tags      SysOperationRecord
+// @Summary   删除SysOperationRecord
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysOperationRecord      true  "SysOperationRecord模型"
+// @Success   200   {object}  response.Response{msg=string}  "删除SysOperationRecord"
+// @Router    /sysOperationRecord/deleteSysOperationRecord [delete]
+func (s *OperationRecordApi) DeleteSysOperationRecord(c *gin.Context) {
+	var sysOperationRecord dao.SysOperationRecord
+	err := c.ShouldBindJSON(&sysOperationRecord)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = operationRecordService.DeleteSysOperationRecord(sysOperationRecord)
+	if err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}
+
+// DeleteSysOperationRecordByIds
+// @Tags      SysOperationRecord
+// @Summary   批量删除SysOperationRecord
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.IdsReq                 true  "批量删除SysOperationRecord"
+// @Success   200   {object}  response.Response{msg=string}  "批量删除SysOperationRecord"
+// @Router    /sysOperationRecord/deleteSysOperationRecordByIds [delete]
+func (s *OperationRecordApi) DeleteSysOperationRecordByIds(c *gin.Context) {
+	var IDS request.IdsReq
+	err := c.ShouldBindJSON(&IDS)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = operationRecordService.DeleteSysOperationRecordByIds(IDS)
+	if err != nil {
+		global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
+		response.FailWithMessage("批量删除失败", c)
+		return
+	}
+	response.OkWithMessage("批量删除成功", c)
+}
+
+// FindSysOperationRecord
+// @Tags      SysOperationRecord
+// @Summary   用id查询SysOperationRecord
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  query     system.SysOperationRecord                                  true  "Id"
+// @Success   200   {object}  response.Response{data=map[string]interface{},msg=string}  "用id查询SysOperationRecord"
+// @Router    /sysOperationRecord/findSysOperationRecord [get]
+func (s *OperationRecordApi) FindSysOperationRecord(c *gin.Context) {
+	var sysOperationRecord dao.SysOperationRecord
+	err := c.ShouldBindJSON(&sysOperationRecord)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(sysOperationRecord, utils.IdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	reSysOperationRecord, err := operationRecordService.GetSysOperationRecord(sysOperationRecord.ID)
+	if err != nil {
+		global.GVA_LOG.Error("查询失败!", zap.Error(err))
+		response.FailWithMessage("查询失败", c)
+		return
+	}
+	response.OkWithDetailed(gin.H{"reSysOperationRecord": reSysOperationRecord}, "查询成功", c)
+}
+
+// GetSysOperationRecordList
+// @Tags      SysOperationRecord
+// @Summary   分页获取SysOperationRecord列表
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  query     request.SysOperationRecordSearch                        true  "页码, 每页大小, 搜索条件"
+// @Success   200   {object}  response.Response{data=response.PageResult,msg=string}  "分页获取SysOperationRecord列表,返回包括列表,总数,页码,每页数量"
+// @Router    /sysOperationRecord/getSysOperationRecordList [get]
+func (s *OperationRecordApi) GetSysOperationRecordList(c *gin.Context) {
+	var pageInfo systemReq.SysOperationRecordSearch
+	err := c.ShouldBindJSON(&pageInfo)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	list, total, err := operationRecordService.GetSysOperationRecordInfoList(pageInfo)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     pageInfo.Page,
+		PageSize: pageInfo.PageSize,
+	}, "获取成功", c)
+}

+ 473 - 0
internal/modules/system/api/sys_user.go

@@ -0,0 +1,473 @@
+package system
+
+import (
+	"wails-app/internal/dao"
+	"strconv"
+	"time"
+
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/request"
+	"wails-app/internal/model/common/response"
+	systemReq "wails-app/internal/modules/system/model/request"
+	systemRes "wails-app/internal/modules/system/model/response"
+	"wails-app/internal/pkg"
+
+	"github.com/gin-gonic/gin"
+	"github.com/redis/go-redis/v9"
+	"go.uber.org/zap"
+)
+
+// 框架自带的登录无加密 返回公钥
+
+func (b *BaseApi) ReturnPublicKey(c *gin.Context) {
+	rsa := utils.RSA{}
+	publicKey := rsa.ReturnPublicKey()
+	response.OkWithData(gin.H{"publicKey": publicKey}, c)
+}
+
+// Login
+// @Tags     Base
+// @Summary  用户登录
+// @Produce   application/json
+// @Param    data  body      systemReq.Login                                             true  "用户名, 密码, 验证码"
+// @Success  200   {object}  response.Response{data=systemRes.LoginResponse,msg=string}  "返回包括用户信息,token,过期时间"
+// @Router   /base/login [post]
+func (b *BaseApi) Login(c *gin.Context) {
+	var l systemReq.Login
+	err := c.ShouldBindJSON(&l)
+	key := c.ClientIP()
+
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(l, utils.LoginVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	// 判断验证码是否开启
+	openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha               // 是否开启防爆次数
+	openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
+	v, ok := global.BlackCache.Get(key)
+	if !ok {
+		global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
+	}
+
+	var oc bool = openCaptcha == 0 || openCaptcha < interfaceToInt(v)
+
+	//解密
+	rsa := utils.RSA{}
+	l.Password = rsa.Encryption(l.Password)
+
+	if !oc || (l.CaptchaId != "" && l.Captcha != "" && store.Verify(l.CaptchaId, l.Captcha, true)) {
+		u := &dao.SysUser{Username: l.Username, Password: l.Password}
+		user, err := userService.Login(u)
+		if err != nil {
+			global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
+			// 验证码次数+1
+			global.BlackCache.Increment(key, 1)
+			response.FailWithMessage("用户名不存在或者密码错误", c)
+			return
+		}
+		if user.Enable != 1 {
+			global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
+			// 验证码次数+1
+			global.BlackCache.Increment(key, 1)
+			response.FailWithMessage("用户被禁止登录", c)
+			return
+		}
+		b.TokenNext(c, *user)
+		return
+	}
+	// 验证码次数+1
+	global.BlackCache.Increment(key, 1)
+	response.FailWithMessage("验证码错误", c)
+}
+
+// TokenNext 登录以后签发jwt
+func (b *BaseApi) TokenNext(c *gin.Context, user dao.SysUser) {
+	j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一签名
+	claims := j.CreateClaims(systemReq.BaseClaims{
+		UUID:        user.UUID,
+		ID:          user.ID,
+		NickName:    user.NickName,
+		Username:    user.Username,
+		AuthorityId: user.AuthorityId,
+	})
+	token, err := j.CreateToken(claims)
+	if err != nil {
+		global.GVA_LOG.Error("获取token失败!", zap.Error(err))
+		response.FailWithMessage("获取token失败", c)
+		return
+	}
+	if !global.GVA_CONFIG.System.UseMultipoint {
+		utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
+		response.OkWithDetailed(systemRes.LoginResponse{
+			User:      user,
+			Token:     token,
+			ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
+		}, "登录成功", c)
+		return
+	}
+
+	if jwtStr, err := jwtService.GetRedisJWT(user.Username); err == redis.Nil {
+		if err := jwtService.SetRedisJWT(token, user.Username); err != nil {
+			global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
+			response.FailWithMessage("设置登录状态失败", c)
+			return
+		}
+		utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
+		response.OkWithDetailed(systemRes.LoginResponse{
+			User:      user,
+			Token:     token,
+			ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
+		}, "登录成功", c)
+	} else if err != nil {
+		global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
+		response.FailWithMessage("设置登录状态失败", c)
+	} else {
+		var blackJWT dao.JwtBlacklist
+		blackJWT.Jwt = jwtStr
+		if err := jwtService.JsonInBlacklist(blackJWT); err != nil {
+			response.FailWithMessage("jwt作废失败", c)
+			return
+		}
+		if err := jwtService.SetRedisJWT(token, user.Username); err != nil {
+			response.FailWithMessage("设置登录状态失败", c)
+			return
+		}
+		utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
+		response.OkWithDetailed(systemRes.LoginResponse{
+			User:      user,
+			Token:     token,
+			ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
+		}, "登录成功", c)
+	}
+}
+
+// Register
+// @Tags     SysUser
+// @Summary  用户注册账号
+// @Produce   application/json
+// @Param    data  body      systemReq.Register                                            true  "用户名, 昵称, 密码, 角色ID"
+// @Success  200   {object}  response.Response{data=systemRes.SysUserResponse,msg=string}  "用户注册账号,返回包括用户信息"
+// @Router   /user/admin_register [post]
+func (b *BaseApi) Register(c *gin.Context) {
+	var r systemReq.Register
+	err := c.ShouldBindJSON(&r)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(r, utils.RegisterVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	user := &dao.SysUser{Username: r.Username, NickName: r.NickName, Password: r.Password, HeaderImg: r.HeaderImg, AuthorityId: r.AuthorityId, Enable: r.Enable, Phone: r.Phone, Email: r.Email}
+	userReturn, err := userService.Register(*user)
+	if err != nil {
+		global.GVA_LOG.Error("注册失败!", zap.Error(err))
+		response.FailWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册失败", c)
+		return
+	}
+	response.OkWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册成功", c)
+}
+
+// ChangePassword
+// @Tags      SysUser
+// @Summary   用户修改密码
+// @Security  ApiKeyAuth
+// @Produce  application/json
+// @Param     data  body      systemReq.ChangePasswordReq    true  "用户名, 原密码, 新密码"
+// @Success   200   {object}  response.Response{msg=string}  "用户修改密码"
+// @Router    /user/changePassword [post]
+func (b *BaseApi) ChangePassword(c *gin.Context) {
+	var req systemReq.ChangePasswordReq
+	err := c.ShouldBindJSON(&req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(req, utils.ChangePasswordVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	uid := utils.GetUserID(c)
+	u := &dao.SysUser{GVA_MODEL: global.GVA_MODEL{ID: uid}, Password: req.Password}
+	_, err = userService.ChangePassword(u, req.NewPassword)
+	if err != nil {
+		global.GVA_LOG.Error("修改失败!", zap.Error(err))
+		response.FailWithMessage("修改失败,原密码与当前账户不符", c)
+		return
+	}
+	response.OkWithMessage("修改成功", c)
+}
+
+// GetUserList
+// @Tags      SysUser
+// @Summary   分页获取用户列表
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.PageInfo                                        true  "页码, 每页大小"
+// @Success   200   {object}  response.Response{data=response.PageResult,msg=string}  "分页获取用户列表,返回包括列表,总数,页码,每页数量"
+// @Router    /user/getUserList [post]
+func (b *BaseApi) GetUserList(c *gin.Context) {
+	var pageInfo request.PageInfo
+	err := c.ShouldBindJSON(&pageInfo)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(pageInfo, utils.PageInfoVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	list, total, err := userService.GetUserInfoList(pageInfo)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     pageInfo.Page,
+		PageSize: pageInfo.PageSize,
+	}, "获取成功", c)
+}
+
+// SetUserAuthority
+// @Tags      SysUser
+// @Summary   更改用户权限
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      systemReq.SetUserAuth          true  "用户UUID, 角色ID"
+// @Success   200   {object}  response.Response{msg=string}  "设置用户权限"
+// @Router    /user/setUserAuthority [post]
+func (b *BaseApi) SetUserAuthority(c *gin.Context) {
+	var sua systemReq.SetUserAuth
+	err := c.ShouldBindJSON(&sua)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if UserVerifyErr := utils.Verify(sua, utils.SetUserAuthorityVerify); UserVerifyErr != nil {
+		response.FailWithMessage(UserVerifyErr.Error(), c)
+		return
+	}
+	userID := utils.GetUserID(c)
+	err = userService.SetUserAuthority(userID, sua.AuthorityId)
+	if err != nil {
+		global.GVA_LOG.Error("修改失败!", zap.Error(err))
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	claims := utils.GetUserInfo(c)
+	j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一签名
+	claims.AuthorityId = sua.AuthorityId
+	if token, err := j.CreateToken(*claims); err != nil {
+		global.GVA_LOG.Error("修改失败!", zap.Error(err))
+		response.FailWithMessage(err.Error(), c)
+	} else {
+		c.Header("new-token", token)
+		c.Header("new-expires-at", strconv.FormatInt(claims.ExpiresAt.Unix(), 10))
+		utils.SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60))
+		response.OkWithMessage("修改成功", c)
+	}
+}
+
+// SetUserAuthorities
+// @Tags      SysUser
+// @Summary   设置用户权限
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      systemReq.SetUserAuthorities   true  "用户UUID, 角色ID"
+// @Success   200   {object}  response.Response{msg=string}  "设置用户权限"
+// @Router    /user/setUserAuthorities [post]
+func (b *BaseApi) SetUserAuthorities(c *gin.Context) {
+	var sua systemReq.SetUserAuthorities
+	err := c.ShouldBindJSON(&sua)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = userService.SetUserAuthorities(sua.ID, sua.AuthorityId)
+	if err != nil {
+		global.GVA_LOG.Error("修改失败!", zap.Error(err))
+		response.FailWithMessage("修改失败", c)
+		return
+	}
+	response.OkWithMessage("修改成功", c)
+}
+
+// DeleteUser
+// @Tags      SysUser
+// @Summary   删除用户
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      request.GetById                true  "用户ID"
+// @Success   200   {object}  response.Response{msg=string}  "删除用户"
+// @Router    /user/deleteUser [delete]
+func (b *BaseApi) DeleteUser(c *gin.Context) {
+	var reqId request.GetById
+	err := c.ShouldBindJSON(&reqId)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(reqId, utils.IdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	jwtId := utils.GetUserID(c)
+	if jwtId == uint(reqId.ID) {
+		response.FailWithMessage("删除失败, 自杀失败", c)
+		return
+	}
+	err = userService.DeleteUser(reqId.ID)
+	if err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}
+
+// SetUserInfo
+// @Tags      SysUser
+// @Summary   设置用户信息
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysUser                                             true  "ID, 用户名, 昵称, 头像链接"
+// @Success   200   {object}  response.Response{data=map[string]interface{},msg=string}  "设置用户信息"
+// @Router    /user/setUserInfo [put]
+func (b *BaseApi) SetUserInfo(c *gin.Context) {
+	var user systemReq.ChangeUserInfo
+	err := c.ShouldBindJSON(&user)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = utils.Verify(user, utils.IdVerify)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	if user.AuthorityIds != 0 {
+		err = userService.SetUserAuthorities(user.ID, user.AuthorityIds)
+		if err != nil {
+			global.GVA_LOG.Error("设置失败!", zap.Error(err))
+			response.FailWithMessage("设置失败", c)
+			return
+		}
+	}
+	err = userService.SetUserInfo(dao.SysUser{
+		GVA_MODEL: global.GVA_MODEL{
+			ID: user.ID,
+		},
+		NickName:  user.NickName,
+		HeaderImg: user.HeaderImg,
+		Phone:     user.Phone,
+		Email:     user.Email,
+		SideMode:  user.SideMode,
+		Enable:    user.Enable,
+	})
+	if err != nil {
+		global.GVA_LOG.Error("设置失败!", zap.Error(err))
+		response.FailWithMessage("设置失败", c)
+		return
+	}
+	response.OkWithMessage("设置成功", c)
+}
+
+// SetSelfInfo
+// @Tags      SysUser
+// @Summary   设置用户信息
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Param     data  body      system.SysUser                                             true  "ID, 用户名, 昵称, 头像链接"
+// @Success   200   {object}  response.Response{data=map[string]interface{},msg=string}  "设置用户信息"
+// @Router    /user/SetSelfInfo [put]
+func (b *BaseApi) SetSelfInfo(c *gin.Context) {
+	var user systemReq.ChangeUserInfo
+	err := c.ShouldBindJSON(&user)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	user.ID = utils.GetUserID(c)
+	err = userService.SetSelfInfo(dao.SysUser{
+		GVA_MODEL: global.GVA_MODEL{
+			ID: user.ID,
+		},
+		NickName:  user.NickName,
+		HeaderImg: user.HeaderImg,
+		Phone:     user.Phone,
+		Email:     user.Email,
+		SideMode:  user.SideMode,
+		Enable:    user.Enable,
+	})
+	if err != nil {
+		global.GVA_LOG.Error("设置失败!", zap.Error(err))
+		response.FailWithMessage("设置失败", c)
+		return
+	}
+	response.OkWithMessage("设置成功", c)
+}
+
+// GetUserInfo
+// @Tags      SysUser
+// @Summary   获取用户信息
+// @Security  ApiKeyAuth
+// @accept    application/json
+// @Produce   application/json
+// @Success   200  {object}  response.Response{data=map[string]interface{},msg=string}  "获取用户信息"
+// @Router    /user/getUserInfo [get]
+func (b *BaseApi) GetUserInfo(c *gin.Context) {
+	uuid := utils.GetUserUuid(c)
+	ReqUser, err := userService.GetUserInfo(uuid)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(gin.H{"userInfo": ReqUser}, "获取成功", c)
+}
+
+// ResetPassword
+// @Tags      SysUser
+// @Summary   重置用户密码
+// @Security  ApiKeyAuth
+// @Produce  application/json
+// @Param     data  body      system.SysUser                 true  "ID"
+// @Success   200   {object}  response.Response{msg=string}  "重置用户密码"
+// @Router    /user/resetPassword [post]
+func (b *BaseApi) ResetPassword(c *gin.Context) {
+	var user dao.SysUser
+	err := c.ShouldBindJSON(&user)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = userService.ResetPassword(user.ID)
+	if err != nil {
+		global.GVA_LOG.Error("重置失败!", zap.Error(err))
+		response.FailWithMessage("重置失败"+err.Error(), c)
+		return
+	}
+	response.OkWithMessage("重置成功", c)
+}

+ 21 - 0
internal/modules/system/model/request/jwt.go

@@ -0,0 +1,21 @@
+package request
+
+import (
+	"github.com/gofrs/uuid/v5"
+	jwt "github.com/golang-jwt/jwt/v4"
+)
+
+// Custom claims structure
+type CustomClaims struct {
+	BaseClaims
+	BufferTime int64
+	jwt.RegisteredClaims
+}
+
+type BaseClaims struct {
+	UUID        uuid.UUID
+	ID          uint
+	Username    string
+	NickName    string
+	AuthorityId uint
+}

+ 14 - 0
internal/modules/system/model/request/sys_api.go

@@ -0,0 +1,14 @@
+package request
+
+import (
+	"wails-app/internal/dao"
+	"wails-app/internal/model/common/request"
+)
+
+// api分页条件查询及排序结构体
+type SearchApiParams struct {
+	dao.SysApi
+	request.PageInfo
+	OrderKey string `json:"orderKey"` // 排序
+	Desc     bool   `json:"desc"`     // 排序方式:升序false(默认)|降序true
+}

+ 7 - 0
internal/modules/system/model/request/sys_authority_btn.go

@@ -0,0 +1,7 @@
+package request
+
+type SysAuthorityBtnReq struct {
+	MenuID      uint   `json:"menuID"`
+	AuthorityId uint   `json:"authorityId"`
+	Selected    []uint `json:"selected"`
+}

+ 13 - 0
internal/modules/system/model/request/sys_auto_history.go

@@ -0,0 +1,13 @@
+package request
+
+import "wails-app/internal/model/common/request"
+
+type SysAutoHistory struct {
+	request.PageInfo
+}
+
+// GetById Find by id structure
+type RollBack struct {
+	ID          int  `json:"id" form:"id"`                   // 主键ID
+	DeleteTable bool `json:"deleteTable" form:"deleteTable"` // 是否删除表
+}

+ 26 - 0
internal/modules/system/model/request/sys_casbin.go

@@ -0,0 +1,26 @@
+package request
+
+// Casbin info structure
+type CasbinInfo struct {
+	Path   string `json:"path"`   // 路径
+	Method string `json:"method"` // 方法
+}
+
+// Casbin structure for input parameters
+type CasbinInReceive struct {
+	AuthorityId uint         `json:"authorityId"` // 权限id
+	CasbinInfos []CasbinInfo `json:"casbinInfos"`
+}
+
+func DefaultCasbin() []CasbinInfo {
+	return []CasbinInfo{
+		{Path: "/menu/getMenu", Method: "POST"},
+		{Path: "/jwt/jsonInBlacklist", Method: "POST"},
+		{Path: "/base/login", Method: "POST"},
+		{Path: "/user/admin_register", Method: "POST"},
+		{Path: "/user/changePassword", Method: "POST"},
+		{Path: "/user/setUserAuthority", Method: "POST"},
+		{Path: "/user/setUserInfo", Method: "PUT"},
+		{Path: "/user/getUserInfo", Method: "GET"},
+	}
+}

+ 11 - 0
internal/modules/system/model/request/sys_dictionary_detail.go

@@ -0,0 +1,11 @@
+package request
+
+import (
+	"wails-app/internal/dao"
+	"wails-app/internal/model/common/request"
+)
+
+type SysDictionaryDetailSearch struct {
+	dao.SysDictionaryDetail
+	request.PageInfo
+}

+ 27 - 0
internal/modules/system/model/request/sys_menu.go

@@ -0,0 +1,27 @@
+package request
+
+import (
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+)
+
+// Add menu authority info structure
+type AddMenuAuthorityInfo struct {
+	Menus       []dao.SysBaseMenu `json:"menus"`
+	AuthorityId uint              `json:"authorityId"` // 角色ID
+}
+
+func DefaultMenu() []dao.SysBaseMenu {
+	return []dao.SysBaseMenu{{
+		GVA_MODEL: global.GVA_MODEL{ID: 1},
+		ParentId:  0,
+		Path:      "dashboard",
+		Name:      "dashboard",
+		Component: "view/dashboard/index.vue",
+		Sort:      1,
+		Meta: dao.Meta{
+			Title: "仪表盘",
+			Icon:  "setting",
+		},
+	}}
+}

+ 11 - 0
internal/modules/system/model/request/sys_operation_record.go

@@ -0,0 +1,11 @@
+package request
+
+import (
+	"wails-app/internal/dao"
+	"wails-app/internal/model/common/request"
+)
+
+type SysOperationRecordSearch struct {
+	dao.SysOperationRecord
+	request.PageInfo
+}

+ 55 - 0
internal/modules/system/model/request/sys_user.go

@@ -0,0 +1,55 @@
+package request
+
+import (
+	"wails-app/internal/dao"
+)
+
+// Register User register structure
+type Register struct {
+	Username    string `json:"userName" example:"用户名"`
+	Password    string `json:"passWord" example:"密码"`
+	NickName    string `json:"nickName" example:"昵称"`
+	HeaderImg   string `json:"headerImg" example:"头像链接"`
+	AuthorityId uint   `json:"authorityId" swaggertype:"string" example:"int 角色id"`
+	Enable      int    `json:"enable" swaggertype:"string" example:"int 是否启用"`
+	Phone       string `json:"phone" example:"电话号码"`
+	Email       string `json:"email" example:"电子邮箱"`
+}
+
+// User login structure
+type Login struct {
+	Username  string `json:"username"`  // 用户名
+	Password  string `json:"password"`  // 密码
+	Captcha   string `json:"captcha"`   // 验证码
+	CaptchaId string `json:"captchaId"` // 验证码ID
+}
+
+// Modify password structure
+type ChangePasswordReq struct {
+	ID          uint   `json:"-"`           // 从 JWT 中提取 user id,避免越权
+	Password    string `json:"password"`    // 密码
+	NewPassword string `json:"newPassword"` // 新密码
+}
+
+// Modify  user's auth structure
+type SetUserAuth struct {
+	AuthorityId uint `json:"authorityId"` // 角色ID
+}
+
+// Modify  user's auth structure
+type SetUserAuthorities struct {
+	ID          uint
+	AuthorityId uint `json:"authorityId"` // 角色ID
+}
+
+type ChangeUserInfo struct {
+	ID           uint               `gorm:"primarykey"`                                                                           // 主键ID
+	NickName     string             `json:"nickName" gorm:"default:系统用户;comment:用户昵称"`                                            // 用户昵称
+	Phone        string             `json:"phone"  gorm:"comment:用户手机号"`                                                          // 用户手机号
+	AuthorityIds uint               `json:"authorityIds" gorm:"-"`                                                                // 角色ID
+	Email        string             `json:"email"  gorm:"comment:用户邮箱"`                                                           // 用户邮箱
+	HeaderImg    string             `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像
+	SideMode     string             `json:"sideMode"  gorm:"comment:用户侧边主题"`                                                      // 用户侧边主题
+	Enable       int                `json:"enable" gorm:"comment:冻结用户"`                                                           //冻结用户
+	Authorities  []dao.SysAuthority `json:"-" gorm:"many2many:sys_user_authority;"`
+}

+ 13 - 0
internal/modules/system/model/response/sys_api.go

@@ -0,0 +1,13 @@
+package response
+
+import (
+	"wails-app/internal/dao"
+)
+
+type SysAPIResponse struct {
+	Api dao.SysApi `json:"api"`
+}
+
+type SysAPIListResponse struct {
+	Apis []dao.SysApi `json:"apis"`
+}

+ 14 - 0
internal/modules/system/model/response/sys_authority.go

@@ -0,0 +1,14 @@
+package response
+
+import (
+	"wails-app/internal/dao"
+)
+
+type SysAuthorityResponse struct {
+	Authority dao.SysAuthority `json:"authority"`
+}
+
+type SysAuthorityCopyResponse struct {
+	Authority      dao.SysAuthority `json:"authority"`
+	OldAuthorityId uint             `json:"oldAuthorityId"` // 旧角色ID
+}

+ 5 - 0
internal/modules/system/model/response/sys_authority_btn.go

@@ -0,0 +1,5 @@
+package response
+
+type SysAuthorityBtnRes struct {
+	Selected []uint `json:"selected"`
+}

+ 8 - 0
internal/modules/system/model/response/sys_captcha.go

@@ -0,0 +1,8 @@
+package response
+
+type SysCaptchaResponse struct {
+	CaptchaId     string `json:"captchaId"`
+	PicPath       string `json:"picPath"`
+	CaptchaLength int    `json:"captchaLength"`
+	OpenCaptcha   bool   `json:"openCaptcha"`
+}

+ 9 - 0
internal/modules/system/model/response/sys_casbin.go

@@ -0,0 +1,9 @@
+package response
+
+import (
+	"wails-app/internal/modules/system/model/request"
+)
+
+type PolicyPathResponse struct {
+	Paths []request.CasbinInfo `json:"paths"`
+}

+ 17 - 0
internal/modules/system/model/response/sys_menu.go

@@ -0,0 +1,17 @@
+package response
+
+import (
+	"wails-app/internal/dao"
+)
+
+type SysMenusResponse struct {
+	Menus []dao.SysMenu `json:"menus"`
+}
+
+type SysBaseMenusResponse struct {
+	Menus []dao.SysBaseMenu `json:"menus"`
+}
+
+type SysBaseMenuResponse struct {
+	Menu dao.SysBaseMenu `json:"menu"`
+}

+ 15 - 0
internal/modules/system/model/response/sys_user.go

@@ -0,0 +1,15 @@
+package response
+
+import (
+	"wails-app/internal/dao"
+)
+
+type SysUserResponse struct {
+	User dao.SysUser `json:"user"`
+}
+
+type LoginResponse struct {
+	User      dao.SysUser `json:"user"`
+	Token     string      `json:"token"`
+	ExpiresAt int64       `json:"expiresAt"`
+}

+ 5 - 0
internal/modules/system/router/enter.go

@@ -0,0 +1,5 @@
+package system
+
+type RouterGroup struct {
+	FileUploadAndDownloadRouter
+}

+ 23 - 0
internal/modules/system/router/exa_file_upload_and_download.go

@@ -0,0 +1,23 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+)
+
+type FileUploadAndDownloadRouter struct{}
+
+func (e *FileUploadAndDownloadRouter) InitFileUploadAndDownloadRouter(Router *gin.RouterGroup) {
+	fileUploadAndDownloadRouter := Router.Group("fileUploadAndDownload")
+	exaFileUploadAndDownloadApi := v1.ApiGroupApp.ExampleApiGroup.FileUploadAndDownloadApi
+	{
+		fileUploadAndDownloadRouter.POST("upload", exaFileUploadAndDownloadApi.UploadFile)                                 // 上传文件
+		fileUploadAndDownloadRouter.POST("getFileList", exaFileUploadAndDownloadApi.GetFileList)                           // 获取上传文件列表
+		fileUploadAndDownloadRouter.POST("deleteFile", exaFileUploadAndDownloadApi.DeleteFile)                             // 删除指定文件
+		fileUploadAndDownloadRouter.POST("editFileName", exaFileUploadAndDownloadApi.EditFileName)                         // 编辑文件名或者备注
+		fileUploadAndDownloadRouter.POST("breakpointContinue", exaFileUploadAndDownloadApi.BreakpointContinue)             // 断点续传
+		fileUploadAndDownloadRouter.GET("findFile", exaFileUploadAndDownloadApi.FindFile)                                  // 查询当前文件成功的切片
+		fileUploadAndDownloadRouter.POST("breakpointContinueFinish", exaFileUploadAndDownloadApi.BreakpointContinueFinish) // 切片传输完成
+		fileUploadAndDownloadRouter.POST("removeChunk", exaFileUploadAndDownloadApi.RemoveChunk)                           // 删除切片
+	}
+}

+ 31 - 0
internal/modules/system/router/sys_api.go

@@ -0,0 +1,31 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+	"wails-app/internal/middleware"
+)
+
+type ApiRouter struct{}
+
+func (s *ApiRouter) InitApiRouter(Router *gin.RouterGroup, RouterPub *gin.RouterGroup) {
+	apiRouter := Router.Group("api").Use(middleware.OperationRecord())
+	apiRouterWithoutRecord := Router.Group("api")
+
+	apiPublicRouterWithoutRecord := RouterPub.Group("api")
+	apiRouterApi := v1.ApiGroupApp.SystemApiGroup.SystemApiApi
+	{
+		apiRouter.POST("createApi", apiRouterApi.CreateApi)               // 创建Api
+		apiRouter.POST("deleteApi", apiRouterApi.DeleteApi)               // 删除Api
+		apiRouter.POST("getApiById", apiRouterApi.GetApiById)             // 获取单条Api消息
+		apiRouter.POST("updateApi", apiRouterApi.UpdateApi)               // 更新api
+		apiRouter.DELETE("deleteApisByIds", apiRouterApi.DeleteApisByIds) // 删除选中api
+	}
+	{
+		apiRouterWithoutRecord.POST("getAllApis", apiRouterApi.GetAllApis) // 获取所有api
+		apiRouterWithoutRecord.POST("getApiList", apiRouterApi.GetApiList) // 获取Api列表
+	}
+	{
+		apiPublicRouterWithoutRecord.GET("freshCasbin", apiRouterApi.FreshCasbin) // 刷新casbin权限
+	}
+}

+ 24 - 0
internal/modules/system/router/sys_authority.go

@@ -0,0 +1,24 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+	"wails-app/internal/middleware"
+)
+
+type AuthorityRouter struct{}
+
+func (s *AuthorityRouter) InitAuthorityRouter(Router *gin.RouterGroup) {
+	authorityRouter := Router.Group("authority").Use(middleware.OperationRecord())
+	authorityRouterWithoutRecord := Router.Group("authority")
+	authorityApi := v1.ApiGroupApp.SystemApiGroup.AuthorityApi
+	{
+		authorityRouter.POST("createAuthority", authorityApi.CreateAuthority)   // 创建角色
+		authorityRouter.POST("deleteAuthority", authorityApi.DeleteAuthority)   // 删除角色
+		authorityRouter.PUT("updateAuthority", authorityApi.UpdateAuthority)    // 更新角色
+		authorityRouter.POST("setDataAuthority", authorityApi.SetDataAuthority) // 设置角色资源权限
+	}
+	{
+		authorityRouterWithoutRecord.POST("getAuthorityList", authorityApi.GetAuthorityList) // 获取角色列表
+	}
+}

+ 19 - 0
internal/modules/system/router/sys_authority_btn.go

@@ -0,0 +1,19 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+)
+
+type AuthorityBtnRouter struct{}
+
+func (s *AuthorityBtnRouter) InitAuthorityBtnRouterRouter(Router *gin.RouterGroup) {
+	//authorityRouter := Router.Group("authorityBtn").Use(middleware.OperationRecord())
+	authorityRouterWithoutRecord := Router.Group("authorityBtn")
+	authorityBtnApi := v1.ApiGroupApp.SystemApiGroup.AuthorityBtnApi
+	{
+		authorityRouterWithoutRecord.POST("getAuthorityBtn", authorityBtnApi.GetAuthorityBtn)
+		authorityRouterWithoutRecord.POST("setAuthorityBtn", authorityBtnApi.SetAuthorityBtn)
+		authorityRouterWithoutRecord.POST("canRemoveAuthorityBtn", authorityBtnApi.CanRemoveAuthorityBtn)
+	}
+}

+ 19 - 0
internal/modules/system/router/sys_base.go

@@ -0,0 +1,19 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+)
+
+type BaseRouter struct{}
+
+func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) (R gin.IRoutes) {
+	baseRouter := Router.Group("base")
+	baseApi := v1.ApiGroupApp.SystemApiGroup.BaseApi
+	{
+		baseRouter.POST("login", baseApi.Login)
+		baseRouter.POST("captcha", baseApi.Captcha)
+		baseRouter.GET("routerPublicKey", baseApi.ReturnPublicKey) //返回公钥
+	}
+	return baseRouter
+}

+ 21 - 0
internal/modules/system/router/sys_casbin.go

@@ -0,0 +1,21 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+	"wails-app/internal/middleware"
+)
+
+type CasbinRouter struct{}
+
+func (s *CasbinRouter) InitCasbinRouter(Router *gin.RouterGroup) {
+	casbinRouter := Router.Group("casbin").Use(middleware.OperationRecord())
+	casbinRouterWithoutRecord := Router.Group("casbin")
+	casbinApi := v1.ApiGroupApp.SystemApiGroup.CasbinApi
+	{
+		casbinRouter.POST("updateCasbin", casbinApi.UpdateCasbin)
+	}
+	{
+		casbinRouterWithoutRecord.POST("getPolicyPathByAuthorityId", casbinApi.GetPolicyPathByAuthorityId)
+	}
+}

+ 24 - 0
internal/modules/system/router/sys_dictionary.go

@@ -0,0 +1,24 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+	"wails-app/internal/middleware"
+)
+
+type DictionaryRouter struct{}
+
+func (s *DictionaryRouter) InitSysDictionaryRouter(Router *gin.RouterGroup) {
+	sysDictionaryRouter := Router.Group("sysDictionary").Use(middleware.OperationRecord())
+	sysDictionaryRouterWithoutRecord := Router.Group("sysDictionary")
+	sysDictionaryApi := v1.ApiGroupApp.SystemApiGroup.DictionaryApi
+	{
+		sysDictionaryRouter.POST("createSysDictionary", sysDictionaryApi.CreateSysDictionary)   // 新建SysDictionary
+		sysDictionaryRouter.DELETE("deleteSysDictionary", sysDictionaryApi.DeleteSysDictionary) // 删除SysDictionary
+		sysDictionaryRouter.PUT("updateSysDictionary", sysDictionaryApi.UpdateSysDictionary)    // 更新SysDictionary
+	}
+	{
+		sysDictionaryRouterWithoutRecord.GET("findSysDictionary", sysDictionaryApi.FindSysDictionary)       // 根据ID获取SysDictionary
+		sysDictionaryRouterWithoutRecord.GET("getSysDictionaryList", sysDictionaryApi.GetSysDictionaryList) // 获取SysDictionary列表
+	}
+}

+ 24 - 0
internal/modules/system/router/sys_dictionary_detail.go

@@ -0,0 +1,24 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+	"wails-app/internal/middleware"
+)
+
+type DictionaryDetailRouter struct{}
+
+func (s *DictionaryDetailRouter) InitSysDictionaryDetailRouter(Router *gin.RouterGroup) {
+	dictionaryDetailRouter := Router.Group("sysDictionaryDetail").Use(middleware.OperationRecord())
+	dictionaryDetailRouterWithoutRecord := Router.Group("sysDictionaryDetail")
+	sysDictionaryDetailApi := v1.ApiGroupApp.SystemApiGroup.DictionaryDetailApi
+	{
+		dictionaryDetailRouter.POST("createSysDictionaryDetail", sysDictionaryDetailApi.CreateSysDictionaryDetail)   // 新建SysDictionaryDetail
+		dictionaryDetailRouter.DELETE("deleteSysDictionaryDetail", sysDictionaryDetailApi.DeleteSysDictionaryDetail) // 删除SysDictionaryDetail
+		dictionaryDetailRouter.PUT("updateSysDictionaryDetail", sysDictionaryDetailApi.UpdateSysDictionaryDetail)    // 更新SysDictionaryDetail
+	}
+	{
+		dictionaryDetailRouterWithoutRecord.GET("findSysDictionaryDetail", sysDictionaryDetailApi.FindSysDictionaryDetail)       // 根据ID获取SysDictionaryDetail
+		dictionaryDetailRouterWithoutRecord.GET("getSysDictionaryDetailList", sysDictionaryDetailApi.GetSysDictionaryDetailList) // 获取SysDictionaryDetail列表
+	}
+}

+ 16 - 0
internal/modules/system/router/sys_jwt.go

@@ -0,0 +1,16 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+)
+
+type JwtRouter struct{}
+
+func (s *JwtRouter) InitJwtRouter(Router *gin.RouterGroup) {
+	jwtRouter := Router.Group("jwt")
+	jwtApi := v1.ApiGroupApp.SystemApiGroup.JwtApi
+	{
+		jwtRouter.POST("jsonInBlacklist", jwtApi.JsonInBlacklist) // jwt加入黑名单
+	}
+}

+ 29 - 0
internal/modules/system/router/sys_menu.go

@@ -0,0 +1,29 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+	"wails-app/internal/middleware"
+)
+
+type MenuRouter struct{}
+
+func (s *MenuRouter) InitMenuRouter(Router *gin.RouterGroup) (R gin.IRoutes) {
+	menuRouter := Router.Group("menu").Use(middleware.OperationRecord())
+	menuRouterWithoutRecord := Router.Group("menu")
+	authorityMenuApi := v1.ApiGroupApp.SystemApiGroup.AuthorityMenuApi
+	{
+		menuRouter.POST("addBaseMenu", authorityMenuApi.AddBaseMenu)           // 新增菜单
+		menuRouter.POST("addMenuAuthority", authorityMenuApi.AddMenuAuthority) //	增加menu和角色关联关系
+		menuRouter.POST("deleteBaseMenu", authorityMenuApi.DeleteBaseMenu)     // 删除菜单
+		menuRouter.POST("updateBaseMenu", authorityMenuApi.UpdateBaseMenu)     // 更新菜单
+	}
+	{
+		menuRouterWithoutRecord.POST("getMenu", authorityMenuApi.GetMenu)                   // 获取菜单树
+		menuRouterWithoutRecord.POST("getMenuList", authorityMenuApi.GetMenuList)           // 分页获取基础menu列表
+		menuRouterWithoutRecord.POST("getBaseMenuTree", authorityMenuApi.GetBaseMenuTree)   // 获取用户动态路由
+		menuRouterWithoutRecord.POST("getMenuAuthority", authorityMenuApi.GetMenuAuthority) // 获取指定角色menu
+		menuRouterWithoutRecord.POST("getBaseMenuById", authorityMenuApi.GetBaseMenuById)   // 根据id获取菜单
+	}
+	return menuRouter
+}

+ 21 - 0
internal/modules/system/router/sys_operation_record.go

@@ -0,0 +1,21 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+)
+
+type OperationRecordRouter struct{}
+
+func (s *OperationRecordRouter) InitSysOperationRecordRouter(Router *gin.RouterGroup) {
+	operationRecordRouter := Router.Group("sysOperationRecord")
+	authorityMenuApi := v1.ApiGroupApp.SystemApiGroup.OperationRecordApi
+	{
+		operationRecordRouter.POST("createSysOperationRecord", authorityMenuApi.CreateSysOperationRecord)             // 新建SysOperationRecord
+		operationRecordRouter.DELETE("deleteSysOperationRecord", authorityMenuApi.DeleteSysOperationRecord)           // 删除SysOperationRecord
+		operationRecordRouter.DELETE("deleteSysOperationRecordByIds", authorityMenuApi.DeleteSysOperationRecordByIds) // 批量删除SysOperationRecord
+		operationRecordRouter.GET("findSysOperationRecord", authorityMenuApi.FindSysOperationRecord)                  // 根据ID获取SysOperationRecord
+		operationRecordRouter.GET("getSysOperationRecordList", authorityMenuApi.GetSysOperationRecordList)            // 获取SysOperationRecord列表
+
+	}
+}

+ 29 - 0
internal/modules/system/router/sys_user.go

@@ -0,0 +1,29 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "wails-app/internal/api/v1"
+	"wails-app/internal/middleware"
+)
+
+type UserRouter struct{}
+
+func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup) {
+	userRouter := Router.Group("user").Use(middleware.OperationRecord())
+	userRouterWithoutRecord := Router.Group("user")
+	baseApi := v1.ApiGroupApp.SystemApiGroup.BaseApi
+	{
+		userRouter.POST("admin_register", baseApi.Register)               // 管理员注册账号
+		userRouter.POST("changePassword", baseApi.ChangePassword)         // 用户修改密码
+		userRouter.POST("setUserAuthority", baseApi.SetUserAuthority)     // 设置用户权限
+		userRouter.DELETE("deleteUser", baseApi.DeleteUser)               // 删除用户
+		userRouter.PUT("setUserInfo", baseApi.SetUserInfo)                // 设置用户信息
+		userRouter.PUT("setSelfInfo", baseApi.SetSelfInfo)                // 设置自身信息
+		userRouter.POST("setUserAuthorities", baseApi.SetUserAuthorities) // 设置用户权限组
+		userRouter.POST("resetPassword", baseApi.ResetPassword)           // 设置用户权限组
+	}
+	{
+		userRouterWithoutRecord.POST("getUserList", baseApi.GetUserList) // 分页获取用户列表
+		userRouterWithoutRecord.GET("getUserInfo", baseApi.GetUserInfo)  // 获取自身信息
+	}
+}

+ 7 - 0
internal/modules/system/service/enter.go

@@ -0,0 +1,7 @@
+package system
+
+import "wails-app/internal/service/example"
+
+type ServiceGroup struct {
+	example.FileUploadAndDownloadService
+}

+ 82 - 0
internal/modules/system/service/jwt_black_list.go

@@ -0,0 +1,82 @@
+package system
+
+import (
+	"context"
+	"wails-app/internal/dao"
+
+	"go.uber.org/zap"
+
+	"wails-app/internal/global"
+	"wails-app/internal/pkg"
+)
+
+type JwtService struct{}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: JsonInBlacklist
+//@description: 拉黑jwt
+//@param: jwtList model.JwtBlacklist
+//@return: err error
+
+func (jwtService *JwtService) JsonInBlacklist(jwtList dao.JwtBlacklist) (err error) {
+	err = jwtList.CreateJwtBlack()
+	if err != nil {
+		return
+	}
+	global.BlackCache.SetDefault(jwtList.Jwt, struct{}{})
+	return
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: IsBlacklist
+//@description: 判断JWT是否在黑名单内部
+//@param: jwt string
+//@return: bool
+
+func (jwtService *JwtService) IsBlacklist(jwt string) bool {
+	_, ok := global.BlackCache.Get(jwt)
+	return ok
+	// err := global.GVA_DB.Where("jwt = ?", jwt).First(&system.JwtBlacklist{}).Error
+	// isNotFound := errors.Is(err, gorm.ErrRecordNotFound)
+	// return !isNotFound
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetRedisJWT
+//@description: 从redis取jwt
+//@param: userName string
+//@return: redisJWT string, err error
+
+func (jwtService *JwtService) GetRedisJWT(userName string) (redisJWT string, err error) {
+	redisJWT, err = global.GVA_REDIS.Get(context.Background(), userName).Result()
+	return redisJWT, err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: SetRedisJWT
+//@description: jwt存入redis并设置过期时间
+//@param: jwt string, userName string
+//@return: err error
+
+func (jwtService *JwtService) SetRedisJWT(jwt string, userName string) (err error) {
+	// 此处过期时间等于jwt过期时间
+	dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
+	if err != nil {
+		return err
+	}
+	timer := dr
+	err = global.GVA_REDIS.Set(context.Background(), userName, jwt, timer).Err()
+	return err
+}
+
+func LoadAll() {
+	var data []string
+	err := global.GVA_DB.Model(&dao.JwtBlacklist{}).Select("jwt").Find(&data).Error
+	if err != nil {
+		global.GVA_LOG.Error("加载数据库jwt黑名单失败!", zap.Error(err))
+		return
+	}
+	for i := 0; i < len(data); i++ {
+		global.BlackCache.SetDefault(data[i], struct{}{})
+	} // jwt黑名单 加入 BlackCache 中
+}

+ 140 - 0
internal/modules/system/service/sys_api.go

@@ -0,0 +1,140 @@
+package system
+
+import (
+	"errors"
+	"gorm.io/gorm"
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/request"
+)
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: CreateApi
+//@description: 新增基础api
+//@param: api model.SysApi
+//@return: err error
+
+type ApiService struct{}
+
+var ApiServiceApp = new(ApiService)
+
+func (apiService *ApiService) CreateApi(api dao.SysApi) (err error) {
+
+	_, err = dao.QueryApiByPathMethod(api.Path, api.Method)
+
+	if !errors.Is(err, gorm.ErrRecordNotFound) {
+		return errors.New("存在相同api")
+	}
+	return api.CreateApi()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: DeleteApi
+//@description: 删除基础api
+//@param: api model.SysApi
+//@return: err error
+
+func (apiService *ApiService) DeleteApi(api dao.SysApi) (err error) {
+	entity, err := dao.QueryApiById(api.ID)
+
+	if errors.Is(err, gorm.ErrRecordNotFound) { // api记录不存在
+		return err
+	}
+	err = api.DeleteApi()
+	if err != nil {
+		return err
+	}
+	CasbinServiceApp.ClearCasbin(1, entity.Path, entity.Method)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetAPIInfoList
+//@description: 分页获取数据,
+//@param: api model.SysApi, info request.PageInfo, order string, desc bool
+//@return: list interface{}, total int64, err error
+
+func (apiService *ApiService) GetAPIInfoList(api dao.SysApi, info request.PageInfo, order string, desc bool) (list interface{}, total int64, err error) {
+	limit := info.PageSize
+	offset := info.PageSize * (info.Page - 1)
+
+	return api.GetAPIInfoList(limit, offset, order, desc)
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetAllApis
+//@description: 获取所有的api
+//@return:  apis []model.SysApi, err error
+
+func (apiService *ApiService) GetAllApis() (apis []dao.SysApi, err error) {
+	return dao.GetAllApis()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetApiById
+//@description: 根据id获取api
+//@param: id float64
+//@return: api model.SysApi, err error
+
+func (apiService *ApiService) GetApiById(id int) (api dao.SysApi, err error) {
+	return dao.QueryApiById(uint(id))
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: UpdateApi
+//@description: 根据id更新api
+//@param: api model.SysApi
+//@return: err error
+
+func (apiService *ApiService) UpdateApi(api dao.SysApi) (err error) {
+	oldA, err := dao.QueryApiById(api.ID)
+	if oldA.Path != api.Path || oldA.Method != api.Method {
+		var duplicateApi dao.SysApi
+		duplicateApi, err := dao.QueryApiByPathMethod(api.Path, api.Method)
+		if err != nil {
+			if !errors.Is(err, gorm.ErrRecordNotFound) {
+				return err
+			}
+		}
+		if duplicateApi.ID != api.ID {
+			return errors.New("存在相同api路径")
+		}
+	}
+	if err != nil {
+		return err
+	}
+
+	err = CasbinServiceApp.UpdateCasbinApi(oldA.Path, api.Path, oldA.Method, api.Method)
+	if err != nil {
+		return err
+	}
+
+	return api.SaveApi()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: DeleteApis
+//@description: 删除选中API
+//@param: apis []model.SysApi
+//@return: err error
+
+func (apiService *ApiService) DeleteApisByIds(ids request.IdsReq) (err error) {
+	return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
+		var apis []dao.SysApi
+		err = tx.Find(&apis, "id in ?", ids.Ids).Error
+		if err != nil {
+			return err
+		}
+		err = tx.Delete(&[]dao.SysApi{}, "id in ?", ids.Ids).Error
+		if err != nil {
+			return err
+		}
+		for _, sysApi := range apis {
+			CasbinServiceApp.ClearCasbin(1, sysApi.Path, sysApi.Method)
+		}
+		return err
+	})
+}

+ 175 - 0
internal/modules/system/service/sys_authority.go

@@ -0,0 +1,175 @@
+package system
+
+import (
+	"errors"
+	"wails-app/internal/dao"
+	"strconv"
+
+	systemReq "wails-app/internal/modules/system/model/request"
+
+	"gorm.io/gorm"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/request"
+)
+
+var ErrRoleExistence = errors.New("存在相同角色id")
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: CreateAuthority
+//@description: 创建一个角色
+//@param: auth model.SysAuthority
+//@return: authority system.SysAuthority, err error
+
+type AuthorityService struct{}
+
+var AuthorityServiceApp = new(AuthorityService)
+
+func (authorityService *AuthorityService) CreateAuthority(auth dao.SysAuthority) (authority dao.SysAuthority, err error) {
+
+	_, err = dao.QueryAuthorityById(auth.AuthorityId)
+
+	if !errors.Is(err, gorm.ErrRecordNotFound) {
+		return auth, ErrRoleExistence
+	}
+
+	e := global.GVA_DB.Transaction(func(tx *gorm.DB) error {
+
+		if err = tx.Create(&auth).Error; err != nil {
+			return err
+		}
+
+		auth.SysBaseMenus = systemReq.DefaultMenu()
+		if err = tx.Model(&auth).Association("SysBaseMenus").Replace(&auth.SysBaseMenus); err != nil {
+			return err
+		}
+		casbinInfos := systemReq.DefaultCasbin()
+		authorityId := strconv.Itoa(int(auth.AuthorityId))
+		rules := [][]string{}
+		for _, v := range casbinInfos {
+			rules = append(rules, []string{authorityId, v.Path, v.Method})
+		}
+		return CasbinServiceApp.AddPolicies(tx, rules)
+	})
+
+	return auth, e
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: UpdateAuthority
+//@description: 更改一个角色
+//@param: auth model.SysAuthority
+//@return: authority system.SysAuthority, err error
+
+func (authorityService *AuthorityService) UpdateAuthority(auth dao.SysAuthority) (authority dao.SysAuthority, err error) {
+	_, err = dao.QueryAuthorityById(auth.AuthorityId)
+	if err != nil {
+		global.GVA_LOG.Debug(err.Error())
+		return dao.SysAuthority{}, errors.New("查询角色数据失败")
+	}
+	err = auth.UpdateAuthority()
+	return auth, err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: DeleteAuthority
+//@description: 删除角色
+//@param: auth *model.SysAuthority
+//@return: err error
+
+func (authorityService *AuthorityService) DeleteAuthority(auth *dao.SysAuthority) error {
+	if errors.Is(global.GVA_DB.Debug().Preload("Users").First(&auth).Error, gorm.ErrRecordNotFound) {
+		return errors.New("该角色不存在")
+	}
+	if len(auth.Users) != 0 {
+		return errors.New("此角色有用户正在使用禁止删除")
+	}
+	if !errors.Is(global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&dao.SysUser{}).Error, gorm.ErrRecordNotFound) {
+		return errors.New("此角色有用户正在使用禁止删除")
+	}
+	if !errors.Is(global.GVA_DB.Where("parent_id = ?", auth.AuthorityId).First(&dao.SysAuthority{}).Error, gorm.ErrRecordNotFound) {
+		return errors.New("此角色存在子角色不允许删除")
+	}
+
+	return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
+		var err error
+		if err = tx.Preload("SysBaseMenus").Preload("DataAuthorityId").Where("authority_id = ?", auth.AuthorityId).First(auth).Unscoped().Delete(auth).Error; err != nil {
+			return err
+		}
+
+		if len(auth.SysBaseMenus) > 0 {
+			if err = tx.Model(auth).Association("SysBaseMenus").Delete(auth.SysBaseMenus); err != nil {
+				return err
+			}
+			// err = db.Association("SysBaseMenus").Delete(&auth)
+		}
+		if len(auth.DataAuthorityId) > 0 {
+			if err = tx.Model(auth).Association("DataAuthorityId").Delete(auth.DataAuthorityId); err != nil {
+				return err
+			}
+		}
+
+		if err = tx.Delete(&dao.SysUserAuthority{}, "sys_authority_authority_id = ?", auth.AuthorityId).Error; err != nil {
+			return err
+		}
+		if err = tx.Where("authority_id = ?", auth.AuthorityId).Delete(&[]dao.SysAuthorityBtn{}).Error; err != nil {
+			return err
+		}
+
+		authorityId := strconv.Itoa(int(auth.AuthorityId))
+
+		if err = CasbinServiceApp.RemoveFilteredPolicy(tx, authorityId); err != nil {
+			return err
+		}
+
+		return nil
+	})
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetAuthorityInfoList
+//@description: 分页获取数据
+//@param: info request.PageInfo
+//@return: list interface{}, total int64, err error
+
+func (authorityService *AuthorityService) GetAuthorityInfoList(info request.PageInfo) (list interface{}, total int64, err error) {
+	limit := info.PageSize
+	offset := info.PageSize * (info.Page - 1)
+
+	return dao.GetAuthorityInfoList(limit, offset)
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetAuthorityInfo
+//@description: 获取所有角色信息
+//@param: auth model.SysAuthority
+//@return: sa system.SysAuthority, err error
+
+func (authorityService *AuthorityService) GetAuthorityInfo(auth dao.SysAuthority) (sa dao.SysAuthority, err error) {
+	return auth.GetAuthorityInfo()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: SetDataAuthority
+//@description: 设置角色资源权限
+//@param: auth model.SysAuthority
+//@return: error
+
+func (authorityService *AuthorityService) SetDataAuthority(auth dao.SysAuthority) error {
+	var s dao.SysAuthority
+	global.GVA_DB.Preload("DataAuthorityId").First(&s, "authority_id = ?", auth.AuthorityId)
+	err := global.GVA_DB.Model(&s).Association("DataAuthorityId").Replace(&auth.DataAuthorityId)
+	return err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: SetMenuAuthority
+//@description: 菜单与角色绑定
+//@param: auth *model.SysAuthority
+//@return: error
+
+func (authorityService *AuthorityService) SetMenuAuthority(auth *dao.SysAuthority) error {
+	var s dao.SysAuthority
+	global.GVA_DB.Preload("SysBaseMenus").First(&s, "authority_id = ?", auth.AuthorityId)
+	err := global.GVA_DB.Model(&s).Association("SysBaseMenus").Replace(&auth.SysBaseMenus)
+	return err
+}

+ 36 - 0
internal/modules/system/service/sys_authority_btn.go

@@ -0,0 +1,36 @@
+package system
+
+import (
+	"errors"
+	"gorm.io/gorm"
+	"wails-app/internal/dao"
+	"wails-app/internal/modules/system/model/request"
+	"wails-app/internal/modules/system/model/response"
+)
+
+type AuthorityBtnService struct{}
+
+func (a *AuthorityBtnService) GetAuthorityBtn(req request.SysAuthorityBtnReq) (res response.SysAuthorityBtnRes, err error) {
+	authorityBtn, err := dao.QueryAuthorityBtnByAuthorityIdMenuId(req.AuthorityId, req.MenuID)
+	if err != nil {
+		return
+	}
+	var selected []uint
+	for _, v := range authorityBtn {
+		selected = append(selected, v.SysBaseMenuBtnID)
+	}
+	res.Selected = selected
+	return res, err
+}
+
+func (a *AuthorityBtnService) SetAuthorityBtn(req request.SysAuthorityBtnReq) (err error) {
+	return dao.SetAuthorityBtn(req.MenuID, req.AuthorityId, req.Selected)
+}
+
+func (a *AuthorityBtnService) CanRemoveAuthorityBtn(ID string) (err error) {
+	_, fErr := dao.QueryAuthorityBtn(ID)
+	if errors.Is(fErr, gorm.ErrRecordNotFound) {
+		return nil
+	}
+	return errors.New("此按钮正在被使用无法删除")
+}

+ 73 - 0
internal/modules/system/service/sys_base_menu.go

@@ -0,0 +1,73 @@
+package system
+
+import (
+	"errors"
+	"wails-app/internal/dao"
+
+	"gorm.io/gorm"
+	"wails-app/internal/global"
+)
+
+type BaseMenuService struct{}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: DeleteBaseMenu
+//@description: 删除基础路由
+//@param: id float64
+//@return: err error
+
+var BaseMenuServiceApp = new(BaseMenuService)
+
+func (baseMenuService *BaseMenuService) DeleteBaseMenu(id int) (err error) {
+	err = global.GVA_DB.First(&dao.SysBaseMenu{}, "parent_id = ?", id).Error
+	if err != nil {
+		return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
+
+			err = tx.Delete(&dao.SysBaseMenu{}, "id = ?", id).Error
+			if err != nil {
+				return err
+			}
+
+			err = tx.Delete(&dao.SysBaseMenuParameter{}, "sys_base_menu_id = ?", id).Error
+			if err != nil {
+				return err
+			}
+
+			err = tx.Delete(&dao.SysBaseMenuBtn{}, "sys_base_menu_id = ?", id).Error
+			if err != nil {
+				return err
+			}
+			err = tx.Delete(&dao.SysAuthorityBtn{}, "sys_menu_id = ?", id).Error
+			if err != nil {
+				return err
+			}
+
+			err = tx.Delete(&dao.SysAuthorityMenu{}, "sys_base_menu_id = ?", id).Error
+			if err != nil {
+				return err
+			}
+			return nil
+		})
+	}
+	return errors.New("此菜单存在子菜单不可删除")
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: UpdateBaseMenu
+//@description: 更新路由
+//@param: menu model.SysBaseMenu
+//@return: err error
+
+func (baseMenuService *BaseMenuService) UpdateBaseMenu(menu dao.SysBaseMenu) (err error) {
+	return menu.UpdateBaseMenu()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetBaseMenuById
+//@description: 返回当前选中menu
+//@param: id float64
+//@return: menu system.SysBaseMenu, err error
+
+func (baseMenuService *BaseMenuService) GetBaseMenuById(id int) (menu dao.SysBaseMenu, err error) {
+	return dao.QueryBaseMenuAndMenuBtnAndParameterById(id)
+}

+ 191 - 0
internal/modules/system/service/sys_casbin.go

@@ -0,0 +1,191 @@
+package system
+
+import (
+	"errors"
+	"gorm.io/gorm"
+	"strconv"
+	"sync"
+
+	"github.com/casbin/casbin/v2"
+	"github.com/casbin/casbin/v2/model"
+	gormadapter "github.com/casbin/gorm-adapter/v3"
+	_ "github.com/go-sql-driver/mysql"
+	"go.uber.org/zap"
+	"wails-app/internal/global"
+	"wails-app/internal/modules/system/model/request"
+)
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: UpdateCasbin
+//@description: 更新casbin权限
+//@param: authorityId string, casbinInfos []request.CasbinInfo
+//@return: error
+
+type CasbinService struct{}
+
+var CasbinServiceApp = new(CasbinService)
+
+func (casbinService *CasbinService) UpdateCasbin(AuthorityID uint, casbinInfos []request.CasbinInfo) error {
+	authorityId := strconv.Itoa(int(AuthorityID))
+	casbinService.ClearCasbin(0, authorityId)
+	rules := [][]string{}
+	//做权限去重处理
+	deduplicateMap := make(map[string]bool)
+	for _, v := range casbinInfos {
+		key := authorityId + v.Path + v.Method
+		if _, ok := deduplicateMap[key]; !ok {
+			deduplicateMap[key] = true
+			rules = append(rules, []string{authorityId, v.Path, v.Method})
+		}
+	}
+	e := casbinService.Casbin()
+	success, _ := e.AddPolicies(rules)
+	if !success {
+		return errors.New("存在相同api,添加失败,请联系管理员")
+	}
+	return nil
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: UpdateCasbinApi
+//@description: API更新随动
+//@param: oldPath string, newPath string, oldMethod string, newMethod string
+//@return: error
+
+func (casbinService *CasbinService) UpdateCasbinApi(oldPath string, newPath string, oldMethod string, newMethod string) error {
+	err := global.GVA_DB.Model(&gormadapter.CasbinRule{}).Where("v1 = ? AND v2 = ?", oldPath, oldMethod).Updates(map[string]interface{}{
+		"v1": newPath,
+		"v2": newMethod,
+	}).Error
+	e := casbinService.Casbin()
+	err = e.LoadPolicy()
+	if err != nil {
+		return err
+	}
+	return err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetPolicyPathByAuthorityId
+//@description: 获取权限列表
+//@param: authorityId string
+//@return: pathMaps []request.CasbinInfo
+
+func (casbinService *CasbinService) GetPolicyPathByAuthorityId(AuthorityID uint) (pathMaps []request.CasbinInfo) {
+	e := casbinService.Casbin()
+	authorityId := strconv.Itoa(int(AuthorityID))
+	list, _ := e.GetFilteredPolicy(0, authorityId)
+	for _, v := range list {
+		pathMaps = append(pathMaps, request.CasbinInfo{
+			Path:   v[1],
+			Method: v[2],
+		})
+	}
+	return pathMaps
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: ClearCasbin
+//@description: 清除匹配的权限
+//@param: v int, p ...string
+//@return: bool
+
+func (casbinService *CasbinService) ClearCasbin(v int, p ...string) bool {
+	e := casbinService.Casbin()
+	success, _ := e.RemoveFilteredPolicy(v, p...)
+	return success
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: RemoveFilteredPolicy
+//@description: 使用数据库方法清理筛选的politicy 此方法需要调用FreshCasbin方法才可以在系统中即刻生效
+//@param: db *gorm.DB, authorityId string
+//@return: error
+
+func (casbinService *CasbinService) RemoveFilteredPolicy(db *gorm.DB, authorityId string) error {
+	return db.Delete(&gormadapter.CasbinRule{}, "v0 = ?", authorityId).Error
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: RemoveFilteredPolicy
+//@description: 同步目前数据库的policy 此方法需要调用FreshCasbin方法才可以在系统中即刻生效
+//@param: db *gorm.DB, authorityId string, rules [][]string
+//@return: error
+
+func (casbinService *CasbinService) SyncPolicy(db *gorm.DB, authorityId string, rules [][]string) error {
+	err := casbinService.RemoveFilteredPolicy(db, authorityId)
+	if err != nil {
+		return err
+	}
+	return casbinService.AddPolicies(db, rules)
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: ClearCasbin
+//@description: 清除匹配的权限
+//@param: v int, p ...string
+//@return: bool
+
+func (casbinService *CasbinService) AddPolicies(db *gorm.DB, rules [][]string) error {
+	var casbinRules []gormadapter.CasbinRule
+	for i := range rules {
+		casbinRules = append(casbinRules, gormadapter.CasbinRule{
+			Ptype: "p",
+			V0:    rules[i][0],
+			V1:    rules[i][1],
+			V2:    rules[i][2],
+		})
+	}
+	return db.Create(&casbinRules).Error
+}
+
+func (CasbinService *CasbinService) FreshCasbin() (err error) {
+	e := CasbinService.Casbin()
+	err = e.LoadPolicy()
+	return err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: Casbin
+//@description: 持久化到数据库  引入自定义规则
+//@return: *casbin.Enforcer
+
+var (
+	syncedCachedEnforcer *casbin.SyncedCachedEnforcer
+	once                 sync.Once
+)
+
+func (casbinService *CasbinService) Casbin() *casbin.SyncedCachedEnforcer {
+	once.Do(func() {
+		a, err := gormadapter.NewAdapterByDB(global.GVA_DB)
+		if err != nil {
+			zap.L().Error("适配数据库失败请检查casbin表是否为InnoDB引擎!", zap.Error(err))
+			return
+		}
+		text := `
+		[request_definition]
+		r = sub, obj, act
+		
+		[policy_definition]
+		p = sub, obj, act
+		
+		[role_definition]
+		g = _, _
+		
+		[policy_effect]
+		e = some(where (p.eft == allow))
+		
+		[matchers]
+		m = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
+		`
+		m, err := model.NewModelFromString(text)
+		if err != nil {
+			zap.L().Error("字符串加载模型失败!", zap.Error(err))
+			return
+		}
+		syncedCachedEnforcer, _ = casbin.NewSyncedCachedEnforcer(m, a)
+		syncedCachedEnforcer.SetExpireTime(60 * 60)
+		_ = syncedCachedEnforcer.LoadPolicy()
+	})
+	return syncedCachedEnforcer
+}

+ 109 - 0
internal/modules/system/service/sys_dictionary.go

@@ -0,0 +1,109 @@
+package system
+
+import (
+	"errors"
+	"wails-app/internal/dao"
+
+	"gorm.io/gorm"
+	"wails-app/internal/global"
+)
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: DeleteSysDictionary
+//@description: 创建字典数据
+//@param: sysDictionary model.SysDictionary
+//@return: err error
+
+type DictionaryService struct{}
+
+func (dictionaryService *DictionaryService) CreateSysDictionary(sysDictionary dao.SysDictionary) (err error) {
+	_, err = dao.QueryDictionaryByType(sysDictionary.Type)
+	if !errors.Is(err, gorm.ErrRecordNotFound) {
+		return errors.New("存在相同的type,不允许创建")
+	}
+	return sysDictionary.CreateDictionary()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: DeleteSysDictionary
+//@description: 删除字典数据
+//@param: sysDictionary model.SysDictionary
+//@return: err error
+
+func (dictionaryService *DictionaryService) DeleteSysDictionary(sysDictionary dao.SysDictionary) (err error) {
+	_, err = dao.QueryDictionaryAndDetailsById(sysDictionary.ID)
+	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
+		return errors.New("请不要搞事")
+	}
+	if err != nil {
+		return err
+	}
+	err = sysDictionary.DeleteDictionary()
+	if err != nil {
+		return err
+	}
+
+	if sysDictionary.SysDictionaryDetails != nil {
+		for _, SysDictionaryDetail := range sysDictionary.SysDictionaryDetails {
+			err = SysDictionaryDetail.DeleteSysDictionaryDetail()
+		}
+		return err
+	}
+	return
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: UpdateSysDictionary
+//@description: 更新字典数据
+//@param: sysDictionary *model.SysDictionary
+//@return: err error
+
+func (dictionaryService *DictionaryService) UpdateSysDictionary(sysDictionary *dao.SysDictionary) (err error) {
+	var dict dao.SysDictionary
+
+	_, err = dao.QueryDictionaryAndDetailsById(sysDictionary.ID)
+	if err != nil {
+		global.GVA_LOG.Debug(err.Error())
+		return errors.New("查询字典数据失败")
+	}
+	if dict.Type != sysDictionary.Type {
+		_, err = dao.QueryDictionaryByType(sysDictionary.Type)
+		if !errors.Is(err, gorm.ErrRecordNotFound) {
+			return errors.New("存在相同的type,不允许创建")
+		}
+	}
+
+	return dao.SysDictionary{
+		Name:   sysDictionary.Name,
+		Type:   sysDictionary.Type,
+		Status: sysDictionary.Status,
+		Desc:   sysDictionary.Desc,
+	}.UpdateDictionary()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetSysDictionary
+//@description: 根据id或者type获取字典单条数据
+//@param: Type string, Id uint
+//@return: err error, sysDictionary model.SysDictionary
+
+func (dictionaryService *DictionaryService) GetSysDictionary(Type string, Id uint, status *bool) (sysDictionary dao.SysDictionary, err error) {
+	var flag = false
+	if status == nil {
+		flag = true
+	} else {
+		flag = *status
+	}
+	return dao.GetSysDictionary(Type, Id, flag)
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@author: [SliverHorn](https://github.com/SliverHorn)
+//@function: GetSysDictionaryInfoList
+//@description: 分页获取字典列表
+//@param: info request.SysDictionarySearch
+//@return: err error, list interface{}, total int64
+
+func (dictionaryService *DictionaryService) GetSysDictionaryInfoList() (sysDictionaries []dao.SysDictionary, err error) {
+	return dao.GetSysDictionaryInfoList()
+}

+ 80 - 0
internal/modules/system/service/sys_dictionary_detail.go

@@ -0,0 +1,80 @@
+package system
+
+import (
+	"wails-app/internal/dao"
+	"wails-app/internal/modules/system/model/request"
+)
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: CreateSysDictionaryDetail
+//@description: 创建字典详情数据
+//@param: sysDictionaryDetail model.SysDictionaryDetail
+//@return: err error
+
+type DictionaryDetailService struct{}
+
+func (dictionaryDetailService *DictionaryDetailService) CreateSysDictionaryDetail(sysDictionaryDetail dao.SysDictionaryDetail) error {
+	return sysDictionaryDetail.CreateSysDictionaryDetail()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: DeleteSysDictionaryDetail
+//@description: 删除字典详情数据
+//@param: sysDictionaryDetail model.SysDictionaryDetail
+//@return: err error
+
+func (dictionaryDetailService *DictionaryDetailService) DeleteSysDictionaryDetail(sysDictionaryDetail dao.SysDictionaryDetail) error {
+	return sysDictionaryDetail.DeleteSysDictionaryDetail()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: UpdateSysDictionaryDetail
+//@description: 更新字典详情数据
+//@param: sysDictionaryDetail *model.SysDictionaryDetail
+//@return: err error
+
+func (dictionaryDetailService *DictionaryDetailService) UpdateSysDictionaryDetail(sysDictionaryDetail *dao.SysDictionaryDetail) error {
+	return sysDictionaryDetail.UpdateSysDictionaryDetail()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetSysDictionaryDetail
+//@description: 根据id获取字典详情单条数据
+//@param: id uint
+//@return: sysDictionaryDetail system.SysDictionaryDetail, err error
+
+func (dictionaryDetailService *DictionaryDetailService) GetSysDictionaryDetail(id uint) (dao.SysDictionaryDetail, error) {
+	return dao.GetSysDictionaryDetailById(id)
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetSysDictionaryDetailInfoList
+//@description: 分页获取字典详情列表
+//@param: info request.SysDictionaryDetailSearch
+//@return: list interface{}, total int64, err error
+
+func (dictionaryDetailService *DictionaryDetailService) GetSysDictionaryDetailInfoList(info request.SysDictionaryDetailSearch) (list interface{}, total int64, err error) {
+	limit := info.PageSize
+	offset := info.PageSize * (info.Page - 1)
+	return info.SysDictionaryDetail.GetSysDictionaryDetailInfoList(limit, offset)
+}
+
+// GetDictionaryList 按照字典id获取字典全部内容的方法
+func (dictionaryDetailService *DictionaryDetailService) GetDictionaryList(dictionaryID uint) ([]dao.SysDictionaryDetail, error) {
+	return dao.GetDictionaryListByDictionaryId(dictionaryID)
+}
+
+// GetDictionaryListByType 按照字典type获取字典全部内容的方法
+func (dictionaryDetailService *DictionaryDetailService) GetDictionaryListByType(t string) ([]dao.SysDictionaryDetail, error) {
+	return dao.GetDictionaryListByType(t)
+}
+
+// GetDictionaryInfoByValue 按照字典id+字典内容value获取单条字典内容
+func (dictionaryDetailService *DictionaryDetailService) GetDictionaryInfoByValue(dictionaryID uint, value uint) (dao.SysDictionaryDetail, error) {
+	return dao.GetDictionaryInfoByValue(dictionaryID, value)
+}
+
+// GetDictionaryInfoByTypeValue 按照字典type+字典内容value获取单条字典内容
+func (dictionaryDetailService *DictionaryDetailService) GetDictionaryInfoByTypeValue(t string, value uint) (dao.SysDictionaryDetail, error) {
+	return dao.GetDictionaryInfoByTypeValue(t, value)
+}

+ 228 - 0
internal/modules/system/service/sys_menu.go

@@ -0,0 +1,228 @@
+package system
+
+import (
+	"errors"
+	"wails-app/internal/dao"
+
+	"gorm.io/gorm"
+	"wails-app/internal/model/common/request"
+)
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: getMenuTreeMap
+//@description: 获取路由总树map
+//@param: authorityId string
+//@return: treeMap map[string][]system.SysMenu, err error
+
+type MenuService struct{}
+
+var MenuServiceApp = new(MenuService)
+
+func (menuService *MenuService) getMenuTreeMap(authorityId uint) (treeMap map[uint][]dao.SysMenu, err error) {
+	var allMenus []dao.SysMenu
+	treeMap = make(map[uint][]dao.SysMenu)
+
+	SysAuthorityMenus, err := dao.QueryAuthorityMenuByAuthorityId(authorityId)
+	if err != nil {
+		return
+	}
+
+	var MenuIds []string
+
+	for i := range SysAuthorityMenus {
+		MenuIds = append(MenuIds, SysAuthorityMenus[i].MenuId)
+	}
+
+	baseMenu, err := dao.QueryBaseMenuAndParametersByMenuId(MenuIds)
+	if err != nil {
+		return
+	}
+
+	for i := range baseMenu {
+		allMenus = append(allMenus, dao.SysMenu{
+			SysBaseMenu: baseMenu[i],
+			AuthorityId: authorityId,
+			MenuId:      baseMenu[i].ID,
+			Parameters:  baseMenu[i].Parameters,
+		})
+	}
+
+	btns, err := dao.QueryAuthorityBtnAndSysBaseMenuBtnById(authorityId)
+
+	if err != nil {
+		return
+	}
+	var btnMap = make(map[uint]map[string]uint)
+	for _, v := range btns {
+		if btnMap[v.SysMenuID] == nil {
+			btnMap[v.SysMenuID] = make(map[string]uint)
+		}
+		btnMap[v.SysMenuID][v.SysBaseMenuBtn.Name] = authorityId
+	}
+	for _, v := range allMenus {
+		v.Btns = btnMap[v.SysBaseMenu.ID]
+		treeMap[v.ParentId] = append(treeMap[v.ParentId], v)
+	}
+	return treeMap, err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetMenuTree
+//@description: 获取动态菜单树
+//@param: authorityId string
+//@return: menus []system.SysMenu, err error
+
+func (menuService *MenuService) GetMenuTree(authorityId uint) (menus []dao.SysMenu, err error) {
+	menuTree, err := menuService.getMenuTreeMap(authorityId)
+	menus = menuTree[0]
+	for i := 0; i < len(menus); i++ {
+		err = menuService.getChildrenList(&menus[i], menuTree)
+	}
+	return menus, err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: getChildrenList
+//@description: 获取子菜单
+//@param: menu *model.SysMenu, treeMap map[string][]model.SysMenu
+//@return: err error
+
+func (menuService *MenuService) getChildrenList(menu *dao.SysMenu, treeMap map[uint][]dao.SysMenu) (err error) {
+	menu.Children = treeMap[menu.MenuId]
+	for i := 0; i < len(menu.Children); i++ {
+		err = menuService.getChildrenList(&menu.Children[i], treeMap)
+	}
+	return err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetInfoList
+//@description: 获取路由分页
+//@return: list interface{}, total int64,err error
+
+func (menuService *MenuService) GetInfoList() (list interface{}, total int64, err error) {
+	var menuList []dao.SysBaseMenu
+	treeMap, err := menuService.getBaseMenuTreeMap()
+	menuList = treeMap[0]
+	for i := 0; i < len(menuList); i++ {
+		err = menuService.getBaseChildrenList(&menuList[i], treeMap)
+	}
+	return menuList, total, err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: getBaseChildrenList
+//@description: 获取菜单的子菜单
+//@param: menu *model.SysBaseMenu, treeMap map[string][]model.SysBaseMenu
+//@return: err error
+
+func (menuService *MenuService) getBaseChildrenList(menu *dao.SysBaseMenu, treeMap map[uint][]dao.SysBaseMenu) (err error) {
+	menu.Children = treeMap[menu.ID]
+	for i := 0; i < len(menu.Children); i++ {
+		err = menuService.getBaseChildrenList(&menu.Children[i], treeMap)
+	}
+	return err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: AddBaseMenu
+//@description: 添加基础路由
+//@param: menu model.SysBaseMenu
+//@return: error
+
+func (menuService *MenuService) AddBaseMenu(menu dao.SysBaseMenu) error {
+	_, err := dao.QueryBaseMenuByName(menu.Name)
+	if !errors.Is(err, gorm.ErrRecordNotFound) {
+		return errors.New("存在重复name,请修改name")
+	}
+	return menu.CreateBaseMenu()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: getBaseMenuTreeMap
+//@description: 获取路由总树map
+//@return: treeMap map[string][]system.SysBaseMenu, err error
+
+func (menuService *MenuService) getBaseMenuTreeMap() (treeMap map[uint][]dao.SysBaseMenu, err error) {
+	treeMap = make(map[uint][]dao.SysBaseMenu)
+	allMenus, err := dao.QueryBaseMenuAndMenuBtnAndParameters()
+	for _, v := range allMenus {
+		treeMap[v.ParentId] = append(treeMap[v.ParentId], v)
+	}
+	return treeMap, err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetBaseMenuTree
+//@description: 获取基础路由树
+//@return: menus []system.SysBaseMenu, err error
+
+func (menuService *MenuService) GetBaseMenuTree() (menus []dao.SysBaseMenu, err error) {
+	treeMap, err := menuService.getBaseMenuTreeMap()
+	menus = treeMap[0]
+	for i := 0; i < len(menus); i++ {
+		err = menuService.getBaseChildrenList(&menus[i], treeMap)
+	}
+	return menus, err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: AddMenuAuthority
+//@description: 为角色增加menu树
+//@param: menus []model.SysBaseMenu, authorityId string
+//@return: err error
+
+func (menuService *MenuService) AddMenuAuthority(menus []dao.SysBaseMenu, authorityId uint) (err error) {
+	var auth dao.SysAuthority
+	auth.AuthorityId = authorityId
+	auth.SysBaseMenus = menus
+	err = AuthorityServiceApp.SetMenuAuthority(&auth)
+	return err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetMenuAuthority
+//@description: 查看当前角色树
+//@param: info *request.GetAuthorityId
+//@return: menus []system.SysMenu, err error
+
+func (menuService *MenuService) GetMenuAuthority(info *request.GetAuthorityId) (menus []dao.SysMenu, err error) {
+	SysAuthorityMenus, err := dao.QueryAuthorityMenuByAuthorityId(info.AuthorityId)
+	if err != nil {
+		return
+	}
+
+	var MenuIds []string
+
+	for i := range SysAuthorityMenus {
+		MenuIds = append(MenuIds, SysAuthorityMenus[i].MenuId)
+	}
+
+	baseMenu, err := dao.QueryBaseMenuAndParametersByMenuId(MenuIds)
+
+	for i := range baseMenu {
+		menus = append(menus, dao.SysMenu{
+			SysBaseMenu: baseMenu[i],
+			AuthorityId: info.AuthorityId,
+			MenuId:      baseMenu[i].ID,
+			Parameters:  baseMenu[i].Parameters,
+		})
+	}
+	return menus, err
+}
+
+// UserAuthorityDefaultRouter 用户角色默认路由检查
+//
+//	Author [SliverHorn](https://github.com/SliverHorn)
+func (menuService *MenuService) UserAuthorityDefaultRouter(user *dao.SysUser) {
+	menuIds, err := dao.QuerySysBaseMenuIdById(user.AuthorityId)
+	if err != nil {
+		return
+	}
+
+	_, err = dao.QueryBaseMenuByIdAndDefaultRouter(user.Authority.DefaultRouter, menuIds)
+
+	if errors.Is(err, gorm.ErrRecordNotFound) {
+		user.Authority.DefaultRouter = "404"
+	}
+}

+ 64 - 0
internal/modules/system/service/sys_operation_record.go

@@ -0,0 +1,64 @@
+package system
+
+import (
+	"wails-app/internal/dao"
+	"wails-app/internal/model/common/request"
+	systemReq "wails-app/internal/modules/system/model/request"
+)
+
+//@author: [granty1](https://github.com/granty1)
+//@function: CreateSysOperationRecord
+//@description: 创建记录
+//@param: sysOperationRecord model.SysOperationRecord
+//@return: err error
+
+type OperationRecordService struct{}
+
+func (operationRecordService *OperationRecordService) CreateSysOperationRecord(sysOperationRecord dao.SysOperationRecord) (err error) {
+	return sysOperationRecord.CreateSysOperationRecord()
+}
+
+//@author: [granty1](https://github.com/granty1)
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: DeleteSysOperationRecordByIds
+//@description: 批量删除记录
+//@param: ids request.IdsReq
+//@return: err error
+
+func (operationRecordService *OperationRecordService) DeleteSysOperationRecordByIds(ids request.IdsReq) error {
+	return dao.DeleteSysOperationRecordByIds(ids.Ids)
+}
+
+//@author: [granty1](https://github.com/granty1)
+//@function: DeleteSysOperationRecord
+//@description: 删除操作记录
+//@param: sysOperationRecord model.SysOperationRecord
+//@return: err error
+
+func (operationRecordService *OperationRecordService) DeleteSysOperationRecord(sysOperationRecord dao.SysOperationRecord) error {
+	return sysOperationRecord.DeleteSysOperationRecord()
+}
+
+//@author: [granty1](https://github.com/granty1)
+//@function: DeleteSysOperationRecord
+//@description: 根据id获取单条操作记录
+//@param: id uint
+//@return: sysOperationRecord system.SysOperationRecord, err error
+
+func (operationRecordService *OperationRecordService) GetSysOperationRecord(id uint) (dao.SysOperationRecord, error) {
+	return dao.GetSysOperationRecord(id)
+}
+
+//@author: [granty1](https://github.com/granty1)
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetSysOperationRecordInfoList
+//@description: 分页获取操作记录列表
+//@param: info systemReq.SysOperationRecordSearch
+//@return: list interface{}, total int64, err error
+
+func (operationRecordService *OperationRecordService) GetSysOperationRecordInfoList(info systemReq.SysOperationRecordSearch) (list interface{}, total int64, err error) {
+	limit := info.PageSize
+	offset := info.PageSize * (info.Page - 1)
+
+	return info.SysOperationRecord.GetSysOperationRecordInfoList(limit, offset)
+}

+ 212 - 0
internal/modules/system/service/sys_user.go

@@ -0,0 +1,212 @@
+package system
+
+import (
+	"errors"
+	"fmt"
+	"wails-app/internal/dao"
+	"time"
+
+	"github.com/gofrs/uuid/v5"
+	"gorm.io/gorm"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/request"
+	"wails-app/internal/pkg"
+)
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: Register
+//@description: 用户注册
+//@param: u model.SysUser
+//@return: userInter system.SysUser, err error
+
+type UserService struct{}
+
+func (userService *UserService) Register(u dao.SysUser) (userInter dao.SysUser, err error) {
+	_, err = dao.QueryUserByUserName(u.Username)
+	if !errors.Is(err, gorm.ErrRecordNotFound) { // 判断用户名是否注册
+		return userInter, errors.New("用户名已注册")
+	}
+	// 否则 附加uuid 密码hash加密 注册
+	u.Password = utils.BcryptHash(u.Password)
+	u.UUID = uuid.Must(uuid.NewV4())
+	err = u.CreateUser()
+	return u, err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@author: [SliverHorn](https://github.com/SliverHorn)
+//@function: Login
+//@description: 用户登录
+//@param: u *model.SysUser
+//@return: err error, userInter *model.SysUser
+
+func (userService *UserService) Login(u *dao.SysUser) (userInter *dao.SysUser, err error) {
+	if nil == global.GVA_DB {
+		return nil, fmt.Errorf("db not init")
+	}
+
+	user, err := dao.QueryUserAndAuthorityByUserName(u.Username)
+
+	if err == nil {
+		if ok := utils.BcryptCheck(u.Password, user.Password); !ok {
+			return nil, errors.New("密码错误")
+		}
+		MenuServiceApp.UserAuthorityDefaultRouter(&user)
+	}
+	return &user, err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: ChangePassword
+//@description: 修改用户密码
+//@param: u *model.SysUser, newPassword string
+//@return: userInter *model.SysUser,err error
+
+func (userService *UserService) ChangePassword(u *dao.SysUser, newPassword string) (userInter *dao.SysUser, err error) {
+	user, err := dao.QueryUserByUserId(u.ID)
+	if err != nil {
+		return nil, err
+	}
+	if ok := utils.BcryptCheck(u.Password, user.Password); !ok {
+		return nil, errors.New("原密码错误")
+	}
+	user.Password = utils.BcryptHash(newPassword)
+	err = u.UpdateUser()
+	return &user, err
+
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: GetUserInfoList
+//@description: 分页获取数据
+//@param: info request.PageInfo
+//@return: err error, list interface{}, total int64
+
+func (userService *UserService) GetUserInfoList(info request.PageInfo) (list interface{}, total int64, err error) {
+	limit := info.PageSize
+	offset := info.PageSize * (info.Page - 1)
+
+	userList, total, err := dao.QueryUserInfoList(limit, offset)
+
+	return userList, total, err
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: SetUserAuthority
+//@description: 设置一个用户的权限
+//@param: uuid uuid.UUID, authorityId string
+//@return: err error
+
+func (userService *UserService) SetUserAuthority(id uint, authorityId uint) (err error) {
+	_, assignErr := dao.QueryUserAuthority(id, authorityId)
+
+	if errors.Is(assignErr, gorm.ErrRecordNotFound) {
+		return errors.New("该用户无此角色")
+	}
+
+	return dao.UpdateUserAuthority(id, authorityId)
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: SetUserAuthorities
+//@description: 设置一个用户的权限
+//@param: id uint, authorityIds []string
+//@return: err error
+
+func (userService *UserService) SetUserAuthorities(id uint, authorityId uint) (err error) {
+	return dao.SetUserAuthorities(id, authorityId)
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: DeleteUser
+//@description: 删除用户
+//@param: id float64
+//@return: err error
+
+func (userService *UserService) DeleteUser(id int) (err error) {
+	return dao.DeleteUser(id)
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: SetUserInfo
+//@description: 设置用户信息
+//@param: reqUser model.SysUser
+//@return: err error, user model.SysUser
+
+func (userService *UserService) SetUserInfo(req dao.SysUser) error {
+	req.UpdatedAt = time.Now()
+	return req.SetUserInfo()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: SetUserInfo
+//@description: 设置用户信息
+//@param: reqUser model.SysUser
+//@return: err error, user model.SysUser
+
+func (userService *UserService) SetSelfInfo(req dao.SysUser) error {
+	return req.SetUserInfoById()
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@author: [SliverHorn](https://github.com/SliverHorn)
+//@function: GetUserInfo
+//@description: 获取用户信息
+//@param: uuid uuid.UUID
+//@return: err error, user system.SysUser
+
+func (userService *UserService) GetUserInfo(uuid uuid.UUID) (user dao.SysUser, err error) {
+	reqUser, err := dao.GetUserInfoByUUID(uuid)
+	if err != nil {
+		return reqUser, err
+	}
+	MenuServiceApp.UserAuthorityDefaultRouter(&reqUser)
+	return reqUser, err
+}
+
+//@author: [SliverHorn](https://github.com/SliverHorn)
+//@function: FindUserById
+//@description: 通过id获取用户信息
+//@param: id int
+//@return: err error, user *model.SysUser
+
+func (userService *UserService) FindUserById(id int) (user *dao.SysUser, err error) {
+	u, err := dao.QueryUserByUserId(uint(id))
+	return &u, err
+}
+
+//@author: [SliverHorn](https://github.com/SliverHorn)
+//@function: FindUserByUuid
+//@description: 通过uuid获取用户信息
+//@param: uuid string
+//@return: err error, user *model.SysUser
+
+func (userService *UserService) FindUserByUuid(uuidStr string) (user *dao.SysUser, err error) {
+	id, err := uuid.FromString(uuidStr)
+	if err != nil {
+		fmt.Println("Error parsing UUID:", err)
+		return
+	}
+
+	u, err := dao.GetUserInfoByUUID(id)
+	if err != nil {
+		return &u, errors.New("用户不存在")
+	}
+
+	return &u, nil
+}
+
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: resetPassword
+//@description: 修改用户密码
+//@param: ID uint
+//@return: err error
+
+func (userService *UserService) ResetPassword(ID uint) error {
+	return dao.SysUser{
+		GVA_MODEL: global.GVA_MODEL{
+			ID: ID,
+		},
+		Password: utils.BcryptHash("123456"),
+	}.SetUserInfoById()
+}

+ 15 - 0
internal/modules/vehicle/api/enter.go

@@ -0,0 +1,15 @@
+package vehicle
+
+import (
+	"wails-app/internal/service"
+	"wails-app/internal/service/owner"
+)
+
+var (
+	feeConfigService     = service.ServiceGroupApp.VehicleServiceGroup.FeeConfigService
+	vehicleService       = service.ServiceGroupApp.VehicleServiceGroup.VehicleService
+	vehicleTypeService   = service.ServiceGroupApp.VehicleServiceGroup.VehicleTypeService
+	vehicleRecordService = service.ServiceGroupApp.VehicleServiceGroup.VehicleRecordService
+	shortlistService     = service.ServiceGroupApp.VehicleServiceGroup.ShortlistService
+	ownerService         = owner.OwnerService
+)

+ 101 - 0
internal/modules/vehicle/api/fee_config.go

@@ -0,0 +1,101 @@
+package vehicle
+
+import (
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/model/vehicle/request"
+	vehicleResponse "wails-app/internal/model/vehicle/response"
+)
+
+// CreateFeeConfig 创建收费配置
+func CreateFeeConfig(c *gin.Context) {
+	var req request.FeeConfigCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := 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 := 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 := 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 := 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.Query("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	err = feeConfigService.DeleteFeeConfig(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}

+ 119 - 0
internal/modules/vehicle/api/owner.go

@@ -0,0 +1,119 @@
+package vehicle
+
+import (
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/model/owner/request"
+	ownerResponse "wails-app/internal/model/owner/response"
+)
+
+// CreateOwner 创建车主信息
+func CreateOwner(c *gin.Context) {
+	var req request.OwnerCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := 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 := 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 := 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 := 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 := 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 = ownerService.DeleteOwner(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}

+ 86 - 0
internal/modules/vehicle/api/shortlist.go

@@ -0,0 +1,86 @@
+package vehicle
+
+import (
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"wails-app/internal/dao"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/model/vehicle/request"
+	"strconv"
+)
+
+type ShortlistApi struct{}
+
+func (sa *ShortlistApi) QueryAllShortlists(c *gin.Context) {
+	shortlists, err := shortlistService.QueryAllShortlists()
+	if err != nil {
+		response.FailWithMessage("查询失败", c)
+		return
+	}
+	response.OkWithData(shortlists, c)
+}
+
+func (sa *ShortlistApi) QueryShortlistList(c *gin.Context) {
+	var info request.ShortlistRequest
+	err := c.ShouldBindJSON(&info)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	fmt.Println(info)
+	list, total, err := shortlistService.QueryShortlistList(info)
+	if err != nil {
+		response.FailWithMessage("查询失败", c)
+		return
+	}
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     info.Page,
+		PageSize: info.PageSize,
+	}, "获取成功", c)
+}
+
+func (sa *ShortlistApi) CreateShortlist(c *gin.Context) {
+	var shortlist dao.Shortlist
+	err := c.ShouldBindJSON(&shortlist)
+	if err != nil {
+		response.FailWithMessage("参数错误"+err.Error(), c)
+		return
+	}
+	err = shortlistService.CreateShortlist(shortlist)
+	if err != nil {
+		response.FailWithMessage("新增失败", c)
+		return
+	}
+	response.OkWithMessage("新增成功", c)
+}
+
+func (sa *ShortlistApi) UpdateShortlist(c *gin.Context) {
+	var shortlist dao.Shortlist
+	err := c.ShouldBindJSON(&shortlist)
+	if err != nil {
+		response.FailWithMessage("参数错误"+err.Error(), c)
+		return
+	}
+	err = shortlistService.UpdateShortlist(shortlist)
+	if err != nil {
+		response.FailWithMessage("修改失败", c)
+		return
+	}
+	response.OkWithMessage("修改成功", c)
+}
+
+func (sa *ShortlistApi) DeleteShortlist(c *gin.Context) {
+	id, err := strconv.Atoi(c.Query("id"))
+	if err != nil {
+		response.FailWithMessage("参数错误", c)
+		return
+	}
+	err = shortlistService.DeleteShortlist(id)
+	if err != nil {
+		response.FailWithMessage("删除失败", c)
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}

+ 160 - 0
internal/modules/vehicle/api/vehicle.go

@@ -0,0 +1,160 @@
+package vehicle
+
+import (
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/model/vehicle/request"
+	vehicleResponse "wails-app/internal/model/vehicle/response"
+)
+
+// CreateVehicle 创建车辆信息
+func CreateVehicle(c *gin.Context) {
+	var req request.VehicleCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err := 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 := 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 := 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 := vehicleService.GetVehicleByPlateNumber(plateNumber, "")
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(vehicleInfo, c)
+}
+
+func QueryAllVehicles(c *gin.Context) {
+	vehicles, err := vehicleService.QueryAllVehicles()
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	response.OkWithData(vehicles, 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 := 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 = 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 := 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 := vehicleService.VehicleExit(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(resp, c)
+}

+ 78 - 0
internal/modules/vehicle/api/vehicle_record.go

@@ -0,0 +1,78 @@
+package vehicle
+
+import (
+	"fmt"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/model/vehicle/request"
+	vehicleResponse "wails-app/internal/model/vehicle/response"
+
+	"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 := 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 := 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) {
+	var info request.VehicleRecordRequest
+
+	err := c.ShouldBindJSON(&info)
+
+	list, total, err := vehicleRecordService.ListVehicleRecords(info)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     info.Page,
+		PageSize: info.PageSize,
+	}, "获取成功", c)
+}

+ 113 - 0
internal/modules/vehicle/api/vehicle_type.go

@@ -0,0 +1,113 @@
+package vehicle
+
+import (
+	"fmt"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/model/vehicle/request"
+
+	"github.com/gin-gonic/gin"
+)
+
+// CreateVehicleType 创建车辆类型
+func CreateVehicleType(c *gin.Context) {
+	var req request.VehicleTypeCreate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err := vehicleTypeService.CreateVehicleType(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("创建成功", c)
+}
+
+// UpdateVehicleType 更新车辆类型
+func UpdateVehicleType(c *gin.Context) {
+	var req request.VehicleTypeUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	err := vehicleTypeService.UpdateVehicleType(req)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("更新成功", c)
+}
+
+// GetVehicleTypeByID 根据ID获取车辆类型
+func GetVehicleTypeByID(c *gin.Context) {
+	id := c.Query("id")
+	uintID := uint(0)
+	_, err := fmt.Sscanf(id, "%d", &uintID)
+	if err != nil {
+		response.FailWithMessage("无效的ID", c)
+		return
+	}
+
+	vehicleTypeInfo, err := vehicleTypeService.GetVehicleTypeByID(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(vehicleTypeInfo, c)
+}
+
+// ListVehicleTypes 获取车辆类型列表
+func ListVehicleTypes(c *gin.Context) {
+	var info request.VehicleTypeQuery
+	if err := c.ShouldBindJSON(&info); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	total, list, err := vehicleTypeService.ListVehicleTypes(info)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     info.Page,
+		PageSize: info.PageSize,
+	}, "获取成功", c)
+}
+
+// DeleteVehicleType 删除车辆类型
+func DeleteVehicleType(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 = vehicleTypeService.DeleteVehicleType(uintID)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithMessage("删除成功", c)
+}
+
+// GetAllVehicleTypes 获取所有车辆类型(用于下拉选择)
+func GetAllVehicleTypes(c *gin.Context) {
+	vehicleTypes, err := vehicleTypeService.GetAllVehicleTypes()
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	response.OkWithData(vehicleTypes, c)
+}

+ 32 - 0
internal/modules/vehicle/model/request/fee_config.go

@@ -0,0 +1,32 @@
+package request
+
+type FeeConfigCreate struct {
+	VehicleTypeID uint    `json:"vehicle_type_id"`
+	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    `json:"is_vip_free"`
+	VIPDiscount   float64 `json:"vip_discount"`
+	Description   string  `json:"description"`
+}
+
+type FeeConfigUpdate struct {
+	ID            uint    `json:"id"`
+	VehicleTypeID uint    `json:"vehicle_type_id"`
+	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    `json:"is_vip_free"`
+	VIPDiscount   float64 `json:"vip_discount"`
+	Description   string  `json:"description"`
+}
+
+type FeeConfigQuery struct {
+	VehicleTypeID uint `form:"vehicle_type_id"`
+	Page          int  `form:"page,default=1"`
+	PageSize      int  `form:"page_size,default=10"`
+}

+ 28 - 0
internal/modules/vehicle/model/request/owner.go

@@ -0,0 +1,28 @@
+package request
+
+import "time"
+
+type OwnerCreate struct {
+	OwnerName     string    `json:"owner_name"`
+	OwnerSurname  string    `json:"owner_surname"`
+	OwnerPhone    string    `json:"owner_phone"`
+	IsVip         bool      `json:"is_vip"`
+	VipExpireTime time.Time `json:"vip_expire_time"`
+}
+
+type OwnerUpdate struct {
+	ID            uint      `json:"id"`
+	OwnerName     string    `json:"owner_name"`
+	OwnerSurname  string    `json:"owner_surname"`
+	OwnerPhone    string    `json:"owner_phone"`
+	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"`
+}

+ 9 - 0
internal/modules/vehicle/model/request/shortlist.go

@@ -0,0 +1,9 @@
+package request
+
+type ShortlistRequest struct {
+	ListType    string `form:"list_type" json:"list_type"`
+	PlateNumber string `form:"plate_number" json:"plate_number"`
+	RFIDTag     string `form:"rfid_tag" json:"rfid_tag"`
+	Page        int    `form:"page" json:"page"`
+	PageSize    int    `form:"pageSize" json:"pageSize"`
+}

+ 91 - 0
internal/modules/vehicle/model/request/vehicle.go

@@ -0,0 +1,91 @@
+package request
+
+type VehicleCreate struct {
+	PlateNumber   string `json:"plate_number"`
+	VehicleTypeID uint   `json:"vehicle_type_id"`
+	VehicleBrand  string `json:"vehicle_brand"`
+	VehicleColor  string `json:"vehicle_color"`
+	RFIDTag       string `json:"rfid_tag"`
+	OwnerId       uint   `json:"owner_id"`
+}
+
+type VehicleUpdate struct {
+	ID            uint   `json:"id"`
+	PlateNumber   string `json:"plate_number"`
+	VehicleTypeID uint   `json:"vehicle_type_id"`
+	VehicleBrand  string `json:"vehicle_brand"`
+	VehicleColor  string `json:"vehicle_color"`
+	RFIDTag       string `json:"rfid_tag"`
+	OwnerId       uint   `json:"owner_id"`
+}
+
+type VehicleQuery struct {
+	PlateNumber   string `form:"plate_number"`
+	VehicleTypeID uint   `form:"vehicle_type_id"`
+	OwnerId       uint   `form:"owner_id"`
+	Page          int    `form:"page,default=1"`
+	PageSize      int    `form:"page_size,default=10"`
+	RfidTag       string `gorm:"column:rfid_tag" json:"rfid_tag"`
+}
+
+type VehicleTypeCreate struct {
+	Name    string `json:"name"`
+	Remarks string `json:"remarks"`
+}
+
+type VehicleTypeUpdate struct {
+	ID      uint   `json:"id"`
+	Name    string `json:"name"`
+	Remarks string `json:"remarks"`
+}
+
+type VehicleTypeQuery struct {
+	Name     string `json:"name" form:"name"`
+	Page     int    `json:"page,default=1" form:"page"`
+	PageSize int    `json:"pageSize,default=1" form:"pageSize"`
+}
+
+type VehicleEntry struct {
+	PlateNumber    string `json:"plate_number"`
+	ParkingLotID   uint   `json:"parking_lot_id"`
+	ParkingSpaceID uint   `json:"parking_space_id"`
+	RFIDTag        string `json:"rfid_tag"`
+	EntryImage     string `json:"entry_image"`
+}
+
+type VehicleExit struct {
+	PlateNumber string `json:"plate_number"`
+	ExitImage   string `json:"exit_image"`
+	RFIDTag     string `json:"rfid_tag"`
+}
+
+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"`
+}
+
+type VehicleRecordRequest struct {
+	PlateNumber string `form:"plate_number" json:"plate_number"`
+	RFIDTag     string `form:"rfid_tag" json:"rfid_tag"`
+	Page        int    `form:"page,default=1" json:"page"`
+	PageSize    int    `form:"page_size,default=10" json:"page_size"`
+}

+ 0 - 0
internal/modules/vehicle/model/response/owner.go


Деякі файли не було показано, через те що забагато файлів було змінено