diff --git a/Projects/DataSource/Sources/DTO/RoutineCreationDTO.swift b/Projects/DataSource/Sources/DTO/RoutineCreationDTO.swift index a499368f..84f0fd9b 100644 --- a/Projects/DataSource/Sources/DTO/RoutineCreationDTO.swift +++ b/Projects/DataSource/Sources/DTO/RoutineCreationDTO.swift @@ -5,9 +5,34 @@ // Created by 이동현 on 8/3/25. // +import Domain + struct RoutineCreationDTO: Codable { + let routineId: String? + let updateApplyDate: String? let routineName: String let repeatDay: [String] + let routineStartDate: String + let routineEndDate: String let executionTime: String let subRoutineName: [String] + let recommendedRoutineType: String? +} + +extension RoutineCreationDTO { + func toRoutineCreationEntity() -> RoutineCreationEntity { + let weeks = repeatDay.compactMap { Week(rawValue: $0) } + let routineCategoryType = RoutineCategoryType(rawValue: recommendedRoutineType ?? "") + + return RoutineCreationEntity( + id: routineId, + name: routineName, + repeatDay: weeks, + startDate: routineStartDate, + endDate: routineEndDate, + executionTime: executionTime, + subroutines: subRoutineName, + recommendedRoutineType: routineCategoryType, + applyDateType: nil) + } } diff --git a/Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift b/Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift index c61777b4..e758bc63 100644 --- a/Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift +++ b/Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift @@ -9,7 +9,7 @@ enum RoutineEndpoint { case createRoutine(routine: RoutineCreationDTO) case fetchRoutine(routineId: String) case fetchRoutines(startDate: String, endDate: String) - case updateRoutine(routine: RoutineUpdateDTO) + case updateRoutine(routine: RoutineCreationDTO) case deleteAllRoutine(routineId: String) case deleteDailyRoutine(routine: DeleteRoutineDTO) case updateRoutineCompletion(routines: RoutineCompletionListDTO) @@ -17,7 +17,12 @@ enum RoutineEndpoint { extension RoutineEndpoint: Endpoint { var baseURL: String { - return AppProperties.baseURL + "/api/v1/routines" + switch self { + case .createRoutine, .updateRoutine: + return AppProperties.baseURL + "/api/v2/routines" + default: + return AppProperties.baseURL + "/api/v1/routines" + } } var path: String { diff --git a/Projects/DataSource/Sources/Repository/RoutineRepository.swift b/Projects/DataSource/Sources/Repository/RoutineRepository.swift index 713ddbb4..d7ba2bc6 100644 --- a/Projects/DataSource/Sources/Repository/RoutineRepository.swift +++ b/Projects/DataSource/Sources/Repository/RoutineRepository.swift @@ -10,14 +10,18 @@ import Domain final class RoutineRepository: RoutineRepositoryProtocol { private let networkService = NetworkService.shared - func createRoutine(routineSummary: RoutineSummaryEntity, subRoutineSummaries: [SubRoutineSummaryEntity]) async throws { - let subRoutineNames = subRoutineSummaries.compactMap { $0.subRoutineName } - + func createRoutine(routine: RoutineCreationEntity) async throws { let routineCreationDTO = RoutineCreationDTO( - routineName: routineSummary.routineName, - repeatDay: routineSummary.repeatDay.map { $0.rawValue }, - executionTime: routineSummary.executionTime, - subRoutineName: subRoutineNames) + routineId: nil, + updateApplyDate: routine.applyDateType?.rawValue, + routineName: routine.name, + repeatDay: routine.repeatDay.map { $0.rawValue }, + routineStartDate: routine.startDate, + routineEndDate: routine.endDate, + executionTime: routine.executionTime, + subRoutineName: routine.subroutines, + recommendedRoutineType: routine.recommendedRoutineType?.rawValue) + let endpoint = RoutineEndpoint.createRoutine(routine: routineCreationDTO) _ = try await networkService.request(endpoint: endpoint, type: EmptyResponseDTO.self) @@ -42,22 +46,17 @@ final class RoutineRepository: RoutineRepositoryProtocol { return result } - func updateRoutine(routineSummary: RoutineSummaryEntity, subRoutineSummaries: [SubRoutineSummaryEntity]) async throws { - guard let routineId = routineSummary.routineId else { return } - - let subRoutineDTO = subRoutineSummaries.map { - SubRoutineUpdateDTO( - subRoutineId: $0.subRoutineId, - subRoutineName: $0.subRoutineName, - sortOrder: $0.sortOrder) - } - - let routineUpdateDTO = RoutineUpdateDTO( - routineId: routineId, - routineName: routineSummary.routineName, - repeatDay: routineSummary.repeatDay.map { $0.rawValue }, - executionTime: routineSummary.executionTime, - subRoutineInfos: subRoutineDTO) + func updateRoutine(routine: RoutineCreationEntity) async throws { + let routineUpdateDTO = RoutineCreationDTO( + routineId: routine.id, + updateApplyDate: routine.applyDateType?.rawValue, + routineName: routine.name, + repeatDay: routine.repeatDay.map { $0.rawValue }, + routineStartDate: routine.startDate, + routineEndDate: routine.endDate, + executionTime: routine.executionTime, + subRoutineName: routine.subroutines, + recommendedRoutineType: routine.recommendedRoutineType?.rawValue) let endpoint = RoutineEndpoint.updateRoutine(routine: routineUpdateDTO) _ = try await networkService.request(endpoint: endpoint, type: EmptyResponseDTO.self) diff --git a/Projects/Domain/Sources/Entity/Enum/RoutineUpdateApplyDateType.swift b/Projects/Domain/Sources/Entity/Enum/RoutineUpdateApplyDateType.swift new file mode 100644 index 00000000..02f6ef69 --- /dev/null +++ b/Projects/Domain/Sources/Entity/Enum/RoutineUpdateApplyDateType.swift @@ -0,0 +1,12 @@ +// +// RoutineUpdateApplyDateType.swift +// Domain +// +// Created by 이동현 on 8/18/25. +// + + +public enum RoutineUpdateApplyDateType: String { + case today = "TODAY" + case tomorrow = "TOMORROW" +} diff --git a/Projects/Domain/Sources/Entity/RoutineCreationEntity.swift b/Projects/Domain/Sources/Entity/RoutineCreationEntity.swift new file mode 100644 index 00000000..ca42f421 --- /dev/null +++ b/Projects/Domain/Sources/Entity/RoutineCreationEntity.swift @@ -0,0 +1,42 @@ +// +// RoutineCreationEntity.swift +// Domain +// +// Created by 이동현 on 8/18/25. +// +import Foundation + + +public struct RoutineCreationEntity { + public let id: String? + public let name: String + public let repeatDay: [Week] + public let startDate: String + public let endDate: String + public let executionTime: String + public let subroutines: [String] + public let recommendedRoutineType: RoutineCategoryType? + public let applyDateType: RoutineUpdateApplyDateType? + + public init( + id: String?, + name: String, + repeatDay: [Week], + startDate: String, + endDate: String, + executionTime: String, + subroutines: [String], + recommendedRoutineType: RoutineCategoryType?, + applyDateType: RoutineUpdateApplyDateType? + ) { + self.id = id + self.name = name + self.repeatDay = repeatDay + self.startDate = startDate + self.endDate = endDate + self.executionTime = executionTime + self.subroutines = subroutines + self.recommendedRoutineType = recommendedRoutineType + self.applyDateType = applyDateType + } +} diff --git a/Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift b/Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift index 1ad364b7..cba984d8 100644 --- a/Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift +++ b/Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift @@ -9,9 +9,8 @@ public protocol RoutineRepositoryProtocol { /// 루틴을 생성합니다. /// - Parameters: - /// - routineSummary: 루틴 요약 정보 - /// - subRoutineSummaries: 서브 루틴 요약 정보 배열 - func createRoutine(routineSummary: RoutineSummaryEntity, subRoutineSummaries: [SubRoutineSummaryEntity]) async throws + /// - routine: 생성할 루틴 엔티티 + func createRoutine(routine: RoutineCreationEntity) async throws /// 루틴을 조회합니다. /// - Parameter routineId: 조회할 루틴 id @@ -26,9 +25,8 @@ public protocol RoutineRepositoryProtocol { /// 루틴을 수정합니다. /// - Parameters: - /// - routineSummary: 루틴 요약 정보 - /// - subRoutineSummaries: 서브 루틴 요약 정보 배열 - func updateRoutine(routineSummary: RoutineSummaryEntity, subRoutineSummaries: [SubRoutineSummaryEntity]) async throws + /// - routine: 수정할 루틴 + func updateRoutine(routine: RoutineCreationEntity) async throws /// 반복되는 루틴을 모두 제거합니다. /// - Parameter routineId: 삭제할 루틴 id diff --git a/Projects/Domain/Sources/Protocol/UseCase/RoutineUseCaseProtocol.swift b/Projects/Domain/Sources/Protocol/UseCase/RoutineUseCaseProtocol.swift index d87afe9b..0ef60c8b 100644 --- a/Projects/Domain/Sources/Protocol/UseCase/RoutineUseCaseProtocol.swift +++ b/Projects/Domain/Sources/Protocol/UseCase/RoutineUseCaseProtocol.swift @@ -12,11 +12,7 @@ public protocol RoutineUseCaseProtocol { func fetchRoutines(startDate: Date, endDate: Date) async throws -> [String: [RoutineEntity]] - func saveRoutine( - routineSummary: RoutineSummaryEntity, - subRoutineSummaries: [SubRoutineSummaryEntity], - deletedSubRoutineSummaries: [SubRoutineSummaryEntity] - ) async throws + func saveRoutine(routine: RoutineCreationEntity) async throws func deleteAllRoutine(routineId: String) async throws diff --git a/Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift b/Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift index b2a7d8bd..13c21479 100644 --- a/Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift +++ b/Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift @@ -28,47 +28,31 @@ public final class RoutineUseCase: RoutineUseCaseProtocol { return routineEntities } - public func saveRoutine( - routineSummary: RoutineSummaryEntity, - subRoutineSummaries: [SubRoutineSummaryEntity], - deletedSubRoutineSummaries: [SubRoutineSummaryEntity] - ) async throws { - if routineSummary.routineId == nil { // 루틴 아이디가 있으면 수정, 없으면 생성 - try await createRoutine(routineSummary: routineSummary, subRoutinesSummaries: subRoutineSummaries) + public func saveRoutine(routine: RoutineCreationEntity) async throws { + let routine = RoutineCreationEntity( + id: routine.id, + name: routine.name, + repeatDay: routine.repeatDay, + startDate: routine.startDate, + endDate: routine.endDate, + executionTime: routine.executionTime, + subroutines: routine.subroutines.filter { !$0.isEmpty }, + recommendedRoutineType: routine.recommendedRoutineType, + applyDateType: routine.applyDateType) + + if routine.id == nil { // 루틴 아이디가 있으면 수정, 없으면 생성 + try await createRoutine(routine: routine) } else { - try await updateRoutine( - routineSummary: routineSummary, - subRoutineSummaries: subRoutineSummaries, - deletedSubRoutineSummaries: deletedSubRoutineSummaries) + try await updateRoutine(routine: routine) } } - private func createRoutine(routineSummary: RoutineSummaryEntity, subRoutinesSummaries: [SubRoutineSummaryEntity]) async throws { - try await routineRepository.createRoutine(routineSummary: routineSummary, subRoutineSummaries: subRoutinesSummaries) + private func createRoutine(routine: RoutineCreationEntity) async throws { + try await routineRepository.createRoutine(routine: routine) } - private func updateRoutine( - routineSummary: RoutineSummaryEntity, - subRoutineSummaries: [SubRoutineSummaryEntity], - deletedSubRoutineSummaries: [SubRoutineSummaryEntity] - ) async throws { - let updatedSubRoutines = subRoutineSummaries - .enumerated() - .map { - SubRoutineSummaryEntity( - subRoutineId: $1.subRoutineId, - subRoutineName: $1.subRoutineName, - sortOrder: $0 + 1) - } - let deletedSubRoutines = deletedSubRoutineSummaries.map { - SubRoutineSummaryEntity( - subRoutineId: $0.subRoutineId, - subRoutineName: nil, - sortOrder: nil) - } - let finalSubRoutines = updatedSubRoutines + deletedSubRoutines - - try await routineRepository.updateRoutine(routineSummary: routineSummary, subRoutineSummaries: finalSubRoutines) + private func updateRoutine(routine: RoutineCreationEntity) async throws { + try await routineRepository.updateRoutine(routine: routine) } public func deleteAllRoutine(routineId: String) async throws { diff --git a/Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineCreationCardView.swift b/Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineCreationCardView.swift index 9306cc7e..5ae19552 100644 --- a/Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineCreationCardView.swift +++ b/Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineCreationCardView.swift @@ -203,8 +203,8 @@ final class RoutineCreationCardView() - init(viewModel: RoutineCreationViewModel, routineId: String? = nil) { - navigationTitle = routineId == nil ? "루틴 등록" : "루틴 수정" - registerButtonTitle = routineId == nil ? "등록하기" : "수정하기" + init( + viewModel: RoutineCreationViewModel, + updateInfo: (routineId: String, updateType: RoutineUpdateApplyDateType)? = nil + ) { + navigationTitle = updateInfo?.routineId == nil ? "루틴 등록" : "루틴 수정" + registerButtonTitle = updateInfo?.routineId == nil ? "등록하기" : "수정하기" super.init(viewModel: viewModel) - if let routineId { - viewModel.action(input: .fetchRoutine(id: routineId)) + + registerButton.setTitle(registerButtonTitle, for: .normal) + if let updateInfo { + viewModel.action(input: .fetchRoutine(id: updateInfo.routineId)) + viewModel.action(input: .configureUpdateType(updateType: updateInfo.updateType)) } } @@ -84,6 +90,8 @@ final class RoutineCreationViewController: BaseViewController let subRoutinesPublisher: AnyPublisher<[String], Never> let repeatTypePublisher: AnyPublisher - let periodPublisher: AnyPublisher<(Date, Date), Never> + let periodPublisher: AnyPublisher<(Date?, Date?), Never> let executionTimePublisher: AnyPublisher let isRoutineValid: AnyPublisher } @@ -47,14 +48,16 @@ final class RoutineCreationViewModel: ViewModel { private let nameSubject = CurrentValueSubject("") private let subRoutinesSubject = CurrentValueSubject<[String], Never>([]) private let repeatTypeSubject = CurrentValueSubject(nil) - private let periodStartSubject = CurrentValueSubject(Date()) - private let periodEndSubject = CurrentValueSubject(Date()) + private let periodStartSubject = CurrentValueSubject(nil) + private let periodEndSubject = CurrentValueSubject(nil) private let executionTimeSubject = CurrentValueSubject(.init(startAt: nil)) private let checkRoutinePublisher = PassthroughSubject() private let routineUseCase: RoutineUseCaseProtocol private let recommenededRoutineUseCase: RecommendedRoutineUseCaseProtocol private var deletedSubroutines = Set() private var routineId: String? + private var routineType: RoutineCategoryType? + private var updateType: RoutineUpdateApplyDateType? init(routineUseCase: RoutineUseCaseProtocol, recommenededRoutineUseCase: RecommendedRoutineUseCaseProtocol) { self.routineUseCase = routineUseCase @@ -76,6 +79,8 @@ final class RoutineCreationViewModel: ViewModel { func action(input: Input) { switch input { + case .configureUpdateType(let updateType): + self.updateType = updateType case .fetchRoutine(let id): fetchRoutine(id: id) case .fetchRecommendedRoutine(let id): @@ -129,6 +134,8 @@ final class RoutineCreationViewModel: ViewModel { let time = Date.convertToDate(from: routine.executionTime, dateType: .amPmTimeShort) executionType = .init(startAt: time ?? Date()) + // TODO: - routine 엔티티 변경 이후 시작일자, 종료 일자 설정 필요 + 추천 타입 있으면 추천 타입도 설정 필요 + nameSubject.send(routine.routineName) subRoutinesSubject.send(subRoutines) repeatTypeSubject.send(repeatType) @@ -151,6 +158,7 @@ final class RoutineCreationViewModel: ViewModel { nameSubject.send(routine.title) subRoutinesSubject.send(subRoutines) + routineType = routine.type updateIsRoutineValid() } catch { @@ -217,7 +225,9 @@ final class RoutineCreationViewModel: ViewModel { guard let name = nameSubject.value, !name.isEmpty, - executionTimeSubject.value.startAt != nil + executionTimeSubject.value.startAt != nil, + periodStartSubject.value != nil, + periodEndSubject.value != nil else { checkRoutinePublisher.send(false) return @@ -229,32 +239,39 @@ final class RoutineCreationViewModel: ViewModel { private func registerRoutine() { Task { do { - - let repeatDay: [String] + guard + let name = nameSubject.value, + let startDate = periodStartSubject.value, + let endDate = periodEndSubject.value, + let executionTime = executionTimeSubject.value.startAt + else { return } + + let repeatDay: [Week] + let startDateString = startDate.convertToString(dateType: .yearMonthDate) + let endDateString = endDate.convertToString(dateType: .yearMonthDate) + let executionTimeString = executionTime.convertToString(dateType: .time) switch repeatTypeSubject.value { case .daily: - repeatDay = Week.allCases.map { $0.rawValue } + repeatDay = Week.allCases case .weekly(let weeks): - repeatDay = weeks.map { $0.rawValue } + repeatDay = weeks.sorted { $0.id < $1.id } case .none: repeatDay = [] } - guard let startAt = executionTimeSubject.value.startAt else { return } - - let executionTime = startAt.convertToString(dateType: .time) - - let routineSummary = RoutineSummaryEntity( - routineId: routineId, - routineName: nameSubject.value ?? "", + let routine = RoutineCreationEntity( + id: routineId, + name: name, repeatDay: repeatDay, - executionTime: executionTime) - - try await routineUseCase.saveRoutine( - routineSummary: routineSummary, - subRoutineSummaries: [], // TODO: - 수정 필요 - deletedSubRoutineSummaries: Array(deletedSubroutines)) + startDate: startDateString, + endDate: endDateString, + executionTime: executionTimeString, + subroutines: subRoutinesSubject.value, + recommendedRoutineType: routineType, + applyDateType: updateType) + + try await routineUseCase.saveRoutine(routine: routine) } catch { }