Spring Projcect/학습 관리 시스템 & 백오피스 구축

Chapter 15. 백 오피스(강좌 신청 처리 구현)

계란💕 2022. 8. 24. 17:25

 

 

  Ex) 수강신청 목록 구현

 

    - AdminTakeCourseController 클래스

 

 

 

    - resource의 admin 아래에 takecourse 라는 패키지를 만든다.

 

    - 컨트롤러

 

 

    - 수강 관련 서비스 TakeCourseService - 매개변수 수정

<hide/>
package com.zerobase.fastlms.course.service;
import com.zerobase.fastlms.course.dto.CourseDto;
import com.zerobase.fastlms.course.model.CourseParam;
import java.util.List;
public interface TakeCourseService {
    /**
     * 수강 목록
     */
    List<CourseDto> list(CourseParam parameter);
}

 

 

    - TakeCourseParam

 

 

 

    - TakeCourseDto

<hide/>
package com.zerobase.fastlms.course.dto;
import java.time.LocalDateTime;
public class TakeCourseDto {
    Long id;
    long courseId;
    String userId;

    long payPrice;  // 결제 금액
    String status;  // 상태 (수강 신청, 결제 완료, 수강 신청)

    LocalDateTime regDt;    // 신청일

    // join
    String userName;
    String phone;
    String subject;
}

 

 

    - TakeCourse 임플

<hide/>
package com.zerobase.fastlms.course.service;
import com.zerobase.fastlms.course.dto.CourseDto;
import com.zerobase.fastlms.course.dto.TakeCourseDto;
import com.zerobase.fastlms.course.mapper.CourseMapper;
import com.zerobase.fastlms.course.mapper.TakeCourseMapper;
import com.zerobase.fastlms.course.model.TakeCourseParam;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Service
@RequiredArgsConstructor
public class TakeCourseServiceImpl implements TakeCourseService{

    private final TakeCourseMapper takeCourseMapper;

    @Override
    public List<TakeCourseDto> list(TakeCourseParam parameter) {
        long totalCount = takeCourseMapper.selectListCount(parameter);
        List<TakeCourseDto> list = takeCourseMapper.selectList(parameter);

        if(!CollectionUtils.isEmpty(list)){
            int i = 0;
            for(TakeCourseDto x : list){
                x.setTotalCount(totalCount);    // 전체 개수를 하나씩 다 넣어준다.
                x.setSeq(totalCount - parameter.getPageStart() - i);
                ++i;
            }
        }
        return list;
    }
}

 

 

    - 매퍼

<hide/>
package com.zerobase.fastlms.course.mapper;
import com.zerobase.fastlms.course.dto.CourseDto;
import com.zerobase.fastlms.course.dto.TakeCourseDto;
import com.zerobase.fastlms.course.entity.TakeCourse;
import com.zerobase.fastlms.course.model.CourseParam;
import com.zerobase.fastlms.course.model.TakeCourseParam;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface TakeCourseMapper {
    long selectListCount(TakeCourseParam parameter);
    List<TakeCourseDto> selectList(TakeCourseParam parameter);
}

 

 

    - 매퍼 xml 파일 만든다.

<hide/>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zerobase.fastlms.course.mapper.TakeCourseMapper">
    <sql id="selectListWhere">
    </sql>
    <select id="selectListCount"
            resultType="long">
        SELECT COUNT(*)
        FROM take_course
        WHERE 1 = 1
            <include refid="selectListWhere"/>
    </select>
    <select id="selectList"
            resultType="com.zerobase.fastlms.course.dto.TakeCourseDto">
        SELECT tc.*
             , c.subject
             , m.user_name
             , m.phone
        FROM take_course tc
                 JOIN course c ON tc.course_id = c.id
                 JOIN member m ON tc.user_id = m.user_id
        WHERE 1 = 1
        <include refid="selectListWhere"/>
        ORDER BY reg_dt DESC
        LIMIT #{pageStart}, #{pageEnd}
    </select>
</mapper>

 

 

    - 수강관리 인덱스

       -> colspan: 표에서 3개의 열을 합친다.

