feat: 实现企业设备授权功能并归档 OpenSpec 变更
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m39s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m39s
- 新增企业设备授权模块(Model、DTO、Service、Handler、Store) - 实现设备授权的创建、查询、更新、删除等完整业务逻辑 - 添加企业卡授权与设备授权的关联关系 - 新增 2 个数据库迁移脚本 - 同步 OpenSpec delta specs 到 main specs - 归档 add-enterprise-device-authorization 变更 - 更新 API 文档和路由配置 - 新增完整的集成测试和单元测试覆盖
This commit is contained in:
621
internal/service/enterprise_device/service.go
Normal file
621
internal/service/enterprise_device/service.go
Normal file
@@ -0,0 +1,621 @@
|
||||
package enterprise_device
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
pkggorm "github.com/break/junhong_cmp_fiber/pkg/gorm"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
db *gorm.DB
|
||||
enterpriseStore *postgres.EnterpriseStore
|
||||
deviceStore *postgres.DeviceStore
|
||||
deviceSimBindingStore *postgres.DeviceSimBindingStore
|
||||
enterpriseDeviceAuthStore *postgres.EnterpriseDeviceAuthorizationStore
|
||||
enterpriseCardAuthStore *postgres.EnterpriseCardAuthorizationStore
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func New(
|
||||
db *gorm.DB,
|
||||
enterpriseStore *postgres.EnterpriseStore,
|
||||
deviceStore *postgres.DeviceStore,
|
||||
deviceSimBindingStore *postgres.DeviceSimBindingStore,
|
||||
enterpriseDeviceAuthStore *postgres.EnterpriseDeviceAuthorizationStore,
|
||||
enterpriseCardAuthStore *postgres.EnterpriseCardAuthorizationStore,
|
||||
logger *zap.Logger,
|
||||
) *Service {
|
||||
return &Service{
|
||||
db: db,
|
||||
enterpriseStore: enterpriseStore,
|
||||
deviceStore: deviceStore,
|
||||
deviceSimBindingStore: deviceSimBindingStore,
|
||||
enterpriseDeviceAuthStore: enterpriseDeviceAuthStore,
|
||||
enterpriseCardAuthStore: enterpriseCardAuthStore,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// AllocateDevices 授权设备给企业
|
||||
func (s *Service) AllocateDevices(ctx context.Context, enterpriseID uint, req *dto.AllocateDevicesReq) (*dto.AllocateDevicesResp, error) {
|
||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||||
if currentUserID == 0 {
|
||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
// 验证企业存在
|
||||
_, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
|
||||
if err != nil {
|
||||
return nil, errors.New(errors.CodeEnterpriseNotFound, "企业不存在")
|
||||
}
|
||||
|
||||
// 查询所有设备
|
||||
var devices []model.Device
|
||||
if err := s.db.WithContext(ctx).Where("device_no IN ?", req.DeviceNos).Find(&devices).Error; err != nil {
|
||||
return nil, fmt.Errorf("查询设备信息失败: %w", err)
|
||||
}
|
||||
|
||||
deviceMap := make(map[string]*model.Device)
|
||||
deviceIDs := make([]uint, 0, len(devices))
|
||||
for i := range devices {
|
||||
deviceMap[devices[i].DeviceNo] = &devices[i]
|
||||
deviceIDs = append(deviceIDs, devices[i].ID)
|
||||
}
|
||||
|
||||
// 获取当前用户的店铺ID(用于验证设备所有权)
|
||||
currentShopID := middleware.GetShopIDFromContext(ctx)
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
|
||||
// 检查已授权的设备
|
||||
existingAuths, err := s.enterpriseDeviceAuthStore.GetActiveAuthsByDeviceIDs(ctx, enterpriseID, deviceIDs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询已有授权失败: %w", err)
|
||||
}
|
||||
|
||||
resp := &dto.AllocateDevicesResp{
|
||||
FailedItems: make([]dto.FailedDeviceItem, 0),
|
||||
AuthorizedDevices: make([]dto.AuthorizedDeviceItem, 0),
|
||||
}
|
||||
|
||||
devicesToAllocate := make([]*model.Device, 0)
|
||||
for _, deviceNo := range req.DeviceNos {
|
||||
device, exists := deviceMap[deviceNo]
|
||||
if !exists {
|
||||
resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
|
||||
DeviceNo: deviceNo,
|
||||
Reason: "设备不存在",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 验证设备状态(必须是"已分销"状态)
|
||||
if device.Status != 2 {
|
||||
resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
|
||||
DeviceNo: deviceNo,
|
||||
Reason: "设备状态不正确,必须是已分销状态",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 验证设备所有权(除非是超级管理员或平台用户)
|
||||
if userType == constants.UserTypeAgent {
|
||||
if device.ShopID == nil || *device.ShopID != currentShopID {
|
||||
resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
|
||||
DeviceNo: deviceNo,
|
||||
Reason: "无权操作此设备",
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否已授权
|
||||
if existingAuths[device.ID] {
|
||||
resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
|
||||
DeviceNo: deviceNo,
|
||||
Reason: "设备已授权给此企业",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
devicesToAllocate = append(devicesToAllocate, device)
|
||||
}
|
||||
|
||||
// 在事务中处理授权
|
||||
if len(devicesToAllocate) > 0 {
|
||||
err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
now := time.Now()
|
||||
authorizerType := userType
|
||||
|
||||
// 1. 创建设备授权记录
|
||||
deviceAuths := make([]*model.EnterpriseDeviceAuthorization, 0, len(devicesToAllocate))
|
||||
for _, device := range devicesToAllocate {
|
||||
deviceAuths = append(deviceAuths, &model.EnterpriseDeviceAuthorization{
|
||||
EnterpriseID: enterpriseID,
|
||||
DeviceID: device.ID,
|
||||
AuthorizedBy: currentUserID,
|
||||
AuthorizedAt: now,
|
||||
AuthorizerType: authorizerType,
|
||||
Remark: req.Remark,
|
||||
})
|
||||
}
|
||||
|
||||
if err := tx.Create(deviceAuths).Error; err != nil {
|
||||
return fmt.Errorf("创建设备授权记录失败: %w", err)
|
||||
}
|
||||
|
||||
// 构建设备ID到授权ID的映射
|
||||
deviceAuthIDMap := make(map[uint]uint)
|
||||
for _, auth := range deviceAuths {
|
||||
deviceAuthIDMap[auth.DeviceID] = auth.ID
|
||||
}
|
||||
|
||||
// 2. 查询所有设备绑定的卡
|
||||
deviceIDsToQuery := make([]uint, 0, len(devicesToAllocate))
|
||||
for _, device := range devicesToAllocate {
|
||||
deviceIDsToQuery = append(deviceIDsToQuery, device.ID)
|
||||
}
|
||||
|
||||
var bindings []model.DeviceSimBinding
|
||||
if err := tx.Where("device_id IN ? AND bind_status = 1", deviceIDsToQuery).Find(&bindings).Error; err != nil {
|
||||
return fmt.Errorf("查询设备绑定卡失败: %w", err)
|
||||
}
|
||||
|
||||
// 3. 为每张绑定的卡创建授权记录
|
||||
if len(bindings) > 0 {
|
||||
cardAuths := make([]*model.EnterpriseCardAuthorization, 0, len(bindings))
|
||||
for _, binding := range bindings {
|
||||
deviceAuthID := deviceAuthIDMap[binding.DeviceID]
|
||||
cardAuths = append(cardAuths, &model.EnterpriseCardAuthorization{
|
||||
EnterpriseID: enterpriseID,
|
||||
CardID: binding.IotCardID,
|
||||
DeviceAuthID: &deviceAuthID,
|
||||
AuthorizedBy: currentUserID,
|
||||
AuthorizedAt: now,
|
||||
AuthorizerType: authorizerType,
|
||||
Remark: req.Remark,
|
||||
})
|
||||
}
|
||||
|
||||
if err := tx.Create(cardAuths).Error; err != nil {
|
||||
return fmt.Errorf("创建卡授权记录失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 统计每个设备的绑定卡数量
|
||||
deviceCardCount := make(map[uint]int)
|
||||
for _, binding := range bindings {
|
||||
deviceCardCount[binding.DeviceID]++
|
||||
}
|
||||
|
||||
// 5. 构建响应
|
||||
for _, device := range devicesToAllocate {
|
||||
resp.AuthorizedDevices = append(resp.AuthorizedDevices, dto.AuthorizedDeviceItem{
|
||||
DeviceID: device.ID,
|
||||
DeviceNo: device.DeviceNo,
|
||||
CardCount: deviceCardCount[device.ID],
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp.SuccessCount = len(devicesToAllocate)
|
||||
resp.FailCount = len(resp.FailedItems)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// RecallDevices 撤销设备授权
|
||||
func (s *Service) RecallDevices(ctx context.Context, enterpriseID uint, req *dto.RecallDevicesReq) (*dto.RecallDevicesResp, error) {
|
||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||||
if currentUserID == 0 {
|
||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
// 验证企业存在
|
||||
_, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
|
||||
if err != nil {
|
||||
return nil, errors.New(errors.CodeEnterpriseNotFound, "企业不存在")
|
||||
}
|
||||
|
||||
// 查询设备
|
||||
var devices []model.Device
|
||||
if err := s.db.WithContext(ctx).Where("device_no IN ?", req.DeviceNos).Find(&devices).Error; err != nil {
|
||||
return nil, fmt.Errorf("查询设备信息失败: %w", err)
|
||||
}
|
||||
|
||||
deviceMap := make(map[string]*model.Device)
|
||||
deviceIDs := make([]uint, 0, len(devices))
|
||||
for i := range devices {
|
||||
deviceMap[devices[i].DeviceNo] = &devices[i]
|
||||
deviceIDs = append(deviceIDs, devices[i].ID)
|
||||
}
|
||||
|
||||
// 检查授权状态
|
||||
existingAuths, err := s.enterpriseDeviceAuthStore.GetActiveAuthsByDeviceIDs(ctx, enterpriseID, deviceIDs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询授权状态失败: %w", err)
|
||||
}
|
||||
|
||||
resp := &dto.RecallDevicesResp{
|
||||
FailedItems: make([]dto.FailedDeviceItem, 0),
|
||||
}
|
||||
|
||||
deviceAuthsToRevoke := make([]uint, 0)
|
||||
for _, deviceNo := range req.DeviceNos {
|
||||
device, exists := deviceMap[deviceNo]
|
||||
if !exists {
|
||||
resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
|
||||
DeviceNo: deviceNo,
|
||||
Reason: "设备不存在",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if !existingAuths[device.ID] {
|
||||
resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
|
||||
DeviceNo: deviceNo,
|
||||
Reason: "设备未授权给此企业",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取授权记录ID
|
||||
auth, err := s.enterpriseDeviceAuthStore.GetByDeviceID(ctx, device.ID)
|
||||
if err != nil || auth.EnterpriseID != enterpriseID {
|
||||
resp.FailedItems = append(resp.FailedItems, dto.FailedDeviceItem{
|
||||
DeviceNo: deviceNo,
|
||||
Reason: "授权记录不存在",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
deviceAuthsToRevoke = append(deviceAuthsToRevoke, auth.ID)
|
||||
}
|
||||
|
||||
// 在事务中处理撤销
|
||||
if len(deviceAuthsToRevoke) > 0 {
|
||||
err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// 1. 撤销设备授权
|
||||
if err := s.enterpriseDeviceAuthStore.RevokeByIDs(ctx, deviceAuthsToRevoke, currentUserID); err != nil {
|
||||
return fmt.Errorf("撤销设备授权失败: %w", err)
|
||||
}
|
||||
|
||||
// 2. 级联撤销卡授权
|
||||
for _, authID := range deviceAuthsToRevoke {
|
||||
if err := s.enterpriseCardAuthStore.RevokeByDeviceAuthID(ctx, authID, currentUserID); err != nil {
|
||||
return fmt.Errorf("撤销卡授权失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp.SuccessCount = len(deviceAuthsToRevoke)
|
||||
resp.FailCount = len(resp.FailedItems)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ListDevices 查询企业授权设备列表(后台管理)
|
||||
func (s *Service) ListDevices(ctx context.Context, enterpriseID uint, req *dto.EnterpriseDeviceListReq) (*dto.EnterpriseDeviceListResp, error) {
|
||||
// 验证企业存在
|
||||
_, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
|
||||
if err != nil {
|
||||
return nil, errors.New(errors.CodeEnterpriseNotFound, "企业不存在")
|
||||
}
|
||||
|
||||
// 查询授权记录
|
||||
opts := postgres.DeviceAuthListOptions{
|
||||
EnterpriseID: &enterpriseID,
|
||||
IncludeRevoked: false,
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
}
|
||||
|
||||
auths, total, err := s.enterpriseDeviceAuthStore.ListByEnterprise(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询授权记录失败: %w", err)
|
||||
}
|
||||
|
||||
if len(auths) == 0 {
|
||||
return &dto.EnterpriseDeviceListResp{
|
||||
List: make([]dto.EnterpriseDeviceItem, 0),
|
||||
Total: 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 收集设备ID
|
||||
deviceIDs := make([]uint, 0, len(auths))
|
||||
authMap := make(map[uint]*model.EnterpriseDeviceAuthorization)
|
||||
for _, auth := range auths {
|
||||
deviceIDs = append(deviceIDs, auth.DeviceID)
|
||||
authMap[auth.DeviceID] = auth
|
||||
}
|
||||
|
||||
// 查询设备信息
|
||||
var devices []model.Device
|
||||
query := s.db.WithContext(ctx).Where("id IN ?", deviceIDs)
|
||||
if req.DeviceNo != "" {
|
||||
query = query.Where("device_no LIKE ?", "%"+req.DeviceNo+"%")
|
||||
}
|
||||
if err := query.Find(&devices).Error; err != nil {
|
||||
return nil, fmt.Errorf("查询设备信息失败: %w", err)
|
||||
}
|
||||
|
||||
// 统计每个设备的绑定卡数量
|
||||
var bindings []model.DeviceSimBinding
|
||||
if err := s.db.WithContext(ctx).
|
||||
Where("device_id IN ? AND bind_status = 1", deviceIDs).
|
||||
Find(&bindings).Error; err != nil {
|
||||
return nil, fmt.Errorf("查询设备绑定卡失败: %w", err)
|
||||
}
|
||||
|
||||
cardCountMap := make(map[uint]int)
|
||||
for _, binding := range bindings {
|
||||
cardCountMap[binding.DeviceID]++
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
items := make([]dto.EnterpriseDeviceItem, 0, len(devices))
|
||||
for _, device := range devices {
|
||||
auth := authMap[device.ID]
|
||||
items = append(items, dto.EnterpriseDeviceItem{
|
||||
DeviceID: device.ID,
|
||||
DeviceNo: device.DeviceNo,
|
||||
DeviceName: device.DeviceName,
|
||||
DeviceModel: device.DeviceModel,
|
||||
CardCount: cardCountMap[device.ID],
|
||||
AuthorizedAt: auth.AuthorizedAt,
|
||||
})
|
||||
}
|
||||
|
||||
return &dto.EnterpriseDeviceListResp{
|
||||
List: items,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListDevicesForEnterprise 查询企业授权设备列表(H5企业用户)
|
||||
func (s *Service) ListDevicesForEnterprise(ctx context.Context, req *dto.EnterpriseDeviceListReq) (*dto.EnterpriseDeviceListResp, error) {
|
||||
enterpriseID := middleware.GetEnterpriseIDFromContext(ctx)
|
||||
if enterpriseID == 0 {
|
||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
opts := postgres.DeviceAuthListOptions{
|
||||
EnterpriseID: &enterpriseID,
|
||||
IncludeRevoked: false,
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
}
|
||||
|
||||
auths, total, err := s.enterpriseDeviceAuthStore.ListByEnterprise(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询授权记录失败: %w", err)
|
||||
}
|
||||
|
||||
if len(auths) == 0 {
|
||||
return &dto.EnterpriseDeviceListResp{
|
||||
List: make([]dto.EnterpriseDeviceItem, 0),
|
||||
Total: 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
deviceIDs := make([]uint, 0, len(auths))
|
||||
authMap := make(map[uint]*model.EnterpriseDeviceAuthorization)
|
||||
for _, auth := range auths {
|
||||
deviceIDs = append(deviceIDs, auth.DeviceID)
|
||||
authMap[auth.DeviceID] = auth
|
||||
}
|
||||
|
||||
skipCtx := pkggorm.SkipDataPermission(ctx)
|
||||
|
||||
var devices []model.Device
|
||||
query := s.db.WithContext(skipCtx).Where("id IN ?", deviceIDs)
|
||||
if req.DeviceNo != "" {
|
||||
query = query.Where("device_no LIKE ?", "%"+req.DeviceNo+"%")
|
||||
}
|
||||
if err := query.Find(&devices).Error; err != nil {
|
||||
return nil, fmt.Errorf("查询设备信息失败: %w", err)
|
||||
}
|
||||
|
||||
var bindings []model.DeviceSimBinding
|
||||
if err := s.db.WithContext(skipCtx).
|
||||
Where("device_id IN ? AND bind_status = 1", deviceIDs).
|
||||
Find(&bindings).Error; err != nil {
|
||||
return nil, fmt.Errorf("查询设备绑定卡失败: %w", err)
|
||||
}
|
||||
|
||||
cardCountMap := make(map[uint]int)
|
||||
for _, binding := range bindings {
|
||||
cardCountMap[binding.DeviceID]++
|
||||
}
|
||||
|
||||
items := make([]dto.EnterpriseDeviceItem, 0, len(devices))
|
||||
for _, device := range devices {
|
||||
auth := authMap[device.ID]
|
||||
items = append(items, dto.EnterpriseDeviceItem{
|
||||
DeviceID: device.ID,
|
||||
DeviceNo: device.DeviceNo,
|
||||
DeviceName: device.DeviceName,
|
||||
DeviceModel: device.DeviceModel,
|
||||
CardCount: cardCountMap[device.ID],
|
||||
AuthorizedAt: auth.AuthorizedAt,
|
||||
})
|
||||
}
|
||||
|
||||
return &dto.EnterpriseDeviceListResp{
|
||||
List: items,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetDeviceDetail 获取设备详情(H5企业用户)
|
||||
func (s *Service) GetDeviceDetail(ctx context.Context, deviceID uint) (*dto.EnterpriseDeviceDetailResp, error) {
|
||||
enterpriseID := middleware.GetEnterpriseIDFromContext(ctx)
|
||||
if enterpriseID == 0 {
|
||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
auth, err := s.enterpriseDeviceAuthStore.GetByDeviceID(ctx, deviceID)
|
||||
if err != nil || auth.EnterpriseID != enterpriseID || auth.RevokedAt != nil {
|
||||
return nil, errors.New(errors.CodeDeviceNotAuthorized, "设备未授权给此企业")
|
||||
}
|
||||
|
||||
skipCtx := pkggorm.SkipDataPermission(ctx)
|
||||
|
||||
var device model.Device
|
||||
if err := s.db.WithContext(skipCtx).Where("id = ?", deviceID).First(&device).Error; err != nil {
|
||||
return nil, fmt.Errorf("查询设备信息失败: %w", err)
|
||||
}
|
||||
|
||||
var bindings []model.DeviceSimBinding
|
||||
if err := s.db.WithContext(skipCtx).
|
||||
Where("device_id = ? AND bind_status = 1", deviceID).
|
||||
Find(&bindings).Error; err != nil {
|
||||
return nil, fmt.Errorf("查询设备绑定卡失败: %w", err)
|
||||
}
|
||||
|
||||
cardIDs := make([]uint, 0, len(bindings))
|
||||
for _, binding := range bindings {
|
||||
cardIDs = append(cardIDs, binding.IotCardID)
|
||||
}
|
||||
|
||||
var cards []model.IotCard
|
||||
cardInfos := make([]dto.DeviceCardInfo, 0)
|
||||
if len(cardIDs) > 0 {
|
||||
if err := s.db.WithContext(skipCtx).Where("id IN ?", cardIDs).Find(&cards).Error; err != nil {
|
||||
return nil, fmt.Errorf("查询卡信息失败: %w", err)
|
||||
}
|
||||
|
||||
carrierIDs := make([]uint, 0, len(cards))
|
||||
for _, card := range cards {
|
||||
carrierIDs = append(carrierIDs, card.CarrierID)
|
||||
}
|
||||
|
||||
var carriers []model.Carrier
|
||||
carrierMap := make(map[uint]string)
|
||||
if len(carrierIDs) > 0 {
|
||||
if err := s.db.WithContext(skipCtx).Where("id IN ?", carrierIDs).Find(&carriers).Error; err == nil {
|
||||
for _, carrier := range carriers {
|
||||
carrierMap[carrier.ID] = carrier.CarrierName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, card := range cards {
|
||||
cardInfos = append(cardInfos, dto.DeviceCardInfo{
|
||||
CardID: card.ID,
|
||||
ICCID: card.ICCID,
|
||||
MSISDN: card.MSISDN,
|
||||
CarrierName: carrierMap[card.CarrierID],
|
||||
NetworkStatus: card.NetworkStatus,
|
||||
NetworkStatusName: getNetworkStatusName(card.NetworkStatus),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return &dto.EnterpriseDeviceDetailResp{
|
||||
Device: dto.EnterpriseDeviceInfo{
|
||||
DeviceID: device.ID,
|
||||
DeviceNo: device.DeviceNo,
|
||||
DeviceName: device.DeviceName,
|
||||
DeviceModel: device.DeviceModel,
|
||||
DeviceType: device.DeviceType,
|
||||
AuthorizedAt: auth.AuthorizedAt,
|
||||
},
|
||||
Cards: cardInfos,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) SuspendCard(ctx context.Context, deviceID, cardID uint, req *dto.DeviceCardOperationReq) (*dto.DeviceCardOperationResp, error) {
|
||||
if err := s.validateCardOperation(ctx, deviceID, cardID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
skipCtx := pkggorm.SkipDataPermission(ctx)
|
||||
if err := s.db.WithContext(skipCtx).Model(&model.IotCard{}).
|
||||
Where("id = ?", cardID).
|
||||
Update("network_status", 0).Error; err != nil {
|
||||
return nil, fmt.Errorf("停机操作失败: %w", err)
|
||||
}
|
||||
|
||||
return &dto.DeviceCardOperationResp{
|
||||
Success: true,
|
||||
Message: "停机成功",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) ResumeCard(ctx context.Context, deviceID, cardID uint, req *dto.DeviceCardOperationReq) (*dto.DeviceCardOperationResp, error) {
|
||||
if err := s.validateCardOperation(ctx, deviceID, cardID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
skipCtx := pkggorm.SkipDataPermission(ctx)
|
||||
if err := s.db.WithContext(skipCtx).Model(&model.IotCard{}).
|
||||
Where("id = ?", cardID).
|
||||
Update("network_status", 1).Error; err != nil {
|
||||
return nil, fmt.Errorf("复机操作失败: %w", err)
|
||||
}
|
||||
|
||||
return &dto.DeviceCardOperationResp{
|
||||
Success: true,
|
||||
Message: "复机成功",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) validateCardOperation(ctx context.Context, deviceID, cardID uint) error {
|
||||
enterpriseID := middleware.GetEnterpriseIDFromContext(ctx)
|
||||
if enterpriseID == 0 {
|
||||
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
auth, err := s.enterpriseDeviceAuthStore.GetByDeviceID(ctx, deviceID)
|
||||
if err != nil || auth.EnterpriseID != enterpriseID || auth.RevokedAt != nil {
|
||||
return errors.New(errors.CodeDeviceNotAuthorized, "设备未授权给此企业")
|
||||
}
|
||||
|
||||
skipCtx := pkggorm.SkipDataPermission(ctx)
|
||||
|
||||
var binding model.DeviceSimBinding
|
||||
if err := s.db.WithContext(skipCtx).
|
||||
Where("device_id = ? AND iot_card_id = ? AND bind_status = 1", deviceID, cardID).
|
||||
First(&binding).Error; err != nil {
|
||||
return errors.New(errors.CodeForbidden, "卡不属于该设备")
|
||||
}
|
||||
|
||||
var cardAuth model.EnterpriseCardAuthorization
|
||||
if err := s.db.WithContext(skipCtx).
|
||||
Where("enterprise_id = ? AND card_id = ? AND device_auth_id IS NOT NULL AND revoked_at IS NULL", enterpriseID, cardID).
|
||||
First(&cardAuth).Error; err != nil {
|
||||
return errors.New(errors.CodeForbidden, "无权操作此卡")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getNetworkStatusName(status int) string {
|
||||
if status == 1 {
|
||||
return "开机"
|
||||
}
|
||||
return "停机"
|
||||
}
|
||||
916
internal/service/enterprise_device/service_test.go
Normal file
916
internal/service/enterprise_device/service_test.go
Normal file
@@ -0,0 +1,916 @@
|
||||
package enterprise_device
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
||||
"github.com/break/junhong_cmp_fiber/tests/testutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func uniqueServiceTestPrefix() string {
|
||||
return fmt.Sprintf("SVC%d", time.Now().UnixNano()%1000000000)
|
||||
}
|
||||
|
||||
func createTestContext(userID uint, userType int, shopID uint, enterpriseID uint) context.Context {
|
||||
ctx := context.Background()
|
||||
return middleware.SetUserContext(ctx, &middleware.UserContextInfo{
|
||||
UserID: userID,
|
||||
UserType: userType,
|
||||
ShopID: shopID,
|
||||
EnterpriseID: enterpriseID,
|
||||
})
|
||||
}
|
||||
|
||||
type testEnv struct {
|
||||
service *Service
|
||||
enterprise *model.Enterprise
|
||||
shop *model.Shop
|
||||
devices []*model.Device
|
||||
cards []*model.IotCard
|
||||
bindings []*model.DeviceSimBinding
|
||||
carrier *model.Carrier
|
||||
}
|
||||
|
||||
func setupTestEnv(t *testing.T, prefix string) *testEnv {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
|
||||
deviceStore := postgres.NewDeviceStore(tx, rdb)
|
||||
deviceSimBindingStore := postgres.NewDeviceSimBindingStore(tx, rdb)
|
||||
enterpriseDeviceAuthStore := postgres.NewEnterpriseDeviceAuthorizationStore(tx, rdb)
|
||||
enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
|
||||
logger := zap.NewNop()
|
||||
|
||||
svc := New(tx, enterpriseStore, deviceStore, deviceSimBindingStore, enterpriseDeviceAuthStore, enterpriseCardAuthStore, logger)
|
||||
|
||||
shop := &model.Shop{
|
||||
ShopName: prefix + "_测试店铺",
|
||||
ShopCode: prefix,
|
||||
Level: 1,
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(shop).Error)
|
||||
|
||||
enterprise := &model.Enterprise{
|
||||
EnterpriseName: prefix + "_测试企业",
|
||||
EnterpriseCode: prefix,
|
||||
OwnerShopID: &shop.ID,
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(enterprise).Error)
|
||||
|
||||
carrier := &model.Carrier{
|
||||
CarrierName: "测试运营商",
|
||||
CarrierType: "CMCC",
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(carrier).Error)
|
||||
|
||||
devices := make([]*model.Device, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
devices[i] = &model.Device{
|
||||
DeviceNo: fmt.Sprintf("%s_D%03d", prefix, i+1),
|
||||
DeviceName: fmt.Sprintf("测试设备%d", i+1),
|
||||
Status: 2,
|
||||
ShopID: &shop.ID,
|
||||
}
|
||||
require.NoError(t, tx.Create(devices[i]).Error)
|
||||
}
|
||||
|
||||
cards := make([]*model.IotCard, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
cards[i] = &model.IotCard{
|
||||
ICCID: fmt.Sprintf("%s%04d", prefix, i+1),
|
||||
CardType: "normal",
|
||||
CarrierID: carrier.ID,
|
||||
Status: 2,
|
||||
ShopID: &shop.ID,
|
||||
}
|
||||
require.NoError(t, tx.Create(cards[i]).Error)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
bindings := []*model.DeviceSimBinding{
|
||||
{DeviceID: devices[0].ID, IotCardID: cards[0].ID, SlotPosition: 1, BindStatus: 1, BindTime: &now},
|
||||
{DeviceID: devices[0].ID, IotCardID: cards[1].ID, SlotPosition: 2, BindStatus: 1, BindTime: &now},
|
||||
{DeviceID: devices[1].ID, IotCardID: cards[2].ID, SlotPosition: 1, BindStatus: 1, BindTime: &now},
|
||||
}
|
||||
for _, b := range bindings {
|
||||
require.NoError(t, tx.Create(b).Error)
|
||||
}
|
||||
|
||||
return &testEnv{
|
||||
service: svc,
|
||||
enterprise: enterprise,
|
||||
shop: shop,
|
||||
devices: devices,
|
||||
cards: cards,
|
||||
bindings: bindings,
|
||||
carrier: carrier,
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_AllocateDevices(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
req *dto.AllocateDevicesReq
|
||||
wantSuccess int
|
||||
wantFail int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "平台用户成功授权设备",
|
||||
ctx: createTestContext(1, constants.UserTypePlatform, 0, 0),
|
||||
req: &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
Remark: "测试授权",
|
||||
},
|
||||
wantSuccess: 1,
|
||||
wantFail: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "代理用户成功授权自己店铺的设备",
|
||||
ctx: createTestContext(2, constants.UserTypeAgent, env.shop.ID, 0),
|
||||
req: &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[1].DeviceNo},
|
||||
},
|
||||
wantSuccess: 1,
|
||||
wantFail: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "设备不存在时记录失败",
|
||||
ctx: createTestContext(1, constants.UserTypePlatform, 0, 0),
|
||||
req: &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{"NOT_EXIST_DEVICE"},
|
||||
},
|
||||
wantSuccess: 0,
|
||||
wantFail: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "未授权用户返回错误",
|
||||
ctx: context.Background(),
|
||||
req: &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[2].DeviceNo},
|
||||
},
|
||||
wantSuccess: 0,
|
||||
wantFail: 0,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
resp, err := env.service.AllocateDevices(tt.ctx, env.enterprise.ID, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.wantSuccess, resp.SuccessCount)
|
||||
assert.Equal(t, tt.wantFail, resp.FailCount)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_AllocateDevices_DeviceStatusValidation(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
|
||||
enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
|
||||
deviceStore := postgres.NewDeviceStore(tx, rdb)
|
||||
deviceSimBindingStore := postgres.NewDeviceSimBindingStore(tx, rdb)
|
||||
enterpriseDeviceAuthStore := postgres.NewEnterpriseDeviceAuthorizationStore(tx, rdb)
|
||||
enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
|
||||
logger := zap.NewNop()
|
||||
|
||||
svc := New(tx, enterpriseStore, deviceStore, deviceSimBindingStore, enterpriseDeviceAuthStore, enterpriseCardAuthStore, logger)
|
||||
|
||||
enterprise := &model.Enterprise{
|
||||
EnterpriseName: prefix + "_测试企业",
|
||||
EnterpriseCode: prefix,
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(enterprise).Error)
|
||||
|
||||
inStockDevice := &model.Device{
|
||||
DeviceNo: prefix + "_INSTOCK",
|
||||
DeviceName: "在库设备",
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(inStockDevice).Error)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
|
||||
t.Run("设备状态不是已分销时失败", func(t *testing.T) {
|
||||
req := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{inStockDevice.DeviceNo},
|
||||
}
|
||||
|
||||
resp, err := svc.AllocateDevices(ctx, enterprise.ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, resp.SuccessCount)
|
||||
assert.Equal(t, 1, resp.FailCount)
|
||||
assert.Contains(t, resp.FailedItems[0].Reason, "状态不正确")
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_AllocateDevices_AgentPermission(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
|
||||
enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
|
||||
deviceStore := postgres.NewDeviceStore(tx, rdb)
|
||||
deviceSimBindingStore := postgres.NewDeviceSimBindingStore(tx, rdb)
|
||||
enterpriseDeviceAuthStore := postgres.NewEnterpriseDeviceAuthorizationStore(tx, rdb)
|
||||
enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
|
||||
logger := zap.NewNop()
|
||||
|
||||
svc := New(tx, enterpriseStore, deviceStore, deviceSimBindingStore, enterpriseDeviceAuthStore, enterpriseCardAuthStore, logger)
|
||||
|
||||
shop1 := &model.Shop{ShopName: prefix + "_店铺1", ShopCode: prefix + "1", Level: 1, Status: 1}
|
||||
require.NoError(t, tx.Create(shop1).Error)
|
||||
|
||||
shop2 := &model.Shop{ShopName: prefix + "_店铺2", ShopCode: prefix + "2", Level: 1, Status: 1}
|
||||
require.NoError(t, tx.Create(shop2).Error)
|
||||
|
||||
enterprise := &model.Enterprise{
|
||||
EnterpriseName: prefix + "_测试企业",
|
||||
EnterpriseCode: prefix,
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(enterprise).Error)
|
||||
|
||||
device := &model.Device{
|
||||
DeviceNo: prefix + "_D001",
|
||||
DeviceName: "测试设备",
|
||||
Status: 2,
|
||||
ShopID: &shop1.ID,
|
||||
}
|
||||
require.NoError(t, tx.Create(device).Error)
|
||||
|
||||
t.Run("代理用户无法授权其他店铺的设备", func(t *testing.T) {
|
||||
ctx := createTestContext(1, constants.UserTypeAgent, shop2.ID, 0)
|
||||
req := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{device.DeviceNo},
|
||||
}
|
||||
|
||||
resp, err := svc.AllocateDevices(ctx, enterprise.ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, resp.SuccessCount)
|
||||
assert.Equal(t, 1, resp.FailCount)
|
||||
assert.Contains(t, resp.FailedItems[0].Reason, "无权操作")
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_AllocateDevices_DuplicateAuthorization(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
|
||||
req := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
}
|
||||
resp, err := env.service.AllocateDevices(ctx, env.enterprise.ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, resp.SuccessCount)
|
||||
|
||||
t.Run("重复授权同一设备时失败", func(t *testing.T) {
|
||||
resp2, err := env.service.AllocateDevices(ctx, env.enterprise.ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, resp2.SuccessCount)
|
||||
assert.Equal(t, 1, resp2.FailCount)
|
||||
assert.Contains(t, resp2.FailedItems[0].Reason, "已授权")
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_AllocateDevices_CascadeCardAuthorization(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
|
||||
t.Run("授权设备时级联授权绑定的卡", func(t *testing.T) {
|
||||
req := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
}
|
||||
|
||||
resp, err := env.service.AllocateDevices(ctx, env.enterprise.ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, resp.SuccessCount)
|
||||
assert.Len(t, resp.AuthorizedDevices, 1)
|
||||
assert.Equal(t, 2, resp.AuthorizedDevices[0].CardCount)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_RecallDevices(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
|
||||
allocateReq := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo, env.devices[1].DeviceNo},
|
||||
}
|
||||
_, err := env.service.AllocateDevices(ctx, env.enterprise.ID, allocateReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *dto.RecallDevicesReq
|
||||
wantSuccess int
|
||||
wantFail int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "成功撤销授权",
|
||||
req: &dto.RecallDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
},
|
||||
wantSuccess: 1,
|
||||
wantFail: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "设备不存在时失败",
|
||||
req: &dto.RecallDevicesReq{
|
||||
DeviceNos: []string{"NOT_EXIST"},
|
||||
},
|
||||
wantSuccess: 0,
|
||||
wantFail: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "设备未授权时失败",
|
||||
req: &dto.RecallDevicesReq{
|
||||
DeviceNos: []string{env.devices[2].DeviceNo},
|
||||
},
|
||||
wantSuccess: 0,
|
||||
wantFail: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
resp, err := env.service.RecallDevices(ctx, env.enterprise.ID, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.wantSuccess, resp.SuccessCount)
|
||||
assert.Equal(t, tt.wantFail, resp.FailCount)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_RecallDevices_Unauthorized(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
t.Run("未授权用户返回错误", func(t *testing.T) {
|
||||
req := &dto.RecallDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
}
|
||||
|
||||
_, err := env.service.RecallDevices(context.Background(), env.enterprise.ID, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_ListDevices(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
|
||||
allocateReq := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo, env.devices[1].DeviceNo},
|
||||
}
|
||||
_, err := env.service.AllocateDevices(ctx, env.enterprise.ID, allocateReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *dto.EnterpriseDeviceListReq
|
||||
wantTotal int64
|
||||
wantLen int
|
||||
}{
|
||||
{
|
||||
name: "获取所有授权设备",
|
||||
req: &dto.EnterpriseDeviceListReq{Page: 1, PageSize: 10},
|
||||
wantTotal: 2,
|
||||
wantLen: 2,
|
||||
},
|
||||
{
|
||||
name: "分页查询",
|
||||
req: &dto.EnterpriseDeviceListReq{Page: 1, PageSize: 1},
|
||||
wantTotal: 2,
|
||||
wantLen: 1,
|
||||
},
|
||||
{
|
||||
name: "按设备号搜索",
|
||||
req: &dto.EnterpriseDeviceListReq{Page: 1, PageSize: 10, DeviceNo: env.devices[0].DeviceNo},
|
||||
wantTotal: 2,
|
||||
wantLen: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
resp, err := env.service.ListDevices(ctx, env.enterprise.ID, tt.req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.wantTotal, resp.Total)
|
||||
assert.Len(t, resp.List, tt.wantLen)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_ListDevices_EnterpriseNotFound(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
|
||||
t.Run("企业不存在返回错误", func(t *testing.T) {
|
||||
req := &dto.EnterpriseDeviceListReq{Page: 1, PageSize: 10}
|
||||
_, err := env.service.ListDevices(ctx, 99999, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_ListDevicesForEnterprise(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
allocateReq := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
}
|
||||
_, err := env.service.AllocateDevices(ctx, env.enterprise.ID, allocateReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("企业用户获取自己的授权设备", func(t *testing.T) {
|
||||
enterpriseCtx := createTestContext(1, constants.UserTypeEnterprise, 0, env.enterprise.ID)
|
||||
req := &dto.EnterpriseDeviceListReq{Page: 1, PageSize: 10}
|
||||
|
||||
resp, err := env.service.ListDevicesForEnterprise(enterpriseCtx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(1), resp.Total)
|
||||
})
|
||||
|
||||
t.Run("未设置企业ID返回错误", func(t *testing.T) {
|
||||
req := &dto.EnterpriseDeviceListReq{Page: 1, PageSize: 10}
|
||||
_, err := env.service.ListDevicesForEnterprise(context.Background(), req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_GetDeviceDetail(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
allocateReq := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
}
|
||||
_, err := env.service.AllocateDevices(ctx, env.enterprise.ID, allocateReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
enterpriseCtx := createTestContext(1, constants.UserTypeEnterprise, 0, env.enterprise.ID)
|
||||
|
||||
t.Run("成功获取设备详情", func(t *testing.T) {
|
||||
resp, err := env.service.GetDeviceDetail(enterpriseCtx, env.devices[0].ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, env.devices[0].ID, resp.Device.DeviceID)
|
||||
assert.Equal(t, env.devices[0].DeviceNo, resp.Device.DeviceNo)
|
||||
assert.Len(t, resp.Cards, 2)
|
||||
})
|
||||
|
||||
t.Run("设备未授权时返回错误", func(t *testing.T) {
|
||||
_, err := env.service.GetDeviceDetail(enterpriseCtx, env.devices[1].ID)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("未设置企业ID返回错误", func(t *testing.T) {
|
||||
_, err := env.service.GetDeviceDetail(context.Background(), env.devices[0].ID)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_SuspendCard(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
allocateReq := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
}
|
||||
_, err := env.service.AllocateDevices(ctx, env.enterprise.ID, allocateReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
enterpriseCtx := createTestContext(1, constants.UserTypeEnterprise, 0, env.enterprise.ID)
|
||||
|
||||
t.Run("成功停机", func(t *testing.T) {
|
||||
req := &dto.DeviceCardOperationReq{Reason: "测试停机"}
|
||||
resp, err := env.service.SuspendCard(enterpriseCtx, env.devices[0].ID, env.cards[0].ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, resp.Success)
|
||||
})
|
||||
|
||||
t.Run("卡不属于设备时返回错误", func(t *testing.T) {
|
||||
req := &dto.DeviceCardOperationReq{Reason: "测试停机"}
|
||||
_, err := env.service.SuspendCard(enterpriseCtx, env.devices[0].ID, env.cards[3].ID, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("设备未授权时返回错误", func(t *testing.T) {
|
||||
req := &dto.DeviceCardOperationReq{Reason: "测试停机"}
|
||||
_, err := env.service.SuspendCard(enterpriseCtx, env.devices[1].ID, env.cards[2].ID, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("未设置企业ID返回错误", func(t *testing.T) {
|
||||
req := &dto.DeviceCardOperationReq{Reason: "测试停机"}
|
||||
_, err := env.service.SuspendCard(context.Background(), env.devices[0].ID, env.cards[0].ID, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_ResumeCard(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
allocateReq := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
}
|
||||
_, err := env.service.AllocateDevices(ctx, env.enterprise.ID, allocateReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
enterpriseCtx := createTestContext(1, constants.UserTypeEnterprise, 0, env.enterprise.ID)
|
||||
|
||||
t.Run("成功复机", func(t *testing.T) {
|
||||
req := &dto.DeviceCardOperationReq{Reason: "测试复机"}
|
||||
resp, err := env.service.ResumeCard(enterpriseCtx, env.devices[0].ID, env.cards[0].ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, resp.Success)
|
||||
})
|
||||
|
||||
t.Run("卡不属于设备时返回错误", func(t *testing.T) {
|
||||
req := &dto.DeviceCardOperationReq{Reason: "测试复机"}
|
||||
_, err := env.service.ResumeCard(enterpriseCtx, env.devices[0].ID, env.cards[3].ID, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("设备未授权时返回错误", func(t *testing.T) {
|
||||
req := &dto.DeviceCardOperationReq{Reason: "测试复机"}
|
||||
_, err := env.service.ResumeCard(enterpriseCtx, env.devices[1].ID, env.cards[2].ID, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("未设置企业ID返回错误", func(t *testing.T) {
|
||||
req := &dto.DeviceCardOperationReq{Reason: "测试复机"}
|
||||
_, err := env.service.ResumeCard(context.Background(), env.devices[0].ID, env.cards[0].ID, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_ListDevices_EmptyResult(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
|
||||
t.Run("企业无授权设备时返回空列表", func(t *testing.T) {
|
||||
req := &dto.EnterpriseDeviceListReq{Page: 1, PageSize: 10}
|
||||
resp, err := env.service.ListDevices(ctx, env.enterprise.ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(0), resp.Total)
|
||||
assert.Empty(t, resp.List)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_GetDeviceDetail_WithCarrierInfo(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
allocateReq := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
}
|
||||
_, err := env.service.AllocateDevices(ctx, env.enterprise.ID, allocateReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
enterpriseCtx := createTestContext(1, constants.UserTypeEnterprise, 0, env.enterprise.ID)
|
||||
|
||||
t.Run("获取设备详情包含运营商信息", func(t *testing.T) {
|
||||
resp, err := env.service.GetDeviceDetail(enterpriseCtx, env.devices[0].ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, resp.Cards, 2)
|
||||
for _, card := range resp.Cards {
|
||||
assert.NotEmpty(t, card.CarrierName)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_GetDeviceDetail_NetworkStatus(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
allocateReq := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
}
|
||||
_, err := env.service.AllocateDevices(ctx, env.enterprise.ID, allocateReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
enterpriseCtx := createTestContext(1, constants.UserTypeEnterprise, 0, env.enterprise.ID)
|
||||
|
||||
t.Run("网络状态名称正确", func(t *testing.T) {
|
||||
resp, err := env.service.GetDeviceDetail(enterpriseCtx, env.devices[0].ID)
|
||||
require.NoError(t, err)
|
||||
for _, card := range resp.Cards {
|
||||
if card.NetworkStatus == 1 {
|
||||
assert.Equal(t, "开机", card.NetworkStatusName)
|
||||
} else {
|
||||
assert.Equal(t, "停机", card.NetworkStatusName)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_GetDeviceDetail_DeviceWithoutCards(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
|
||||
enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
|
||||
deviceStore := postgres.NewDeviceStore(tx, rdb)
|
||||
deviceSimBindingStore := postgres.NewDeviceSimBindingStore(tx, rdb)
|
||||
enterpriseDeviceAuthStore := postgres.NewEnterpriseDeviceAuthorizationStore(tx, rdb)
|
||||
enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
|
||||
logger := zap.NewNop()
|
||||
|
||||
svc := New(tx, enterpriseStore, deviceStore, deviceSimBindingStore, enterpriseDeviceAuthStore, enterpriseCardAuthStore, logger)
|
||||
|
||||
enterprise := &model.Enterprise{
|
||||
EnterpriseName: prefix + "_测试企业",
|
||||
EnterpriseCode: prefix,
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(enterprise).Error)
|
||||
|
||||
device := &model.Device{
|
||||
DeviceNo: prefix + "_D001",
|
||||
DeviceName: "无卡设备",
|
||||
Status: 2,
|
||||
}
|
||||
require.NoError(t, tx.Create(device).Error)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
allocateReq := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{device.DeviceNo},
|
||||
}
|
||||
_, err := svc.AllocateDevices(ctx, enterprise.ID, allocateReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("设备无绑定卡时返回空卡列表", func(t *testing.T) {
|
||||
enterpriseCtx := createTestContext(1, constants.UserTypeEnterprise, 0, enterprise.ID)
|
||||
resp, err := svc.GetDeviceDetail(enterpriseCtx, device.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, device.ID, resp.Device.DeviceID)
|
||||
assert.Empty(t, resp.Cards)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_RecallDevices_CascadeRevoke(t *testing.T) {
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
env := setupTestEnv(t, prefix)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
|
||||
allocateReq := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
}
|
||||
resp, err := env.service.AllocateDevices(ctx, env.enterprise.ID, allocateReq)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, resp.AuthorizedDevices[0].CardCount)
|
||||
|
||||
t.Run("撤销设备授权时级联撤销卡授权", func(t *testing.T) {
|
||||
recallReq := &dto.RecallDevicesReq{
|
||||
DeviceNos: []string{env.devices[0].DeviceNo},
|
||||
}
|
||||
|
||||
recallResp, err := env.service.RecallDevices(ctx, env.enterprise.ID, recallReq)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, recallResp.SuccessCount)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_GetDeviceDetail_WithNetworkStatusOn(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
|
||||
enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
|
||||
deviceStore := postgres.NewDeviceStore(tx, rdb)
|
||||
deviceSimBindingStore := postgres.NewDeviceSimBindingStore(tx, rdb)
|
||||
enterpriseDeviceAuthStore := postgres.NewEnterpriseDeviceAuthorizationStore(tx, rdb)
|
||||
enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
|
||||
logger := zap.NewNop()
|
||||
|
||||
svc := New(tx, enterpriseStore, deviceStore, deviceSimBindingStore, enterpriseDeviceAuthStore, enterpriseCardAuthStore, logger)
|
||||
|
||||
enterprise := &model.Enterprise{
|
||||
EnterpriseName: prefix + "_测试企业",
|
||||
EnterpriseCode: prefix,
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(enterprise).Error)
|
||||
|
||||
carrier := &model.Carrier{
|
||||
CarrierName: "测试运营商",
|
||||
CarrierType: "CMCC",
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(carrier).Error)
|
||||
|
||||
device := &model.Device{
|
||||
DeviceNo: prefix + "_D001",
|
||||
DeviceName: "测试设备",
|
||||
Status: 2,
|
||||
}
|
||||
require.NoError(t, tx.Create(device).Error)
|
||||
|
||||
card := &model.IotCard{
|
||||
ICCID: prefix + "0001",
|
||||
CardType: "normal",
|
||||
CarrierID: carrier.ID,
|
||||
Status: 2,
|
||||
NetworkStatus: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(card).Error)
|
||||
|
||||
now := time.Now()
|
||||
binding := &model.DeviceSimBinding{
|
||||
DeviceID: device.ID,
|
||||
IotCardID: card.ID,
|
||||
SlotPosition: 1,
|
||||
BindStatus: 1,
|
||||
BindTime: &now,
|
||||
}
|
||||
require.NoError(t, tx.Create(binding).Error)
|
||||
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
allocateReq := &dto.AllocateDevicesReq{
|
||||
DeviceNos: []string{device.DeviceNo},
|
||||
}
|
||||
_, err := svc.AllocateDevices(ctx, enterprise.ID, allocateReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("开机状态卡显示正确", func(t *testing.T) {
|
||||
enterpriseCtx := createTestContext(1, constants.UserTypeEnterprise, 0, enterprise.ID)
|
||||
resp, err := svc.GetDeviceDetail(enterpriseCtx, device.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, resp.Cards, 1)
|
||||
assert.Equal(t, 1, resp.Cards[0].NetworkStatus)
|
||||
assert.Equal(t, "开机", resp.Cards[0].NetworkStatusName)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_EnterpriseNotFound(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
|
||||
deviceStore := postgres.NewDeviceStore(tx, rdb)
|
||||
deviceSimBindingStore := postgres.NewDeviceSimBindingStore(tx, rdb)
|
||||
enterpriseDeviceAuthStore := postgres.NewEnterpriseDeviceAuthorizationStore(tx, rdb)
|
||||
enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
|
||||
logger := zap.NewNop()
|
||||
|
||||
svc := New(tx, enterpriseStore, deviceStore, deviceSimBindingStore, enterpriseDeviceAuthStore, enterpriseCardAuthStore, logger)
|
||||
ctx := createTestContext(1, constants.UserTypePlatform, 0, 0)
|
||||
|
||||
t.Run("AllocateDevices企业不存在", func(t *testing.T) {
|
||||
req := &dto.AllocateDevicesReq{DeviceNos: []string{"D001"}}
|
||||
_, err := svc.AllocateDevices(ctx, 99999, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("RecallDevices企业不存在", func(t *testing.T) {
|
||||
req := &dto.RecallDevicesReq{DeviceNos: []string{"D001"}}
|
||||
_, err := svc.RecallDevices(ctx, 99999, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_ValidateCardOperation_RevokedDeviceAuth(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
prefix := uniqueServiceTestPrefix()
|
||||
|
||||
enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
|
||||
deviceStore := postgres.NewDeviceStore(tx, rdb)
|
||||
deviceSimBindingStore := postgres.NewDeviceSimBindingStore(tx, rdb)
|
||||
enterpriseDeviceAuthStore := postgres.NewEnterpriseDeviceAuthorizationStore(tx, rdb)
|
||||
enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
|
||||
logger := zap.NewNop()
|
||||
|
||||
svc := New(tx, enterpriseStore, deviceStore, deviceSimBindingStore, enterpriseDeviceAuthStore, enterpriseCardAuthStore, logger)
|
||||
|
||||
enterprise := &model.Enterprise{
|
||||
EnterpriseName: prefix + "_测试企业",
|
||||
EnterpriseCode: prefix,
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(enterprise).Error)
|
||||
|
||||
carrier := &model.Carrier{
|
||||
CarrierName: "测试运营商",
|
||||
CarrierType: "CMCC",
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(carrier).Error)
|
||||
|
||||
device := &model.Device{
|
||||
DeviceNo: prefix + "_D001",
|
||||
DeviceName: "测试设备",
|
||||
Status: 2,
|
||||
}
|
||||
require.NoError(t, tx.Create(device).Error)
|
||||
|
||||
card := &model.IotCard{
|
||||
ICCID: prefix + "0001",
|
||||
CardType: "normal",
|
||||
CarrierID: carrier.ID,
|
||||
Status: 2,
|
||||
}
|
||||
require.NoError(t, tx.Create(card).Error)
|
||||
|
||||
now := time.Now()
|
||||
binding := &model.DeviceSimBinding{
|
||||
DeviceID: device.ID,
|
||||
IotCardID: card.ID,
|
||||
SlotPosition: 1,
|
||||
BindStatus: 1,
|
||||
BindTime: &now,
|
||||
}
|
||||
require.NoError(t, tx.Create(binding).Error)
|
||||
|
||||
deviceAuth := &model.EnterpriseDeviceAuthorization{
|
||||
EnterpriseID: enterprise.ID,
|
||||
DeviceID: device.ID,
|
||||
AuthorizedBy: 1,
|
||||
AuthorizedAt: now,
|
||||
AuthorizerType: 2,
|
||||
RevokedBy: ptrUintED(1),
|
||||
RevokedAt: &now,
|
||||
}
|
||||
require.NoError(t, tx.Create(deviceAuth).Error)
|
||||
|
||||
t.Run("已撤销的设备授权无法操作卡", func(t *testing.T) {
|
||||
enterpriseCtx := createTestContext(1, constants.UserTypeEnterprise, 0, enterprise.ID)
|
||||
req := &dto.DeviceCardOperationReq{Reason: "测试"}
|
||||
_, err := svc.SuspendCard(enterpriseCtx, device.ID, card.ID, req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func ptrUintED(v uint) *uint {
|
||||
return &v
|
||||
}
|
||||
Reference in New Issue
Block a user