Skip to content

Commit aff09e1

Browse files
authored
Merge pull request #177 from Runnect/dev
Merge pull request from Runnect/dev
2 parents 738e22d + 0b649e8 commit aff09e1

16 files changed

Lines changed: 741 additions & 2 deletions

โ€Žsrc/main/java/org/runnect/server/common/constant/ErrorStatus.javaโ€Ž

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ public enum ErrorStatus {
3131
NOT_FOUND_SCRAP_EXCEPTION(HttpStatus.BAD_REQUEST, "์Šคํฌ๋žฉํ•œ ์ฝ”์Šค๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."),
3232
NOT_FOUND_IMAGE_EXCEPTION(HttpStatus.BAD_REQUEST, "์ž˜๋ชป๋œ ์ด๋ฏธ์ง€ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค"),
3333
NOT_FOUND_PUBLICCOURSE_EXCEPTION(HttpStatus.BAD_REQUEST, "์กด์žฌํ•˜์ง€ ์•Š๋Š” public course id์ž…๋‹ˆ๋‹ค."),
34+
INVALID_HEALTH_DATA_EXCEPTION(HttpStatus.BAD_REQUEST, "์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฑด๊ฐ• ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค"),
35+
INVALID_DATE_RANGE_EXCEPTION(HttpStatus.BAD_REQUEST, "์œ ํšจํ•˜์ง€ ์•Š์€ ๋‚ ์งœ ๋ฒ”์œ„์ž…๋‹ˆ๋‹ค"),
36+
EXCEED_HEART_RATE_SAMPLES_EXCEPTION(HttpStatus.BAD_REQUEST, "์‹ฌ๋ฐ•์ˆ˜ ์ƒ˜ํ”Œ์€ ์ตœ๋Œ€ 5000๊ฑด๊นŒ์ง€ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค"),
3437

3538
/**
3639
* 401 UNAUTHORIZED
@@ -51,6 +54,7 @@ public enum ErrorStatus {
5154
*/
5255
PERMISSION_DENIED_PUBLIC_COURSE_DELETE_EXCEPTION(HttpStatus.FORBIDDEN, "ํผ๋ธ”๋ฆญ ์ฝ”์Šค๋ฅผ ์‚ญ์ œํ•  ๊ถŒํ•œ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."),
5356
PERMISSION_DENIED_RECORD_DELETE_EXCEPTION(HttpStatus.FORBIDDEN, "๊ธฐ๋ก์„ ์‚ญ์ œํ•  ๊ถŒํ•œ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."),
57+
PERMISSION_DENIED_HEALTH_DATA_EXCEPTION(HttpStatus.FORBIDDEN, "๊ฑด๊ฐ• ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์ ‘๊ทผ ๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค"),
5458

5559
/**
5660
* 404 NOT FOUND
@@ -63,6 +67,7 @@ public enum ErrorStatus {
6367
ALREADY_EXIST_USER_EXCEPTION(HttpStatus.CONFLICT, "์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์œ ์ €์ž…๋‹ˆ๋‹ค"),
6468
ALREADY_EXIST_NICKNAME_EXCEPTION(HttpStatus.CONFLICT, "์ค‘๋ณต๋œ ๋‹‰๋„ค์ž„์ž…๋‹ˆ๋‹ค."),
6569
ALREADY_UPLOAD_COURSE_EXCEPTION(HttpStatus.CONFLICT, "์ด๋ฏธ ์—…๋กœ๋“œ๋œ ์ฝ”์Šค์ž…๋‹ˆ๋‹ค."),
70+
ALREADY_EXIST_HEALTH_DATA_EXCEPTION(HttpStatus.CONFLICT, "์ด๋ฏธ ๊ฑด๊ฐ• ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ฑ๋ก๋œ ๊ธฐ๋ก์ž…๋‹ˆ๋‹ค"),
6671

6772
/**
6873
* 500 INTERNAL SERVER ERROR

โ€Žsrc/main/java/org/runnect/server/common/constant/SuccessStatus.javaโ€Ž

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ public enum SuccessStatus {
2929

3030
SEARCH_PUBLIC_COURSE_SUCCESS(HttpStatus.OK,"์—…๋กœ๋“œ๋œ ์ฝ”์Šค ๊ฒ€์ƒ‰ ์„ฑ๊ณต"),
3131

32+
GET_HEALTH_DATA_SUCCESS(HttpStatus.OK, "๊ฑด๊ฐ• ๋ฐ์ดํ„ฐ ์กฐํšŒ ์„ฑ๊ณต"),
33+
GET_HEALTH_SUMMARY_SUCCESS(HttpStatus.OK, "๊ฑด๊ฐ• ํ†ต๊ณ„ ์กฐํšŒ ์„ฑ๊ณต"),
34+
3235

3336
UPDATE_RECORD_SUCCESS(HttpStatus.OK, "ํ™œ๋™ ๊ธฐ๋ก ์ˆ˜์ • ์„ฑ๊ณต"),
3437
UPDATE_USER_NICKNAME_SUCCESS(HttpStatus.OK, "๋‹‰๋„ค์ž„ ๋ณ€๊ฒฝ์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค."),
@@ -41,6 +44,7 @@ public enum SuccessStatus {
4144
DELETE_PUBLIC_COURSE_SUCCESS(HttpStatus.OK, "ํผ๋ธ”๋ฆญ ์ฝ”์Šค ์‚ญ์ œ์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค."),
4245
DELETE_RECORD_SUCCESS(HttpStatus.OK, "๊ธฐ๋ก ์‚ญ์ œ์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค."),
4346
DELETE_COURSES_SUCCESS(HttpStatus.OK, "์ฝ”์Šค ์‚ญ์ œ ์„ฑ๊ณต"),
47+
DELETE_HEALTH_DATA_SUCCESS(HttpStatus.OK, "๊ฑด๊ฐ• ๋ฐ์ดํ„ฐ ์‚ญ์ œ ์„ฑ๊ณต"),
4448

4549

4650
/**
@@ -52,6 +56,7 @@ public enum SuccessStatus {
5256
CREATE_PUBLIC_COURSE_SUCCESS(HttpStatus.CREATED, "์ฝ”๋“œ ์—…๋กœ๋“œ์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค."),
5357
CREATE_SCRAP_SUCCESS(HttpStatus.CREATED, "์ฝ”์Šค ์Šคํฌ๋žฉ ์„ฑ๊ณต"),
5458
NEW_TOKEN_SUCCESS(HttpStatus.CREATED, "ํ† ํฐ ์žฌ๋ฐœ๊ธ‰์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค."),
59+
CREATE_HEALTH_DATA_SUCCESS(HttpStatus.CREATED, "๊ฑด๊ฐ• ๋ฐ์ดํ„ฐ ์ €์žฅ ์„ฑ๊ณต"),
5560
;
5661

5762
private final HttpStatus httpStatus;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.runnect.server.health.controller;
2+
3+
import javax.validation.Valid;
4+
import lombok.RequiredArgsConstructor;
5+
import org.runnect.server.common.constant.SuccessStatus;
6+
import org.runnect.server.common.dto.ApiResponseDto;
7+
import org.runnect.server.common.resolver.userId.UserId;
8+
import org.runnect.server.health.dto.request.HealthDataRequestDto;
9+
import org.runnect.server.health.dto.response.CreateHealthDataResponseDto;
10+
import org.runnect.server.health.dto.response.GetHealthDataResponseDto;
11+
import org.runnect.server.health.dto.response.GetHealthSummaryResponseDto;
12+
import org.runnect.server.health.service.HealthService;
13+
import org.springframework.http.HttpStatus;
14+
import org.springframework.web.bind.annotation.DeleteMapping;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.PathVariable;
17+
import org.springframework.web.bind.annotation.PostMapping;
18+
import org.springframework.web.bind.annotation.RequestBody;
19+
import org.springframework.web.bind.annotation.RequestMapping;
20+
import org.springframework.web.bind.annotation.RequestParam;
21+
import org.springframework.web.bind.annotation.ResponseStatus;
22+
import org.springframework.web.bind.annotation.RestController;
23+
24+
@RestController
25+
@RequiredArgsConstructor
26+
@RequestMapping("/api")
27+
public class HealthController {
28+
29+
private final HealthService healthService;
30+
31+
@PostMapping("record/{recordId}/health")
32+
@ResponseStatus(HttpStatus.CREATED)
33+
public ApiResponseDto<CreateHealthDataResponseDto> createHealthData(
34+
@UserId Long userId,
35+
@PathVariable(name = "recordId") Long recordId,
36+
@RequestBody @Valid final HealthDataRequestDto request) {
37+
return ApiResponseDto.success(SuccessStatus.CREATE_HEALTH_DATA_SUCCESS,
38+
healthService.createHealthData(userId, recordId, request));
39+
}
40+
41+
@GetMapping("record/{recordId}/health")
42+
@ResponseStatus(HttpStatus.OK)
43+
public ApiResponseDto<GetHealthDataResponseDto> getHealthData(
44+
@UserId Long userId,
45+
@PathVariable(name = "recordId") Long recordId) {
46+
return ApiResponseDto.success(SuccessStatus.GET_HEALTH_DATA_SUCCESS,
47+
healthService.getHealthData(userId, recordId));
48+
}
49+
50+
@GetMapping("health/summary")
51+
@ResponseStatus(HttpStatus.OK)
52+
public ApiResponseDto<GetHealthSummaryResponseDto> getHealthSummary(
53+
@UserId Long userId,
54+
@RequestParam(name = "startDate") String startDate,
55+
@RequestParam(name = "endDate") String endDate) {
56+
return ApiResponseDto.success(SuccessStatus.GET_HEALTH_SUMMARY_SUCCESS,
57+
healthService.getHealthSummary(userId, startDate, endDate));
58+
}
59+
60+
@DeleteMapping("record/{recordId}/health")
61+
@ResponseStatus(HttpStatus.OK)
62+
public ApiResponseDto deleteHealthData(
63+
@UserId Long userId,
64+
@PathVariable(name = "recordId") Long recordId) {
65+
healthService.deleteHealthData(userId, recordId);
66+
return ApiResponseDto.success(SuccessStatus.DELETE_HEALTH_DATA_SUCCESS);
67+
}
68+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.runnect.server.health.dto.request;
2+
3+
import java.util.List;
4+
import javax.validation.Valid;
5+
import javax.validation.constraints.NotNull;
6+
import lombok.AccessLevel;
7+
import lombok.AllArgsConstructor;
8+
import lombok.Getter;
9+
import lombok.NoArgsConstructor;
10+
11+
@Getter
12+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
13+
@AllArgsConstructor
14+
public class HealthDataRequestDto {
15+
@NotNull
16+
private Double avgHeartRate;
17+
18+
private Double maxHeartRate;
19+
20+
private Double minHeartRate;
21+
22+
@NotNull
23+
private Double calories;
24+
25+
@NotNull
26+
private Integer zone1Seconds;
27+
28+
@NotNull
29+
private Integer zone2Seconds;
30+
31+
@NotNull
32+
private Integer zone3Seconds;
33+
34+
@NotNull
35+
private Integer zone4Seconds;
36+
37+
@NotNull
38+
private Integer zone5Seconds;
39+
40+
private Double maxHeartRateConfig;
41+
42+
@Valid
43+
private List<HeartRateSampleRequestDto> heartRateSamples;
44+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.runnect.server.health.dto.request;
2+
3+
import javax.validation.constraints.Max;
4+
import javax.validation.constraints.Min;
5+
import javax.validation.constraints.NotNull;
6+
import lombok.AccessLevel;
7+
import lombok.AllArgsConstructor;
8+
import lombok.Getter;
9+
import lombok.NoArgsConstructor;
10+
11+
@Getter
12+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
13+
@AllArgsConstructor
14+
public class HeartRateSampleRequestDto {
15+
@NotNull
16+
private Double heartRate;
17+
18+
@NotNull
19+
private Integer elapsedSeconds;
20+
21+
@NotNull
22+
@Min(1) @Max(5)
23+
private Integer zone;
24+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.runnect.server.health.dto.response;
2+
3+
import lombok.AccessLevel;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
@Getter
9+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
10+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
11+
public class CreateHealthDataResponseDto {
12+
private Long healthDataId;
13+
14+
public static CreateHealthDataResponseDto of(Long healthDataId) {
15+
return new CreateHealthDataResponseDto(healthDataId);
16+
}
17+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.runnect.server.health.dto.response;
2+
3+
import java.util.List;
4+
import lombok.AccessLevel;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
@Getter
10+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
11+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
12+
public class GetHealthDataResponseDto {
13+
private HealthDataDetailResponse healthData;
14+
15+
public static GetHealthDataResponseDto of(HealthDataDetailResponse healthData) {
16+
return new GetHealthDataResponseDto(healthData);
17+
}
18+
19+
@Getter
20+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
21+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
22+
public static class HealthDataDetailResponse {
23+
private Long id;
24+
private Long recordId;
25+
private Double avgHeartRate;
26+
private Double maxHeartRate;
27+
private Double minHeartRate;
28+
private Double calories;
29+
private ZoneResponse zones;
30+
private Double maxHeartRateConfig;
31+
private List<HeartRateSampleResponse> heartRateSamples;
32+
33+
public static HealthDataDetailResponse of(Long id, Long recordId, Double avgHeartRate,
34+
Double maxHeartRate, Double minHeartRate, Double calories,
35+
ZoneResponse zones, Double maxHeartRateConfig,
36+
List<HeartRateSampleResponse> heartRateSamples) {
37+
return new HealthDataDetailResponse(id, recordId, avgHeartRate, maxHeartRate,
38+
minHeartRate, calories, zones, maxHeartRateConfig, heartRateSamples);
39+
}
40+
}
41+
42+
@Getter
43+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
44+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
45+
public static class ZoneResponse {
46+
private Integer zone1Seconds;
47+
private Integer zone2Seconds;
48+
private Integer zone3Seconds;
49+
private Integer zone4Seconds;
50+
private Integer zone5Seconds;
51+
52+
public static ZoneResponse of(Integer zone1Seconds, Integer zone2Seconds,
53+
Integer zone3Seconds, Integer zone4Seconds, Integer zone5Seconds) {
54+
return new ZoneResponse(zone1Seconds, zone2Seconds, zone3Seconds, zone4Seconds, zone5Seconds);
55+
}
56+
}
57+
58+
@Getter
59+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
60+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
61+
public static class HeartRateSampleResponse {
62+
private Double heartRate;
63+
private Integer elapsedSeconds;
64+
private Integer zone;
65+
66+
public static HeartRateSampleResponse of(Double heartRate, Integer elapsedSeconds, Integer zone) {
67+
return new HeartRateSampleResponse(heartRate, elapsedSeconds, zone);
68+
}
69+
}
70+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.runnect.server.health.dto.response;
2+
3+
import lombok.AccessLevel;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
@Getter
9+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
10+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
11+
public class GetHealthSummaryResponseDto {
12+
private HealthSummaryResponse summary;
13+
14+
public static GetHealthSummaryResponseDto of(HealthSummaryResponse summary) {
15+
return new GetHealthSummaryResponseDto(summary);
16+
}
17+
18+
@Getter
19+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
20+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
21+
public static class HealthSummaryResponse {
22+
private Long totalRecords;
23+
private Long recordsWithHealth;
24+
private Double avgHeartRate;
25+
private Double avgCalories;
26+
private Double totalCalories;
27+
private GetHealthDataResponseDto.ZoneResponse zoneDistribution;
28+
29+
public static HealthSummaryResponse of(Long totalRecords, Long recordsWithHealth,
30+
Double avgHeartRate, Double avgCalories, Double totalCalories,
31+
GetHealthDataResponseDto.ZoneResponse zoneDistribution) {
32+
return new HealthSummaryResponse(totalRecords, recordsWithHealth, avgHeartRate,
33+
avgCalories, totalCalories, zoneDistribution);
34+
}
35+
}
36+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.runnect.server.health.entity;
2+
3+
import javax.persistence.Column;
4+
import javax.persistence.Entity;
5+
import javax.persistence.FetchType;
6+
import javax.persistence.GeneratedValue;
7+
import javax.persistence.GenerationType;
8+
import javax.persistence.Id;
9+
import javax.persistence.JoinColumn;
10+
import javax.persistence.ManyToOne;
11+
import lombok.AccessLevel;
12+
import lombok.Builder;
13+
import lombok.Getter;
14+
import lombok.NoArgsConstructor;
15+
import org.runnect.server.common.entity.AuditingTimeEntity;
16+
17+
@Getter
18+
@Entity
19+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
20+
public class HeartRateSample extends AuditingTimeEntity {
21+
22+
@Id
23+
@GeneratedValue(strategy = GenerationType.IDENTITY)
24+
private Long id;
25+
26+
@ManyToOne(fetch = FetchType.LAZY)
27+
@JoinColumn(name = "record_health_data_id", nullable = false)
28+
private RecordHealthData recordHealthData;
29+
30+
@Column(nullable = false)
31+
private Double heartRate;
32+
33+
@Column(nullable = false)
34+
private Integer elapsedSeconds;
35+
36+
@Column(nullable = false)
37+
private Integer zone;
38+
39+
@Builder
40+
public HeartRateSample(RecordHealthData recordHealthData, Double heartRate, Integer elapsedSeconds, Integer zone) {
41+
this.recordHealthData = recordHealthData;
42+
this.heartRate = heartRate;
43+
this.elapsedSeconds = elapsedSeconds;
44+
this.zone = zone;
45+
}
46+
47+
public void setRecordHealthData(RecordHealthData recordHealthData) {
48+
this.recordHealthData = recordHealthData;
49+
}
50+
51+
@Override
52+
public void updateDeletedAt() {
53+
throw new RuntimeException("Course๋ฅผ ์ œ์™ธํ•œ ํ…Œ์ด๋ธ”์€ ์ •์ƒ์ ์œผ๋กœ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.");
54+
}
55+
}

0 commit comments

Comments
ย (0)