<hide/>
<!doctype html>
<html lang="ko"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>관리자 화면</title>
    <style>
        .list table{
            width: 100%;
            border-collapse: collapse;
        }
        .list table th, .list table td{
            border: solid 1px #000;
        }
        p.nothing {
            text-align: center;
            padding: 100px;
        }
        .list .buttons{
            position: relative;
            padding: 10px 0;
        }
        .list .buttons a , .list .buttons button{
            border-width: 0;
            background-color: transparent;
            text-decoration: underline;
            font-size: 14px;
            line-height: 20px;
            height: 20px;
            color: #000;
            cursor: pointer;
        }
        .list .buttons .total-count{
            text-align: right;
            /*position: absolute;*/
            /*top: 0;*/
            /*right: 0;*/
        }
        .pager{
            margin-top: 10px;
            text-align: center;
        }
        .pager a.on{
            font-weight: bold;
            color: red;
        }
    </style>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script>
        $(document).ready(function (){
            $('#selectAll').on('click', function (){
                var checked = $(this).is(':checked');

                $('#dataList input[type=checkbox]').each(function (k, v){
                    $(this).prop('checked', checked);
                });
            });


            $('#deleteButton').on('click', function (){

                var $checked = $('#dataList input[type=checkbox]:checked');

                if($checked.length < 1){
                    alert("삭제할 데이터를 선택해주세요.")
                    return false;
                }
                if(!confirm('선택한 데이터를 삭제하겠습니까?')){  // 체크 박스를 누르고나서 선택 삭제를 누른 경우에만 실행된다.
                    return false;
                }

                var idList = [];

                $.each($checked, function (k, v){
                       idList.push($(this).val());
                });
                console.log(idList);
                console.log(idList.join(','));

                var $deleteForm = $('form[name=deleteForm]');
                $deleteForm.find('input[name=idList]').val(idList.join(','));
                $deleteForm.submit();   // 서버로 보낸다.
            });
        });
    </script>
</head>
<body>
<!--<div th:replace="/fragments/layout.html :: fragment-admin-body-menu" ></div>-->
<h1>수강 관리</h1>
<div class="list">
    <div class="buttons">

        <p class="total-count">전체 <span th:text="${totalCount}"></span>개</p>

    </div>
    <table>
        <thead>
            <tr>
                <th>NO</th>
                <th>등록일</th>
                <th>강좌명</th>
                <th colspan="3">신청인</th>
                <th>상태</th>
            </tr>
        </thead>
        <tbody id ="dataList">
        <tr th:each="x : ${list}">
            <td th:text="${x.seq}">1</td>
            <td>
               <p th:text="${x.regDt}">2022.01.01</p>
            </td>
            <td>
                <p th:text="${x.subject}">강좌명</p>
            </td>
            <td>
                <p th:text="${x.userName}"></p>
            </td>
            <td>
                <p th:text="${x.userId}"></p>
            </td>
            <td>
                <p th:text="${x.phone}"></p>
            </td>
            <td>
                <p th:if="${x.status eq 'REQ'}">수강신청</p>
                <p th:if="${x.status eq 'COMPLETE'}">결제완료</p>
                <p th:if="${x.status eq 'CANCEL'}">수강취소</p>
            </td>
        </tr>
        </tbody>
    </table>
    <div class="pager" th:utext="${pager}"></div>
</div>
<form name="deleteForm" method="post" action="/admin/course/delete.do">
    <input type="hidden" name="idList"/>
</form>
</body>
</html>

  Note) 실행 결과

 

 

 

  Ex) 수강 관리 창에서 결제완료 처리

 

    - 리스트 페이지

<hide/>
<!doctype html>
<html lang="ko"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>관리자 화면</title>
    <style>
        .list table{
            width: 100%;
            border-collapse: collapse;
        }
        .list table th, .list table td{
            border: solid 1px #000;
        }
        p.nothing {
            text-align: center;
            padding: 100px;
        }
        .list .buttons{
            position: relative;
            padding: 10px 0;
        }
        .list .buttons a , .list .buttons button{
            border-width: 0;
            background-color: transparent;
            text-decoration: underline;
            font-size: 14px;
            line-height: 20px;
            height: 20px;
            color: #000;
            cursor: pointer;
        }
        .list .buttons .total-count{
            text-align: right;
            /*position: absolute;*/
            /*top: 0;*/
            /*right: 0;*/
        }
        .list .row-buttons{
            text-align: center;
        }
        .pager{
            margin-top: 10px;
            text-align: center;
        }
        .pager a.on{
            font-weight: bold;
            color: red;
        }
    </style>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script>
        $(document).ready(function (){
            $('.row-buttons button').on('click', function (){
                var status = $(this).val();
                var id = $(this).closest('div').find('input[name=id]').val();

                var msg = status ==  'COMPLETE' ? '결제완료 처리하시겠습니까?' : '수강취소 처리하시겠습니까?';
                if(!confirm(msg)){
                    return false;
                }
                var procFrom = $('#procFrom');
                procFrom.find('input[name=id]').val(id);
                procFrom.find('input[name=status]').val(status);
                procFrom.submit();
            });
        });
    </script>
