Thuộc Thiết Kế CSDL — Nhóm Ma Trận Hiện Trạng
Chi Tiết Entity — Ma Trận Hiện Trạng (Source Mapping)¶
Bảng:
source_mapping,source_mapping_approval,ownership_plan
source_mapping¶
Mỗi record đại diện cho một ô trong ma trận Source Mapping (Data Element x Đơn vị).
CREATE TABLE source_mapping (
id BIGSERIAL PRIMARY KEY,
public_id UUID NOT NULL DEFAULT uuidv7(), -- ID public cho API/external integration
data_element_id BIGINT NOT NULL REFERENCES data_element(id),
organization_unit_id BIGINT NOT NULL REFERENCES organization_unit(id),
current_state TEXT NOT NULL DEFAULT 'EMPTY'
CHECK (current_state IN (
'DATABASE', 'DATABASE_OVERLAP', 'FRAGMENTED',
'READY', 'EMPTY', 'DISPUTED', 'CONFIRMED', 'REJECTED',
'AI_SUGGESTED', 'AI_HIGH_CONFIDENCE'
)),
-- AI Matching fields
confidence_score NUMERIC(4,3), -- 0.000–1.000
algo_breakdown JSONB, -- Chi tiết scoring
-- DISPUTED fields
dispute_note TEXT, -- Lý do tranh chấp (≥ 20 ký tự)
disputed_by BIGINT REFERENCES app_user(id),
disputed_at TIMESTAMPTZ,
resolved_by BIGINT REFERENCES app_user(id),
resolved_at TIMESTAMPTZ,
resolve_note TEXT, -- Lý do giải quyết
-- Confirmation fields
confirmed_by BIGINT REFERENCES app_user(id),
confirmed_at TIMESTAMPTZ,
-- Discovery reference
discovery_session_id BIGINT REFERENCES discovery_session(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (data_element_id, organization_unit_id), -- Mỗi ô duy nhất
CONSTRAINT chk_sm_dispute_note_required CHECK (
current_state != 'DISPUTED'
OR (dispute_note IS NOT NULL AND length(btrim(dispute_note)) >= 20)
)
);
-- Core indexes
CREATE UNIQUE INDEX idx_sm_public_id ON source_mapping (public_id);
CREATE INDEX idx_sm_de_org ON source_mapping (data_element_id, organization_unit_id);
CREATE INDEX idx_sm_org_state ON source_mapping (organization_unit_id, current_state);
CREATE INDEX idx_sm_state ON source_mapping (current_state);
CREATE INDEX idx_sm_session ON source_mapping (discovery_session_id);
-- Partial indexes cho query thường gặp
CREATE INDEX idx_sm_disputed ON source_mapping (current_state) WHERE current_state = 'DISPUTED';
CREATE INDEX idx_sm_confirmed ON source_mapping (current_state) WHERE current_state = 'CONFIRMED';
-- Indexes tối ưu cho AI Matching Worker (TS-P0-06b)
CREATE INDEX idx_sm_confidence ON source_mapping (confidence_score DESC NULLS LAST) WHERE current_state IN ('AI_SUGGESTED', 'AI_HIGH_CONFIDENCE');
Trạng thái ô ma trận (Source Mapping State):
| Trạng thái | Mô tả | Ai set |
|---|---|---|
EMPTY |
Sở không có DE này | Mặc định / Discovery |
DATABASE |
Có trong CSDL | Discovery tự động / khai báo |
DATABASE_OVERLAP |
Cùng DE lưu ở nhiều bảng trong cùng đơn vị | Discovery tự động |
FRAGMENTED |
Dữ liệu rải rác, chưa cấu trúc | Sở khai báo |
READY |
Có CSDL + API, sẵn sàng kết nối | Sở khai báo |
AI_SUGGESTED |
AI gợi ý (score ≥ 0.60), chờ review | Matching Worker |
AI_HIGH_CONFIDENCE |
AI gợi ý với confidence cao (score ≥ 0.90) | Matching Worker |
DISPUTED |
Sở không đồng ý | Data Owner / Manager |
CONFIRMED |
Đã xác nhận đúng | Data Owner / Approver |
REJECTED |
Bác bỏ ánh xạ | Approver |
Business Rules:
- Unique constraint (data_element_id, organization_unit_id) — mỗi ô duy nhất
- DISPUTED bắt buộc dispute_note ≥ 20 ký tự
- Data Owner chỉ dispute/confirm ô thuộc organization_unit_id của mình (PBAC)
- Guard cứng: Không phê duyệt ma trận khi COUNT(*) WHERE current_state = 'DISPUTED' > 0
- Data Mapping Flow:
- Hệ thống tự động thiết lập (Pre-populate) toàn bộ matrix (data_element_id, organization_unit_id) thành trạng thái EMPTY.
- Khi Discovery AI chạy ra Matching Result có score >= 0.60, AI Worker sẽ cập nhật dòng record tương ứng trong source_mapping từ EMPTY thành AI_SUGGESTED (hoặc AI_HIGH_CONFIDENCE nếu >= 0.90), gán kèm discovery_session_id và confidence_score.
source_mapping_approval¶
Ghi nhận việc phê duyệt ma trận hiện trạng (Source Mapping) tổng thể bởi Approver.
CREATE TABLE source_mapping_approval (
id BIGSERIAL PRIMARY KEY,
public_id UUID NOT NULL DEFAULT uuidv7(),
approval_version INTEGER NOT NULL DEFAULT 1,
status TEXT NOT NULL
CHECK (status IN ('APPROVED', 'REVOKED')),
confirmed_orgs_count INTEGER NOT NULL,
total_orgs_count INTEGER NOT NULL,
confirmed_orgs_percent NUMERIC(5,2) NOT NULL,
disputed_count INTEGER NOT NULL DEFAULT 0,
total_cells INTEGER NOT NULL DEFAULT 0,
confirmed_cells INTEGER NOT NULL DEFAULT 0,
approval_note TEXT,
approved_by BIGINT NOT NULL REFERENCES app_user(id),
approved_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
revoked_by BIGINT REFERENCES app_user(id),
revoked_at TIMESTAMPTZ,
revoke_reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_sma_percent_nonneg CHECK (confirmed_orgs_percent >= 0),
CONSTRAINT chk_sma_percent_max100 CHECK (confirmed_orgs_percent <= 100),
CONSTRAINT chk_sma_revoke_require_reason CHECK (
status <> 'REVOKED' OR (revoked_by IS NOT NULL AND revoke_reason IS NOT NULL)
)
);
CREATE UNIQUE INDEX idx_sma_public_id ON source_mapping_approval (public_id);
CREATE INDEX idx_sma_status ON source_mapping_approval (status);
CREATE INDEX idx_sma_approved_at ON source_mapping_approval (approved_at DESC);
CREATE UNIQUE INDEX idx_sma_singleton_active_approval
ON source_mapping_approval (('constant')) WHERE status = 'APPROVED';
Business Rules:
- Tính Duy Nhất: Tại một thời điểm chỉ có duy nhất một bản phê duyệt có trạng thái APPROVED (được đảm bảo bằng singleton partial index).
- Guard phê duyệt: Chỉ APPROVED khi confirmed_orgs_percent > 70 VÀ disputed_count = 0.
- Thu hồi (Revoke): Một bản phê duyệt có thể bị thu hồi. Khi đó, approval_version tiếp theo sẽ được tăng lên để theo dõi lịch sử.
ownership_plan¶
Bảng D — Gán chủ quản duy nhất cho mỗi Data Element đến cấp Phòng/Ban.
CREATE TABLE ownership_plan (
id BIGSERIAL PRIMARY KEY,
public_id UUID NOT NULL DEFAULT uuidv7(), -- ID public cho API/external integration
data_element_id BIGINT NOT NULL REFERENCES data_element(id),
owner_unit_id BIGINT REFERENCES organization_unit(id), -- Chủ quản cấp Phòng (nullable = "Dữ liệu cần xây dựng mới")
status TEXT NOT NULL DEFAULT 'PROPOSED'
CHECK (status IN ('PROPOSED', 'APPROVED', 'PUBLISHED')),
proposed_by BIGINT REFERENCES app_user(id), -- Manager đề xuất
approved_by BIGINT REFERENCES app_user(id), -- Approver chốt
override_reason TEXT, -- Lý do nếu Approver override đề xuất
approved_at TIMESTAMPTZ,
published_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (data_element_id) -- Mỗi DE chỉ có 1 ownership plan (quy tắc 1:1)
);
CREATE UNIQUE INDEX idx_op_public_id ON ownership_plan (public_id);
CREATE INDEX idx_op_status ON ownership_plan (status);
CREATE INDEX idx_op_owner ON ownership_plan (owner_unit_id);
CREATE INDEX idx_op_de ON ownership_plan (data_element_id);
Business Rules:
- Quy tắc 1:1: UNIQUE constraint trên data_element_id
- owner_unit_id phải tham chiếu đơn vị cấp PHONG
- owner_unit_id = NULL → ghi nhận "Dữ liệu cần xây dựng mới"
- Khi APPROVED → cập nhật data_element.owner_unit_id
- Khi PUBLISHED → cập nhật data_element.status = 'PUBLISHED'
Cập nhật lần cuối: 2026-04-19