feat(routes): 注册 7 个设备 Gateway 路由

This commit is contained in:
2026-02-02 17:33:39 +08:00
parent 246ea6e287
commit 543c454f16
7 changed files with 1499 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
# Execution Status
## Completed Tasks
### ✅ Task 1: Bootstrap Dependency Injection
- **Status**: COMPLETED AND VERIFIED
- **Verification**:
- LSP diagnostics: CLEAN
- Build: SUCCESS
- Changes verified in files:
- `internal/handler/admin/iot_card.go` - Added gatewayClient field and updated constructor
- `internal/handler/admin/device.go` - Added gatewayClient field and updated constructor
- `internal/bootstrap/handlers.go` - Updated handler instantiation to pass deps.GatewayClient
- **Commit**: `修改 Bootstrap 注入 Gateway Client 依赖到 IotCardHandler 和 DeviceHandler`
- **Session**: ses_3e2531368ffes11sTWCVuBm9XX
## Next Wave (Wave 2 - PARALLEL)
### Task 2: IotCardHandler - Add 6 Gateway Methods
**Blocked By**: Task 1 ✅ (unblocked)
**Blocks**: Task 4
**Can Run In Parallel**: YES (with Task 3)
Methods to add:
- GetGatewayStatus (GET /:iccid/gateway-status)
- GetGatewayFlow (GET /:iccid/gateway-flow)
- GetGatewayRealname (GET /:iccid/gateway-realname)
- GetRealnameLink (GET /:iccid/realname-link)
- StopCard (POST /:iccid/stop)
- StartCard (POST /:iccid/start)
### Task 3: DeviceHandler - Add 7 Gateway Methods
**Blocked By**: Task 1 ✅ (unblocked)
**Blocks**: Task 5
**Can Run In Parallel**: YES (with Task 2)
Methods to add:
- GetGatewayInfo (GET /by-imei/:imei/gateway-info)
- GetGatewaySlots (GET /by-imei/:imei/gateway-slots)
- SetSpeedLimit (PUT /by-imei/:imei/speed-limit)
- SetWiFi (PUT /by-imei/:imei/wifi)
- SwitchCard (POST /by-imei/:imei/switch-card)
- RebootDevice (POST /by-imei/:imei/reboot)
- ResetDevice (POST /by-imei/:imei/reset)
## Implementation Notes
### Handler Method Pattern
```go
func (h *IotCardHandler) GetGatewayStatus(c *fiber.Ctx) error {
iccid := c.Params("iccid")
if iccid == "" {
return errors.New(errors.CodeInvalidParam, "ICCID不能为空")
}
// 1. Validate permission: Query DB to confirm ownership
card, err := h.service.GetByICCID(c.UserContext(), iccid)
if err != nil {
return errors.New(errors.CodeNotFound, "卡不存在或无权限访问")
}
// 2. Call Gateway
resp, err := h.gatewayClient.QueryCardStatus(c.UserContext(), &gateway.CardStatusReq{
CardNo: iccid,
})
if err != nil {
return err
}
return response.Success(c, resp)
}
```
### Gateway Param Conversion
- ICCID (path param) = CardNo (Gateway param)
- IMEI (path param) = DeviceID (Gateway param)

File diff suppressed because it is too large Load Diff

View File