</head>
<body>
<!--<div th:replace="/fragments/layout.html :: fragment-admin-body-menu" ></div>-->
<h1>수강 관리</h1>
<div class="list">
    <div class="buttons">
        <p class="total-count">전체 <span th:text="${totalCount}"></span>개</p>
    </div>
    <table>
        <thead>
            <tr>
                <th>NO</th>
                <th>등록일</th>
                <th>강좌명</th>
                <th colspan="3">신청인</th>
                <th>상태</th>
                <th>비고</th>
            </tr>
        </thead>
        <tbody id ="dataList">
        <tr th:each="x : ${list}">
            <td th:text="${x.seq}">1</td>
            <td>
               <p th:text="${x.regDt}">2022.01.01</p>
            </td>
            <td>
                <p th:text="${x.subject}">강좌명</p>
            </td>
            <td>
                <p th:text="${x.userName}"></p>
            </td>
            <td>
                <p th:text="${x.userId}"></p>
            </td>
            <td>
                <p th:text="${x.phone}"></p>
            </td>
            <td>

                <p th:if="${x.status eq 'REQ'}">수강신청</p>
                <p th:if="${x.status eq 'COMPLETE'}">결제완료</p>
                <p th:if="${x.status eq 'CANCEL'}">수강취소</p>
            </td>
            <td>
                <div class="row-buttons" th:if="${x.status eq 'REQ'}">
                    <input type="hidden" name="id" th:value="${x.id}"/>
                    <button value="COMPLETE" type="button">결제완료 처리</button>
                    <button value="CANCEL" type="button">수강취소 처리</button>
                </div>
            </td>
        </tr>
        </tbody>
    </table>
    <div class="pager" th:utext="${pager}"></div>
</div>
<form id="procFrom" method="post" action="/admin/takecourse/status.do">
    <input type="hidden" name="id"/>
    <input type="hidden" name="status"/>
</form>
</body>
</html>

 

    - 서비스

<hide/>
/**
 * 수강 내용 변경
 */
ServiceResult updateStatus(Long id, String status);

 

    - 컨트롤러 status()

<hide/>
@PostMapping("/admin/takecourse/status.do")
public String status(Model model, TakeCourseParam parameter) {
    ServiceResult result = takeCourseService.updateStatus(parameter.getId(), parameter.getStatus());    // 값이 저장되면 토탈카운트가 존재한다.
    if(!result.isResult()){
        model.addAttribute("message", result.getMessage());
        return "common/error";
    }
    return "redirect:/admin/takecourse/list.do";
}

 

    - Impl 

<hide/>
@Override
public ServiceResult updateStatus(Long id, String status) {

    Optional<TakeCourse> optionalTakeCourse = takeCourseRepository.findById(id);
    if(!optionalTakeCourse.isPresent()){
        return new ServiceResult(false, "수강 정보가 존재하지 않습니다.");
    }
    TakeCourse takeCourse = optionalTakeCourse.get();
    takeCourse.setStatus(status);
    takeCourseRepository.save(takeCourse);
    return new ServiceResult(true);
}

 

    - 서비스 result

<hide/>
package com.zerobase.fastlms.course.model;
import lombok.Data;
@Data
public class ServiceResult{
    boolean result;
    String message;
    public ServiceResult(boolean result, String message) {
        this.result = result;
        this.message = message;
    }
}

 

  Note) 실행 결과  - 하나는 결제 완료, 하나는 수강 취소 누른 상황

 

 

  Ex) 수강 관리 페이지 등록일 날짜 포맷 

 

    - Dto에 게터 추가

<hide/>
public String getRegDtText(){
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm");
    return regDt != null ? regDt.format(formatter) : "";
}

 

    - 리스트 페이지

<hide/>
<tr th:each="x : ${list}">
    <td th:text="${x.seq}">1</td>
    <td>
       <p th:text="${x.regDtText}">2022.01.01</p>
    </td>
    <td>
        <p th:text="${x.subject}">강좌명</p>
    </td>
    <td>
        <p th:text="${x.userName}"></p>
    </td>
    <td>
        <p th:text="${x.userId}"></p>
    </td>
    <td>
        <p th:text="${x.phone}"></p>
    </td>
    <td>

        <p th:if="${x.status eq 'REQ'}">수강신청</p>
        <p th:if="${x.status eq 'COMPLETE'}">결제완료</p>
        <p th:if="${x.status eq 'CANCEL'}">수강취소</p>
    </td>
    <td>
        <div class="row-buttons" th:if="${x.status eq 'REQ'}">
            <input type="hidden" name="id" th:value="${x.id}"/>
            <button value="COMPLETE" type="button">결제완료 처리</button>
            <button value="CANCEL" type="button">수강취소 처리</button>
        </div>
    </td>
</tr>

 

 Note) 실행 결과