========================================
建立 HR Portal 獨立 PostgreSQL 容器
微服務架構 - 第三個資料庫容器
========================================

請在 Ubuntu Server 上執行以下命令:

1. 登入: ssh ubuntu@10.1.0.254
2. 複製下面所有命令並執行

========================================
命令開始 (複製以下全部)
========================================

# 建立 hr-postgres 容器
docker run -d \
  --name hr-postgres \
  --restart unless-stopped \
  -e POSTGRES_PASSWORD="DC1qaz2wsx" \
  -e POSTGRES_INITDB_ARGS="--encoding=UTF-8" \
  -e TZ=Asia/Taipei \
  -p 0.0.0.0:5432:5432 \
  -v hr-postgres-data:/var/lib/postgresql/data \
  --health-cmd="pg_isready -U postgres" \
  --health-interval=10s \
  --health-timeout=5s \
  --health-retries=5 \
  postgres:16-alpine

# 等待啟動
echo "Waiting for PostgreSQL to start..."
sleep 10

# 建立 hr_user 和 hr_portal 資料庫
docker exec -i hr-postgres psql -U postgres <<EOF
CREATE USER hr_user WITH PASSWORD 'DC1qaz2wsx';
ALTER USER hr_user WITH SUPERUSER;
CREATE DATABASE hr_portal OWNER hr_user;
GRANT ALL PRIVILEGES ON DATABASE hr_portal TO hr_user;
\l
EOF

# 初始化 Schema
docker exec -i hr-postgres psql -U hr_user -d hr_portal <<'SCHEMA'
SET timezone = 'Asia/Taipei';