@@ -223,3 +223,183 @@ func (h *DeviceHandler) BatchSetSeriesBinding(c *fiber.Ctx) error {
return response.Success(c, result)
}
// GetGatewayInfo 查询设备信息
func (h *DeviceHandler) GetGatewayInfo(c *fiber.Ctx) error {
imei := c.Params("imei")
if imei == "" {
return errors.New(errors.CodeInvalidParam, "设备号不能为空")
}
// 验证权限:查询数据库确认设备存在且用户有权限访问
_, err := h.service.GetByDeviceNo(c.UserContext(), imei)
if err != nil {
return errors.New(errors.CodeNotFound, "设备不存在或无权限访问")
}
// 调用 Gateway
resp, err := h.gatewayClient.GetDeviceInfo(c.UserContext(), &gateway.DeviceInfoReq{
DeviceID: imei,
})
if err != nil {
return err
}
return response.Success(c, resp)
}
// GetGatewaySlots 查询设备卡槽信息
func (h *DeviceHandler) GetGatewaySlots(c *fiber.Ctx) error {
imei := c.Params("imei")
if imei == "" {
return errors.New(errors.CodeInvalidParam, "设备号不能为空")
}
// 验证权限:查询数据库确认设备存在且用户有权限访问
_, err := h.service.GetByDeviceNo(c.UserContext(), imei)
if err != nil {
return errors.New(errors.CodeNotFound, "设备不存在或无权限访问")
}
// 调用 Gateway
resp, err := h.gatewayClient.GetSlotInfo(c.UserContext(), &gateway.DeviceInfoReq{
DeviceID: imei,
})
if err != nil {
return err
}
return response.Success(c, resp)
}
// SetSpeedLimit 设置设备限速
func (h *DeviceHandler) SetSpeedLimit(c *fiber.Ctx) error {
imei := c.Params("imei")
if imei == "" {
return errors.New(errors.CodeInvalidParam, "设备号不能为空")
}
var req gateway.SpeedLimitReq
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
// 验证权限:查询数据库确认设备存在且用户有权限访问
_, err := h.service.GetByDeviceNo(c.UserContext(), imei)
if err != nil {
return errors.New(errors.CodeNotFound, "设备不存在或无权限访问")
}
// 调用 Gateway
req.DeviceID = imei
err = h.gatewayClient.SetSpeedLimit(c.UserContext(), &req)
if err != nil {
return err
}
return response.Success(c, nil)
}
// SetWiFi 设置设备 WiFi
func (h *DeviceHandler) SetWiFi(c *fiber.Ctx) error {
imei := c.Params("imei")
if imei == "" {
return errors.New(errors.CodeInvalidParam, "设备号不能为空")
}
var req gateway.WiFiReq
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
// 验证权限:查询数据库确认设备存在且用户有权限访问
_, err := h.service.GetByDeviceNo(c.UserContext(), imei)
if err != nil {
return errors.New(errors.CodeNotFound, "设备不存在或无权限访问")
}
// 调用 Gateway
req.DeviceID = imei
err = h.gatewayClient.SetWiFi(c.UserContext(), &req)
if err != nil {
return err
}
return response.Success(c, nil)
}
// SwitchCard 切换设备使用的卡
func (h *DeviceHandler) SwitchCard(c *fiber.Ctx) error {
imei := c.Params("imei")
if imei == "" {
return errors.New(errors.CodeInvalidParam, "设备号不能为空")
}
var req gateway.SwitchCardReq
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
// 验证权限:查询数据库确认设备存在且用户有权限访问
_, err := h.service.GetByDeviceNo(c.UserContext(), imei)
if err != nil {
return errors.New(errors.CodeNotFound, "设备不存在或无权限访问")
}
// 调用 Gateway
req.DeviceID = imei
err = h.gatewayClient.SwitchCard(c.UserContext(), &req)
if err != nil {
return err
}
return response.Success(c, nil)
}
// RebootDevice 重启设备
func (h *DeviceHandler) RebootDevice(c *fiber.Ctx) error {
imei := c.Params("imei")
if imei == "" {
return errors.New(errors.CodeInvalidParam, "设备号不能为空")
}
// 验证权限:查询数据库确认设备存在且用户有权限访问
_, err := h.service.GetByDeviceNo(c.UserContext(), imei)
if err != nil {
return errors.New(errors.CodeNotFound, "设备不存在或无权限访问")
}
// 调用 Gateway
err = h.gatewayClient.RebootDevice(c.UserContext(), &gateway.DeviceOperationReq{
DeviceID: imei,
})
if err != nil {
return err
}
return response.Success(c, nil)
}
// ResetDevice 恢复设备出厂设置
func (h *DeviceHandler) ResetDevice(c *fiber.Ctx) error {
imei := c.Params("imei")
if imei == "" {
return errors.New(errors.CodeInvalidParam, "设备号不能为空")
}
// 验证权限:查询数据库确认设备存在且用户有权限访问
_, err := h.service.GetByDeviceNo(c.UserContext(), imei)
if err != nil {
return errors.New(errors.CodeNotFound, "设备不存在或无权限访问")
}
// 调用 Gateway
err = h.gatewayClient.ResetDevice(c.UserContext(), &gateway.DeviceOperationReq{
DeviceID: imei,
})
if err != nil {
return err
}
return response.Success(c, nil)
}

