Giới thiệu Hệ thống
Hệ thống này được thiết kế để quản lý hiệu quả việc điều phối nhân sự và phương tiện trong lĩnh vực giao nhận và vận chuyển. Mục tiêu chính là tối ưu hóa quy trình làm việc, nâng cao hiệu quả hoạt động logistics và cung cấp các công cụ quản lý toàn diện.
Hệ thống kết hợp các công nghệ hiện đại:
- **SSM (Spring, Spring MVC, MyBatis)** cho tầng backend, đảm bảo khả năng mở rộng và xử lý logic nghiệp vụ mạnh mẽ.
- **Vue.js** cho giao diện người dùng web quản trị, cung cấp trải nghiệm tương tác và phản hồi nhanh chóng.
- **UniApp** để phát triển ứng dụng di động (mini-app) cho nhân viên giao nhận, giúp họ dễ dàng truy cập và cập nhật thông tin khi đang di chuyển.
Ngăn xếp Công nghệ
Framework Backend: SSM
SSM là sự kết hợp mạnh mẽ của ba framework Java chính: Spring, Spring MVC và MyBatis. Mỗi thành phần đảm nhiệm một vai trò cụ thể để xây dựng một ứng dụng web Java hoàn chỉnh và hiệu quả.
-
Spring Framework: Là một framework phát triển Java nhẹ và toàn diện, cung cấp một loạt các tính năng cốt lõi như Dependency Injection (DI) để quản lý vòng đời của các đối tượng và mối quan hệ giữa chúng, Aspect-Oriented Programming (AOP) để tách biệt các mối quan tâm xuyên suốt, và quản lý giao dịch. Spring đóng vai trò là hạt nhân, giúp giảm sự phụ thuộc và tăng cường tính mô-đun hóa trong ứng dụng.
-
Spring MVC Framework: Là một phần của Spring, được xây dựng để tạo ra các ứng dụng web tuân theo mô hình kiến trúc Model-View-Controller (MVC). Trong đó, Model chịu trách nhiệm xử lý logic nghiệp vụ và dữ liệu, View hiển thị thông tin cho người dùng, và Controller xử lý các yêu cầu HTTP đến, điều phối giữa Model và View để tạo ra phản hồi phù hợp.
-
MyBatis Framework: Là một framework tầng truy cập dữ liệu, giúp đơn giản hóa việc tương tác với cơ sở dữ liệu. Thay vì viết mã JDBC thủ công, MyBatis cho phép các nhà phát triển ánh xạ các đối tượng Java với các bảng cơ sở dữ liệu thông qua các tệp cấu hình XML hoặc chú thích. Điều này mang lại sự linh hoạt cao trong việc định nghĩa và thực thi các câu lệnh SQL, đồng thời tích hợp tốt với Spring.
Sự kết hợp của SSM mang lại khả năng cấu hình linh hoạt, dễ học và dễ sử dụng, cho phép các nhà phát triển nhanh chóng xây dựng các ứng dụng web Java ổn định và hiệu suất cao.
Ví dụ mã nguồn Spring Boot đơn giản:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class DeliveryServiceApplication {
public static void main(String[] args) {
SpringApplication.run(DeliveryServiceApplication.class, args);
}
@GetMapping("/welcome")
public String getWelcomeMessage() {
return "Chào mừng đến với Dịch vụ Giao nhận!";
}
}
Đoạn mã trên trình bày một ứng dụng Spring Boot đơn giản. Lớp DeliveryServiceApplication được đánh dấu là một ứng dụng Spring Boot và cũng là một bộ điều khiển REST. Phương thức getWelcomeMessage xử lý yêu cầu GET tới đường dẫn /welcome và trả về một chuỗi chào mừng.
Framework Frontend: Vue.js
Vue.js là một framework JavaScript mạnh mẽ và phổ biến để xây dựng giao diện người dùng tương tác. Một trong những ưu điểm chính của Vue.js là kỹ thuật Virtual DOM, giúp tối ưu hóa hiệu suất cập nhật DOM bằng cách giảm thiểu số lần thao tác trực tiếp với DOM thật.
Với cơ chế ràng buộc dữ liệu phản ứng (reactive data binding), Virtual DOM và kiến trúc dựa trên component, Vue.js cung cấp một mô hình phát triển linh hoạt, hiệu quả và dễ bảo trì. Khi dữ liệu thay đổi, Vue.js tự động cập nhật giao diện người dùng mà không yêu cầu nhà phát triển phải thao tác DOM thủ công, cho phép tập trung vào logic xử lý dữ liệu.
Ví dụ minh họa tính năng cơ bản của Vue.js:
<!DOCTYPE html>
<html>
<head>
<title>Demo Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
<div id="admin-dashboard">
<h3>{{ dashboardMessage }}</h3>
<button @click="updateDashboardMessage">Cập nhật thông điệp</button>
</div>
<script>
var adminApp = new Vue({
el: '#admin-dashboard',
data: {
dashboardMessage: 'Chào mừng bạn đến với Bảng điều khiển quản trị!'
},
methods: {
updateDashboardMessage: function() {
this.dashboardMessage = 'Vue.js giúp quản lý dễ dàng!';
}
}
});
</script>
</body>
<!-- Lưu ý: Trong môi trường thực tế, nên dùng phiên bản Vue.js được tối ưu cho sản xuất (production) -->
</html>
Trong ví dụ này, một thể hiện Vue được tạo và liên kết với phần tử <div id="admin-dashboard">. Biến dashboardMessage trong thuộc tính data được hiển thị trên trang. Khi người dùng nhấp vào nút, phương thức updateDashboardMessage sẽ thay đổi giá trị của dashboardMessage, và nhờ cơ chế phản ứng của Vue.js, giao diện sẽ tự động cập nhật.
Framework Tầng bền vững: MyBatis
MyBatis là một framework tầng bền vững mã nguồn mở, giúp đơn giản hóa việc tương tác và quản lý cơ sở dữ liệu. Nguyên tắc cốt lõi của MyBatis là tách biệt mã SQL khỏi mã Java, mô tả các thao tác cơ sở dữ liệu thông qua các tệp XML hoặc chú thích, từ đó đạt được sự tách rời và linh hoạt cho tầng truy cập dữ liệu.
Những lợi thế chính của MyBatis bao gồm:
- Đơn giản hóa thao tác cơ sở dữ liệu: MyBatis thông qua khả năng ánh xạ SQL mạnh mẽ, cho phép ánh xạ các đối tượng Java với các bảng cơ sở dữ liệu, giảm đáng kể công sức viết và bảo trì các câu lệnh SQL thủ công.
- Kiểm soát SQL linh hoạt: Hỗ trợ SQL động, cho phép tạo các câu lệnh SQL tùy chỉnh dựa trên các điều kiện và logic khác nhau, giúp các thao tác truy vấn, cập nhật trở nên linh hoạt và mạnh mẽ hơn.
- Hỗ trợ Cache: Cung cấp hỗ trợ cho bộ nhớ đệm (cache) cấp một và cấp hai, giúp giảm số lần truy cập cơ sở dữ liệu và cải thiện hiệu suất hệ thống.
- Khả năng mở rộng cao: Sử dụng cơ chế plugin, cho phép các nhà phát triển dễ dàng mở rộng và tùy chỉnh các chức năng để đáp ứng các yêu cầu nghiệp vụ đa dạng.
Kiểm thử Hệ thống
Để đảm bảo chất lượng và độ tin cậy cao nhất cho hệ thống, một quy trình kiểm thử toàn diện đã được thực hiện. Mục tiêu là xác định các vấn đề tiềm ẩn từ nhiều góc độ và thực hiện các cải tiến kịp thời nhằm đảm bảo tính toàn vẹn và ổn định của hệ thống.
Mục tiêu Kiểm thử Hệ thống
Kiểm thử hệ thống là một giai đoạn then chốt trong chu kỳ phát triển phần mềm, đóng vai trò là hàng rào cuối cùng để đảm bảo chất lượng và độ tin cậy của sản phẩm. Mục tiêu chính là ngăn ngừa các vấn đề mà người dùng có thể gặp phải, từ đó nâng cao trải nghiệm người dùng.
Chúng tôi tiếp cận việc kiểm thử từ nhiều khía cạnh và kịch bản khác nhau, mô phỏng các tình huống sử dụng thực tế để phát hiện và khắc phục các lỗi. Quá trình này cũng cho phép đánh giá chất lượng tổng thể của hệ thống, bao gồm tính hoàn chỉnh của chức năng và tính hợp lý của logic nghiệp vụ. Một quy trình kiểm thử thành công sẽ cải thiện đáng kể chất lượng hệ thống và sự hài lòng của người dùng.
Mục tiêu cuối cùng là xác minh rằng hệ thống đáp ứng đúng các yêu cầu đã định trong tài liệu đặc tả và xác định mọi mâu thuẫn hoặc thiếu sót. Luôn giữ góc nhìn của người dùng trong suốt quá trình kiểm thử, chúng tôi đảm bảo rằng kết quả mong đợi khớp với kết quả thực tế.
Kiểm thử Chức năng Hệ thống
Kiểm thử chức năng tập trung vào việc xác minh từng mô-đun chức năng của hệ thống. Chúng tôi áp dụng phương pháp kiểm thử hộp đen, bao gồm các hành động như nhấp chuột, nhập giá trị biên, và kiểm tra các trường bắt buộc/không bắt buộc. Các trường hợp kiểm thử được thiết kế và thực thi để đưa ra kết luận về hoạt động của hệ thống.
Ví dụ, đối với chức năng đăng nhập, hệ thống sẽ xác thực thông tin tài khoản và mật khẩu. Nếu bất kỳ thông tin nào không chính xác, hệ thống sẽ hiển thị thông báo lỗi. Giao diện cũng kiểm tra quyền hạn của vai trò; ví dụ, một người dùng có vai trò quản trị viên sẽ gặp lỗi nếu cố gắng đăng nhập với vai trò không phù hợp. Dưới đây là bảng ví dụ về các trường hợp kiểm thử cho chức năng đăng nhập:
| Dữ liệu đầu vào | Kết quả dự kiến | Kết quả thực tế | Phân tích |
|---|---|---|---|
| Tên người dùng: admin_01, Mật khẩu: 123456, Mã xác nhận: 7890 (đúng) | Đăng nhập thành công | Đăng nhập thành công | Khớp |
| Tên người dùng: admin_01, Mật khẩu: sai_mk, Mã xác nhận: 7890 (đúng) | Mật khẩu không đúng | Mật khẩu không đúng, vui lòng thử lại | Khớp |
| Tên người dùng: admin_01, Mật khẩu: 123456, Mã xác nhận: 1234 (sai) | Mã xác nhận không đúng | Thông tin mã xác nhận không đúng | Khớp |
| Tên người dùng: (trống), Mật khẩu: 123456, Mã xác nhận: 7890 (đúng) | Tên người dùng bắt buộc | Vui lòng nhập tên người dùng | Khớp |
| Tên người dùng: admin_01, Mật khẩu: (trống), Mã xác nhận: 7890 (đúng) | Mật khẩu không đúng | Mật khẩu không đúng, vui lòng thử lại | Khớp |
Ngoài ra, hệ thống cũng đã được kiểm thử chức năng quản lý người dùng, bao gồm thêm, chỉnh sửa, xóa và tìm kiếm người dùng. Dưới đây là các trường hợp kiểm thử cho các chức năng này:
1. Trường hợp kiểm thử chức năng Thêm người dùng:
| Dữ liệu đầu vào | Kết quả dự kiến | Kết quả thực tế | Phân tích |
|---|---|---|---|
| Tên người dùng: user_alpha, Mật khẩu: pass_user, Vai trò: Người dùng thường | Thêm thành công, hiển thị trong danh sách người dùng | User_alpha xuất hiện trong danh sách | Khớp |
| Tên người dùng: user_beta, Mật khẩu: pass_beta, Vai trò: Người dùng thường | Thêm thành công, hiển thị trong danh sách người dùng | User_beta xuất hiện trong danh sách | Khớp |
| Tên người dùng: user_alpha, Mật khẩu: pass_user, Vai trò: Người dùng thường | Thêm thất bại, thông báo tên người dùng đã tồn tại | Thêm thất bại, tên người dùng đã tồn tại | Khớp |
| Tên người dùng: (trống), Mật khẩu: pass_new, Vai trò: Người dùng thường | Thêm thất bại, thông báo tên người dùng không được trống | Thêm thất bại, tên người dùng không được trống | Khớp |
2. Trường hợp kiểm thử chức năng Chỉnh sửa người dùng:
| Dữ liệu đầu vào | Kết quả dự kiến | Kết quả thực tế | Phân tích |
|---|---|---|---|
| Chọn user_alpha, sửa mật khẩu thành new_pass_alpha | Chỉnh sửa thành công, mật khẩu đã thay đổi | Mật khẩu của user_alpha đã đổi thành new_pass_alpha | Khớp |
| Chọn user_beta, sửa vai trò thành Quản trị viên | Chỉnh sửa thành công, vai trò đã thay đổi | Vai trò của user_beta đã đổi thành Quản trị viên | Khớp |
| Chọn user_alpha, xóa tên người dùng | Chỉnh sửa thất bại, thông báo tên người dùng không được trống | Chỉnh sửa thất bại, tên người dùng không được trống | Khớp |
3. Trường hợp kiểm thử chức năng Xóa người dùng:
| Dữ liệu đầu vào | Kết quả dự kiến | Kết quả thực tế | Phân tích |
|---|---|---|---|
| Chọn user_alpha để xóa, xác nhận hành động | Hệ thống hỏi xác nhận, sau đó người dùng bị xóa | User_alpha đã bị xóa thành công | Khớp |
| Chọn user_beta để xóa, hủy bỏ hành động | Hệ thống hỏi xác nhận, sau đó người dùng không bị xóa | User_beta không bị xóa | Khớp |
Thông qua các bài kiểm thử chức năng này, chúng tôi đảm bảo rằng hệ thống hoạt động đúng theo các yêu cầu đã định và cung cấp trải nghiệm người dùng đầy đủ chức năng.
Kết luận Kiểm thử Hệ thống
Hệ thống đã được kiểm thử chủ yếu bằng phương pháp kiểm thử hộp đen, thông qua việc mô phỏng các tình huống người dùng thực tế để xác minh từng chức năng. Điều này giúp đảm bảo tính đúng đắn của luồng hoạt động hệ thống. Kiểm thử hệ thống là một bước không thể thiếu để hoàn thiện hệ thống và nâng cao khả năng sử dụng.
Mục đích chính của việc kiểm thử là xác minh các mô-đun chức năng có đáp ứng triết lý thiết kế ban đầu và logic của chúng có chính xác hay không. Hệ thống này không yêu cầu xử lý logic quá phức tạp, nhằm mục đích dễ sử dụng. Mục tiêu cuối cùng của kiểm thử luôn xoay quanh trải nghiệm người dùng. Tất cả các kịch bản kiểm thử phải phù hợp với nhu cầu người dùng. Sau một loạt các quy trình kiểm thử, kết quả cho thấy hệ thống đã triển khai đáp ứng các yêu cầu về chức năng và hiệu suất.
Tham khảo Mã nguồn
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Calendar;
import java.util.Date;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import org.springframework.util.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
// Định nghĩa chú thích để bỏ qua xác thực
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface SkipAuth {}
// Lớp điều khiển xác thực người dùng
@RestController
public class AuthenticationController {
private final UserAccountService accountService;
private final AuthTokenService authTokenService;
public AuthenticationController(UserAccountService accountService, AuthTokenService authTokenService) {
this.accountService = accountService;
this.authTokenService = authTokenService;
}
// Endpoint đăng nhập
@SkipAuth
@PostMapping("/authenticate")
public ApiResponse loginUser(String username, String passwordHash, String verificationCode, HttpServletRequest request) {
UserAccount user = accountService.findByUsername(username); // Giả định tồn tại UserAccountService và UserAccount
if (user == null || !user.getPassword().equals(passwordHash)) {
return ApiResponse.error("Tên đăng nhập hoặc mật khẩu không đúng.");
}
// Logic kiểm tra mã xác nhận có thể được thêm vào đây
// if (!verifyCaptcha(verificationCode)) { return ApiResponse.error("Mã xác nhận sai."); }
String token = authTokenService.issueAuthToken(user.getAccountId(), username, "users", user.getRole());
return ApiResponse.success().put("accessToken", token); // Giả định ApiResponse là một lớp tiện ích
}
}
// Giao diện cho dịch vụ quản lý token xác thực
interface AuthTokenService {
String issueAuthToken(Long userId, String userName, String entityType, String userRole);
AuthToken findTokenByValue(String tokenValue);
}
// Lớp triển khai dịch vụ quản lý token
@Service
class AuthTokenServiceImpl implements AuthTokenService {
private final AuthTokenRepository tokenRepository; // Giả định tồn tại AuthTokenRepository và AuthToken
public AuthTokenServiceImpl(AuthTokenRepository tokenRepository) {
this.tokenRepository = tokenRepository;
}
@Override
public String issueAuthToken(Long userId, String userName, String entityType, String userRole) {
AuthToken existingToken = tokenRepository.findByUserIdAndRole(userId, userRole);
String newAccessToken = generateRandomTokenString(40); // Tạo chuỗi token ngẫu nhiên
Calendar expiryTime = Calendar.getInstance();
expiryTime.add(Calendar.HOUR_OF_DAY, 2); // Token có giá trị trong 2 giờ
if (existingToken != null) {
existingToken.setTokenValue(newAccessToken);
existingToken.setExpiresAt(expiryTime.getTime());
tokenRepository.save(existingToken);
} else {
AuthToken newToken = new AuthToken(userId, userName, entityType, userRole, newAccessToken, expiryTime.getTime());
tokenRepository.save(newToken);
}
return newAccessToken;
}
@Override
public AuthToken findTokenByValue(String tokenValue) {
return tokenRepository.findByTokenValue(tokenValue);
}
private String generateRandomTokenString(int length) {
// Cách đơn giản để tạo chuỗi ngẫu nhiên (nên dùng phương pháp an toàn hơn trong môi trường thực tế)
return java.util.UUID.randomUUID().toString().replace("-", "").substring(0, Math.min(length, 32));
}
}
// Lớp Interceptor để xác thực yêu cầu
@Component
class RequestAuthInterceptor implements HandlerInterceptor {
public static final String AUTH_HEADER_NAME = "X-Auth-Token";
private final AuthTokenService authTokenService;
public RequestAuthInterceptor(AuthTokenService authTokenService) {
this.authTokenService = authTokenService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Cấu hình tiêu đề CORS để cho phép các yêu cầu đa nguồn gốc
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600"); // Cache preflight request for 1 hour
response.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Auth-Token, Origin, Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
// Xử lý yêu cầu OPTIONS (preflight request)
if (request.getMethod().equals(HttpMethod.OPTIONS.name())) {
response.setStatus(HttpServletResponse.SC_OK);
return false;
}
SkipAuth skipAuthAnnotation = null;
if (handler instanceof HandlerMethod) {
skipAuthAnnotation = ((HandlerMethod) handler).getMethodAnnotation(SkipAuth.class);
} else {
return true; // Nếu không phải HandlerMethod, cho phép tiếp tục
}
// Nếu phương thức được đánh dấu @SkipAuth, bỏ qua xác thực
if (skipAuthAnnotation != null) {
return true;
}
// Lấy token từ tiêu đề yêu cầu
String tokenValue = request.getHeader(AUTH_HEADER_NAME);
AuthToken tokenRecord = null;
if (StringUtils.hasText(tokenValue)) {
tokenRecord = authTokenService.findTokenByValue(tokenValue);
}
// Kiểm tra token có hợp lệ và chưa hết hạn
if (tokenRecord != null && tokenRecord.getExpiresAt().after(new Date())) {
// Lưu thông tin người dùng vào session hoặc request attributes để sử dụng sau này
request.setAttribute("currentUserId", tokenRecord.getUserId());
request.setAttribute("currentUserRole", tokenRecord.getRole());
request.setAttribute("currentUserName", tokenRecord.getUserName());
return true; // Token hợp lệ, cho phép yêu cầu tiếp tục
}
// Xác thực thất bại: trả về lỗi 401 Unauthorized
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.print("{\"code\":401,\"message\":\"Bạn cần đăng nhập để truy cập tài nguyên này.\"}");
writer.flush();
return false; // Chặn yêu cầu
}
}
// Giả định các lớp đơn giản cho ví dụ
class UserAccount {
private Long accountId;
private String username;
private String password;
private String role;
// Getters and setters
public Long getAccountId() { return accountId; }
public String getUsername() { return username; }
public String getPassword() { return password; }
public String getRole() { return role; }
public void setAccountId(Long accountId) { this.accountId = accountId; }
public void setUsername(String username) { this.username = username; }
public void setPassword(String password) { this.password = password; }
public void setRole(String role) { this.role = role; }
}
interface UserAccountService {
UserAccount findByUsername(String username);
}
class AuthToken {
private Long id;
private Long userId;
private String userName;
private String entityType;
private String role;
private String tokenValue;
private Date expiresAt;
public AuthToken(Long userId, String userName, String entityType, String role, String tokenValue, Date expiresAt) {
this.userId = userId;
this.userName = userName;
this.entityType = entityType;
this.role = role;
this.tokenValue = tokenValue;
this.expiresAt = expiresAt;
}
// Getters and setters
public Long getId() { return id; }
public Long getUserId() { return userId; }
public String getUserName() { return userName; }
public String getEntityType() { return entityType; }
public String getRole() { return role; }
public String getTokenValue() { return tokenValue; }
public Date getExpiresAt() { return expiresAt; }
public void setId(Long id) { this.id = id; }
public void setUserId(Long userId) { this.userId = userId; }
public void setUserName(String userName) { this.userName = userName; }
public void setEntityType(String entityType) { this.entityType = entityType; }
public void setRole(String role) { this.role = role; }
public void setTokenValue(String tokenValue) { this.tokenValue = tokenValue; }
public void setExpiresAt(Date expiresAt) { this.expiresAt = expiresAt; }
}
interface AuthTokenRepository {
AuthToken findByUserIdAndRole(Long userId, String role);
AuthToken findByTokenValue(String tokenValue);
void save(AuthToken token);
}
class ApiResponse {
private int code;
private String message;
private java.util.Map<String, Object> data = new java.util.HashMap<>();
public static ApiResponse success() {
ApiResponse resp = new ApiResponse();
resp.setCode(200);
resp.setMessage("Thành công");
return resp;
}
public static ApiResponse error(String message) {
ApiResponse resp = new ApiResponse();
resp.setCode(500);
resp.setMessage(message);
return resp;
}
public ApiResponse put(String key, Object value) {
this.data.put(key, value);
return this;
}
// Getters and setters
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public java.util.Map<String, Object> getData() { return data; }
public void setData(java.util.Map<String, Object> data) { this.data = data; }
}
Các đoạn mã Java trên minh họa quy trình đăng nhập người dùng, tạo và quản lý token xác thực, cùng với một interceptor để kiểm tra quyền truy cập dựa trên token. Chú thích @SkipAuth được sử dụng để bỏ qua kiểm tra quyền cho các phương thức cụ thể. Dịch vụ AuthTokenService chịu trách nhiệm phát hành và quản lý token, bao gồm việc kiểm tra token hiện có và đặt thời gian hết hạn.
RequestAuthInterceptor là một interceptor HTTP, được cấu hình để xử lý các yêu cầu. Nó thiết lập các tiêu đề CORS và kiểm tra sự hiện diện của @SkipAuth. Nếu một token được tìm thấy trong tiêu đề yêu cầu và còn hiệu lực, thông tin người dùng được lưu vào thuộc tính yêu cầu và yêu cầu được cho phép tiếp tục. Nếu không, một phản hồi 401 Unauthorized sẽ được gửi, yêu cầu người dùng đăng nhập.
Thiết kế Cơ sở Dữ liệu
Dưới đây là ví dụ về định nghĩa bảng cho một bảng quản lý hàng hóa trong kho, có thể áp dụng trong hệ thống quản lý logistics để theo dõi các mặt hàng được giao nhận:
DROP TABLE IF EXISTS `inventory_item`;
CREATE TABLE `inventory_item` (
`item_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'Mã sản phẩm (khóa chính)',
`item_name` VARCHAR(150) NOT NULL COMMENT 'Tên sản phẩm',
`unit_price` DECIMAL(12, 2) NOT NULL COMMENT 'Giá đơn vị sản phẩm',
`item_description` VARCHAR(500) DEFAULT NULL COMMENT 'Mô tả chi tiết sản phẩm',
`quantity_in_stock` INT(11) NOT NULL COMMENT 'Số lượng tồn kho',
`added_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Thời gian thêm vào',
`last_updated` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Thời gian cập nhật gần nhất',
PRIMARY KEY (`item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Bảng quản lý sản phẩm trong kho';
Bảng inventory_item bao gồm các trường sau:
item_id: Khóa chính tự động tăng, định danh duy nhất cho mỗi mặt hàng.item_name: Tên của sản phẩm, là trường bắt buộc và có độ dài tối đa 150 ký tự.unit_price: Giá bán của mỗi đơn vị sản phẩm, lưu trữ với độ chính xác 12 chữ số, trong đó có 2 chữ số sau dấu thập phân. Đây là trường bắt buộc.item_description: Mô tả chi tiết về sản phẩm, có thể để trống và có độ dài tối đa 500 ký tự.quantity_in_stock: Số lượng sản phẩm hiện có trong kho, là một số nguyên và là trường bắt buộc.added_at: Thời điểm sản phẩm được thêm vào hệ thống, tự động ghi nhận thời gian hiện tại khi bản ghi được tạo.last_updated: Thời điểm thông tin sản phẩm được cập nhật gần nhất, tự động cập nhật mỗi khi bản ghi có thay đổi.
Ví dụ câu lệnh chèn dữ liệu vào bảng inventory_item:
INSERT INTO `inventory_item` (`item_name`, `unit_price`, `item_description`, `quantity_in_stock`)
VALUES ('Máy tính bảng Pro X', 1299.99, 'Máy tính bảng hiệu suất cao với màn hình Retina', 80);
INSERT INTO `inventory_item` (`item_name`, `unit_price`, `item_description`, `quantity_in_stock`)`
VALUES ('Điện thoại thông minh Ultra 5G', 999.50, 'Thiết bị di động tiên tiến hỗ trợ 5G', 120);
INSERT INTO `inventory_item` (`item_name`, `unit_price`, `item_description`, `quantity_in_stock`)
VALUES ('Tai nghe không dây Premium', 249.00, 'Tai nghe khử tiếng ồn cao cấp', 200);