🌎🪐⭐️3차 팀 프로젝트 WORLD 기술서
'WORLD' 는 게임 시리얼넘버(접속코드) 판매 쇼핑몰 사이트입니다.
게임 구매 , 리뷰 , 문의 , 최신게임 출시정보확인 등 컨텐츠 이용이 가능합니다
프로젝트 산출물
발표영상
![movie](https://img.youtube.com/vi/mf2Ge_z0mPc/0.jpg)
진행 기간: 2023.08.01 ~ 2023.08.30
😃 팀원
![](https://velog.velcdn.com/images/ohtj6644/post/98d290ff-eede-4ce0-92fd-2c07c6ec4393/image.png)
목차
🔗 배포 링크
https://www.world.gh5.site
🌎배포 환경
![](https://velog.velcdn.com/images/ohtj6644/post/416f238d-ac1e-4fc9-9cd6-7f610669614f/image.png)
🔗 프로젝트 저장소
🤝 팀 규칙
브랜치 전략
- upstream에는 main 브랜치만 존재
- 브랜치명:
작업명/작업자이니셜
- 코드리뷰 받고 승인 받으면
origin:main
에 merge
### 협업 툴 Trello
![](https://i.postimg.cc/x806CZmC/2023-08-16-12-08-04.png)
📂 폴더 구조
📦world
├── 📄스타일.css
├── 📄template.html
├── 📄test.java
├── 📄test.java
├── 📂admin
├── 📂calenbar
├── 📂email
├── 📂file
├── 📂mypage
└── 📂notice
└── 📂order
└── 📂product
└── 📂qna
└── 📂qnaAnswer
└── 📂review
└── 📂user
🛠️ 기술 스택
![](https://velog.velcdn.com/images/ohtj6644/post/3f931921-ec49-4bd3-8429-0e92677a07ea/image.png)
개발도구
- SpringBoot
- JAVA
- Thymeleaf
- MariaDB
- HTML
- CSS
- JS
- Ajax
- Bootstrap
- Tailwind css
- CentOS
- jQuery
- NAVER Cloud
개발환경
- IntelliJ IDEA
- DBeaver
- Termius
- GitHub-Action CI/CD
📖 서비스 소개
기능 구현
- 회원 가입
- 상품상세
- 상품구매
- 상품목록
- 관리자페이지
- 마이페이지
ERD
![](https://trello.com/1/cards/64b0ef974ea6db7b489e8b34/attachments/64d5a8295fd40d8abdfa0cdd/previews/64d5a82a5fd40d8abdfa130b/download/world_erd.png)
페이지
회원가입 |
---|
![회원가입](https://i.postimg.cc/ncwM3Q0S/image.png) |
상품상세 |
---|
![상품상세](https://i.postimg.cc/Pr8kkpdR/image.png) |
상품구매 |
---|
![상품구매](https://i.postimg.cc/cJhpcfcr/image.png) |
상품목록 |
---|
![상품목록](https://i.postimg.cc/MZgpR8Tg/image.png) |
관리자페이지 |
---|
![관리자페이지](https://i.postimg.cc/QM6Xd2w4/image.png) |
마이페이지* |
---|
![마이페이지](https://i.postimg.cc/SQYymtrd/image.png) |
👑 구현미리보기
구현사항
📌 회원가입
- 이메일
-
내용
1) 설명
@GetMapping("/mailCheck")
@ResponseBody
public int processMailCheck(@RequestParam("email") String email) throws Exception {
int mailKey = (int) ((Math.random() * (99999 - 10000 + 1)) + 10000);
String to = email;
String title = "환영합니다! WORLD 입니다. 회원가입시 필요한 인증번호 입니다.";
String content = "[인증번호] " + mailKey + " 입니다. <br/> 인증번호 확인란에 기입해주세요.";
try {
MimeMessage mail = mailSender.createMimeMessage();
MimeMessageHelper mailHelper = new MimeMessageHelper(mail, true, "UTF-8");
mailHelper.setTo(to);
mailHelper.setSubject(title);
mailHelper.setText(content, true);
mailSender.send(mail);
} catch (Exception e) {
throw new DataNotFoundException("error");
}
return mailKey;
}
- 닉네임 중복 확인
-
내용
1) 설명
```js
$.ajax({
url: '/user/checkDuplicate',
type: 'GET',
dataType: 'JSON',
data: {
nickname: nickname
},
success: function(response) {
if (!response) {
isNicknameDuplicate = false;
}
if (isNicknameDuplicate) {
$('#nicknameCheckResult')
.text('이미 사용 중인 닉네임입니다.')
.css('color', 'red')
.css('margin-left', '40px');
} else {
$('#nicknameCheckResult')
.text('사용 가능한 닉네임입니다.')
.css('margin-left', '70px')
.css('color', 'white');
}
},
error: function(xhr, status, error) {
console.error('유저 삭제 오류:', error);
}
});
}
```
📌 관리자페이지 매출
@PostMapping("/")
public String adminMainSearsh(Model model, @Valid AdminSearchForm adminSearchForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "redirect:/ad/order";
}
LocalDateTime start = adminSearchForm.getStart();
LocalDateTime end = adminSearchForm.getEnd();
List<ProductOrder> list = this.orderService.getOrdersBetweenDates(start,end);
int num=list.size();
int totalPrice=1;
for (ProductOrder order : list) {
Product product = order.getProduct();
if (product != null) {
totalPrice += product.getPrice();
}
}
DecimalFormat decimalFormat = new DecimalFormat("#,###");
String formattedAllPrice = decimalFormat.format(totalPrice);
LocalDate todayLocalDate = LocalDate.now();
int priceM1 = this.adminService.requestMonthPrice(YearMonth.now());
int priceM2 = this.adminService.requestMonthPrice(this.adminService.MonthMinus(1));
int priceM3 = this.adminService.requestMonthPrice(this.adminService.MonthMinus(2));
int priceM4 = this.adminService.requestMonthPrice(this.adminService.MonthMinus(3));
int priceM5 = this.adminService.requestMonthPrice(this.adminService.MonthMinus(4));
String month1 = YearMonth.now().toString();
String month2 = this.adminService.MonthMinus(1).toString();
String month3 = this.adminService.MonthMinus(2).toString();
String month4 = this.adminService.MonthMinus(3).toString();
String month5 = this.adminService.MonthMinus(4).toString();
model.addAttribute("month1",month1);
model.addAttribute("month2",month2);
model.addAttribute("month3",month3);
model.addAttribute("month4",month4);
model.addAttribute("month5",month5);
model.addAttribute("priceM1",priceM1);
model.addAttribute("priceM2",priceM2);
model.addAttribute("priceM3",priceM3);
model.addAttribute("priceM4",priceM4);
model.addAttribute("priceM5",priceM5);
model.addAttribute("listSize",num);
model.addAttribute("allPrice",formattedAllPrice);
return "admin/admin_main";
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/172203/font-awesome.min.css">
<link rel="stylesheet" href="/layout/admin.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.1.0/chart.min.js"></script>
</head>
<body>
<nav class="navbar navbar-default no-margin">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header fixed-brand">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" id="menu-toggle">
<span class="glyphicon glyphicon-th-large" aria-hidden="true"></span>
</button>
<a class="navbar-brand" th:href="@{/}"><img class="rogoimg" src="https://i.postimg.cc/qRMyWWkr/image.png"/></a>
</div>
<!-- navbar-header-->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
</ul>
</div>
<!-- bs-example-navbar-collapse-1 -->
</nav>
<div id="wrapper">
<!-- Sidebar -->
<div id="sidebar-wrapper">
<ul class="sidebar-nav nav-pills nav-stacked" id="menu">
<li>
<a th:href="@{/admin/}"><span class="fa-stack fa-lg pull-left"><i class="fa fa-cloud-download fa-stack-1x "></i></span>매출</a>
</li>
<li>
<a th:href="@{/admin/order}"><span class="fa-stack fa-lg pull-left"><i class="fa fa-cart-plus fa-stack-1x "></i></span>주문 관리</a>
</li>
<li>
<a th:href="@{/admin/product}"><span class="fa-stack fa-lg pull-left"><i class="fa fa-cart-plus fa-stack-1x "></i></span>상품 관리</a>
</li>
<li>
<a th:href="@{/admin/user}"><span class="fa-stack fa-lg pull-left"><i class="fa fa-wrench fa-stack-1x "></i></span>회원 관리</a>
</li>
<li>
<a th:href="@{/admin/review}"><span class="fa-stack fa-lg pull-left"><i class="fa fa-server fa-stack-1x "></i></span>리뷰 관리</a>
</li>
<li>
<a th:href="@{/admin/qna}"><span class="fa-stack fa-lg pull-left"><i class="fa fa-server fa-stack-1x "></i></span>문의사항 관리</a>
</li>
<li>
<a th:href="@{/admin/notice}"><span class="fa-stack fa-lg pull-left"><i class="fa fa-server fa-stack-1x "></i></span>공지사항 관리</a>
</li>
</ul>
</div>
<!-- /#sidebar-wrapper -->
<!-- Page Content -->
<div id="page-content-wrapper">
<div class="container-fluid xyz">
<div class="row">
<div class="col-lg-12">
<h1>매출</h1>
<div class="date_content">
<div class="date_title"><p>기간별 조회. </p></div>
<form th:action="@{/ad}" th:object="${adminSearchForm}" method="POST" class="date_form">
<div class="birthday">
<p>조회기간 :</p>
<input type="datetime-local" th:field="*{start}">
<p>~</p>
<input type="datetime-local" th:field="*{end}">
<button type="submit" class="date_btn"> 조회하기 </button>
</div>
</form>
<div class="date_data"><div>
<p >기간 내 판매 건 수 :</p>
<p th:text="${listSize}">10(타임리프)</p>
<p> 건</p>
</div>
<div>
<p>기간 내 총 매출 :</p>
<p th:text="${allPrice}"></p>
<p>won</p>
</div>
</div>
</div>
<div class="date_content">
<div class="date_title"><p>매출 동향 </p></div>
<canvas id="myChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- /#page-content-wrapper -->
</div>
<!-- /#wrapper -->
<!-- jQuery -->
<script>
var ctx = document.getElementById('myChart').getContext('2d');
var priceM1 = [[${priceM1}]];
var priceM2 = [[${priceM2}]];
var priceM3 = [[${priceM3}]];
var priceM4 = [[${priceM4}]];
var priceM5 = [[${priceM5}]];
var month1 = '[[${month1}]]';
var month2 = '[[${month2}]]';
var month3 = '[[${month3}]]';
var month4 = '[[${month4}]]';
var month5 = '[[${month5}]]';
console.log(month4.toString())
var chart = new Chart(ctx, {
type: 'bar',
data: {
labels: [month5, month4, month3, month2, month1],
datasets: [{
label: '매출 (단위 : 원)',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: [priceM5, priceM4, priceM3, priceM2, priceM1]
}]
},
});
</script>
</body>
</html>
👨👨👧👦 단위업무표
![](https://velog.velcdn.com/images/ohtj6644/post/9fa6da06-ef81-4dce-8f56-de59d922b9d3/image.jpg)
![](https://velog.velcdn.com/images/ohtj6644/post/2ad8fd50-a196-480a-97c6-8a60a307cc33/image.jpg)
![](https://velog.velcdn.com/images/ohtj6644/post/4c8de645-f288-4094-948c-65f960fbda38/image.jpg)
![](https://velog.velcdn.com/images/ohtj6644/post/251e761c-56c7-4286-bb9e-812e53e64f49/image.jpg)
감사합니다🙆🏼♀️
정보 감사합니다.