Dữ liệu động theo entity
Dựng danh mục/trang động theo mô hình entity–field definition: khai báo entity và field qua REST API rồi lưu bản ghi theo mô hình EAV, không cần viết entity/repository/controller mới. Dùng khi cần bổ sung loại danh mục mới thường xuyên mà không muốn sửa code.
Khi nào sử dụng
- Cần thêm danh mục mới (chức danh, dân tộc, loại văn bản...) cấu hình được qua API thay vì deploy code.
- Cần một REST API chung cho nhiều entity động với cùng nghiệp vụ CRUD, tìm kiếm, phân trang và cây phân cấp.
- Cần sinh form nhập liệu từ schema (
uiSchemaJson) cho frontend. - Cần kiểm tra quyền theo từng entity (public, yêu cầu đăng nhập, hoặc yêu cầu permission riêng).
Cài đặt
<dependency>
<groupId>vn.govex.cloud</groupId>
<artifactId>govex-cloud-dynamic</artifactId>
</dependency>
Version do BOM quản lý — xem Cài đặt.
Cấu hình
| Property | Mô tả | Mặc định |
|---|---|---|
govex.dynamic.enabled | Bật dependency; phải đặt true thì auto-configuration mới chạy. | false |
govex.dynamic.api-prefix | Tiền tố API chung; path bản ghi là {api-prefix}/dynamic/{entityCode}. | /api/v1 |
govex.dynamic.permission-prefix | Tiền tố quyền khi kiểm tra permission dạng {permission-prefix}:{apiPath}:{action}. | — |
govex.dynamic.table-prefix | Tiền tố tên bảng động (khai báo sẵn, hiện chưa áp dụng — xem Lưu ý). | DYN_ |
Sử dụng
Bước 1 — bật dependency:
govex:
dynamic:
enabled: true
api-prefix: /api/v1
permission-prefix: qtdm
Bước 2 — tạo định nghĩa entity bằng API quản trị (POST /api/v1/entity-definitions) với code, name, apiPath, cờ requireAuth/requirePermission và uiSchemaJson:
{
"code": "DM_CHUC_DANH",
"name": "Danh mục chức danh",
"apiPath": "dm-chuc-danh",
"requireAuth": true,
"requirePermission": true,
"uiSchemaJson": "{\"type\":\"object\",\"properties\":{...}}"
}
Field definition được đồng bộ tự động từ uiSchemaJson (Formily ISchema) xuống bảng DYN_FIELD_DEFINITION; có thể cập nhật schema qua PUT /api/v1/entity-definitions/code/{code}/schema.
Bước 3 — thao tác dữ liệu qua các endpoint dùng chung:
| Method | Path | Mô tả |
|---|---|---|
| GET | {api-prefix}/dynamic/{entityCode} | Danh sách có phân trang (page, size), tìm kiếm theo tham số. |
| GET | {api-prefix}/dynamic/{entityCode}/{id} | Chi tiết bản ghi (response phẳng). |
| POST | {api-prefix}/dynamic/{entityCode} | Tạo mới, body là map field-code → giá trị. |
| PUT | {api-prefix}/dynamic/{entityCode}/{id} | Cập nhật. |
| DELETE | {api-prefix}/dynamic/{entityCode}/{id} | Xoá mềm. |
| GET | {api-prefix}/dynamic/{entityCode}/tree | Cây phân cấp (entity có hierarchical). |
| GET | {api-prefix}/dynamic/{entityCode}/schema | Schema để frontend render form. |
Bước 4 — cấu hình security. Với entity requireAuth=false, đăng ký các path public do DynamicSecurityConfigurer.getPublicPaths() trả về vào SecurityFilterChain:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http,
DynamicSecurityConfigurer dynamicSecurityConfigurer) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers(dynamicSecurityConfigurer.getPublicPaths().toArray(String[]::new)).permitAll()
.anyRequest().authenticated());
return http.build();
}
Khi requirePermission=true, hệ thống kiểm tra permission qua PermissionEvaluator với target {permission-prefix}:{apiPath} và action view, add, edit, delete.
Lưu ý
- Điều kiện tiên quyết: JPA + datasource; các bảng
DYN_ENTITY_DEFINITION,DYN_FIELD_DEFINITION,DYN_RECORD,DYN_FIELD_VALUEdo ứng dụng tạo bằng migration/DDL. govex.dynamic.table-prefixhiện chỉ được khai báo trong properties, chưa dùng để đặt tên bảng (entity dùng tên cố địnhDYN_*).- Khi
requireAuth=truecần Spring Security trong ứng dụng; kiểm tra permission chỉ chạy khi có beanPermissionEvaluator. - Sự kiện đồng bộ menu phát qua Kafka topic
dynamic-entity-definitionchỉ khi cógovex-cloud-mq; nếu thiếu, dependency vẫn phát sự kiện nội bộ. apiPathlà phần đường dẫn sau{api-prefix}, dùng cho cả endpoint bản ghi lẫn path public/permission.