Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package flowfit.domain.ptrelation.domain.repository;

import flowfit.domain.ptrelation.domain.entity.prepare.PreparePt;
import flowfit.domain.ptrelation.domain.entity.ptrelation.PtRelation;
import flowfit.domain.user.domain.entity.member.Member;
import flowfit.domain.user.domain.entity.trainer.Trainer;
Expand All @@ -22,4 +21,5 @@ public interface PtRelationRepository extends JpaRepository<PtRelation, Long> {
List<PtRelation> findAllByTrainer(Trainer trainer);

Optional<PtRelation> findByIdAndTrainer(Long relationId, Trainer trainer);
List<PtRelation> findAllByTrainer_IdAndMember_NameContaining(String trainerId, String name);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package flowfit.domain.ptsession.application.service;

import flowfit.domain.ptsession.presentation.dto.req.ScheduleRequest;
import flowfit.domain.ptsession.presentation.dto.res.CalendarMonthResponse;
import flowfit.domain.ptsession.presentation.dto.res.ScheduleResponse;

import java.time.LocalDate;
import java.util.List;

public interface ScheduleService {

Long createSession(ScheduleRequest request);

void updateSession(Long sessionId, ScheduleRequest request);

void deleteSession(Long sessionId, String reason, boolean charge);

CalendarMonthResponse getMonthlySummary(int year, int month);

List<ScheduleResponse> getSessionsByDate(LocalDate date);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package flowfit.domain.ptsession.application.service.impl;

import flowfit.domain.ptrelation.domain.entity.ptrelation.PtRelation;
import flowfit.domain.ptrelation.domain.repository.PtRelationRepository;
import flowfit.domain.ptsession.application.service.ScheduleService;
import flowfit.domain.ptsession.domain.entity.PtSession;
import flowfit.domain.ptsession.domain.entity.Status;
import flowfit.domain.ptsession.domain.repository.PtSessionRepository;
import flowfit.domain.ptsession.presentation.dto.req.ScheduleRequest;
import flowfit.domain.ptsession.presentation.dto.res.CalendarMonthResponse;
import flowfit.domain.ptsession.presentation.dto.res.ScheduleResponse;
import flowfit.domain.user.infra.exception.PtSessionNotFoundException;
import flowfit.domain.user.infra.exception.PtRelationNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import static java.util.stream.Collectors.toList;

@Service
@RequiredArgsConstructor
public class ScheduleServiceImpl implements ScheduleService {

private final PtSessionRepository sessionRepository;
private final PtRelationRepository relationRepository;

@Override
public Long createSession(ScheduleRequest req) {
PtRelation relation = relationRepository.findById(req.getRelationId())
.orElseThrow(PtRelationNotFoundException::new);

LocalDateTime start = LocalDateTime.parse(req.getDate() + "T" + req.getStartTime());
LocalDateTime end = LocalDateTime.parse(req.getDate() + "T" + req.getEndTime());

PtSession session = PtSession.builder()
.ptRelation(relation)
.startTime(start)
.endTime(end)
.status(Status.PROCESS)
.message(req.getMessage())
.build();

return sessionRepository.save(session).getId();
}

@Override
public void updateSession(Long sessionId, ScheduleRequest req) {
PtSession session = sessionRepository.findById(sessionId)
.orElseThrow(PtSessionNotFoundException::new);

session.updateStartTime(LocalDateTime.parse(req.getDate() + "T" + req.getStartTime()));
session.updateEndTime(LocalDateTime.parse(req.getDate() + "T" + req.getEndTime()));
session.updateMessage(req.getMessage());
}

@Override
public void deleteSession(Long sessionId, String reason, boolean charge) {
PtSession session = sessionRepository.findById(sessionId)
.orElseThrow(PtSessionNotFoundException::new);
session.updateStatus(Status.CANCEL_T); // TODO: 사유 분기처리
}

@Override
public List<ScheduleResponse> getSessionsByDate(LocalDate date) {
LocalDateTime startOfDay = date.atStartOfDay();
LocalDateTime endOfDay = date.atTime(23, 59);

String trainerId = getCurrentTrainerId();

return sessionRepository
.findAllByPtRelation_Trainer_IdAndStartTimeBetween(trainerId, startOfDay, endOfDay)
.stream()
.map(ScheduleResponse::from)
.collect(toList());
}

private String getCurrentTrainerId() {
return SecurityContextHolder.getContext().getAuthentication().getName();
}

@Override
public CalendarMonthResponse getMonthlySummary(int year, int month) {
String trainerId = getCurrentTrainerId();
LocalDate start = LocalDate.of(year, month, 1);
LocalDate end = start.withDayOfMonth(start.lengthOfMonth());

List<PtSession> sessions = sessionRepository
.findAllByPtRelation_Trainer_IdAndStartTimeBetween(trainerId, start.atStartOfDay(), end.atTime(23, 59));

// 일 수입을 구하고 더하는 식으로 월 수입 도출
Map<LocalDate, Long> dailyIncome = new TreeMap<>();
long total = 0L;

for (PtSession s : sessions) {
if (s.getStatus() == Status.PROCESS || s.getStatus() == Status.COMPLETED) {
LocalDate day = s.getStartTime().toLocalDate();
long income = s.getPtRelation().getTotalMoney() / s.getPtRelation().getSession();
dailyIncome.put(day, dailyIncome.getOrDefault(day, 0L) + income);
total += income;
}
}

return CalendarMonthResponse.of(total, dailyIncome);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ public class PtSession {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;


@Column(nullable = false)
private LocalDateTime startTime;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

Expand All @@ -31,4 +32,10 @@ List<PtSession> findByPtRelationAndDateAll(@Param("ptRelation") PtRelation ptRel
List<PtSession> findAllByPtRelation(PtRelation ptRelation);

List<PtSession> findByPtRelationAndStatus(PtRelation ptRelation, Status status);

List<PtSession> findAllByPtRelation_Trainer_IdAndStartTimeBetween(
String trainerId,
LocalDateTime start,
LocalDateTime end
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package flowfit.domain.ptsession.presentation.controller;

import flowfit.domain.ptrelation.application.service.PtRelationService;
import flowfit.domain.ptrelation.domain.entity.ptrelation.PtRelation;
import flowfit.domain.ptrelation.domain.repository.PtRelationRepository;
import flowfit.domain.ptsession.presentation.dto.res.ClientSearchResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/clients")
public class ClientSearchController {

private final PtRelationRepository ptRelationRepository;

@GetMapping("/search")
public ResponseEntity<?> searchClientByName(@RequestParam String name) {
String trainerId = SecurityContextHolder.getContext().getAuthentication().getName();

List<PtRelation> relations = ptRelationRepository
.findAllByTrainer_IdAndMember_NameContaining(trainerId, name);

List<ClientSearchResponse> clients = relations.stream()
.map(ClientSearchResponse::from)
.toList();

return ResponseEntity.ok().body(new ClientSearchResponse.Wrapper(clients));
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,50 @@
package flowfit.domain.ptsession.presentation.controller;

import flowfit.domain.ptsession.application.service.ScheduleService;
import flowfit.domain.ptsession.presentation.dto.req.ScheduleRequest;
import flowfit.domain.ptsession.presentation.dto.res.CalendarMonthResponse;
import flowfit.domain.ptsession.presentation.dto.res.ScheduleResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/sessions")
public class ScheduleController {

private final ScheduleService scheduleService;

@PostMapping
public ResponseEntity<?> create(@RequestBody ScheduleRequest request) {
Long id = scheduleService.createSession(request);
return ResponseEntity.ok().body(id);
}

@PatchMapping("/{sessionId}")
public ResponseEntity<?> update(@PathVariable Long sessionId, @RequestBody ScheduleRequest request) {
scheduleService.updateSession(sessionId, request);
return ResponseEntity.ok().build();
}

@DeleteMapping("/{sessionId}")
public ResponseEntity<?> delete(@PathVariable Long sessionId,
@RequestParam String reason,
@RequestParam boolean charge) {
scheduleService.deleteSession(sessionId, reason, charge);
return ResponseEntity.ok().build();
}

@GetMapping(params = {"year", "month"})
public ResponseEntity<CalendarMonthResponse> getMonthly(@RequestParam int year, @RequestParam int month) {
return ResponseEntity.ok(scheduleService.getMonthlySummary(year, month));
}

@GetMapping(params = "date")
public ResponseEntity<List<ScheduleResponse>> getDaily(@RequestParam String date) {
return ResponseEntity.ok(scheduleService.getSessionsByDate(LocalDate.parse(date)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package flowfit.domain.ptsession.presentation.dto.req;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ScheduleRequest {
private Long relationId;
private String date; // "2025-01-28"
private String startTime; // "19:00"
private String endTime; // "20:00"
private String message; // optional
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package flowfit.domain.ptsession.presentation.dto.res;

import lombok.Builder;
import lombok.Getter;

import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Getter
@Builder
public class CalendarMonthResponse {

private long totalIncome;
private List<DailyIncome> monthlySchedules;

@Getter
@Builder
public static class DailyIncome {
private LocalDate date;
private long income;
}

public static CalendarMonthResponse of(long total, Map<LocalDate, Long> incomeMap) {
List<DailyIncome> daily = incomeMap.entrySet().stream()
.map(e -> DailyIncome.builder().date(e.getKey()).income(e.getValue()).build())
.collect(Collectors.toList());

return CalendarMonthResponse.builder()
.totalIncome(total)
.monthlySchedules(daily)
.build();

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package flowfit.domain.ptsession.presentation.dto.res;

import flowfit.domain.ptrelation.domain.entity.ptrelation.PtRelation;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

import java.util.List;

@Getter
@Builder
@AllArgsConstructor
public class ClientSearchResponse {

private Long memberId;
private String name;
private String phone;
private int remainingSessions;

public static ClientSearchResponse from(PtRelation relation) {
return ClientSearchResponse.builder()
.memberId(Long.parseLong(relation.getMember().getId()))
.name(relation.getMember().getName())
.phone(relation.getMember().getPhoneNumber())
.remainingSessions(relation.getSession())
.build();
}

@Getter
@AllArgsConstructor
public static class Wrapper {
private List<ClientSearchResponse> clients;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package flowfit.domain.ptsession.presentation.dto.res;

import flowfit.domain.ptsession.domain.entity.PtSession;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class ScheduleResponse {
private Long sessionId;
private String memberName;
private String startTime;
private String endTime;
private String status;

public static ScheduleResponse from(PtSession session) {
return ScheduleResponse.builder()
.sessionId(session.getId())
.memberName(session.getPtRelation().getMember().getName())
.startTime(session.getStartTime().toLocalTime().toString())
.endTime(session.getEndTime().toLocalTime().toString())
.status(session.getStatus().name())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package flowfit.domain.user.infra.exception;

import flowfit.global.infra.exception.FlowfitException;
import org.springframework.http.HttpStatus;

public class PtRelationNotFoundException extends FlowfitException {
public PtRelationNotFoundException() {
super(HttpStatus.NOT_FOUND, "해당 PT 계약이 존재하지 않습니다.");
}
}
Loading