441 lines
14 KiB
Markdown
441 lines
14 KiB
Markdown
# Database Relationships & Entity Details
|
|
|
|
## 🗄️ **Entity Relationship Overview**
|
|
|
|
### **Core Entities**
|
|
|
|
#### **1. CLIENTS**
|
|
```sql
|
|
CREATE TABLE clients (
|
|
id UUID PRIMARY KEY,
|
|
name VARCHAR NOT NULL,
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
#### **2. USER_LEVELS**
|
|
```sql
|
|
CREATE TABLE user_levels (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR NOT NULL,
|
|
alias_name VARCHAR NOT NULL,
|
|
level_number INTEGER NOT NULL, -- 1=Highest, 2=Mid, 3=Lowest
|
|
parent_level_id INTEGER REFERENCES user_levels(id),
|
|
province_id INTEGER,
|
|
group VARCHAR,
|
|
is_approval_active BOOLEAN DEFAULT false,
|
|
client_id UUID REFERENCES clients(id),
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
#### **3. USERS**
|
|
```sql
|
|
CREATE TABLE users (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR NOT NULL,
|
|
email VARCHAR UNIQUE NOT NULL,
|
|
user_level_id INTEGER REFERENCES user_levels(id),
|
|
client_id UUID REFERENCES clients(id),
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### **Workflow Configuration Entities**
|
|
|
|
#### **4. APPROVAL_WORKFLOWS**
|
|
```sql
|
|
CREATE TABLE approval_workflows (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR NOT NULL,
|
|
description TEXT,
|
|
is_default BOOLEAN DEFAULT false,
|
|
is_active BOOLEAN DEFAULT true,
|
|
client_id UUID REFERENCES clients(id),
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
#### **5. APPROVAL_WORKFLOW_STEPS**
|
|
```sql
|
|
CREATE TABLE approval_workflow_steps (
|
|
id SERIAL PRIMARY KEY,
|
|
workflow_id INTEGER REFERENCES approval_workflows(id) ON DELETE CASCADE,
|
|
step_order INTEGER NOT NULL, -- 1, 2, 3, etc.
|
|
step_name VARCHAR NOT NULL,
|
|
required_user_level_id INTEGER REFERENCES user_levels(id),
|
|
is_required BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
#### **6. CLIENT_APPROVAL_SETTINGS**
|
|
```sql
|
|
CREATE TABLE client_approval_settings (
|
|
id SERIAL PRIMARY KEY,
|
|
client_id UUID REFERENCES clients(id),
|
|
workflow_id INTEGER REFERENCES approval_workflows(id),
|
|
auto_approve_levels JSONB, -- Array of level numbers
|
|
notification_settings JSONB,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### **Article & Approval Execution Entities**
|
|
|
|
#### **7. ARTICLES**
|
|
```sql
|
|
CREATE TABLE articles (
|
|
id SERIAL PRIMARY KEY,
|
|
title VARCHAR NOT NULL,
|
|
slug VARCHAR UNIQUE,
|
|
description TEXT,
|
|
html_description TEXT,
|
|
workflow_id INTEGER REFERENCES approval_workflows(id),
|
|
current_approval_step INTEGER,
|
|
status_id INTEGER, -- 1=pending, 2=approved, 3=rejected
|
|
is_publish BOOLEAN DEFAULT false,
|
|
published_at TIMESTAMP,
|
|
created_by_id INTEGER REFERENCES users(id),
|
|
client_id UUID REFERENCES clients(id),
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
#### **8. ARTICLE_APPROVAL_FLOWS**
|
|
```sql
|
|
CREATE TABLE article_approval_flows (
|
|
id SERIAL PRIMARY KEY,
|
|
article_id INTEGER REFERENCES articles(id) ON DELETE CASCADE,
|
|
workflow_id INTEGER REFERENCES approval_workflows(id),
|
|
current_step INTEGER DEFAULT 1,
|
|
status_id INTEGER DEFAULT 1, -- 1=pending, 2=approved, 3=rejected, 4=revision_requested
|
|
submitted_by_id INTEGER REFERENCES users(id),
|
|
submitted_at TIMESTAMP DEFAULT NOW(),
|
|
completed_at TIMESTAMP,
|
|
rejection_reason TEXT,
|
|
revision_requested BOOLEAN DEFAULT false,
|
|
revision_message TEXT,
|
|
client_id UUID REFERENCES clients(id),
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
#### **9. ARTICLE_APPROVAL_STEP_LOGS**
|
|
```sql
|
|
CREATE TABLE article_approval_step_logs (
|
|
id SERIAL PRIMARY KEY,
|
|
approval_flow_id INTEGER REFERENCES article_approval_flows(id) ON DELETE CASCADE,
|
|
step_order INTEGER NOT NULL,
|
|
step_name VARCHAR NOT NULL,
|
|
approved_by_id INTEGER REFERENCES users(id),
|
|
action VARCHAR NOT NULL, -- approve, reject, auto_skip, request_revision
|
|
message TEXT,
|
|
processed_at TIMESTAMP DEFAULT NOW(),
|
|
user_level_id INTEGER REFERENCES user_levels(id),
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
#### **10. ARTICLE_APPROVALS (Legacy)**
|
|
```sql
|
|
CREATE TABLE article_approvals (
|
|
id SERIAL PRIMARY KEY,
|
|
article_id INTEGER REFERENCES articles(id) ON DELETE CASCADE,
|
|
approval_by INTEGER REFERENCES users(id),
|
|
status_id INTEGER, -- 1=pending, 2=approved, 3=rejected
|
|
message TEXT,
|
|
approval_at_level INTEGER REFERENCES user_levels(id),
|
|
approval_at TIMESTAMP DEFAULT NOW(),
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
## 🔗 **Relationship Details**
|
|
|
|
### **One-to-Many Relationships**
|
|
|
|
```
|
|
CLIENTS (1) ──→ (N) USER_LEVELS
|
|
CLIENTS (1) ──→ (N) USERS
|
|
CLIENTS (1) ──→ (N) APPROVAL_WORKFLOWS
|
|
CLIENTS (1) ──→ (N) ARTICLES
|
|
CLIENTS (1) ──→ (N) CLIENT_APPROVAL_SETTINGS
|
|
|
|
USER_LEVELS (1) ──→ (N) USERS
|
|
USER_LEVELS (1) ──→ (N) APPROVAL_WORKFLOW_STEPS
|
|
USER_LEVELS (1) ──→ (N) ARTICLE_APPROVAL_STEP_LOGS
|
|
|
|
APPROVAL_WORKFLOWS (1) ──→ (N) APPROVAL_WORKFLOW_STEPS
|
|
APPROVAL_WORKFLOWS (1) ──→ (N) ARTICLE_APPROVAL_FLOWS
|
|
APPROVAL_WORKFLOWS (1) ──→ (N) ARTICLES
|
|
|
|
ARTICLES (1) ──→ (N) ARTICLE_APPROVAL_FLOWS
|
|
ARTICLES (1) ──→ (N) ARTICLE_APPROVALS
|
|
|
|
ARTICLE_APPROVAL_FLOWS (1) ──→ (N) ARTICLE_APPROVAL_STEP_LOGS
|
|
|
|
USERS (1) ──→ (N) ARTICLES (as creator)
|
|
USERS (1) ──→ (N) ARTICLE_APPROVAL_FLOWS (as submitter)
|
|
USERS (1) ──→ (N) ARTICLE_APPROVAL_STEP_LOGS (as approver)
|
|
USERS (1) ──→ (N) ARTICLE_APPROVALS (as approver)
|
|
```
|
|
|
|
### **Many-to-Many Relationships (Implicit)**
|
|
|
|
```
|
|
USERS ←→ USER_LEVELS (through user_level_id)
|
|
ARTICLES ←→ APPROVAL_WORKFLOWS (through workflow_id)
|
|
ARTICLE_APPROVAL_FLOWS ←→ APPROVAL_WORKFLOW_STEPS (through workflow_id and step_order)
|
|
```
|
|
|
|
## 📊 **Data Flow Examples**
|
|
|
|
### **Example 1: Complete Approval Process**
|
|
|
|
#### **Step 1: Setup Data**
|
|
```sql
|
|
-- Client
|
|
INSERT INTO clients (id, name) VALUES ('b1ce6602-07ad-46c2-85eb-0cd6decfefa3', 'Test Client');
|
|
|
|
-- User Levels
|
|
INSERT INTO user_levels (name, alias_name, level_number, is_approval_active, client_id) VALUES
|
|
('POLDAS', 'Poldas', 1, true, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3'),
|
|
('POLDAS', 'Poldas', 2, true, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3'),
|
|
('POLRES', 'Polres', 3, true, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3');
|
|
|
|
-- Users
|
|
INSERT INTO users (name, email, user_level_id, client_id) VALUES
|
|
('Admin Level 1', 'admin1@test.com', 1, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3'),
|
|
('Admin Level 2', 'admin2@test.com', 2, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3'),
|
|
('User Level 3', 'user3@test.com', 3, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3');
|
|
|
|
-- Workflow
|
|
INSERT INTO approval_workflows (name, description, is_default, client_id) VALUES
|
|
('Standard Approval', 'Standard 3-step approval process', true, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3');
|
|
|
|
-- Workflow Steps
|
|
INSERT INTO approval_workflow_steps (workflow_id, step_order, step_name, required_user_level_id) VALUES
|
|
(1, 1, 'Level 2 Review', 2),
|
|
(1, 2, 'Level 1 Final Approval', 1);
|
|
```
|
|
|
|
#### **Step 2: Article Creation**
|
|
```sql
|
|
-- Article created by User Level 3
|
|
INSERT INTO articles (title, slug, workflow_id, current_approval_step, status_id, created_by_id, client_id) VALUES
|
|
('Test Article', 'test-article', 1, 1, 1, 3, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3');
|
|
|
|
-- Approval Flow
|
|
INSERT INTO article_approval_flows (article_id, workflow_id, current_step, status_id, submitted_by_id, client_id) VALUES
|
|
(1, 1, 1, 1, 3, 'b1ce6602-07ad-46c2-85eb-0cd6decfefa3');
|
|
|
|
-- Legacy Approval
|
|
INSERT INTO article_approvals (article_id, approval_by, status_id, message, approval_at_level) VALUES
|
|
(1, 3, 1, 'Need Approval', 2);
|
|
```
|
|
|
|
#### **Step 3: Level 2 Approval**
|
|
```sql
|
|
-- Step Log
|
|
INSERT INTO article_approval_step_logs (approval_flow_id, step_order, step_name, approved_by_id, action, message, user_level_id) VALUES
|
|
(1, 1, 'Level 2 Review', 2, 'approve', 'Approved by Level 2', 2);
|
|
|
|
-- Update Flow
|
|
UPDATE article_approval_flows SET current_step = 2 WHERE id = 1;
|
|
|
|
-- Update Article
|
|
UPDATE articles SET current_approval_step = 2 WHERE id = 1;
|
|
```
|
|
|
|
#### **Step 4: Level 1 Final Approval**
|
|
```sql
|
|
-- Step Log
|
|
INSERT INTO article_approval_step_logs (approval_flow_id, step_order, step_name, approved_by_id, action, message, user_level_id) VALUES
|
|
(1, 2, 'Level 1 Final Approval', 1, 'approve', 'Final approval by Level 1', 1);
|
|
|
|
-- Complete Flow
|
|
UPDATE article_approval_flows SET
|
|
status_id = 2,
|
|
completed_at = NOW()
|
|
WHERE id = 1;
|
|
|
|
-- Publish Article
|
|
UPDATE articles SET
|
|
status_id = 2,
|
|
is_publish = true,
|
|
published_at = NOW(),
|
|
current_approval_step = NULL
|
|
WHERE id = 1;
|
|
```
|
|
|
|
## 🔍 **Key Queries for Monitoring**
|
|
|
|
### **Active Approval Flows**
|
|
```sql
|
|
SELECT
|
|
aaf.id as flow_id,
|
|
a.title as article_title,
|
|
aaf.current_step,
|
|
aws.step_name as current_step_name,
|
|
ul.name as required_approver_level,
|
|
u.name as submitted_by,
|
|
aaf.submitted_at,
|
|
aaf.status_id
|
|
FROM article_approval_flows aaf
|
|
JOIN articles a ON aaf.article_id = a.id
|
|
JOIN approval_workflow_steps aws ON aaf.workflow_id = aws.workflow_id
|
|
AND aaf.current_step = aws.step_order
|
|
JOIN user_levels ul ON aws.required_user_level_id = ul.id
|
|
JOIN users u ON aaf.submitted_by_id = u.id
|
|
WHERE aaf.status_id = 1 -- pending
|
|
ORDER BY aaf.submitted_at DESC;
|
|
```
|
|
|
|
### **Approval History**
|
|
```sql
|
|
SELECT
|
|
a.title as article_title,
|
|
aasl.step_name,
|
|
aasl.action,
|
|
aasl.message,
|
|
u.name as approver_name,
|
|
ul.name as approver_level,
|
|
aasl.processed_at
|
|
FROM article_approval_step_logs aasl
|
|
JOIN article_approval_flows aaf ON aasl.approval_flow_id = aaf.id
|
|
JOIN articles a ON aaf.article_id = a.id
|
|
JOIN users u ON aasl.approved_by_id = u.id
|
|
JOIN user_levels ul ON aasl.user_level_id = ul.id
|
|
WHERE aaf.article_id = ? -- specific article
|
|
ORDER BY aasl.step_order, aasl.processed_at;
|
|
```
|
|
|
|
### **Workflow Performance**
|
|
```sql
|
|
SELECT
|
|
aw.name as workflow_name,
|
|
COUNT(aaf.id) as total_flows,
|
|
COUNT(CASE WHEN aaf.status_id = 2 THEN 1 END) as approved,
|
|
COUNT(CASE WHEN aaf.status_id = 3 THEN 1 END) as rejected,
|
|
AVG(EXTRACT(EPOCH FROM (aaf.completed_at - aaf.submitted_at))/3600) as avg_hours
|
|
FROM approval_workflows aw
|
|
LEFT JOIN article_approval_flows aaf ON aw.id = aaf.workflow_id
|
|
WHERE aw.client_id = ?
|
|
GROUP BY aw.id, aw.name;
|
|
```
|
|
|
|
## ⚠️ **Common Data Issues**
|
|
|
|
### **Issue 1: Orphaned Records**
|
|
```sql
|
|
-- Find articles without approval flows
|
|
SELECT a.id, a.title
|
|
FROM articles a
|
|
LEFT JOIN article_approval_flows aaf ON a.id = aaf.article_id
|
|
WHERE aaf.id IS NULL AND a.workflow_id IS NOT NULL;
|
|
|
|
-- Find approval flows without articles
|
|
SELECT aaf.id, aaf.article_id
|
|
FROM article_approval_flows aaf
|
|
LEFT JOIN articles a ON aaf.article_id = a.id
|
|
WHERE a.id IS NULL;
|
|
```
|
|
|
|
### **Issue 2: Inconsistent Step Data**
|
|
```sql
|
|
-- Find flows with invalid current_step
|
|
SELECT aaf.id, aaf.current_step, aws.step_order
|
|
FROM article_approval_flows aaf
|
|
JOIN approval_workflow_steps aws ON aaf.workflow_id = aws.workflow_id
|
|
WHERE aaf.current_step != aws.step_order;
|
|
```
|
|
|
|
### **Issue 3: Missing Step Logs**
|
|
```sql
|
|
-- Find approved flows without step logs
|
|
SELECT aaf.id, aaf.article_id, aaf.status_id
|
|
FROM article_approval_flows aaf
|
|
LEFT JOIN article_approval_step_logs aasl ON aaf.id = aasl.approval_flow_id
|
|
WHERE aaf.status_id = 2 AND aasl.id IS NULL;
|
|
```
|
|
|
|
## 🚀 **Performance Optimization**
|
|
|
|
### **Indexes**
|
|
```sql
|
|
-- Primary indexes
|
|
CREATE INDEX idx_article_approval_flows_status ON article_approval_flows(status_id);
|
|
CREATE INDEX idx_article_approval_flows_current_step ON article_approval_flows(current_step);
|
|
CREATE INDEX idx_article_approval_flows_client_id ON article_approval_flows(client_id);
|
|
CREATE INDEX idx_article_approval_step_logs_flow_id ON article_approval_step_logs(approval_flow_id);
|
|
CREATE INDEX idx_article_approval_step_logs_processed_at ON article_approval_step_logs(processed_at);
|
|
CREATE INDEX idx_articles_workflow_id ON articles(workflow_id);
|
|
CREATE INDEX idx_articles_status_id ON articles(status_id);
|
|
CREATE INDEX idx_users_user_level_id ON users(user_level_id);
|
|
CREATE INDEX idx_user_levels_level_number ON user_levels(level_number);
|
|
|
|
-- Composite indexes
|
|
CREATE INDEX idx_article_approval_flows_status_step ON article_approval_flows(status_id, current_step);
|
|
CREATE INDEX idx_approval_workflow_steps_workflow_order ON approval_workflow_steps(workflow_id, step_order);
|
|
```
|
|
|
|
### **Partitioning (for large datasets)**
|
|
```sql
|
|
-- Partition article_approval_step_logs by month
|
|
CREATE TABLE article_approval_step_logs_2025_01 PARTITION OF article_approval_step_logs
|
|
FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
|
|
```
|
|
|
|
## 📝 **Data Validation Rules**
|
|
|
|
### **Business Rules**
|
|
1. **Workflow Steps**: Must be sequential (1, 2, 3, etc.)
|
|
2. **User Levels**: Level number must be unique per client
|
|
3. **Approval Flows**: Can only have one active flow per article
|
|
4. **Step Logs**: Must have corresponding workflow step
|
|
5. **Status Transitions**: Only valid transitions allowed
|
|
|
|
### **Database Constraints**
|
|
```sql
|
|
-- Ensure step order is sequential
|
|
ALTER TABLE approval_workflow_steps
|
|
ADD CONSTRAINT chk_step_order_positive CHECK (step_order > 0);
|
|
|
|
-- Ensure level number is positive
|
|
ALTER TABLE user_levels
|
|
ADD CONSTRAINT chk_level_number_positive CHECK (level_number > 0);
|
|
|
|
-- Ensure status values are valid
|
|
ALTER TABLE article_approval_flows
|
|
ADD CONSTRAINT chk_status_valid CHECK (status_id IN (1, 2, 3, 4));
|
|
|
|
-- Ensure current step is valid
|
|
ALTER TABLE article_approval_flows
|
|
ADD CONSTRAINT chk_current_step_positive CHECK (current_step > 0);
|
|
```
|
|
|
|
## 🎯 **Summary**
|
|
|
|
Database design untuk sistem approval workflow MEDOLS menggunakan:
|
|
|
|
1. **Normalized Structure**: Memisahkan konfigurasi dari eksekusi
|
|
2. **Audit Trail**: Step logs untuk tracking lengkap
|
|
3. **Flexibility**: Workflow dapat dikonfigurasi per client
|
|
4. **Performance**: Indexes untuk query yang efisien
|
|
5. **Data Integrity**: Constraints dan validasi
|
|
6. **Scalability**: Partitioning untuk data besar
|
|
|
|
Dengan struktur ini, sistem dapat menangani berbagai skenario approval dengan efisien dan dapat diandalkan.
|