# ============================================ # 複製以下所有內容到 Ubuntu Server 執行 # ============================================ # # 步驟: # 1. SSH 登入: ssh ubuntu@10.1.0.254 # 2. 執行: bash # 3. 複製貼上以下全部內容並按 Enter # 4. 等待完成 # # ============================================ docker exec -i postgres psql -U postgres <<'EOF' DROP USER IF EXISTS hr_user CASCADE; CREATE USER hr_user WITH PASSWORD 'DC1qaz2wsx'; ALTER USER hr_user WITH SUPERUSER; DROP DATABASE IF EXISTS hr_portal; CREATE DATABASE hr_portal OWNER hr_user; GRANT ALL PRIVILEGES ON DATABASE hr_portal TO hr_user; EOF docker exec -i postgres psql -U hr_user -d hr_portal <<'EOF' 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; EOF echo "" echo "==========================================" echo " ✓ Database Setup Complete!" echo "==========================================" echo "" echo "Verification:" docker exec -i postgres psql -U hr_user -d hr_portal -c "\dt" echo "" echo "Connection String:" echo "postgresql://hr_user:DC1qaz2wsx@10.1.0.254:5432/hr_portal" echo ""