// Package miniapp 提供被纳管小程序的数据访问。 package miniapp import ( "context" "log/slog" "gorm.io/gorm" "portal-mini-go/internal/model" "portal-mini-go/internal/pkg/errors" ) // Repository 小程序数据访问。 type Repository struct { db *gorm.DB } func New(db *gorm.DB) *Repository { return &Repository{db: db} } // Create 新建小程序。AppID 已存在时返回可读业务错误。 // 注意:软删除记录仍占用 app_id 唯一索引,故此处一并查软删除态。 func (r *Repository) Create(ctx context.Context, m *model.MiniProgram) error { if _, err := r.GetByAppID(ctx, m.AppID); err == nil { return errors.New(40900, "app_id 已存在") } else if !errors.Is(err, errors.ErrNotFound) { return err } if err := r.db.WithContext(ctx).Create(m).Error; err != nil { return errors.Wrap(50000, "create mini program", err) } return nil } // Update 按 ID 更新小程序非零值字段(cols 为待更新列)。 func (r *Repository) Update(ctx context.Context, id uint64, cols map[string]interface{}) error { res := r.db.WithContext(ctx). Model(&model.MiniProgram{}). Where("id = ?", id). Updates(cols) if res.Error != nil { return errors.Wrap(50000, "update mini program", res.Error) } if res.RowsAffected == 0 { slog.Warn("update mini program affected 0 rows", slog.Uint64("id", id), slog.Any("cols", cols)) return errors.ErrNotFound } slog.Info("update mini program", slog.Uint64("id", id), slog.Int64("rows_affected", res.RowsAffected), slog.Any("cols", cols)) return nil } // GetByID 按 ID 查询小程序,软删除记录不可见。 func (r *Repository) GetByID(ctx context.Context, id uint64) (*model.MiniProgram, error) { var m model.MiniProgram if err := r.db.WithContext(ctx).Where("id = ?", id).First(&m).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.ErrNotFound } return nil, errors.Wrap(50000, "query mini program", err) } return &m, nil } // GetByAppID 按 AppID 查询(含软删除态),不存在返回 ErrNotFound。 // 用于唯一性预校验与按 AppID 取配置。 func (r *Repository) GetByAppID(ctx context.Context, appID string) (*model.MiniProgram, error) { var m model.MiniProgram if err := r.db.WithContext(ctx).Unscoped().Where("app_id = ?", appID).First(&m).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.ErrNotFound } return nil, errors.Wrap(50000, "query mini program by app_id", err) } return &m, nil } // GetActiveByAppID 按 AppID 查询“已启用且未删除”的小程序, // 用于登录鉴权与 code2session 取凭证;不存在/已停用/已删除均返回 ErrNotFound。 func (r *Repository) GetActiveByAppID(ctx context.Context, appID string) (*model.MiniProgram, error) { var m model.MiniProgram if err := r.db.WithContext(ctx). Where("app_id = ? AND status = ?", appID, model.MiniProgramStatusEnabled). First(&m).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.ErrNotFound } return nil, errors.Wrap(50000, "query active mini program", err) } return &m, nil } // List 分页查询,status < 0 表示不过滤。 func (r *Repository) List(ctx context.Context, status int8, page, size int) ([]model.MiniProgram, int64, error) { q := r.db.WithContext(ctx).Model(&model.MiniProgram{}) if status >= 0 { q = q.Where("status = ?", status) } var total int64 if err := q.Count(&total).Error; err != nil { return nil, 0, errors.Wrap(50000, "count mini program", err) } var list []model.MiniProgram if err := q.Order("id DESC").Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil { return nil, 0, errors.Wrap(50000, "list mini program", err) } return list, total, nil } // Delete 软删除小程序。 func (r *Repository) Delete(ctx context.Context, id uint64) error { res := r.db.WithContext(ctx).Where("id = ?", id).Delete(&model.MiniProgram{}) if res.Error != nil { return errors.Wrap(50000, "delete mini program", res.Error) } if res.RowsAffected == 0 { return errors.ErrNotFound } return nil }