package enterprise_card 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" "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 *dto.AllocateCardsPreviewReq) (*dto.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]bool) for _, binding := range bindings { cardToDevice[binding.IotCardID] = true } resp := &dto.AllocateCardsPreviewResp{ StandaloneCards: make([]dto.StandaloneCard, 0), FailedItems: make([]dto.FailedItem, 0), } for _, iccid := range req.ICCIDs { card, exists := cardMap[iccid] if !exists { resp.FailedItems = append(resp.FailedItems, dto.FailedItem{ ICCID: iccid, Reason: "卡不存在", }) continue } if cardToDevice[card.ID] { resp.FailedItems = append(resp.FailedItems, dto.FailedItem{ ICCID: iccid, Reason: "该卡已绑定设备,请使用设备授权功能", }) continue } 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: 0, DeviceCardCount: 0, TotalCardCount: len(resp.StandaloneCards), FailedCount: len(resp.FailedItems), } return resp, nil } func (s *Service) AllocateCards(ctx context.Context, enterpriseID uint, req *dto.AllocateCardsReq) (*dto.AllocateCardsResp, 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, "企业不存在") } preview, err := s.AllocateCardsPreview(ctx, enterpriseID, &dto.AllocateCardsPreviewReq{ICCIDs: req.ICCIDs}) if err != nil { return nil, err } resp := &dto.AllocateCardsResp{ FailedItems: preview.FailedItems, FailCount: len(preview.FailedItems), } cardIDsToAllocate := make([]uint, 0) for _, card := range preview.StandaloneCards { cardIDsToAllocate = append(cardIDsToAllocate, card.IotCardID) } existingAuths, err := s.enterpriseCardAuthStore.GetActiveAuthsByCardIDs(ctx, enterpriseID, cardIDsToAllocate) if err != nil { return nil, fmt.Errorf("查询已有授权失败: %w", err) } now := time.Now() userType := middleware.GetUserTypeFromContext(ctx) auths := make([]*model.EnterpriseCardAuthorization, 0) for _, cardID := range cardIDsToAllocate { if existingAuths[cardID] { continue } auths = append(auths, &model.EnterpriseCardAuthorization{ EnterpriseID: enterpriseID, CardID: cardID, AuthorizedBy: currentUserID, AuthorizedAt: now, AuthorizerType: userType, Remark: req.Remark, }) } 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 *dto.RecallCardsReq) (*dto.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 := &dto.RecallCardsResp{ FailedItems: make([]dto.FailedItem, 0), RecalledDevices: make([]dto.RecalledDevice, 0), } cardIDsToRecall := make([]uint, 0) for _, iccid := range req.ICCIDs { card, exists := cardMap[iccid] if !exists { resp.FailedItems = append(resp.FailedItems, dto.FailedItem{ ICCID: iccid, Reason: "卡不存在", }) continue } if !existingAuths[card.ID] { resp.FailedItems = append(resp.FailedItems, dto.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 *dto.EnterpriseCardListReq) (*dto.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 &dto.EnterpriseCardPageResult{ Items: make([]dto.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([]dto.EnterpriseCardItem, 0, len(cards)) for _, card := range cards { items = append(items, dto.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 &dto.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.RevokedAt != nil { 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 "停机" }