View File

@@ -128,3 +128,147 @@ func (h *IotCardHandler) BatchSetSeriesBinding(c *fiber.Ctx) error {
return response.Success(c, result)
}
// GetGatewayStatus 查询卡实时状态
func (h *IotCardHandler) GetGatewayStatus(c *fiber.Ctx) error {
iccid := c.Params("iccid")
if iccid == "" {
return errors.New(errors.CodeInvalidParam, "ICCID不能为空")
}
// 验证权限:查询数据库确认卡存在且用户有权限访问
_, err := h.service.GetByICCID(c.UserContext(), iccid)
if err != nil {
return errors.New(errors.CodeNotFound, "卡不存在或无权限访问")
}
// 调用 Gateway
resp, err := h.gatewayClient.QueryCardStatus(c.UserContext(), &gateway.CardStatusReq{
CardNo: iccid,
})
if err != nil {
return err
}
return response.Success(c, resp)
}
// GetGatewayFlow 查询流量使用情况
func (h *IotCardHandler) GetGatewayFlow(c *fiber.Ctx) error {
iccid := c.Params("iccid")
if iccid == "" {
return errors.New(errors.CodeInvalidParam, "ICCID不能为空")
}
// 验证权限:查询数据库确认卡存在且用户有权限访问
_, err := h.service.GetByICCID(c.UserContext(), iccid)
if err != nil {
return errors.New(errors.CodeNotFound, "卡不存在或无权限访问")
}
// 调用 Gateway
resp, err := h.gatewayClient.QueryFlow(c.UserContext(), &gateway.FlowQueryReq{
CardNo: iccid,
})
if err != nil {
return err
}
return response.Success(c, resp)
}
// GetGatewayRealname 查询实名认证状态
func (h *IotCardHandler) GetGatewayRealname(c *fiber.Ctx) error {
iccid := c.Params("iccid")
if iccid == "" {
return errors.New(errors.CodeInvalidParam, "ICCID不能为空")
}
// 验证权限:查询数据库确认卡存在且用户有权限访问
_, err := h.service.GetByICCID(c.UserContext(), iccid)
if err != nil {
return errors.New(errors.CodeNotFound, "卡不存在或无权限访问")
}
// 调用 Gateway
resp, err := h.gatewayClient.QueryRealnameStatus(c.UserContext(), &gateway.CardStatusReq{
CardNo: iccid,
})
if err != nil {
return err
}
return response.Success(c, resp)
}
// GetRealnameLink 获取实名认证链接
func (h *IotCardHandler) GetRealnameLink(c *fiber.Ctx) error {
iccid := c.Params("iccid")
if iccid == "" {
return errors.New(errors.CodeInvalidParam, "ICCID不能为空")
}
// 验证权限:查询数据库确认卡存在且用户有权限访问
_, err := h.service.GetByICCID(c.UserContext(), iccid)
if err != nil {
return errors.New(errors.CodeNotFound, "卡不存在或无权限访问")
}
// 调用 Gateway
link, err := h.gatewayClient.GetRealnameLink(c.UserContext(), &gateway.CardStatusReq{
CardNo: iccid,
})
if err != nil {
return err
}
return response.Success(c, link)
}
// StopCard 停止卡服务
func (h *IotCardHandler) StopCard(c *fiber.Ctx) error {
iccid := c.Params("iccid")
if iccid == "" {
return errors.New(errors.CodeInvalidParam, "ICCID不能为空")
}
// 验证权限:查询数据库确认卡存在且用户有权限访问
_, err := h.service.GetByICCID(c.UserContext(), iccid)
if err != nil {
return errors.New(errors.CodeNotFound, "卡不存在或无权限访问")
}
// 调用 Gateway
err = h.gatewayClient.StopCard(c.UserContext(), &gateway.CardOperationReq{
CardNo: iccid,
})
if err != nil {
return err
}
return response.Success(c, nil)
}
// StartCard 恢复卡服务
func (h *IotCardHandler) StartCard(c *fiber.Ctx) error {
iccid := c.Params("iccid")
if iccid == "" {
return errors.New(errors.CodeInvalidParam, "ICCID不能为空")
}
// 验证权限:查询数据库确认卡存在且用户有权限访问
_, err := h.service.GetByICCID(c.UserContext(), iccid)
if err != nil {
return errors.New(errors.CodeNotFound, "卡不存在或无权限访问")
}
// 调用 Gateway
err = h.gatewayClient.StartCard(c.UserContext(), &gateway.CardOperationReq{
CardNo: iccid,
})
if err != nil {
return err
}
return response.Success(c, nil)
}

