<a href="/course">강좌 목록</a>
@RequiredArgsConstructor
@Controller
public class CourseController extends BaseController{
private final CourseService courseService;
@GetMapping("/course")
public String course(Model model, CourseParam parameter) {
return "course/index";
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>강좌 정보 페이지</title>
</head>
<body>
<h1> 강좌 정보 페이지 </h1>
<div th:replace="/fragments/layout.html :: fragment-body-menu"></div>
</body>
</html>
<div th:text="${list}"></div>
List<CourseDto> list = courseService.frontList(parameter);
model.addAttribute("list", list); // list 목록이 이제 내려감
List<CourseDto> frontList(CourseParam parameter);
List<CourseDto> list(CourseParam parameter);
이거를 쓸 수도 있지만
강좌에 대해서 관리자가 강좌를 화면에 안나오게 하거나 강좌 판매를 중단할 수 있기 때문에 따로 만들었음
즉 관리자 페이지에 나오는 리스트랑 프론트에 나오는 목록 리스트랑 같으면 안됨 프론트에 필요한 내용만 나오게 만들었음!
courseList를 가져올 수 있도록
public static List<CourseDto> of(List<Course> courses) {
if (courses == null) {
return null;
}
List<CourseDto> courseList = new ArrayList<>();
for(Course x : courses) {
courseList.add(CourseDto.of(x));
}
return courseList;
}
@Override
public List<CourseDto> frontList(CourseParam parameter) {
List<Course> courseList = courseRepository.findAll();
return CourseDto.of(courseList);
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>강좌 정보 페이지</title>
<style>
span.price {
text-decoration: line-through;
}
span.salePrice {
font-weight: bold;
}
</style>
</head>
<body>
<div th:replace="/fragments/layout.html :: fragment-body-menu"></div>
<h1> 강좌 정보 페이지 </h1>
<div>
<a href="/course">
전체 (<span th:text="${courseTotalCount}">0</span>)
</a>
<th:block th:each="y : ${categoryList}">
|
<a th:href="'/course?categoryId=' + ${y.id}">
<span th:text="${y.categoryName}">카테고리명</span> (<span th:text="${y.courseCount}">0</span>)
</a>
</th:block>
<hr>
</div>
<ul>
<li th:each="x : ${list}">
<div>
<a th:href="'/course/' + ${x.id}">
<h3 th:text="${x.subject}">강좌명</h3>
</a>
<div>
<p th:text="${x.summary}"></p>
<p>
판매가 : <span class="price" th:text="${x.price}"></span>
할인가 : <span class="salePrice"th:text="${x.salePrice}"></span>
</p>
</div>
</div>
</li>
</ul>
</body>
</html>
List<CategoryDto> select(CategoryDto parameter);
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kdew.dewlms.admin.mapper.CategoryMapper">
<select id="select"
resultType="com.kdew.dewlms.admin.dto.CategoryDto">
SELECT c.*, (SELECT COUNT(*) FROM course WHERE category_id = c.id) AS course_count
FROM category c
WHERE using_yn = 1
ORDER BY sort_value DESC
</select>
</mapper>
course_count
가 mapper에서 새로 만들어졌기 때문에 dto에서도 추가!
int courseCount;
카테고리 클릭하면 해당하는 카테고리 id로 페이지 이동
@GetMapping("/course")
public String course(Model model, CourseParam parameter) {
// 카테고리id 값 가져오기
List<CourseDto> list = courseService.frontList(parameter);
model.addAttribute("list", list); // list 목록이 이제 내려감
int courseTotalCount = 0;
List<CategoryDto> categoryList = categoryService.frontList(CategoryDto.builder().build());
if (categoryList != null) {
for(CategoryDto x : categoryList ) {
courseTotalCount += x.getCourseCount();
}
}
model.addAttribute("categoryList", categoryList);
model.addAttribute("courseTotalCount", courseTotalCount);
return "course/index";
}
@Override
public List<CourseDto> frontList(CourseParam parameter) {
if (parameter.getCategoryId() < 1) {
// 전체를 다 가져온 것이기 때문
List<Course> courseList = courseRepository.findAll();
return CourseDto.of(courseList);
}
Optional<List<Course>> optionalCourse = courseRepository.findByCategoryId(parameter.getCategoryId());
if (optionalCourse.isPresent()) {
return CourseDto.of(optionalCourse.get());
}
return null;
}
Optional<List<Course>> findByCategoryId(long categoryId);
long categoryId;
long id;
@GetMapping("/course/{id}")
public String courseDetail(Model model, CourseParam parameter) {
CourseDto detail = courseService.frontDetail(parameter.getId());
model.addAttribute("detail",detail);
return "course/detail";
}
"/course/{id}"
에서 id는 CourseParam에 id
로 자동 매핑이 됨
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>강좌 상세 페이지</title>
</head>
<body>
<h1>강좌 상세 정보</h1>
<div th:replace="/fragments/layout.html :: fragment-admin-body-menu"></div>
<div th:text="${detail}">
</div>
</body>
</html>
CourseDto frontDetail(long id);
@Override
public CourseDto frontDetail(long id) {
Optional<Course> optionalCourse = courseRepository.findById(id);
if (optionalCourse.isPresent()) {
return CourseDto.of(optionalCourse.get());
}
return null;
}
<h1>강좌 상세 정보</h1>
<div th:replace="/fragments/layout.html :: fragment-admin-body-menu"></div>
<div>
<h2>강좌명 : <span th:text="${detail.subject}">강좌</span></h2>
<div th:utext="${detail.contents}"></div>
<div>
<p>가격 : <span th:text="${detail.price}">0</span></p>
<p>할인가격 : <span th:text="${detail.salePrice}">0</span></p>
</div>
<div>
<button type="button">수강신청</button>
<a href="/course">강좌목록</a>
</div>
</div>
public interface TakeCourseCode {
String STATUS_REQ = "REQ"; // 수강신청
String STATUS_COMPLETE = "COMPLETE"; // 결제완료
String STATUS_CANCEL = "CANCEL"; // 수강취소
}
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Data
public class TakeCourse implements TakeCourseCode{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
long courseId;
String userId;
long payPrice; // 결제 금액(할인된 금액으로)
String status; // 상태(수강신청,결제완료, 수강취소)
LocalDateTime regDt; // 신청일
}
public interface TakeCourseRepository extends JpaRepository<TakeCourse, Long> {
}
<script src="https://cdn.jsdelivr.net/npm/axios@1.1.2/dist/axios.min.js"></script>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>강좌 상세 페이지</title>
<script src="https://cdn.jsdelivr.net/npm/axios@1.1.2/dist/axios.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script>
<script>
$(function () {
$('#submitForm').on('submit', function () {
if (!confirm('수강 신청을 하시겠습니까?')) {
return false;
}
var $thisForm = $(this);
var url = '/api/course/req.api';
var parameter = {
courseId: $thisForm.find('input[name=id]').val()
}
axios.post(url, parameter).then(function (response) {
}).catch(function (err) {
});
return false;
});
});
</script>
</head>
<body>
<h1>강좌 상세 정보</h1>
<div th:replace="/fragments/layout.html :: fragment-admin-body-menu"></div>
<div>
<h2>강좌명 : <span th:text="${detail.subject}">강좌</span></h2>
<div th:utext="${detail.contents}"></div>
<div>
<p>가격 : <span th:text="${detail.price}">0</span></p>
<p>할인가격 : <span th:text="${detail.salePrice}">0</span></p>
</div>
<div>
<form id="submitForm" method="post">
<input type="hidden" name="id" th:value="${detail.id}">
<button type="submit">수강신청</button>
<a href="/course">강좌목록</a>
</form>
</div>
</div>
</body>
</html>
적용 완료!
@Controller
: 뷰 엔진의 파일 형태를 리턴
@RestController
: JSON 바디 형태로 데이터가 리턴
long courseId;
String userId;
@RequiredArgsConstructor
@RestController
public class ApiCourseController extends BaseController{
@PostMapping("/api/course/req.api")
public ResponseEntity<?> courseReq(Model model
, @RequestBody TakeCourseInput parameter
, Principal principal) {
parameter.setUserId(principal.getName());
boolean result = courseService.req(parameter);
if(!result) {
return ResponseEntity.badRequest().body("수강신청에 실패하였습니다.");
}
return ResponseEntity.ok().body(parameter);
}
}
boolean req(TakeCourseInput parameter);
courseId와 userId를 잘 가져왔음!
데이터 받기!
private final TakeCourseRepository takeCourseRepository;
@Override
public boolean req(TakeCourseInput parameter) {
Optional<Course> optionalCourse = courseRepository.findById(parameter.getCourseId());
if (!optionalCourse.isPresent()) {
return false;
}
Course course = optionalCourse.get();
TakeCourse takeCourse = TakeCourse.builder()
.courseId(course.getId())
.userId(parameter.getUserId())
.payPrice(course.getSalePrice())
.regDt(LocalDateTime.now())
.status(TakeCourse.STATUS_REQ)
.build();
takeCourseRepository.save(takeCourse);
return true;
}
@Data
public class ServiceResult {
boolean result; // 서비스가 true를 리턴할 것인지 false를 리턴할 것인지
String message; // 만약 false면 그 에러 메세지는 무엇인지
}
long countByCourseIdAndUserIdAndStatusIn(long courseId, String userId, Collection<String> statusList);
ServiceResult req(TakeCourseInput parameter);
public ServiceResult req(TakeCourseInput parameter) {
ServiceResult result = new ServiceResult();
// 이미 신청 정보가 있는지 확인 (아이디와 강좌 아이디를 통해!)
String[] statusList = {TakeCourse.STATUS_REQ, TakeCourse.STATUS_COMPLETE};
long count = takeCourseRepository.countByCourseIdAndUserIdAndStatusIn(course.getId()
, parameter.getUserId(), List.of(statusList));
if(count > 0) {
result.setResult(false);
result.setMessage("이미 신청한 강좌 정보가 존재합니다.");
return result;
}
result.setResult(true);
return result;
}
ServiceResult result = courseService.req(parameter);
if(!result.isResult()) {
return ResponseEntity.badRequest().body(result.getMessage());
}
Optional<Course> optionalCourse = courseRepository.findById(parameter.getCourseId());
if (!optionalCourse.isPresent()) {
result.setResult(false);
result.setMessage("강좌 정보가 존재하지 않습니다.");
return result;
}
Course course = optionalCourse.get();
서버에서 200을 내리고 -> alert로 처리!
ServiceResult result = courseService.req(parameter);
if(!result.isResult()) {
ResponseResult responseResult = new ResponseResult(false, result.getMessage());
return ResponseEntity.ok().body(responseResult);
}
ResponseResult responseResult = new ResponseResult(true);
return ResponseEntity.ok().body(responseResult);
@Data
public class ResponseResult {
ResponseReultHeader header;
Object body;
public ResponseResult(boolean result, String message) {
header = new ResponseReultHeader(result, message);
}
public ResponseResult(boolean result) {
header = new ResponseReultHeader(result);
}
}
@Data
public class ResponseReultHeader {
boolean result;
String message;
public ResponseReultHeader(boolean result, String message) {
this.result = result;
this.message = message;
}
public ResponseReultHeader(boolean result) {
this.result = result;
}
}
response.data = response.data || {};
response.data.header = response.data.header || {};
// 강좌 등록에 실패할 때
if (!response.data.header.result) {
alert(response.data.header.message);
return false;
}
// 강좌 등록에 정상적일때
alert(' 강좌가 정상적으로 신청되었습니다.');
location.href='/';