From fd795d8742d20ff878a337d8bf2a8e783737d870 Mon Sep 17 00:00:00 2001 From: huang Date: Sat, 18 Apr 2026 16:22:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=88=87=E5=8D=A1=E6=A8=A1=E5=BC=8F=E6=8E=A5=E5=8F=A3=20SetSwi?= =?UTF-8?q?tchMode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/admin-openapi.yaml | 84 ++++++++++++++++++++++ internal/gateway/device.go | 8 +++ internal/gateway/models.go | 7 ++ internal/handler/admin/device.go | 20 ++++++ internal/model/dto/device_dto.go | 6 ++ internal/routes/device.go | 9 +++ internal/service/device/gateway_service.go | 12 ++++ 7 files changed, 146 insertions(+) diff --git a/docs/admin-openapi.yaml b/docs/admin-openapi.yaml index 04a7908..d259771 100644 --- a/docs/admin-openapi.yaml +++ b/docs/admin-openapi.yaml @@ -871,6 +871,10 @@ components: description: 套餐使用记录ID minimum: 0 type: integer + paid_amount: + description: 购买实付金额(分),线下支付或无订单分配时为空 + nullable: true + type: integer priority: description: 优先级 type: integer @@ -6295,6 +6299,14 @@ components: required: - speed_limit type: object + DtoSetSwitchModeRequest: + properties: + switch_mode: + description: 切卡模式 (0:自动切卡, 1:手动切卡) + type: integer + required: + - switch_mode + type: object DtoSetWiFiRequest: properties: card_no: @@ -12700,6 +12712,78 @@ paths: summary: 切卡 tags: - 设备管理 + /api/admin/devices/by-identifier/{identifier}/switch-mode: + post: + description: 通过虚拟号/IMEI/SN 设置设备切卡模式(0:自动切卡, 1:手动切卡)。设备必须已配置 IMEI。 + parameters: + - description: 设备标识符(支持虚拟号/IMEI/SN) + in: path + name: identifier + required: true + schema: + description: 设备标识符(支持虚拟号/IMEI/SN) + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DtoSetSwitchModeRequest' + responses: + "200": + content: + application/json: + schema: + properties: + code: + description: 响应码 + example: 0 + type: integer + data: + $ref: '#/components/schemas/DtoEmptyResponse' + msg: + description: 响应消息 + example: success + type: string + timestamp: + description: 时间戳 + format: date-time + type: string + required: + - code + - msg + - data + - timestamp + type: object + description: 成功 + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 设置切卡模式 + tags: + - 设备管理 /api/admin/devices/by-identifier/{identifier}/wifi: put: description: 通过虚拟号/IMEI/SN 设置设备 WiFi。设备必须已配置 IMEI。 diff --git a/internal/gateway/device.go b/internal/gateway/device.go index 5fc1cb1..53097cd 100644 --- a/internal/gateway/device.go +++ b/internal/gateway/device.go @@ -69,6 +69,14 @@ func (c *Client) RebootDevice(ctx context.Context, req *DeviceOperationReq) erro return err } +// SwitchMode 设置设备切卡模式 +// 切换设备卡槽的自动/手动模式,0=自动切卡,1=手动切卡 +// POST /device/SwitchMode +func (c *Client) SwitchMode(ctx context.Context, req *SwitchModeReq) error { + _, err := c.doRequest(ctx, "/device/SwitchMode", req) + return err +} + // SyncDeviceInfo 同步查询设备信息(直接返回,无需回调) // 与异步 /device/info 的区别:直接返回全量数据,无需 callbackUrl // POST /device/sync-info diff --git a/internal/gateway/models.go b/internal/gateway/models.go index 0d386c9..a3df6ff 100644 --- a/internal/gateway/models.go +++ b/internal/gateway/models.go @@ -159,6 +159,13 @@ type DeviceOperationReq struct { Extend string `json:"extend,omitempty" description:"扩展字段(广电国网特殊参数)"` } +// SwitchModeReq 是设置设备切卡模式的请求 +type SwitchModeReq struct { + CardNo string `json:"cardNo" validate:"required" required:"true" description:"设备编号(IMEI)"` + SwitchMode int `json:"switch_mode" description:"切卡模式(0:自动切卡, 1:手动切卡)"` + CallbackURL string `json:"callbackUrl,omitempty" description:"异步回调通知地址"` +} + // SlotInfo 是单个卡槽信息 type SlotInfo struct { SlotNo int `json:"slotNo" description:"卡槽编号"` diff --git a/internal/handler/admin/device.go b/internal/handler/admin/device.go index f2d125e..5da08a6 100644 --- a/internal/handler/admin/device.go +++ b/internal/handler/admin/device.go @@ -286,6 +286,26 @@ func (h *DeviceHandler) SwitchCard(c *fiber.Ctx) error { return response.Success(c, nil) } +// SetSwitchMode 设置设备切卡模式 +// POST /api/admin/devices/by-identifier/:identifier/switch-mode +func (h *DeviceHandler) SetSwitchMode(c *fiber.Ctx) error { + identifier := c.Params("identifier") + if identifier == "" { + return errors.New(errors.CodeInvalidParam, "设备标识符不能为空") + } + + var req dto.SetSwitchModeRequest + if err := c.BodyParser(&req); err != nil { + return errors.New(errors.CodeInvalidParam, "请求参数解析失败") + } + + if err := h.service.GatewaySwitchMode(c.UserContext(), identifier, &req); err != nil { + return err + } + + return response.Success(c, nil) +} + // RebootDevice 重启设备 // POST /api/admin/devices/by-identifier/:identifier/reboot func (h *DeviceHandler) RebootDevice(c *fiber.Ctx) error { diff --git a/internal/model/dto/device_dto.go b/internal/model/dto/device_dto.go index 86b2e4a..3661a66 100644 --- a/internal/model/dto/device_dto.go +++ b/internal/model/dto/device_dto.go @@ -180,6 +180,12 @@ type SwitchCardRequest struct { TargetICCID string `json:"target_iccid" validate:"required" required:"true" description:"目标卡 ICCID"` } +// SetSwitchModeRequest 设置切卡模式请求 +type SetSwitchModeRequest struct { + Identifier string `path:"identifier" description:"设备标识符(支持虚拟号/IMEI/SN)" required:"true"` + SwitchMode int `json:"switch_mode" validate:"oneof=0 1" required:"true" description:"切卡模式 (0:自动切卡, 1:手动切卡)"` +} + type EmptyResponse struct { Message string `json:"message,omitempty" description:"提示信息"` } diff --git a/internal/routes/device.go b/internal/routes/device.go index e43e69f..9c56334 100644 --- a/internal/routes/device.go +++ b/internal/routes/device.go @@ -165,6 +165,15 @@ func registerDeviceRoutes(router fiber.Router, handler *admin.DeviceHandler, imp Auth: true, }) + Register(devices, doc, groupPath, "POST", "/by-identifier/:identifier/switch-mode", handler.SetSwitchMode, RouteSpec{ + Summary: "设置切卡模式", + Description: "通过虚拟号/IMEI/SN 设置设备切卡模式(0:自动切卡, 1:手动切卡)。设备必须已配置 IMEI。", + Tags: []string{"设备管理"}, + Input: new(dto.SetSwitchModeRequest), + Output: new(dto.EmptyResponse), + Auth: true, + }) + Register(devices, doc, groupPath, "POST", "/by-identifier/:identifier/reboot", handler.RebootDevice, RouteSpec{ Summary: "重启设备", Description: "通过虚拟号/IMEI/SN 重启设备。设备必须已配置 IMEI。", diff --git a/internal/service/device/gateway_service.go b/internal/service/device/gateway_service.go index 4f14352..9dd43b4 100644 --- a/internal/service/device/gateway_service.go +++ b/internal/service/device/gateway_service.go @@ -103,3 +103,15 @@ func (s *Service) GatewayResetDevice(ctx context.Context, identifier string) err DeviceID: imei, }) } + +// GatewaySwitchMode 通过标识符设置设备切卡模式 +func (s *Service) GatewaySwitchMode(ctx context.Context, identifier string, req *dto.SetSwitchModeRequest) error { + imei, err := s.getDeviceIMEI(ctx, identifier) + if err != nil { + return err + } + return s.gatewayClient.SwitchMode(ctx, &gateway.SwitchModeReq{ + CardNo: imei, + SwitchMode: req.SwitchMode, + }) +}