View File

@@ -146,3 +146,25 @@ type BatchSetDeviceSeriesBindngResponse struct {
FailCount int `json:"fail_count" description:"失败数量"`
FailedItems []DeviceSeriesBindngFailedItem `json:"failed_items" description:"失败详情列表"`
}
type SetSpeedLimitRequest struct {
IMEI string `path:"imei" description:"设备号(IMEI)" required:"true"`
UploadSpeed int `json:"upload_speed" validate:"required,min=1" required:"true" minimum:"1" description:"上行速率KB/s"`
DownloadSpeed int `json:"download_speed" validate:"required,min=1" required:"true" minimum:"1" description:"下行速率KB/s"`
}
type SetWiFiRequest struct {
IMEI string `path:"imei" description:"设备号(IMEI)" required:"true"`
SSID string `json:"ssid" validate:"required,min=1,max=32" required:"true" minLength:"1" maxLength:"32" description:"WiFi 名称"`
Password string `json:"password" validate:"required,min=8,max=63" required:"true" minLength:"8" maxLength:"63" description:"WiFi 密码"`
Enabled int `json:"enabled" validate:"required,oneof=0 1" required:"true" description:"启用状态0:禁用, 1:启用)"`
}
type SwitchCardRequest struct {
IMEI string `path:"imei" description:"设备号(IMEI)" required:"true"`
TargetICCID string `json:"target_iccid" validate:"required" required:"true" description:"目标卡 ICCID"`
}
type EmptyResponse struct {
Message string `json:"message,omitempty" description:"提示信息"`
}

View File