CREATE TABLE business_units (
    id SERIAL PRIMARY KEY,
    code VARCHAR(50) UNIQUE NOT NULL,
    name VARCHAR(100) NOT NULL,
    name_en VARCHAR(100),
    description TEXT,
    manager_id INTEGER,
    email_domain VARCHAR(50),
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO business_units (code, name, name_en, email_domain, description) VALUES
('wind-energy', '玄鐵風能授權服務事業部', 'Wind Energy Licensing', 'ease.taipei', '風力發電技術授權與風場評估服務'),
('carbon-credit', '國際碳權申請服務事業部', 'Carbon Credit Services', 'ease.taipei', '碳權申請、碳盤查與碳交易媒合服務'),
('smart-rd', '智能研發服務事業部', 'Smart R&D Services', 'lab.taipei', 'AI/ML、IoT 與能源管理系統研發'),
('management', '管理部門', 'Management', 'porscheworld.tw', '人資、財務、行政、資訊等管理部門');

CREATE TABLE divisions (
    id SERIAL PRIMARY KEY,
    business_unit_id INTEGER REFERENCES business_units(id) ON DELETE CASCADE,
    code VARCHAR(50) UNIQUE NOT NULL,
    name VARCHAR(100) NOT NULL,
    name_en VARCHAR(100),
    manager_id INTEGER,
    email VARCHAR(100),
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE employees (
    id SERIAL PRIMARY KEY,
    employee_id VARCHAR(20) UNIQUE NOT NULL,
    keycloak_user_id UUID UNIQUE,
    username VARCHAR(100) UNIQUE NOT NULL,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    chinese_name VARCHAR(50),
    email VARCHAR(100) UNIQUE NOT NULL,
    phone VARCHAR(20),
    mobile VARCHAR(20),
    business_unit_id INTEGER REFERENCES business_units(id),
    division_id INTEGER REFERENCES divisions(id),
    position VARCHAR(100),
    job_level VARCHAR(50),
    hire_date DATE,
    termination_date DATE,
    status VARCHAR(20) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_employees_username ON employees(username);
CREATE INDEX idx_employees_email ON employees(email);
CREATE INDEX idx_employees_keycloak_id ON employees(keycloak_user_id);
CREATE INDEX idx_employees_status ON employees(status);

CREATE TABLE email_accounts (
    id SERIAL PRIMARY KEY,
    employee_id INTEGER REFERENCES employees(id) ON DELETE CASCADE,
    email_address VARCHAR(100) UNIQUE NOT NULL,
    mailbox_quota_mb INTEGER DEFAULT 5120,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE network_drives (
    id SERIAL PRIMARY KEY,
    employee_id INTEGER REFERENCES employees(id) ON DELETE CASCADE,
    drive_name VARCHAR(100) NOT NULL,
    drive_path VARCHAR(255),
    quota_gb INTEGER DEFAULT 50,
    webdav_url VARCHAR(255),
    smb_path VARCHAR(255),
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE system_permissions (
    id SERIAL PRIMARY KEY,
    employee_id INTEGER REFERENCES employees(id) ON DELETE CASCADE,
    system_name VARCHAR(100) NOT NULL,
    access_level VARCHAR(50),
    granted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    revoked_at TIMESTAMP,
    is_active BOOLEAN DEFAULT TRUE,
    notes TEXT
);

CREATE TABLE projects (
    id SERIAL PRIMARY KEY,
    project_code VARCHAR(50) UNIQUE NOT NULL,
    project_name VARCHAR(200) NOT NULL,
    description TEXT,
    project_manager_id INTEGER REFERENCES employees(id),
    start_date DATE,
    end_date DATE,
    status VARCHAR(50) DEFAULT 'planning',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE project_members (
    id SERIAL PRIMARY KEY,
    project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE,
    employee_id INTEGER REFERENCES employees(id) ON DELETE CASCADE,
    role VARCHAR(100),
    joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    left_at TIMESTAMP,
    UNIQUE(project_id, employee_id)
);

CREATE TABLE audit_logs (
    id SERIAL PRIMARY KEY,
    employee_id INTEGER REFERENCES employees(id),
    action VARCHAR(100) NOT NULL,
    table_name VARCHAR(100),
    record_id INTEGER,
    old_values JSONB,
    new_values JSONB,
    ip_address INET,
    user_agent TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_audit_logs_employee ON audit_logs(employee_id);
CREATE INDEX idx_audit_logs_action ON audit_logs(action);
CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at);

CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS \$\$
BEGIN
    NEW.updated_at = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
\$\$ language 'plpgsql';

CREATE TRIGGER update_business_units_updated_at BEFORE UPDATE ON business_units FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_divisions_updated_at BEFORE UPDATE ON divisions FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_employees_updated_at BEFORE UPDATE ON employees FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_email_accounts_updated_at BEFORE UPDATE ON email_accounts FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_network_drives_updated_at BEFORE UPDATE ON network_drives FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_projects_updated_at BEFORE UPDATE ON projects FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE OR REPLACE VIEW v_employees_full AS
SELECT e.id, e.employee_id, e.username, e.first_name, e.last_name, e.chinese_name, e.email, e.phone, e.mobile,
       bu.name as business_unit, bu.code as business_unit_code, d.name as division, d.code as division_code,
       e.position, e.job_level, e.hire_date, e.status, e.created_at, e.updated_at
FROM employees e
LEFT JOIN business_units bu ON e.business_unit_id = bu.id
LEFT JOIN divisions d ON e.division_id = d.id;

CREATE OR REPLACE VIEW v_division_headcount AS
SELECT bu.name as business_unit, d.name as division,
       COUNT(e.id) as employee_count,
       COUNT(CASE WHEN e.status = 'active' THEN 1 END) as active_count
FROM divisions d
LEFT JOIN business_units bu ON d.business_unit_id = bu.id
LEFT JOIN employees e ON d.id = e.division_id
GROUP BY bu.name, d.name;

CREATE OR REPLACE VIEW v_project_stats AS
SELECT p.project_code, p.project_name, p.status, e.chinese_name as project_manager,
       COUNT(pm.id) as member_count, p.start_date, p.end_date
FROM projects p
LEFT JOIN employees e ON p.project_manager_id = e.id
LEFT JOIN project_members pm ON p.id = pm.project_id
GROUP BY p.id, p.project_code, p.project_name, p.status, e.chinese_name, p.start_date, p.end_date;
SCHEMA

# 驗證設定
echo ""
echo "=========================================="
echo "Verifying setup..."
echo "=========================================="

docker exec hr-postgres psql -U hr_user -d hr_portal -c "SELECT 'Tables: ' || COUNT(*)::text FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE';"

docker exec hr-postgres psql -U hr_user -d hr_portal -c "\dt"

echo ""
echo "=========================================="
echo "Microservice Architecture Complete!"
echo "=========================================="
echo ""
docker ps | grep -E "keycloak-db|gitea-db|hr-postgres"
echo ""
echo "Connection String:"
echo "postgresql://hr_user:DC1qaz2wsx@10.1.0.254:5432/hr_portal"
echo ""

========================================
命令結束
========================================

完成後會看到:
✓ Tables: 9
✓ 三個獨立的 PostgreSQL 容器運行中
✓ Connection String

然後回來告訴我「完成」! 🚀
========================================
