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:
@@ -65,34 +65,16 @@ func (s *Service) AllocateCardsPreview(ctx context.Context, enterpriseID uint, r
|
||||
s.db.WithContext(ctx).Where("iot_card_id IN ? AND bind_status = 1", cardIDs).Find(&bindings)
|
||||
}
|
||||
|
||||
cardToDevice := make(map[uint]uint)
|
||||
deviceCards := make(map[uint][]uint)
|
||||
cardToDevice := make(map[uint]bool)
|
||||
for _, binding := range bindings {
|
||||
cardToDevice[binding.IotCardID] = binding.DeviceID
|
||||
deviceCards[binding.DeviceID] = append(deviceCards[binding.DeviceID], binding.IotCardID)
|
||||
}
|
||||
|
||||
deviceIDs := make([]uint, 0, len(deviceCards))
|
||||
for deviceID := range deviceCards {
|
||||
deviceIDs = append(deviceIDs, deviceID)
|
||||
}
|
||||
var devices []model.Device
|
||||
deviceMap := make(map[uint]*model.Device)
|
||||
if len(deviceIDs) > 0 {
|
||||
s.db.WithContext(ctx).Where("id IN ?", deviceIDs).Find(&devices)
|
||||
for i := range devices {
|
||||
deviceMap[devices[i].ID] = &devices[i]
|
||||
}
|
||||
cardToDevice[binding.IotCardID] = true
|
||||
}
|
||||
|
||||
resp := &dto.AllocateCardsPreviewResp{
|
||||
StandaloneCards: make([]dto.StandaloneCard, 0),
|
||||
DeviceBundles: make([]dto.DeviceBundle, 0),
|
||||
FailedItems: make([]dto.FailedItem, 0),
|
||||
}
|
||||
|
||||
processedDevices := make(map[uint]bool)
|
||||
|
||||
for _, iccid := range req.ICCIDs {
|
||||
card, exists := cardMap[iccid]
|
||||
if !exists {
|
||||
@@ -103,67 +85,28 @@ func (s *Service) AllocateCardsPreview(ctx context.Context, enterpriseID uint, r
|
||||
continue
|
||||
}
|
||||
|
||||
deviceID, hasDevice := cardToDevice[card.ID]
|
||||
if !hasDevice {
|
||||
resp.StandaloneCards = append(resp.StandaloneCards, dto.StandaloneCard{
|
||||
ICCID: card.ICCID,
|
||||
IotCardID: card.ID,
|
||||
MSISDN: card.MSISDN,
|
||||
CarrierID: card.CarrierID,
|
||||
StatusName: getCardStatusName(card.Status),
|
||||
if cardToDevice[card.ID] {
|
||||
resp.FailedItems = append(resp.FailedItems, dto.FailedItem{
|
||||
ICCID: iccid,
|
||||
Reason: "该卡已绑定设备,请使用设备授权功能",
|
||||
})
|
||||
} else {
|
||||
if processedDevices[deviceID] {
|
||||
continue
|
||||
}
|
||||
processedDevices[deviceID] = true
|
||||
|
||||
device := deviceMap[deviceID]
|
||||
if device == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
bundleCardIDs := deviceCards[deviceID]
|
||||
bundle := dto.DeviceBundle{
|
||||
DeviceID: deviceID,
|
||||
DeviceNo: device.DeviceNo,
|
||||
BundleCards: make([]dto.DeviceBundleCard, 0),
|
||||
}
|
||||
|
||||
for _, bundleCardID := range bundleCardIDs {
|
||||
bundleCard := cardIDMap[bundleCardID]
|
||||
if bundleCard == nil {
|
||||
continue
|
||||
}
|
||||
if bundleCard.ID == card.ID {
|
||||
bundle.TriggerCard = dto.DeviceBundleCard{
|
||||
ICCID: bundleCard.ICCID,
|
||||
IotCardID: bundleCard.ID,
|
||||
MSISDN: bundleCard.MSISDN,
|
||||
}
|
||||
} else {
|
||||
bundle.BundleCards = append(bundle.BundleCards, dto.DeviceBundleCard{
|
||||
ICCID: bundleCard.ICCID,
|
||||
IotCardID: bundleCard.ID,
|
||||
MSISDN: bundleCard.MSISDN,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
resp.DeviceBundles = append(resp.DeviceBundles, bundle)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
deviceCardCount := 0
|
||||
for _, bundle := range resp.DeviceBundles {
|
||||
deviceCardCount += 1 + len(bundle.BundleCards)
|
||||
resp.StandaloneCards = append(resp.StandaloneCards, dto.StandaloneCard{
|
||||
ICCID: card.ICCID,
|
||||
IotCardID: card.ID,
|
||||
MSISDN: card.MSISDN,
|
||||
CarrierID: card.CarrierID,
|
||||
StatusName: getCardStatusName(card.Status),
|
||||
})
|
||||
}
|
||||
|
||||
resp.Summary = dto.AllocatePreviewSummary{
|
||||
StandaloneCardCount: len(resp.StandaloneCards),
|
||||
DeviceCount: len(resp.DeviceBundles),
|
||||
DeviceCardCount: deviceCardCount,
|
||||
TotalCardCount: len(resp.StandaloneCards) + deviceCardCount,
|
||||
DeviceCount: 0,
|
||||
DeviceCardCount: 0,
|
||||
TotalCardCount: len(resp.StandaloneCards),
|
||||
FailedCount: len(resp.FailedItems),
|
||||
}
|
||||
|
||||
@@ -186,36 +129,15 @@ func (s *Service) AllocateCards(ctx context.Context, enterpriseID uint, req *dto
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(preview.DeviceBundles) > 0 && !req.ConfirmDeviceBundles {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "存在设备包,请确认整体授权设备下所有卡")
|
||||
}
|
||||
|
||||
resp := &dto.AllocateCardsResp{
|
||||
FailedItems: preview.FailedItems,
|
||||
FailCount: len(preview.FailedItems),
|
||||
AllocatedDevices: make([]dto.AllocatedDevice, 0),
|
||||
FailedItems: preview.FailedItems,
|
||||
FailCount: len(preview.FailedItems),
|
||||
}
|
||||
|
||||
cardIDsToAllocate := make([]uint, 0)
|
||||
for _, card := range preview.StandaloneCards {
|
||||
cardIDsToAllocate = append(cardIDsToAllocate, card.IotCardID)
|
||||
}
|
||||
for _, bundle := range preview.DeviceBundles {
|
||||
cardIDsToAllocate = append(cardIDsToAllocate, bundle.TriggerCard.IotCardID)
|
||||
for _, card := range bundle.BundleCards {
|
||||
cardIDsToAllocate = append(cardIDsToAllocate, card.IotCardID)
|
||||
}
|
||||
iccids := []string{bundle.TriggerCard.ICCID}
|
||||
for _, card := range bundle.BundleCards {
|
||||
iccids = append(iccids, card.ICCID)
|
||||
}
|
||||
resp.AllocatedDevices = append(resp.AllocatedDevices, dto.AllocatedDevice{
|
||||
DeviceID: bundle.DeviceID,
|
||||
DeviceNo: bundle.DeviceNo,
|
||||
CardCount: 1 + len(bundle.BundleCards),
|
||||
ICCIDs: iccids,
|
||||
})
|
||||
}
|
||||
|
||||
existingAuths, err := s.enterpriseCardAuthStore.GetActiveAuthsByCardIDs(ctx, enterpriseID, cardIDsToAllocate)
|
||||
if err != nil {
|
||||
@@ -235,6 +157,7 @@ func (s *Service) AllocateCards(ctx context.Context, enterpriseID uint, req *dto
|
||||
AuthorizedBy: currentUserID,
|
||||
AuthorizedAt: now,
|
||||
AuthorizerType: userType,
|
||||
Remark: req.Remark,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user