@@ -3,6 +3,7 @@ package routes
import (
"github.com/gofiber/fiber/v2"
"github.com/break/junhong_cmp_fiber/internal/gateway"
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/pkg/openapi"
@@ -143,4 +144,60 @@ func registerDeviceRoutes(router fiber.Router, handler *admin.DeviceHandler, imp
Output: new(dto.BatchSetDeviceSeriesBindngResponse),
Auth: true,
})
Register(devices, doc, groupPath, "GET", "/by-imei/:imei/gateway-info", handler.GetGatewayInfo, RouteSpec{
Summary: "查询设备信息",
Tags: []string{"设备管理"},
Input: new(dto.GetDeviceByIMEIRequest),
Output: new(gateway.DeviceInfoResp),
Auth: true,
})
Register(devices, doc, groupPath, "GET", "/by-imei/:imei/gateway-slots", handler.GetGatewaySlots, RouteSpec{
Summary: "查询卡槽信息",
Tags: []string{"设备管理"},
Input: new(dto.GetDeviceByIMEIRequest),
Output: new(gateway.SlotInfoResp),
Auth: true,
})
Register(devices, doc, groupPath, "PUT", "/by-imei/:imei/speed-limit", handler.SetSpeedLimit, RouteSpec{
Summary: "设置限速",
Tags: []string{"设备管理"},
Input: new(dto.SetSpeedLimitRequest),
Output: new(dto.EmptyResponse),
Auth: true,
})
Register(devices, doc, groupPath, "PUT", "/by-imei/:imei/wifi", handler.SetWiFi, RouteSpec{
Summary: "设置 WiFi",
Tags: []string{"设备管理"},
Input: new(dto.SetWiFiRequest),
Output: new(dto.EmptyResponse),
Auth: true,
})
Register(devices, doc, groupPath, "POST", "/by-imei/:imei/switch-card", handler.SwitchCard, RouteSpec{
Summary: "切卡",
Tags: []string{"设备管理"},
Input: new(dto.SwitchCardRequest),
Output: new(dto.EmptyResponse),
Auth: true,
})
Register(devices, doc, groupPath, "POST", "/by-imei/:imei/reboot", handler.RebootDevice, RouteSpec{
Summary: "重启设备",
Tags: []string{"设备管理"},
Input: new(dto.GetDeviceByIMEIRequest),
Output: new(dto.EmptyResponse),
Auth: true,
})
Register(devices, doc, groupPath, "POST", "/by-imei/:imei/reset", handler.ResetDevice, RouteSpec{
Summary: "恢复出厂",
Tags: []string{"设备管理"},
Input: new(dto.GetDeviceByIMEIRequest),
Output: new(dto.EmptyResponse),
Auth: true,
})
}

View File

@@ -3,6 +3,7 @@ package routes
import (
"github.com/gofiber/fiber/v2"
"github.com/break/junhong_cmp_fiber/internal/gateway"
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/pkg/openapi"
@@ -107,4 +108,52 @@ func registerIotCardRoutes(router fiber.Router, handler *admin.IotCardHandler, i
Output: new(dto.BatchSetCardSeriesBindngResponse),
Auth: true,
})
Register(iotCards, doc, groupPath, "GET", "/:iccid/gateway-status", handler.GetGatewayStatus, RouteSpec{
Summary: "查询卡实时状态",
Tags: []string{"IoT卡管理"},
Input: new(dto.GetIotCardByICCIDRequest),
Output: new(gateway.CardStatusResp),
Auth: true,
})
Register(iotCards, doc, groupPath, "GET", "/:iccid/gateway-flow", handler.GetGatewayFlow, RouteSpec{
Summary: "查询流量使用",
Tags: []string{"IoT卡管理"},
Input: new(dto.GetIotCardByICCIDRequest),
Output: new(gateway.FlowUsageResp),
Auth: true,
})
Register(iotCards, doc, groupPath, "GET", "/:iccid/gateway-realname", handler.GetGatewayRealname, RouteSpec{
Summary: "查询实名认证状态",
Tags: []string{"IoT卡管理"},
Input: new(dto.GetIotCardByICCIDRequest),
Output: new(gateway.RealnameStatusResp),
Auth: true,
})
Register(iotCards, doc, groupPath, "GET", "/:iccid/realname-link", handler.GetRealnameLink, RouteSpec{
Summary: "获取实名认证链接",
Tags: []string{"IoT卡管理"},
Input: new(dto.GetIotCardByICCIDRequest),
Output: new(gateway.RealnameLinkResp),
Auth: true,
})
Register(iotCards, doc, groupPath, "POST", "/:iccid/stop", handler.StopCard, RouteSpec{
Summary: "停机",
Tags: []string{"IoT卡管理"},
Input: new(dto.GetIotCardByICCIDRequest),
Output: nil,
Auth: true,
})
Register(iotCards, doc, groupPath, "POST", "/:iccid/start", handler.StartCard, RouteSpec{
Summary: "复机",
Tags: []string{"IoT卡管理"},
Input: new(dto.GetIotCardByICCIDRequest),
Output: nil,
Auth: true,
})
}