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" "go.uber.org/zap" "gorm.io/gorm" ) type AuthorizationService struct { enterpriseStore *postgres.EnterpriseStore iotCardStore *postgres.IotCardStore authorizationStore *postgres.EnterpriseCardAuthorizationStore logger *zap.Logger } func NewAuthorizationService( enterpriseStore *postgres.EnterpriseStore, iotCardStore *postgres.IotCardStore, authorizationStore *postgres.EnterpriseCardAuthorizationStore, logger *zap.Logger, ) *AuthorizationService { return &AuthorizationService{ enterpriseStore: enterpriseStore, iotCardStore: iotCardStore, authorizationStore: authorizationStore, logger: logger, } } type BatchAuthorizeRequest struct { EnterpriseID uint CardIDs []uint AuthorizerID uint AuthorizerType int Remark string } func (s *AuthorizationService) BatchAuthorize(ctx context.Context, req BatchAuthorizeRequest) error { if len(req.CardIDs) == 0 { return errors.New(errors.CodeInvalidParam, "卡ID列表不能为空") } userID := middleware.GetUserIDFromContext(ctx) userType := middleware.GetUserTypeFromContext(ctx) shopID := middleware.GetShopIDFromContext(ctx) if userID == 0 { return errors.New(errors.CodeUnauthorized, "用户信息无效") } enterprise, err := s.enterpriseStore.GetByID(ctx, req.EnterpriseID) if err != nil { if err == gorm.ErrRecordNotFound { return errors.New(errors.CodeEnterpriseNotFound, "企业不存在") } return err } if userType == constants.UserTypeAgent { if enterprise.OwnerShopID == nil || *enterprise.OwnerShopID != shopID { return errors.New(errors.CodeCannotAuthorizeToOthersEnterprise, "只能授权给自己的企业") } } cards, err := s.iotCardStore.GetByIDs(ctx, req.CardIDs) if err != nil { return err } if len(cards) != len(req.CardIDs) { return errors.New(errors.CodeIotCardNotFound, "部分卡不存在") } cardMap := make(map[uint]*model.IotCard) for _, card := range cards { cardMap[card.ID] = card } for _, cardID := range req.CardIDs { card := cardMap[cardID] if card.ShopID == nil { return errors.New(errors.CodeIotCardStatusNotAllowed, fmt.Sprintf("卡 %s 未分销,不能授权", card.ICCID)) } if userType == constants.UserTypeAgent && *card.ShopID != shopID { return errors.New(errors.CodeCannotAuthorizeOthersCard, fmt.Sprintf("卡 %s 不属于您的店铺", card.ICCID)) } } boundCardIDs, err := s.iotCardStore.GetBoundCardIDs(ctx, req.CardIDs) if err != nil { return err } if len(boundCardIDs) > 0 { return errors.New(errors.CodeCannotAuthorizeBoundCard, "部分卡已绑定设备,不能授权") } existingAuths, err := s.authorizationStore.ListByCards(ctx, req.CardIDs, false) if err != nil { return err } existingMap := make(map[uint]bool) for _, auth := range existingAuths { if auth.EnterpriseID == req.EnterpriseID { existingMap[auth.CardID] = true } } var newAuths []*model.EnterpriseCardAuthorization for _, cardID := range req.CardIDs { if existingMap[cardID] { continue } newAuths = append(newAuths, &model.EnterpriseCardAuthorization{ EnterpriseID: req.EnterpriseID, CardID: cardID, AuthorizedBy: req.AuthorizerID, AuthorizerType: req.AuthorizerType, Remark: req.Remark, }) } if len(newAuths) == 0 { return errors.New(errors.CodeCardAlreadyAuthorized, "所有卡已授权给该企业") } return s.authorizationStore.BatchCreate(ctx, newAuths) } type RevokeAuthorizationsRequest struct { EnterpriseID uint CardIDs []uint RevokedBy uint } func (s *AuthorizationService) RevokeAuthorizations(ctx context.Context, req RevokeAuthorizationsRequest) error { if len(req.CardIDs) == 0 { return errors.New(errors.CodeInvalidParam, "卡ID列表不能为空") } userID := middleware.GetUserIDFromContext(ctx) userType := middleware.GetUserTypeFromContext(ctx) if userID == 0 { return errors.New(errors.CodeUnauthorized, "用户信息无效") } existingAuths, err := s.authorizationStore.ListByCards(ctx, req.CardIDs, false) if err != nil { return err } authMap := make(map[uint]*model.EnterpriseCardAuthorization) for _, auth := range existingAuths { if auth.EnterpriseID == req.EnterpriseID { authMap[auth.CardID] = auth } } if len(authMap) == 0 { return errors.New(errors.CodeCardNotAuthorized, "卡未授权给该企业") } if userType == constants.UserTypeAgent { for _, auth := range authMap { if auth.AuthorizedBy != userID { return errors.New(errors.CodeCannotRevokeOthersAuthorization, "只能回收自己创建的授权") } } } var cardIDsToRevoke []uint for cardID := range authMap { cardIDsToRevoke = append(cardIDsToRevoke, cardID) } return s.authorizationStore.RevokeAuthorizations(ctx, req.EnterpriseID, cardIDsToRevoke, req.RevokedBy) } type ListAuthorizationsRequest struct { EnterpriseID *uint AuthorizedBy *uint IncludeRevoked bool Page int PageSize int } type ListAuthorizationsResponse struct { Authorizations []*model.EnterpriseCardAuthorization Total int64 } func (s *AuthorizationService) ListAuthorizations(ctx context.Context, req ListAuthorizationsRequest) (*ListAuthorizationsResponse, error) { if req.Page <= 0 { req.Page = 1 } if req.PageSize <= 0 { req.PageSize = constants.DefaultPageSize } if req.PageSize > constants.MaxPageSize { req.PageSize = constants.MaxPageSize } opts := postgres.AuthorizationListOptions{ EnterpriseID: req.EnterpriseID, AuthorizedBy: req.AuthorizedBy, IncludeRevoked: req.IncludeRevoked, Offset: (req.Page - 1) * req.PageSize, Limit: req.PageSize, } auths, total, err := s.authorizationStore.ListWithOptions(ctx, opts) if err != nil { return nil, err } return &ListAuthorizationsResponse{ Authorizations: auths, Total: total, }, nil } func (s *AuthorizationService) GetAuthorizedCardIDs(ctx context.Context, enterpriseID uint) ([]uint, error) { return s.authorizationStore.GetActiveAuthorizedCardIDs(ctx, enterpriseID) } type ListRecordsRequest struct { EnterpriseID *uint ICCID string AuthorizerType *int Status *int StartTime string EndTime string Page int PageSize int } type AuthorizationRecord struct { ID uint EnterpriseID uint EnterpriseName string CardID uint ICCID string MSISDN string AuthorizedBy uint AuthorizerName string AuthorizerType int AuthorizedAt string RevokedBy *uint RevokerName string RevokedAt *string Status int Remark string } type ListRecordsResponse struct { Items []AuthorizationRecord Total int64 Page int Size int } func (s *AuthorizationService) ListRecords(ctx context.Context, req ListRecordsRequest) (*ListRecordsResponse, error) { if req.Page <= 0 { req.Page = 1 } if req.PageSize <= 0 { req.PageSize = constants.DefaultPageSize } if req.PageSize > constants.MaxPageSize { req.PageSize = constants.MaxPageSize } opts := postgres.AuthorizationWithJoinListOptions{ EnterpriseID: req.EnterpriseID, ICCID: req.ICCID, AuthorizerType: req.AuthorizerType, Status: req.Status, Offset: (req.Page - 1) * req.PageSize, Limit: req.PageSize, } if req.StartTime != "" { t, err := parseDate(req.StartTime) if err == nil { opts.StartTime = &t } } if req.EndTime != "" { t, err := parseDate(req.EndTime) if err == nil { endTime := t.AddDate(0, 0, 1) opts.EndTime = &endTime } } results, total, err := s.authorizationStore.ListWithJoin(ctx, opts) if err != nil { return nil, err } items := make([]AuthorizationRecord, len(results)) for i, r := range results { status := 1 if r.RevokedAt != nil { status = 0 } var revokedAt *string if r.RevokedAt != nil { t := r.RevokedAt.Format("2006-01-02 15:04:05") revokedAt = &t } revokerName := "" if r.RevokerName != nil { revokerName = *r.RevokerName } items[i] = AuthorizationRecord{ ID: r.ID, EnterpriseID: r.EnterpriseID, EnterpriseName: r.EnterpriseName, CardID: r.CardID, ICCID: r.ICCID, MSISDN: r.MSISDN, AuthorizedBy: r.AuthorizedBy, AuthorizerName: r.AuthorizerName, AuthorizerType: r.AuthorizerType, AuthorizedAt: r.AuthorizedAt.Format("2006-01-02 15:04:05"), RevokedBy: r.RevokedBy, RevokerName: revokerName, RevokedAt: revokedAt, Status: status, Remark: r.Remark, } } return &ListRecordsResponse{ Items: items, Total: total, Page: req.Page, Size: req.PageSize, }, nil } func (s *AuthorizationService) GetRecordDetail(ctx context.Context, id uint) (*AuthorizationRecord, error) { r, err := s.authorizationStore.GetByIDWithJoin(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeNotFound, "授权记录不存在") } return nil, err } status := 1 if r.RevokedAt != nil { status = 0 } var revokedAt *string if r.RevokedAt != nil { t := r.RevokedAt.Format("2006-01-02 15:04:05") revokedAt = &t } revokerName := "" if r.RevokerName != nil { revokerName = *r.RevokerName } return &AuthorizationRecord{ ID: r.ID, EnterpriseID: r.EnterpriseID, EnterpriseName: r.EnterpriseName, CardID: r.CardID, ICCID: r.ICCID, MSISDN: r.MSISDN, AuthorizedBy: r.AuthorizedBy, AuthorizerName: r.AuthorizerName, AuthorizerType: r.AuthorizerType, AuthorizedAt: r.AuthorizedAt.Format("2006-01-02 15:04:05"), RevokedBy: r.RevokedBy, RevokerName: revokerName, RevokedAt: revokedAt, Status: status, Remark: r.Remark, }, nil } func (s *AuthorizationService) UpdateRecordRemark(ctx context.Context, id uint, remark string) (*AuthorizationRecord, error) { if err := s.authorizationStore.UpdateRemark(ctx, id, remark); err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeNotFound, "授权记录不存在") } return nil, err } return s.GetRecordDetail(ctx, id) } func parseDate(dateStr string) (time.Time, error) { return time.ParseInLocation("2006-01-02", dateStr, time.Local) }