package enterprise_card import ( "context" "fmt" "time" "github.com/break/junhong_cmp_fiber/internal/model" "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" "github.com/break/junhong_cmp_fiber/pkg/middleware" "gorm.io/gorm" ) type Service struct { db *gorm.DB enterpriseStore *postgres.EnterpriseStore enterpriseCardAuthStore *postgres.EnterpriseCardAuthorizationStore } func New( db *gorm.DB, enterpriseStore *postgres.EnterpriseStore, enterpriseCardAuthStore *postgres.EnterpriseCardAuthorizationStore, ) *Service { return &Service{ db: db, enterpriseStore: enterpriseStore, enterpriseCardAuthStore: enterpriseCardAuthStore, } } func (s *Service) AllocateCardsPreview(ctx context.Context, enterpriseID uint, req *model.AllocateCardsPreviewReq) (*model.AllocateCardsPreviewResp, 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 iotCards []model.IotCard if err := s.db.WithContext(ctx).Where("iccid IN ?", req.ICCIDs).Find(&iotCards).Error; err != nil { return nil, fmt.Errorf("查询卡信息失败: %w", err) } cardMap := make(map[string]*model.IotCard) cardIDMap := make(map[uint]*model.IotCard) for i := range iotCards { cardMap[iotCards[i].ICCID] = &iotCards[i] cardIDMap[iotCards[i].ID] = &iotCards[i] } cardIDs := make([]uint, 0, len(iotCards)) for _, card := range iotCards { cardIDs = append(cardIDs, card.ID) } var bindings []model.DeviceSimBinding if len(cardIDs) > 0 { 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) 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] } } resp := &model.AllocateCardsPreviewResp{ StandaloneCards: make([]model.StandaloneCard, 0), DeviceBundles: make([]model.DeviceBundle, 0), FailedItems: make([]model.FailedItem, 0), } processedDevices := make(map[uint]bool) for _, iccid := range req.ICCIDs { card, exists := cardMap[iccid] if !exists { resp.FailedItems = append(resp.FailedItems, model.FailedItem{ ICCID: iccid, Reason: "卡不存在", }) continue } deviceID, hasDevice := cardToDevice[card.ID] if !hasDevice { resp.StandaloneCards = append(resp.StandaloneCards, model.StandaloneCard{ ICCID: card.ICCID, IotCardID: card.ID, MSISDN: card.MSISDN, CarrierID: card.CarrierID, StatusName: getCardStatusName(card.Status), }) } else { if processedDevices[deviceID] { continue } processedDevices[deviceID] = true device := deviceMap[deviceID] if device == nil { continue } bundleCardIDs := deviceCards[deviceID] bundle := model.DeviceBundle{ DeviceID: deviceID, DeviceNo: device.DeviceNo, BundleCards: make([]model.DeviceBundleCard, 0), } for _, bundleCardID := range bundleCardIDs { bundleCard := cardIDMap[bundleCardID] if bundleCard == nil { continue } if bundleCard.ID == card.ID { bundle.TriggerCard = model.DeviceBundleCard{ ICCID: bundleCard.ICCID, IotCardID: bundleCard.ID, MSISDN: bundleCard.MSISDN, } } else { bundle.BundleCards = append(bundle.BundleCards, model.DeviceBundleCard{ ICCID: bundleCard.ICCID, IotCardID: bundleCard.ID, MSISDN: bundleCard.MSISDN, }) } } resp.DeviceBundles = append(resp.DeviceBundles, bundle) } } deviceCardCount := 0 for _, bundle := range resp.DeviceBundles { deviceCardCount += 1 + len(bundle.BundleCards) } resp.Summary = model.AllocatePreviewSummary{ StandaloneCardCount: len(resp.StandaloneCards), DeviceCount: len(resp.DeviceBundles), DeviceCardCount: deviceCardCount, TotalCardCount: len(resp.StandaloneCards) + deviceCardCount, FailedCount: len(resp.FailedItems), } return resp, nil } func (s *Service) AllocateCards(ctx context.Context, enterpriseID uint, req *model.AllocateCardsReq) (*model.AllocateCardsResp, error) { currentUserID := middleware.GetUserIDFromContext(ctx) currentShopID := middleware.GetShopIDFromContext(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, "企业不存在") } preview, err := s.AllocateCardsPreview(ctx, enterpriseID, &model.AllocateCardsPreviewReq{ICCIDs: req.ICCIDs}) if err != nil { return nil, err } if len(preview.DeviceBundles) > 0 && !req.ConfirmDeviceBundles { return nil, errors.New(errors.CodeInvalidParam, "存在设备包,请确认整体授权设备下所有卡") } resp := &model.AllocateCardsResp{ FailedItems: preview.FailedItems, FailCount: len(preview.FailedItems), AllocatedDevices: make([]model.AllocatedDevice, 0), } 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, model.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 { return nil, fmt.Errorf("查询已有授权失败: %w", err) } now := time.Now() auths := make([]*model.EnterpriseCardAuthorization, 0) for _, cardID := range cardIDsToAllocate { if existingAuths[cardID] { continue } auths = append(auths, &model.EnterpriseCardAuthorization{ EnterpriseID: enterpriseID, IotCardID: cardID, ShopID: currentShopID, AuthorizedBy: currentUserID, AuthorizedAt: &now, Status: 1, }) } if len(auths) > 0 { if err := s.enterpriseCardAuthStore.BatchCreate(ctx, auths); err != nil { return nil, fmt.Errorf("创建授权记录失败: %w", err) } } resp.SuccessCount = len(cardIDsToAllocate) return resp, nil } func (s *Service) RecallCards(ctx context.Context, enterpriseID uint, req *model.RecallCardsReq) (*model.RecallCardsResp, 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 iotCards []model.IotCard if err := s.db.WithContext(ctx).Where("iccid IN ?", req.ICCIDs).Find(&iotCards).Error; err != nil { return nil, fmt.Errorf("查询卡信息失败: %w", err) } cardMap := make(map[string]*model.IotCard) cardIDMap := make(map[uint]*model.IotCard) cardIDs := make([]uint, 0, len(iotCards)) for i := range iotCards { cardMap[iotCards[i].ICCID] = &iotCards[i] cardIDMap[iotCards[i].ID] = &iotCards[i] cardIDs = append(cardIDs, iotCards[i].ID) } existingAuths, err := s.enterpriseCardAuthStore.GetActiveAuthsByCardIDs(ctx, enterpriseID, cardIDs) if err != nil { return nil, fmt.Errorf("查询已有授权失败: %w", err) } resp := &model.RecallCardsResp{ FailedItems: make([]model.FailedItem, 0), RecalledDevices: make([]model.RecalledDevice, 0), } cardIDsToRecall := make([]uint, 0) for _, iccid := range req.ICCIDs { card, exists := cardMap[iccid] if !exists { resp.FailedItems = append(resp.FailedItems, model.FailedItem{ ICCID: iccid, Reason: "卡不存在", }) continue } if !existingAuths[card.ID] { resp.FailedItems = append(resp.FailedItems, model.FailedItem{ ICCID: iccid, Reason: "该卡未授权给此企业", }) continue } cardIDsToRecall = append(cardIDsToRecall, card.ID) } if len(cardIDsToRecall) > 0 { if err := s.enterpriseCardAuthStore.BatchUpdateStatus(ctx, enterpriseID, cardIDsToRecall, 0); err != nil { return nil, fmt.Errorf("回收授权失败: %w", err) } } resp.SuccessCount = len(cardIDsToRecall) resp.FailCount = len(resp.FailedItems) return resp, nil } func (s *Service) ListCards(ctx context.Context, enterpriseID uint, req *model.EnterpriseCardListReq) (*model.EnterpriseCardPageResult, error) { _, err := s.enterpriseStore.GetByID(ctx, enterpriseID) if err != nil { return nil, errors.New(errors.CodeEnterpriseNotFound, "企业不存在") } cardIDs, err := s.enterpriseCardAuthStore.ListCardIDsByEnterprise(ctx, enterpriseID) if err != nil { return nil, fmt.Errorf("查询授权卡ID失败: %w", err) } if len(cardIDs) == 0 { return &model.EnterpriseCardPageResult{ Items: make([]model.EnterpriseCardItem, 0), Total: 0, Page: req.Page, Size: req.PageSize, }, nil } page := req.Page pageSize := req.PageSize if page == 0 { page = 1 } if pageSize == 0 { pageSize = constants.DefaultPageSize } query := s.db.WithContext(ctx).Model(&model.IotCard{}).Where("id IN ?", cardIDs) if req.Status != nil { query = query.Where("status = ?", *req.Status) } if req.CarrierID != nil { query = query.Where("carrier_id = ?", *req.CarrierID) } if req.ICCID != "" { query = query.Where("iccid LIKE ?", "%"+req.ICCID+"%") } var total int64 if err := query.Count(&total).Error; err != nil { return nil, fmt.Errorf("统计卡数量失败: %w", err) } var cards []model.IotCard offset := (page - 1) * pageSize if err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&cards).Error; err != nil { return nil, fmt.Errorf("查询卡列表失败: %w", err) } items := make([]model.EnterpriseCardItem, 0, len(cards)) for _, card := range cards { items = append(items, model.EnterpriseCardItem{ ID: card.ID, ICCID: card.ICCID, MSISDN: card.MSISDN, CarrierID: card.CarrierID, Status: card.Status, StatusName: getCardStatusName(card.Status), NetworkStatus: card.NetworkStatus, NetworkStatusName: getNetworkStatusName(card.NetworkStatus), }) } return &model.EnterpriseCardPageResult{ Items: items, Total: total, Page: page, Size: pageSize, }, nil } func (s *Service) SuspendCard(ctx context.Context, enterpriseID, cardID uint) error { return s.updateCardNetworkStatus(ctx, enterpriseID, cardID, 0) } func (s *Service) ResumeCard(ctx context.Context, enterpriseID, cardID uint) error { return s.updateCardNetworkStatus(ctx, enterpriseID, cardID, 1) } func (s *Service) updateCardNetworkStatus(ctx context.Context, enterpriseID, cardID uint, networkStatus int) error { currentUserID := middleware.GetUserIDFromContext(ctx) if currentUserID == 0 { return errors.New(errors.CodeUnauthorized, "未授权访问") } _, err := s.enterpriseStore.GetByID(ctx, enterpriseID) if err != nil { return errors.New(errors.CodeEnterpriseNotFound, "企业不存在") } auth, err := s.enterpriseCardAuthStore.GetByEnterpriseAndCard(ctx, enterpriseID, cardID) if err != nil || auth.Status != 1 { return errors.New(errors.CodeForbidden, "无权限操作此卡") } return s.db.WithContext(ctx).Model(&model.IotCard{}). Where("id = ?", cardID). Update("network_status", networkStatus).Error } func getCardStatusName(status int) string { switch status { case 1: return "在库" case 2: return "已分销" case 3: return "已激活" case 4: return "已停用" default: return "未知" } } func getNetworkStatusName(status int) string { if status == 1 { return "开机" } return "停机" }