Tổng Quan Hệ Thống
Hệ thống đặt món ăn căn trường được phát triển nhằm tối ưu hóa quy trình order thực phẩm cho sinh viên và giảng viên. Giải pháp này kết hợp giữa backend mạnh mẽ, frontend linh hoạt và ứng dụng đa nền tảng để đảm bảo trải nghiệm người dùng mượt mà.
Kiến Trúc Công Nghệ
Backend: Spring Boot
Spring Boot đóng vai trò là nền tảng cốt lõi cho phía máy chủ. Framework này loại bỏ các cấu hình phức tạp của Spring truyền thống thông qua cơ chế tự động cấu hình (auto-configuration). Nó tích hợp sẵn các container như Tomcat, giúp triển khai ứng dụng độc lập mà không cần môi trường máy chủ bên ngoài.
Ưu điểm chính bao gồm khả năng tích hợp dễ dàng với các module như Spring Security cho xác thực, Spring Data cho truy xuất dữ liệu và cộng đồng hỗ trợ lớn. Dưới đây là ví dụ về cách khởi tạo một dịch vụ cơ bản:
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@EnableAutoConfiguration
@ComponentScan
@RestController
public class DeliveryPlatform {
public static void main(String[] args) {
org.springframework.boot.SpringApplication.run(DeliveryPlatform.class, args);
}
@RequestMapping("/api/status")
public String checkSystemStatus() {
return "System Operational";
}
}
Đoạn mã trên định nghĩa lớp chính `DeliveryPlatform` với các注解 cần thiết để kích hoạt ứng dụng. Phương thức `checkSystemStatus` phản hồi trạng thái hệ thống khi gọi đến đường dẫn `/api/status`.
Frontend: Vue.js
Vue.js được sử dụng để xây dựng giao diện người dùng nhờ vào cơ chế reactive data binding và Virtual DOM. Khi trạng thái dữ liệu thay đổi, giao diện sẽ tự động cập nhật mà không cần thao tác DOM thủ công, giúp tăng hiệu suất render.
Ví dụ minh họa cách Vue xử lý dữ liệu động:
<html>
<head>
<title>Campus Order Interface</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="campus-order">
<h3>Trạng Thái Đơn: {{ orderStatus }}</h3>
<button @click="updateStatus">Cập Nhật Trạng Thái</button>
</div>
<script>
var orderApp = new Vue({
el: '#campus-order',
data: {
orderStatus: 'Đang chờ xử lý'
},
methods: {
updateStatus: function() {
this.orderStatus = 'Đã xác nhận';
}
}
});
</script>
</body>
</html>
Trong ví dụ này, biến `orderStatus` được liên kết với giao diện. Khi hàm `updateStatus` được kích hoạt qua sự kiện click, giá trị hiển thị sẽ thay đổi ngay lập tức.
Persistence: MyBatis
MyBatis hoạt động như lớp trung gian giữa ứng dụng Java và cơ sở dữ liệu. Nó cho phép ánh xạ các đối tượng Java tới các câu lệnh SQL thông qua file XML hoặc注解, giúp tách biệt logic business và truy vấn dữ liệu.
Các lợi ích chính bao gồm hỗ trợ SQL động, cơ chế caching để giảm tải cho database và khả năng tùy biến cao thông qua plugin.
Triển Khai Chức Năng Xác Thực
Hệ thống sử dụng cơ chế Token để quản lý phiên làm việc và phân quyền truy cập. Dưới đây là cách triển khai logic đăng nhập và bộ lọc bảo mật.
// Annotation cho phép truy cập công khai
@PublicAccess
@PostMapping(value = "/authenticate")
public Response authenticate(String account, String passcode, String verifyCode, HttpServletRequest req) {
// Truy vấn thông tin tài khoản
AccountInfo acc = accountRepo.findByAccountName(account);
// Xác thực thông tin
if(acc == null || !acc.getPasscode().equals(passcode)) {
return Response.fail("Thông tin đăng nhập không hợp lệ");
}
// Tạo chuỗi xác thực
String accessToken = credentialMgr.createCredential(acc.getId(), account, "students", acc.getRole());
return Response.success().add("accessToken", accessToken);
}
// Phương thức tạo credential
@Override
public String createCredential(Long uid, String uname, String tableName, String role) {
// Kiểm tra credential cũ
AuthCredential oldCredential = this.findByUserId(uid, role);
// Tạo chuỗi ngẫu nhiên
String newToken = Utility.generateRandomString(32);
// Thiết lập thời gian hết hạn
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.HOUR_OF_DAY, 1);
if(oldCredential != null) {
// Cập nhật thông tin
oldCredential.setTokenValue(newToken);
oldCredential.setExpireTime(cal.getTime());
this.updateRecord(oldCredential);
} else {
// Ghi mới
this.insertRecord(new AuthCredential(uid, uname, tableName, role, newToken, cal.getTime()));
}
return newToken;
}
/**
* Bộ lọc kiểm soát truy cập
*/
@Component
public class RequestSecurityFilter implements HandlerInterceptor {
public static final String HEADER_TOKEN = "Authorization";
@Autowired
private CredentialManager credentialMgr;
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
// Cấu hình CORS
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Origin");
res.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
// Xử lý preflight request
if (req.getMethod().equals(RequestMethod.OPTIONS.name())) {
res.setStatus(HttpStatus.OK.value());
return false;
}
// Kiểm tra annotation
PublicAccess access;
if (handler instanceof HandlerMethod) {
access = ((HandlerMethod) handler).getMethodAnnotation(PublicAccess.class);
} else {
return true;
}
// Lấy token từ header
String token = req.getHeader(HEADER_TOKEN);
/**
* Bỏ qua kiểm tra nếu là public
*/
if(access != null) {
return true;
}
// Xác thực token
AuthCredential credential = null;
if(StringUtils.isNotBlank(token)) {
credential = credentialMgr.getCredentialByToken(token);
}
if(credential != null) {
// Lưu thông tin vào session
req.getSession().setAttribute("uid", credential.getUserId());
req.getSession().setAttribute("role", credential.getRole());
req.getSession().setAttribute("table", credential.getTableName());
req.getSession().setAttribute("uname", credential.getUserName());
return true;
}
// Trả về lỗi 401 nếu không hợp lệ
PrintWriter out = null;
res.setCharacterEncoding("UTF-8");
res.setContentType("application/json; charset=utf-8");
try {
out = res.getWriter();
out.print(JSONObject.toJSONString(Response.fail(401, "Yêu cầu xác thực")));
} finally {
if(out != null){
out.close();
}
}
return false;
}
}
Đoạn mã trên mô tả quy trình xác thực người dùng. Annotation `@PublicAccess` đánh dấu các endpoint không cần kiểm tra quyền. Bộ lọc `RequestSecurityFilter` sẽ chặn các yêu cầu thiếu token hợp lệ và trả về mã lỗi 401.
Thiết Kế Cơ Sở Dữ Liệu
Dưới đây là cấu trúc bảng lưu trữ thông tin món ăn trong hệ thống.
-- ----------------------------
-- Table structure for food_menu
-- ----------------------------
DROP TABLE IF EXISTS `food_menu`;
CREATE TABLE `food_menu` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Mã định danh',
`dish_name` varchar(100) NOT NULL COMMENT 'Tên món',
`unit_price` decimal(10, 2) NOT NULL COMMENT 'Giá bán',
`detail_info` varchar(200) DEFAULT NULL COMMENT 'Mô tả chi tiết',
`quantity` int(11) NOT NULL COMMENT 'Số lượng tồn',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Thời gian tạo',
`modified_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Thời gian sửa',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Bảng thực đơn';
Các trường dữ liệu bao gồm mã định danh duy nhất, tên món, giá tiền, mô tả, số lượng库存 và các mốc thời gian ghi nhận thay đổi.
Dữ liệu mẫu được插入 vào bảng như sau:
INSERT INTO `food_menu` (`dish_name`, `unit_price`, `detail_info`, `quantity`)
VALUES ('Cơm Gà Rang', 45000.00, 'Cơm trắng ăn kèm gà rang gừng', 200);
INSERT INTO `food_menu` (`dish_name`, `unit_price`, `detail_info`, `quantity`)
VALUES ('Bún Bò Huế', 50000.00, 'Bún bò đặc biệt nhiều thịt', 150);
INSERT INTO `food_menu` (`dish_name`, `unit_price`, `detail_info`, `quantity`)
VALUES ('Trà Sữa Trân Châu', 25000.00, 'Trà sữa đường đen hạt trân châu', 300);
Quy Trình Kiểm Thử Hệ Thống
Mục Đích Kiểm Thử
Kiểm thử hệ thống là bước cuối cùng trong vòng đời phát triển để đảm bảo tính ổn định trước khi bàn giao. Mục tiêu chính là phát hiện các lỗi tiềm ẩn về logic nghiệp vụ và trải nghiệm người dùng.
Quá trình này tập trung vào việc xác minh xem hệ thống có đáp ứng đúng các yêu cầu kỹ thuật đã đề ra hay không. Các kịch bản kiểm thử được xây dựng dựa trên hành vi thực tế của người dùng cuối.
Kiểm Thử Chức Năng
Phương pháp kiểm thử hộp đen được áp dụng để đánh giá các module chức năng. Dưới đây là ví dụ về kịch bản kiểm thử đăng nhập:
| Dữ Liệu Đầu Vào | Kết Quả Mong Đợi | Kết Quả Thực Tế | Đánh Giá |
|---|---|---|---|
| Tài khoản: admin, Mật khẩu: 123456, Mã xác thực: Đúng | Đăng nhập thành công | Chuyển hướng trang chủ | Đạt |
| Tài khoản: admin, Mật khẩu: sai, Mã xác thực: Đúng | Thông báo lỗi mật khẩu | Hiện thông báo lỗi | Đạt |
| Tài khoản: admin, Mật khẩu: 123456, Mã xác thực: Sai | Thông báo lỗi mã xác thực | Hiện thông báo lỗi | Đạt |
| Tài khoản: trống, Mật khẩu: 123456, Mã xác thực: Đúng | Yêu cầu nhập tài khoản | Hiện cảnh báo | Đạt |
Các chức năng quản lý người dùng như thêm, sửa, xóa cũng được kiểm tra tương tự以确保 tính toàn vẹn dữ liệu. Ví dụ, khi thêm một người dùng trùng tên hệ thống, phải hiển thị thông báo lỗi phù hợp thay vì ghi đè dữ liệu cũ.