EzyPlatform quản lý các mẫu thế nào?
Back To BlogsThiết kế cơ sở dữ liệu
Cũng không có gì phức tạp, EzyPlatform sử dụng một bảng có tên ezy_content_templates
để quản lý toàn bộ các mẫu. Bảng này có mã nguồn SQL như sau:
CREATE TABLE IF NOT EXISTS `ezy_content_templates` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `template_type` varchar(25) COLLATE utf8mb4_unicode_520_ci NOT NULL, `template_name` varchar(300) COLLATE utf8mb4_unicode_520_ci NOT NULL, `title_template` varchar(1200) COLLATE utf8mb4_unicode_520_ci NOT NULL, `content_template` varchar(12000) COLLATE utf8mb4_unicode_520_ci NOT NULL, `creator_id` bigint unsigned NOT NULL, `status` varchar(25) COLLATE utf8mb4_unicode_520_ci NOT NULL DEFAULT 'DRAFT', `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_template_type_name` (`template_type`, `template_name`), INDEX `index_template_type` (`template_type`), INDEX `index_template_name` (`template_name`), INDEX `index_creator_id` (`creator_id`), INDEX `index_status` (`status`), INDEX `index_created_at` (`created_at`), INDEX `index_updated_at` (`updated_at`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
Ý nghĩa của một số trường đó là:
- template_type: Loại mẫu, ví dụ
MAIL
,LETTER
,NOTIFICATION
. - template_name: Là tên của mẫu, ví dụ:
new_orders
là mẫu để thông báo có đơn hàng mới qua email cho quản trị viên. - title_template: Là tiêu đề của mẫu.
- content_template: Là nội dung của mẫu.
- creator_id: Là id của admin đã tạo ra mẫu.
- status: Trạng thái của mẫu có thể là
DRAFT
(đang nháp) hayCOMPLETED
(đã hoàn thành, có thể đưa vào sử dụng).
Bạn cần lưu ý rằng trong một loại mẫu sẽ không chấp nhận tên trùng nhau, nhưng trong nhiều loại mẫu thì có thể.
Thiết kế lớp
Từ bảng ezy_content_templates
EzyPlatform sẽ thiết kế các lớp như sau:
- ContentTemplate: Là lớp entity ánh xạ với bảng
ezy_content_templates
. - ContentTemplateRepository: Là giao diện repository để tương tác với cơ sở dữ liệu.
- ContentTemplateService: Là giao diện cơ sở chứa một số hàm cơ bản để lấy dữ liệu mẫu.
- DefaultContentTemplateService: Là hàm cài đặt
ContentTemplateService
và sử dụngContentTemplateRepository
lưu hoặc lấy dữ liệu mẫu từ cơ sở dữ liệu. - AdminContentTemplateService: Là lớp bạn sẽ dùng khi làm việc với admin plugin.
- WebContentTemplateService: Là lớp bạn sẽ dùng khi làm việc với web plugin.
Sử dụng
Trong dự án thực tế thường bạn sẽ trải qua các bước sau để sử dụng các mẫu:
- Là định nghĩa sẵn mẫu.
- Cài đặt logic sử dụng mẫu.
- Cho phép người sử dụng tuỳ chỉnh mẫu nếu họ muốn.
Định nghĩa sẵn mẫu
Giả sử bạn đang cần một mẫu để gửi thông báo cho admin khi có đơn hàng mới, bạn có thể tạo một tập tin có tên mail_template_new_orders_for_managers.html
trong thứ mục admin plugin/resources
của bạn với nội dung như sau:
<!DOCTYPE html> <html> <body> <p>Hi managers,</p> <p>There are ${newOrders} new orders and ${waitingForConfirmationOrders} orders need to confirm.<p> <p>Please access <a href="${adminUrl}/ecommerce/orders">here</a> to review.<p> <p>Thank you!</p> </body> </html>
Sau đó bạn có thể tạo ra một lớp config để đọc và lưu mẫu này vào cơ sở dữ liệu như sau:
@AllArgsConstructor @EzyConfigurationAfter public class AdminEcommerceMailConfig implements EzyBeanConfig { private final EzyInputStreamLoader inputStreamLoader; private final AdminContentTemplateService contentTemplateService; @Override public void config() { addMailTemplates(); } @SuppressWarnings("MethodLength") private void addMailTemplates() { contentTemplateService.addTemplateIfAbsent( ContentTemplateType.MAIL.toString(), TEMPLATE_NEW_ORDERS_FOR_MANAGERS, () -> AddContentTemplateModel.builder() .templateName(TEMPLATE_NEW_ORDERS_FOR_MANAGERS) .titleTemplate("${orders} new orders need to review") .contentTemplate( readMailTemplateContentOrDefault( TEMPLATE_NEW_ORDERS_FOR_MANAGERS, "${orders} new orders need to review" ) ) .status(ContentTemplateStatus.COMPLETED.toString()) .build() ); } private String readMailTemplateContentOrDefault( String templateName, String defaultContent ) { try { String templateFullName = getMailTemplateFullName( templateName ); return EzyInputStreams.toStringUtf8( inputStreamLoader.load( "ecommerce/" + templateFullName + ".html" ) ); } catch (IOException e) { return defaultContent; } } }
Cài đặt logic sử dụng mẫu
Giải sử bạn có một đối tượng lập lịch để định kỳ kiểm tra có đơn hàng mới hay không sau đó gửi mail, bạn có thể cài đặt mã nguồn như sau:
@EzySingleton public class AdminNewOrdersMailAppender extends AdminDataAppender<Order, Order, Void> { private final AdminMailServiceProxy mailServiceProxy; private final AdminEventHandlerManager eventHandlerManager; private final AdminEcommerceSettingService ecommerceSettingService; private final AdminSettingService settingService; private final AdminOrderRepository orderRepository; private final AtomicLong lastSendMailTime = new AtomicLong( System.currentTimeMillis() ); public AdminNewOrdersMailAppender( AdminMailServiceProxy mailServiceProxy, AdminEventHandlerManager eventHandlerManager, ObjectMapper objectMapper, AdminEcommerceSettingService ecommerceSettingService, AdminSettingService settingService, AdminOrderRepository orderRepository ) { super(objectMapper, settingService); this.mailServiceProxy = mailServiceProxy; this.eventHandlerManager = eventHandlerManager; this.ecommerceSettingService = ecommerceSettingService; this.settingService = settingService; this.orderRepository = orderRepository; } @Override protected void doAppend() { int period = ecommerceSettingService.getNotifyNewOrdersPeriod(); long sendMailTime = lastSendMailTime.get() + period * 60 * 1000L; long now = System.currentTimeMillis(); if (sendMailTime > now) { return; } long lastOrderId = ecommerceSettingService .getLastNotifiedNewOrderId(); if (lastOrderId == 0) { Order lastOrder = orderRepository.findLast(); lastOrderId = lastOrder != null ? lastOrder.getId() : 0L; } List<Order> orders = orderRepository.findByIdGtOrStatus( lastOrderId, OrderStatus.WAITING_FOR_CONFIRMATION.toString(), Next.limit(LIMIT_300_RECORDS) ); if (orders.isEmpty()) { return; } long waitingForConfirmationOrders = orders .stream() .filter(it -> it .getStatus() .equals(OrderStatus.WAITING_FOR_CONFIRMATION.toString()) ) .count(); int ordersSize = orders.size(); long newOrders = ordersSize - waitingForConfirmationOrders; Map<String, Object> parameters = EzyMapBuilder.mapBuilder() .put("orders", ordersSize) .put("newOrders", newOrders) .put("waitingForConfirmationOrders", waitingForConfirmationOrders) .put("adminUrl", settingService.getAdminUrl()) .toMap(); Collection<String> orderManagementEmails = ecommerceSettingService .getOrderManagementEmails(); if (orderManagementEmails.size() > 0) { mailServiceProxy.send( AdminMailModel.builder() .templateName(TEMPLATE_NEW_ORDERS_FOR_MANAGERS) .to(orderManagementEmails) .parameters(parameters) .build() ); } eventHandlerManager.handleEvent( INTERNAL_EVENT_NAME_NEW_ORDERS_APPEND, parameters ); lastSendMailTime.set(now); ecommerceSettingService.setLastNotifiedNewOrderId(now); long newLastOrderId = last(orders).getId(); if (newLastOrderId > lastOrderId) { ecommerceSettingService.setLastNotifiedNewOrderId( newLastOrderId ); } } @Override protected List<Order> getValueList(Void unused) { throw new UnsupportedOperationException("unused"); } @Override protected Void extractNewLastPageToken(List<Order> list, Void unused) { throw new UnsupportedOperationException("unused"); } @Override protected String getAppenderNamePrefix() { return TEMPLATE_NEW_ORDERS_FOR_MANAGERS; } @Override protected Void defaultPageToken() { throw new UnsupportedOperationException("unused"); } @Override protected Class<Void> pageTokenType() { return Void.class; } }
Cho phép người dùng tuỳ chỉnh
Bạn có thể cài đặt EzySupport plugin, nó sẽ cung cấp giao diện cho phép quản trị viên quản lý và thay đổi các mẫu theo ý muốn.
Tổng kết
Quản lý mẫu là một trong những tính năng cơ bản nhưng cực kỳ quan trọng đối với bất kỳ dự án phần mềm nào. Với EzyPlatform các nhà phát triển sẽ không cần phải tạo lại tính năng này nữa mà sẽ chỉ cần tập trung vào sử dụng với nghiệp vụ tuỳ ý của mình.