Yimaru-BackEnd/internal/services/examprep/service.go

408 lines
12 KiB
Go

package examprep
import (
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/ports"
"context"
"errors"
"github.com/jackc/pgx/v5"
)
var ErrCatalogCourseNotFound = errors.New("exam prep catalog course not found")
var ErrUnitNotFound = errors.New("exam prep unit not found")
var ErrModuleNotFound = errors.New("exam prep module not found")
var ErrLessonNotFound = errors.New("exam prep lesson not found")
var ErrPracticeNotFound = errors.New("exam prep practice not found")
// examPrepStore is implemented by *repository.Store (catalog courses, units, modules, lessons, practices).
type examPrepStore interface {
ports.ExamPrepCatalogCourseStore
ports.ExamPrepUnitStore
ports.ExamPrepModuleStore
ports.ExamPrepLessonStore
ports.ExamPrepPracticeStore
}
type Service struct {
store examPrepStore
}
func NewService(store examPrepStore) *Service {
return &Service{store: store}
}
func (s *Service) CreateCatalogCourse(ctx context.Context, input domain.CreateExamPrepCatalogCourseInput) (domain.ExamPrepCatalogCourse, error) {
return s.store.CreateExamPrepCatalogCourse(ctx, input)
}
func (s *Service) GetCatalogCourseByID(ctx context.Context, id int64) (domain.ExamPrepCatalogCourse, error) {
c, err := s.store.GetExamPrepCatalogCourseByID(ctx, id)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepCatalogCourse{}, ErrCatalogCourseNotFound
}
return domain.ExamPrepCatalogCourse{}, err
}
return c, nil
}
func (s *Service) ListCatalogCourses(ctx context.Context, limit, offset int32) ([]domain.ExamPrepCatalogCourse, int64, error) {
if limit <= 0 {
limit = 20
}
if limit > 200 {
limit = 200
}
if offset < 0 {
offset = 0
}
return s.store.ListExamPrepCatalogCourses(ctx, limit, offset)
}
func (s *Service) UpdateCatalogCourse(ctx context.Context, id int64, input domain.UpdateExamPrepCatalogCourseInput) (domain.ExamPrepCatalogCourse, error) {
c, err := s.store.UpdateExamPrepCatalogCourse(ctx, id, input)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepCatalogCourse{}, ErrCatalogCourseNotFound
}
return domain.ExamPrepCatalogCourse{}, err
}
return c, nil
}
func (s *Service) DeleteCatalogCourse(ctx context.Context, id int64) error {
if _, err := s.store.GetExamPrepCatalogCourseByID(ctx, id); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrCatalogCourseNotFound
}
return err
}
return s.store.DeleteExamPrepCatalogCourse(ctx, id)
}
func (s *Service) ReorderCatalogCourses(ctx context.Context, ordered []int64) error {
expected, err := s.store.ListAllExamPrepCatalogCourseIDs(ctx)
if err != nil {
return err
}
if err := domain.ValidateReorderPermutation(ordered, expected); err != nil {
return err
}
if len(ordered) == 0 {
return nil
}
return s.store.ReorderExamPrepCatalogCourses(ctx, ordered)
}
func (s *Service) ensureCatalogCourse(ctx context.Context, catalogCourseID int64) error {
if _, err := s.store.GetExamPrepCatalogCourseByID(ctx, catalogCourseID); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrCatalogCourseNotFound
}
return err
}
return nil
}
func (s *Service) CreateUnit(ctx context.Context, catalogCourseID int64, input domain.CreateExamPrepUnitInput) (domain.ExamPrepUnit, error) {
if err := s.ensureCatalogCourse(ctx, catalogCourseID); err != nil {
return domain.ExamPrepUnit{}, err
}
return s.store.CreateExamPrepUnit(ctx, catalogCourseID, input)
}
func (s *Service) ListUnitsByCatalogCourse(ctx context.Context, catalogCourseID int64, limit, offset int32) ([]domain.ExamPrepUnit, int64, error) {
if err := s.ensureCatalogCourse(ctx, catalogCourseID); err != nil {
return nil, 0, err
}
if limit <= 0 {
limit = 20
}
if limit > 200 {
limit = 200
}
if offset < 0 {
offset = 0
}
return s.store.ListExamPrepUnitsByCatalogCourse(ctx, catalogCourseID, limit, offset)
}
func (s *Service) GetUnitByID(ctx context.Context, id int64) (domain.ExamPrepUnit, error) {
u, err := s.store.GetExamPrepUnitByID(ctx, id)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepUnit{}, ErrUnitNotFound
}
return domain.ExamPrepUnit{}, err
}
return u, nil
}
func (s *Service) UpdateUnit(ctx context.Context, id int64, input domain.UpdateExamPrepUnitInput) (domain.ExamPrepUnit, error) {
u, err := s.store.UpdateExamPrepUnit(ctx, id, input)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepUnit{}, ErrUnitNotFound
}
return domain.ExamPrepUnit{}, err
}
return u, nil
}
func (s *Service) DeleteUnit(ctx context.Context, id int64) error {
if _, err := s.store.GetExamPrepUnitByID(ctx, id); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrUnitNotFound
}
return err
}
return s.store.DeleteExamPrepUnit(ctx, id)
}
func (s *Service) ReorderUnitsInCatalogCourse(ctx context.Context, catalogCourseID int64, ordered []int64) error {
if err := s.ensureCatalogCourse(ctx, catalogCourseID); err != nil {
return err
}
expected, err := s.store.ListExamPrepUnitIDsByCatalogCourse(ctx, catalogCourseID)
if err != nil {
return err
}
if err := domain.ValidateReorderPermutation(ordered, expected); err != nil {
return err
}
if len(ordered) == 0 {
return nil
}
return s.store.ReorderExamPrepUnitsInCatalogCourse(ctx, catalogCourseID, ordered)
}
func (s *Service) ensureUnit(ctx context.Context, unitID int64) error {
if _, err := s.store.GetExamPrepUnitByID(ctx, unitID); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrUnitNotFound
}
return err
}
return nil
}
func (s *Service) CreateModule(ctx context.Context, unitID int64, input domain.CreateExamPrepModuleInput) (domain.ExamPrepModule, error) {
if err := s.ensureUnit(ctx, unitID); err != nil {
return domain.ExamPrepModule{}, err
}
return s.store.CreateExamPrepUnitModule(ctx, unitID, input)
}
func (s *Service) ListModulesByUnit(ctx context.Context, unitID int64, limit, offset int32) ([]domain.ExamPrepModule, int64, error) {
if err := s.ensureUnit(ctx, unitID); err != nil {
return nil, 0, err
}
if limit <= 0 {
limit = 20
}
if limit > 200 {
limit = 200
}
if offset < 0 {
offset = 0
}
return s.store.ListExamPrepUnitModulesByUnit(ctx, unitID, limit, offset)
}
func (s *Service) GetModuleByID(ctx context.Context, id int64) (domain.ExamPrepModule, error) {
m, err := s.store.GetExamPrepUnitModuleByID(ctx, id)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepModule{}, ErrModuleNotFound
}
return domain.ExamPrepModule{}, err
}
return m, nil
}
func (s *Service) UpdateModule(ctx context.Context, id int64, input domain.UpdateExamPrepModuleInput) (domain.ExamPrepModule, error) {
m, err := s.store.UpdateExamPrepUnitModule(ctx, id, input)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepModule{}, ErrModuleNotFound
}
return domain.ExamPrepModule{}, err
}
return m, nil
}
func (s *Service) DeleteModule(ctx context.Context, id int64) error {
if _, err := s.store.GetExamPrepUnitModuleByID(ctx, id); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrModuleNotFound
}
return err
}
return s.store.DeleteExamPrepUnitModule(ctx, id)
}
func (s *Service) ReorderModulesInUnit(ctx context.Context, unitID int64, ordered []int64) error {
if err := s.ensureUnit(ctx, unitID); err != nil {
return err
}
expected, err := s.store.ListExamPrepUnitModuleIDsByUnit(ctx, unitID)
if err != nil {
return err
}
if err := domain.ValidateReorderPermutation(ordered, expected); err != nil {
return err
}
if len(ordered) == 0 {
return nil
}
return s.store.ReorderExamPrepUnitModulesInUnit(ctx, unitID, ordered)
}
func (s *Service) ensureModule(ctx context.Context, unitModuleID int64) error {
if _, err := s.store.GetExamPrepUnitModuleByID(ctx, unitModuleID); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrModuleNotFound
}
return err
}
return nil
}
func (s *Service) CreateLesson(ctx context.Context, unitModuleID int64, input domain.CreateExamPrepLessonInput) (domain.ExamPrepLesson, error) {
if err := s.ensureModule(ctx, unitModuleID); err != nil {
return domain.ExamPrepLesson{}, err
}
return s.store.CreateExamPrepUnitModuleLesson(ctx, unitModuleID, input)
}
func (s *Service) ListLessonsByUnitModule(ctx context.Context, unitModuleID int64, limit, offset int32) ([]domain.ExamPrepLesson, int64, error) {
if err := s.ensureModule(ctx, unitModuleID); err != nil {
return nil, 0, err
}
if limit <= 0 {
limit = 20
}
if limit > 200 {
limit = 200
}
if offset < 0 {
offset = 0
}
return s.store.ListExamPrepUnitModuleLessonsByUnitModuleID(ctx, unitModuleID, limit, offset)
}
func (s *Service) GetLessonByID(ctx context.Context, id int64) (domain.ExamPrepLesson, error) {
l, err := s.store.GetExamPrepUnitModuleLessonByID(ctx, id)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepLesson{}, ErrLessonNotFound
}
return domain.ExamPrepLesson{}, err
}
return l, nil
}
func (s *Service) UpdateLesson(ctx context.Context, id int64, input domain.UpdateExamPrepLessonInput) (domain.ExamPrepLesson, error) {
l, err := s.store.UpdateExamPrepUnitModuleLesson(ctx, id, input)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepLesson{}, ErrLessonNotFound
}
return domain.ExamPrepLesson{}, err
}
return l, nil
}
func (s *Service) DeleteLesson(ctx context.Context, id int64) error {
if _, err := s.store.GetExamPrepUnitModuleLessonByID(ctx, id); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrLessonNotFound
}
return err
}
return s.store.DeleteExamPrepUnitModuleLesson(ctx, id)
}
func (s *Service) ReorderLessonsInUnitModule(ctx context.Context, unitModuleID int64, ordered []int64) error {
if err := s.ensureModule(ctx, unitModuleID); err != nil {
return err
}
expected, err := s.store.ListExamPrepUnitModuleLessonIDsByUnitModule(ctx, unitModuleID)
if err != nil {
return err
}
if err := domain.ValidateReorderPermutation(ordered, expected); err != nil {
return err
}
if len(ordered) == 0 {
return nil
}
return s.store.ReorderExamPrepUnitModuleLessonsInUnitModule(ctx, unitModuleID, ordered)
}
func (s *Service) ensureLesson(ctx context.Context, lessonID int64) error {
if _, err := s.store.GetExamPrepUnitModuleLessonByID(ctx, lessonID); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrLessonNotFound
}
return err
}
return nil
}
func (s *Service) CreateExamPrepPractice(ctx context.Context, lessonID int64, input domain.CreateExamPrepPracticeInput) (domain.ExamPrepPractice, error) {
if err := s.ensureLesson(ctx, lessonID); err != nil {
return domain.ExamPrepPractice{}, err
}
return s.store.CreateExamPrepLessonPractice(ctx, lessonID, input)
}
func (s *Service) ListExamPrepPracticesByLesson(ctx context.Context, lessonID int64, limit, offset int32) ([]domain.ExamPrepPractice, int64, error) {
if err := s.ensureLesson(ctx, lessonID); err != nil {
return nil, 0, err
}
if limit <= 0 {
limit = 20
}
if limit > 200 {
limit = 200
}
if offset < 0 {
offset = 0
}
return s.store.ListExamPrepLessonPracticesByLessonID(ctx, lessonID, limit, offset)
}
func (s *Service) GetExamPrepPracticeByID(ctx context.Context, id int64) (domain.ExamPrepPractice, error) {
p, err := s.store.GetExamPrepLessonPracticeByID(ctx, id)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepPractice{}, ErrPracticeNotFound
}
return domain.ExamPrepPractice{}, err
}
return p, nil
}
func (s *Service) UpdateExamPrepPractice(ctx context.Context, id int64, input domain.UpdateExamPrepPracticeInput) (domain.ExamPrepPractice, error) {
p, err := s.store.UpdateExamPrepLessonPractice(ctx, id, input)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.ExamPrepPractice{}, ErrPracticeNotFound
}
return domain.ExamPrepPractice{}, err
}
return p, nil
}
func (s *Service) DeleteExamPrepPractice(ctx context.Context, id int64) error {
if _, err := s.store.GetExamPrepLessonPracticeByID(ctx, id); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrPracticeNotFound
}
return err
}
return s.store.DeleteExamPrepLessonPractice(ctx, id)
}