feat: 添加设备IMEI和单卡ICCID查询接口
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
- 新增 GET /api/admin/devices/by-imei/:imei 接口,支持通过设备号查询设备详情 - 新增 GET /api/admin/iot-cards/by-iccid/:iccid 接口,支持通过ICCID查询单卡详情 - 添加对应的 Service 层方法和 Handler - 更新 OpenAPI 文档 - 添加集成测试并修复测试环境配置(使用环境变量) - 归档已完成的 OpenSpec 变更记录 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,20 @@ func (h *DeviceHandler) GetByID(c *fiber.Ctx) error {
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
func (h *DeviceHandler) GetByIMEI(c *fiber.Ctx) error {
|
||||
imei := c.Params("imei")
|
||||
if imei == "" {
|
||||
return errors.New(errors.CodeInvalidParam, "设备号不能为空")
|
||||
}
|
||||
|
||||
result, err := h.service.GetByDeviceNo(c.UserContext(), imei)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
func (h *DeviceHandler) Delete(c *fiber.Ctx) error {
|
||||
userType := middleware.GetUserTypeFromContext(c.UserContext())
|
||||
if userType != constants.UserTypeSuperAdmin && userType != constants.UserTypePlatform {
|
||||
|
||||
@@ -33,6 +33,20 @@ func (h *IotCardHandler) ListStandalone(c *fiber.Ctx) error {
|
||||
return response.SuccessWithPagination(c, result.List, result.Total, result.Page, result.PageSize)
|
||||
}
|
||||
|
||||
func (h *IotCardHandler) GetByICCID(c *fiber.Ctx) error {
|
||||
iccid := c.Params("iccid")
|
||||
if iccid == "" {
|
||||
return errors.New(errors.CodeInvalidParam, "ICCID不能为空")
|
||||
}
|
||||
|
||||
result, err := h.service.GetByICCID(c.UserContext(), iccid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
func (h *IotCardHandler) AllocateCards(c *fiber.Ctx) error {
|
||||
var req dto.AllocateStandaloneCardsRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
|
||||
@@ -47,6 +47,10 @@ type GetDeviceRequest struct {
|
||||
ID uint `path:"id" description:"设备ID" required:"true"`
|
||||
}
|
||||
|
||||
type GetDeviceByIMEIRequest struct {
|
||||
DeviceNo string `path:"imei" description:"设备号(IMEI)" required:"true"`
|
||||
}
|
||||
|
||||
type DeleteDeviceRequest struct {
|
||||
ID uint `path:"id" description:"设备ID" required:"true"`
|
||||
}
|
||||
|
||||
@@ -116,3 +116,11 @@ type ImportTaskDetailResponse struct {
|
||||
type GetImportTaskRequest struct {
|
||||
ID uint `path:"id" description:"任务ID" required:"true"`
|
||||
}
|
||||
|
||||
type GetIotCardByICCIDRequest struct {
|
||||
ICCID string `path:"iccid" description:"ICCID" required:"true"`
|
||||
}
|
||||
|
||||
type IotCardDetailResponse struct {
|
||||
StandaloneIotCardResponse
|
||||
}
|
||||
|
||||
@@ -28,6 +28,14 @@ func registerDeviceRoutes(router fiber.Router, handler *admin.DeviceHandler, imp
|
||||
Auth: true,
|
||||
})
|
||||
|
||||
Register(devices, doc, groupPath, "GET", "/by-imei/:imei", handler.GetByIMEI, RouteSpec{
|
||||
Summary: "通过设备号查询设备详情",
|
||||
Tags: []string{"设备管理"},
|
||||
Input: new(dto.GetDeviceByIMEIRequest),
|
||||
Output: new(dto.DeviceResponse),
|
||||
Auth: true,
|
||||
})
|
||||
|
||||
Register(devices, doc, groupPath, "DELETE", "/:id", handler.Delete, RouteSpec{
|
||||
Summary: "删除设备",
|
||||
Description: "仅平台用户可操作。删除设备时自动解绑所有卡(卡不会被删除)。",
|
||||
|
||||
@@ -20,6 +20,14 @@ func registerIotCardRoutes(router fiber.Router, handler *admin.IotCardHandler, i
|
||||
Auth: true,
|
||||
})
|
||||
|
||||
Register(iotCards, doc, groupPath, "GET", "/by-iccid/:iccid", handler.GetByICCID, RouteSpec{
|
||||
Summary: "通过ICCID查询单卡详情",
|
||||
Tags: []string{"IoT卡管理"},
|
||||
Input: new(dto.GetIotCardByICCIDRequest),
|
||||
Output: new(dto.IotCardDetailResponse),
|
||||
Auth: true,
|
||||
})
|
||||
|
||||
Register(iotCards, doc, groupPath, "POST", "/import", importHandler.Import, RouteSpec{
|
||||
Summary: "批量导入IoT卡(ICCID+MSISDN)",
|
||||
Description: `## ⚠️ 接口变更说明(BREAKING CHANGE)
|
||||
|
||||
@@ -134,6 +134,25 @@ func (s *Service) Get(ctx context.Context, id uint) (*dto.DeviceResponse, error)
|
||||
return s.toDeviceResponse(device, shopMap, bindingCounts), nil
|
||||
}
|
||||
|
||||
// GetByDeviceNo 通过设备号获取设备详情
|
||||
func (s *Service) GetByDeviceNo(ctx context.Context, deviceNo string) (*dto.DeviceResponse, error) {
|
||||
device, err := s.deviceStore.GetByDeviceNo(ctx, deviceNo)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "设备不存在")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shopMap := s.loadShopData(ctx, []*model.Device{device})
|
||||
bindingCounts, err := s.getBindingCounts(ctx, []uint{device.ID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.toDeviceResponse(device, shopMap, bindingCounts), nil
|
||||
}
|
||||
|
||||
func (s *Service) Delete(ctx context.Context, id uint) error {
|
||||
device, err := s.deviceStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
|
||||
@@ -110,6 +110,24 @@ func (s *Service) ListStandalone(ctx context.Context, req *dto.ListStandaloneIot
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetByICCID 通过 ICCID 获取单卡详情
|
||||
func (s *Service) GetByICCID(ctx context.Context, iccid string) (*dto.IotCardDetailResponse, error) {
|
||||
card, err := s.iotCardStore.GetByICCID(ctx, iccid)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "IoT卡不存在")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
carrierMap, shopMap := s.loadRelatedData(ctx, []*model.IotCard{card})
|
||||
standaloneResp := s.toStandaloneResponse(card, carrierMap, shopMap)
|
||||
|
||||
return &dto.IotCardDetailResponse{
|
||||
StandaloneIotCardResponse: *standaloneResp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) loadRelatedData(ctx context.Context, cards []*model.IotCard) (map[uint]string, map[uint]string) {
|
||||
carrierIDs := make([]uint, 0)
|
||||
shopIDs := make([]uint, 0)
|
||||
|
||||
Reference in New Issue
Block a user