Skip to content
Merged
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
25 changes: 24 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Auto detect text files and perform LF normalization
# Chuẩn hóa line ending trong repository.
# Source/config dùng LF ổn định trên mọi môi trường; script Windows giữ CRLF.
* text=auto
*.java text eol=lf
*.xml text eol=lf
*.properties text eol=lf
*.md text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.json text eol=lf
*.sql text eol=lf
*.html text eol=lf
*.css text eol=lf
*.js text eol=lf
*.sh text eol=lf
.gitignore text eol=lf
.gitattributes text eol=lf
Dockerfile text eol=lf
Jenkinsfile text eol=lf
Procfile text eol=lf
*.cmd text eol=crlf
*.bat text eol=crlf
*.jpg filter=lfs diff=lfs merge=lfs -text
*.mwb filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
Expand All @@ -9,3 +29,6 @@
*.woff2 filter=lfs diff=lfs merge=lfs -text
*.eot filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text

# Bộ icon vendor này đang được lưu trực tiếp trong Git, không phải pointer LFS.
src/main/resources/static/assets/vendor/remixicon/** -filter -diff -merge -text
17 changes: 7 additions & 10 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
# Workflow này build và test dự án ecommerce_project bằng Maven trên JDK 17.
# Tài liệu tham khảo: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# Một số action bên dưới do bên thứ ba cung cấp và tuân theo điều khoản riêng của nhà phát hành.

name: Java CI with Maven
name: Ecommerce CI with Maven

on:
push:
Expand All @@ -21,15 +18,15 @@ jobs:

steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B package --file pom.xml

# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
# Gửi dependency graph để Dependabot có dữ liệu cảnh báo bảo mật chính xác hơn.
- name: Update dependency graph
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
28 changes: 19 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
FROM eclipse-temurin:17-jdk-focal

WORKDIR /app

# Build WAR ở stage riêng, runtime chỉ cần JRE để image deploy gọn hơn.
FROM eclipse-temurin:17-jdk-focal AS build

WORKDIR /workspace

COPY .mvn/ .mvn
COPY mvnw pom.xml ./

RUN chmod +x mvnw
RUN chmod +x mvnw && ./mvnw -B -DskipTests dependency:go-offline

RUN ./mvnw dependency:go-offline

COPY src ./src

CMD ["./mvnw", "spring-boot:run"]

RUN ./mvnw -B -DskipTests clean package

FROM eclipse-temurin:17-jre-focal

WORKDIR /app

ENV PORT=8080
EXPOSE 8080

COPY --from=build /workspace/target/ecommerce_project-0.0.1-SNAPSHOT.war app.war

ENTRYPOINT ["java", "-jar", "/app/app.war"]
10 changes: 5 additions & 5 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ pipeline {
agent any

environment {
// Telegram configre
// Cấu hình gửi thông báo Telegram từ credential Jenkins.
TOKEN = credentials('telegram_token')
CHAT_ID = credentials('telegram_chatid')

// Telegram message
// Nội dung thông báo mô tả commit đang được build/deploy.
GIT_MESSAGE = sh(returnStdout: true, script: "git log -n 1 --format=%s ${GIT_COMMIT}").trim()
GIT_AUTHOR = sh(returnStdout: true, script: "git log -n 1 --format=%ae ${GIT_COMMIT}").trim()
GIT_COMMIT_SHORT = sh(returnStdout: true, script: "git rev-parse --short ${GIT_COMMIT}").trim()
Expand All @@ -18,7 +18,7 @@ pipeline {
TEXT_CLEAN = "${JOB_NAME} is Cleaning"
TEXT_RUN = "${JOB_NAME} is Running"

// Telegram parameters
// Trạng thái cuối pipeline gửi về Telegram.
TEXT_SUCCESS_BUILD = "${JOB_NAME} is Success"
TEXT_FAILURE_BUILD = "${JOB_NAME} is Failure"
}
Expand Down Expand Up @@ -59,9 +59,9 @@ pipeline {
stage('Run') {
steps {
sh "curl --location --request POST 'https://api.telegram.org/bot${TOKEN}/sendMessage' --form text='${TEXT_RUN}' --form chat_id='${CHAT_ID}'"
sh 'docker container stop ecommerce || echo "this container does not exist"'
sh 'docker rm -f ecommerce || echo "this container does not exist"'
sh 'docker network create yan || echo "this network exist"'
sh 'echo y | docker container prune'
sh 'docker container prune -f'
sh 'docker run --name ecommerce --network yan --restart=unless-stopped -d yamiannephilim/ecommerce:latest'
}
}
Expand Down
2 changes: 1 addition & 1 deletion Procfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
web: java -jar target/jira_project-0.0.1-SNAPSHOT.war
web: java -jar target/ecommerce_project-0.0.1-SNAPSHOT.war
121 changes: 65 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,72 +1,89 @@
# DỰ ÁN WEB THƯƠNG MẠI ĐIỆN TỬ
Trang web thương mại điện tử được thiết lập để phục vụ một phần hoặc toàn bộ quy trình của hoạt động mua bán hàng hóa hay cung ứng dịch vụ, từ trưng bày giới thiệu hàng hóa, dịch vụ đến giao kết hợp đồng, cung ứng dịch vụ, thanh toán và dịch vụ sau bán hàng.

## LINK DEMO
<div align='center'>
`ecommerce_project` là website thương mại điện tử xây dựng bằng Spring Boot, Thymeleaf, Spring Security và MySQL. Ứng dụng hỗ trợ luồng mua sắm cơ bản: xem sản phẩm, phân loại, chi tiết sản phẩm, giỏ hàng, thanh toán, lịch sử đơn hàng, hồ sơ khách hàng, đăng ký, đăng nhập và đặt lại mật khẩu qua email.

## TÍNH NĂNG CHÍNH

- Hiển thị danh sách sản phẩm, sản phẩm mới, sản phẩm bán chạy và sản phẩm theo phân loại.
- Quản lý giỏ hàng, mã giảm giá, phí vận chuyển và thông tin người nhận.
- Tạo đơn hàng với các phương thức thanh toán tiền mặt, chuyển khoản hoặc thẻ.
- Xác thực người dùng bằng Spring Security và phát JWT cho API refresh token.
- Gửi email đặt lại mật khẩu bằng cấu hình SMTP qua biến môi trường.

## CÔNG NGHỆ

- Java 17
- Spring Boot 2.7.x
- Spring MVC, Spring Data JPA, Spring Security, Thymeleaf
- MySQL
- Maven Wrapper
- Docker, Jenkins, GitHub Actions

## CẤU HÌNH MÔI TRƯỜNG

Ứng dụng không còn lưu credential trực tiếp trong source. Khi chạy local hoặc deploy, cấu hình các biến môi trường sau nếu giá trị mặc định chưa phù hợp:

```bash
PORT=8080
DB_URL=jdbc:mysql://localhost:3306/ecommerce?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Ho_Chi_Minh
DB_USERNAME=root
DB_PASSWORD=your_database_password
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=your_email@gmail.com
MAIL_PASSWORD=your_app_password
JWT_SECRET=your_jwt_secret
JPA_SHOW_SQL=false
JPA_FORMAT_SQL=false
JPA_DDL_AUTO=update
```

[Click vào đây để xem chi tiết](https://ecommerce.yamiannephilim.com)
## CHẠY LOCAL

</div>
```bash
./mvnw spring-boot:run
```

Trên Windows:

```powershell
.\mvnw.cmd spring-boot:run
```

## BUILD

```bash
./mvnw clean package
```

File WAR sau khi build nằm tại:

```text
target/ecommerce_project-0.0.1-SNAPSHOT.war
```

## HÌNH ẢNH DEMO

<p align='center'>
<img src='pic/0.jpg'></img>
</p>

## VIDEO DEMO

<div align='center'>

[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/YXG24rEs8Q4/0.jpg)](https://youtu.be/YXG24rEs8Q4)
[![Video demo dự án thương mại điện tử](https://img.youtube.com/vi/YXG24rEs8Q4/0.jpg)](https://youtu.be/YXG24rEs8Q4)

</div>

## CẤU HÌNH API REFRESH TOKEN
```java
// Refresh token
@GetMapping(TOKEN_VIEW + REFRESH_VIEW)
public void refreshToken(HttpServletRequest request, HttpServletResponse response)
throws StreamWriteException, DatabindException, IOException {
var header = request.getHeader(AUTHORIZATION);
// check token format in authorization header
if (header != null && header.startsWith(TOKEN_PREFIX)) {
// get token from authorization header
try {
var refreshToken = header.substring(TOKEN_PREFIX.length());
var algorithm = HMAC256(SECRET_KEY.getBytes());
var user = userService.getUser(require(algorithm).build().verify(refreshToken).getSubject());
var tokens = new HashMap<>();
tokens.put(ACCESS_TOKEN_KEY,
create().withSubject(user.getEmail())
.withExpiresAt(new Date(currentTimeMillis() + EXPIRATION_TIME))
.withIssuer(request.getRequestURL().toString())
.withClaim(ROLE_CLAIM_KEY,
singleton(new Role(ROLE_PREFIX + user.getRole().getName().toUpperCase()))
.stream().map(Role::getName).collect(toList()))
.sign(algorithm));
tokens.put(REFRESH_TOKEN_KEY, refreshToken);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), tokens);
} catch (Exception e) {
var errorMsg = e.getMessage();
response.setHeader(ERROR_HEADER_KEY, errorMsg);
response.setStatus(FORBIDDEN.value());
var error = new HashMap<>();
error.put(ERROR_MESSAGE_KEY, errorMsg);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), error);
}
} else {
throw new RuntimeException("Refresh token is missing");
}
}
```
## EER DIAGRAM

## EER Diagram
<p align='center'>
<img src='design/EER%20Diagram.jpg'></img>
</p>

### THÀNH VIÊN
## THÀNH VIÊN

Nhóm NOHIT gồm các thành viên:

<img src='pic/2.jpg' align='right' width='21%' height='21%'></img>
Expand All @@ -79,11 +96,3 @@ Nhóm NOHIT gồm các thành viên:
- Nguyễn Tiến Đạt

</div>

### TÍCH HỢP
<img src='pic/1.jpg' align='left' width='3%' height='3%'></img>
<div style='display:flex;'>

- Java JWT » 4.0.0

</div>
1 change: 1 addition & 0 deletions database/ecommerce.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
-- Summary: Script khởi tạo database ecommerce, bảng dữ liệu và dữ liệu mẫu cho website thương mại điện tử.
-- dispose db

DROP DATABASE IF EXISTS ecommerce;
Expand Down
3 changes: 3 additions & 0 deletions design/Ecommerce.mwb
Git LFS file not shown
3 changes: 3 additions & 0 deletions design/Ecommerce.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions design/Ecommerce.xmind
Git LFS file not shown
Binary file removed design/Jira.mwb
Binary file not shown.
Binary file removed design/Jira.png
Binary file not shown.
Binary file removed design/Jira.xmind
Binary file not shown.
11 changes: 6 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.nohit</groupId>
<artifactId>jira_project</artifactId>
<artifactId>ecommerce_project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>jira_project</name>
<description>Demo project for Spring Boot</description>
<!-- Metadata Maven của website thương mại điện tử. -->
<name>ecommerce_project</name>
<description>Website thương mại điện tử xây dựng bằng Spring Boot, Thymeleaf, Spring Security và MySQL.</description>
<properties>
<java.version>11</java.version>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
Expand Down Expand Up @@ -178,4 +179,4 @@
</plugins>
</build>

</project>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.nohit.ecommerce_project;

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;

/**
* Điểm khởi chạy chính của ứng dụng thương mại điện tử Spring Boot.
*/
@SpringBootApplication
public class EcommerceProjectApplication {
public static void main(String[] args) {
SpringApplication.run(EcommerceProjectApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.nohit.jira_project;
package com.nohit.ecommerce_project;

import org.springframework.boot.builder.*;
import org.springframework.boot.web.servlet.support.*;

/**
* Cấu hình khởi tạo ứng dụng khi đóng gói WAR và chạy trên servlet container ngoài.
*/
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(JiraProjectApplication.class);
return application.sources(EcommerceProjectApplication.class);
}
}
9 changes: 9 additions & 0 deletions src/main/java/com/nohit/ecommerce_project/common/Bean.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.nohit.ecommerce_project.common;

/**
* Lưu trạng thái thông báo dùng chung giữa các controller theo thiết kế ban đầu của dự án.
*/
public class Bean {
public static String _msg;
public static boolean _isMsgShow;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.nohit.jira_project.config;
package com.nohit.ecommerce_project.config;

import org.springframework.context.annotation.*;
import org.springframework.security.crypto.bcrypt.*;
import org.springframework.security.crypto.password.*;
import org.springframework.security.web.savedrequest.*;

/**
* Khai báo bean dùng chung cho ứng dụng, gồm PasswordEncoder và request cache không tự tạo session.
*/
@Configuration
public class ApplicationConfig {
@Bean
Expand Down
Loading
Loading