Files
TODO_list_system/BEST_PRACTICES.md
beabigegg b0c86302ff 1ST
2025-08-29 16:25:46 +08:00

14 KiB
Raw Blame History

暫時規範管理系統 V3 - 開發者最佳實踐指南

⚠️ 重要提醒:本文件包含敏感的系統配置和最佳實踐資訊,僅供開發團隊內部使用。 此文件已在 .gitignore 中排除,請勿提交至版本控制系統。

🎯 文件目的

本文件記錄在開發暫時規範管理系統 V3 過程中遇到的技術難點及最佳解決方案,避免後續開發者重複踩坑。


🔐 LDAP/Active Directory 整合最佳實踐

1. LDAP 連接配置

關鍵發現LDAP 連接的穩定性很大程度取決於正確的配置參數組合。

正確的配置模式

# config.py - 推薦配置
LDAP_SERVER = "ldap://dc.company.com"  # 或使用 IP
LDAP_PORT = 389  # 標準 LDAP port
LDAP_USE_SSL = False  # 內網環境通常不需要 SSL
LDAP_SEARCH_BASE = "DC=company,DC=com"
LDAP_BIND_USER_DN = "CN=ServiceAccount,OU=ServiceAccounts,DC=company,DC=com"
LDAP_USER_LOGIN_ATTR = "userPrincipalName"  # AD 環境必須使用此屬性

常見錯誤及解決方案

錯誤 1:使用 sAMAccountName 作為登入屬性

# ❌ 錯誤做法
LDAP_USER_LOGIN_ATTR = "sAMAccountName"

# ✅ 正確做法
LDAP_USER_LOGIN_ATTR = "userPrincipalName"

錯誤 2:服務帳號權限不足

# 服務帳號至少需要以下權限:
# - Read permission on the search base
# - List Contents permission  
# - Read All Properties permission

2. LDAP 搜尋最佳化

關鍵發現:正確的搜尋篩選器可以大幅提升效能並避免權限問題。

用戶搜尋最佳實踐

# ldap_utils.py - 優化後的搜尋篩選器
def search_ldap_principals(search_term):
    # 多屬性搜尋,提高命中率
    search_filter = f"""
    (&
        (objectClass=person)
        (objectCategory=person)
        (!(userAccountControl:1.2.840.113556.1.4.803:=2))  # 排除已停用帳號
        (|
            (displayName=*{search_term}*)
            (mail=*{search_term}*)
            (sAMAccountName=*{search_term}*)
            (userPrincipalName=*{search_term}*)
        )
    )
    """

關鍵技巧

  1. 使用 objectCategory=person 而不是只用 objectClass=user
  2. 排除停用帳號避免無效結果
  3. 多屬性搜尋提高使用者體驗
  4. 限制搜尋結果數量避免效能問題

群組搜尋最佳實踐

# 同時支援 AD 群組和 OU
def get_ldap_group_members(group_name):
    # 先嘗試搜尋 AD 群組
    group_filter = f"(&(objectClass=group)(cn={group_name}))"
    
    # 如果找不到群組,嘗試搜尋 OU
    if not found:
        ou_filter = f"(&(objectClass=organizationalUnit)(name=*{group_name}*))"

3. LDAP 連接穩定性

關鍵發現:連接池和重試機制對生產環境至關重要。

# ldap_utils.py - 連接重試機制
def create_ldap_connection(retries=3):
    for attempt in range(retries):
        try:
            server = Server(ldap_server, port=ldap_port, use_ssl=use_ssl)
            conn = Connection(server, user=bind_dn, password=bind_password, auto_bind=True)
            return conn
        except Exception as e:
            if attempt == retries - 1:
                raise e
            time.sleep(1)  # 短暫等待後重試

📧 SMTP 郵件系統最佳實踐

1. 多種 SMTP 配置支援

關鍵發現:企業環境中可能遇到多種 SMTP 配置需求,系統必須具備彈性。

配置架構設計

# config.py - 彈性 SMTP 配置
class Config:
    SMTP_SERVER = os.getenv('SMTP_SERVER', 'mail.company.com')
    SMTP_PORT = int(os.getenv('SMTP_PORT', 25))
    SMTP_USE_TLS = os.getenv('SMTP_USE_TLS', 'false').lower() in ['true', '1', 't']
    SMTP_USE_SSL = os.getenv('SMTP_USE_SSL', 'false').lower() in ['true', '1', 't']
    SMTP_AUTH_REQUIRED = os.getenv('SMTP_AUTH_REQUIRED', 'false').lower() in ['true', '1', 't']
    SMTP_SENDER_EMAIL = os.getenv('SMTP_SENDER_EMAIL', 'temp-spec-system@company.com')
    SMTP_SENDER_PASSWORD = os.getenv('SMTP_SENDER_PASSWORD', '')

智能連接邏輯

# utils.py - 智能 SMTP 連接
def send_email(to_addrs, subject, body):
    # 根據 port 和配置自動選擇連接方式
    if use_ssl and smtp_port == 465:
        server = smtplib.SMTP_SSL(smtp_server, smtp_port)
    else:
        server = smtplib.SMTP(smtp_server, smtp_port)
        if use_tls and smtp_port == 587:
            server.starttls()
    
    # 只在需要認證時才登入
    if auth_required and sender_password:
        server.login(sender_email, sender_password)

2. 郵件發送可靠性

關鍵發現:詳細的日誌和錯誤處理對於診斷郵件問題至關重要。

# utils.py - 完整的錯誤處理
def send_email(to_addrs, subject, body):
    try:
        # ... 發送邏輯 ...
        result = server.sendmail(sender_email, to_addrs, msg.as_string())
        
        # 檢查發送結果
        if result:
            # 某些收件者失敗
            print(f"[EMAIL WARNING] 部分收件者發送失敗: {result}")
        else:
            print(f"[EMAIL SUCCESS] 郵件成功發送至: {', '.join(to_addrs)}")
        
        return True
        
    except smtplib.SMTPAuthenticationError as e:
        print(f"[EMAIL ERROR] SMTP 認證失敗: {e}")
        return False
    except smtplib.SMTPConnectError as e:
        print(f"[EMAIL ERROR] SMTP 連接失敗: {e}")
        return False
    # ... 其他異常處理 ...

3. 郵件內容最佳化

關鍵發現HTML 格式郵件必須考慮各種郵件客戶端的相容性。

# 推薦的 HTML 郵件格式
def create_email_body(spec, action):
    body = f"""
    <html>
    <head>
        <meta charset="utf-8">
        <style>
            body {{ font-family: Arial, sans-serif; }}
            .header {{ color: #2c3e50; border-bottom: 2px solid #3498db; }}
            .content {{ margin: 20px 0; }}
            .highlight {{ background-color: #f8f9fa; padding: 10px; }}
        </style>
    </head>
    <body>
        <div class="header">
            <h2>[暫規通知] 規範 '{spec.spec_code}' 已{action}</h2>
        </div>
        <div class="content">
            <p>您好,</p>
            <!-- 內容... -->
        </div>
    </body>
    </html>
    """
    return body

🗄️ 資料庫設計最佳實踐

1. 資料庫遷移策略

關鍵發現:平滑的資料庫升級對於生產環境至關重要。

遷移腳本模板

# migrate_*.py - 標準遷移腳本結構
def migrate_database():
    engine = create_engine(Config.SQLALCHEMY_DATABASE_URI)
    
    try:
        with engine.connect() as conn:
            # 檢查是否已經遷移
            result = conn.execute(text("""
                SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS 
                WHERE TABLE_NAME = 'table_name' AND COLUMN_NAME = 'new_column'
                AND TABLE_SCHEMA = DATABASE()
            """))
            
            if result.fetchone():
                print("✓ 遷移已完成,無需重複執行")
                return True
            
            # 執行遷移
            conn.execute(text("ALTER TABLE table_name ADD COLUMN new_column TYPE"))
            conn.commit()
            
            # 驗證遷移結果
            # ...
            
    except Exception as e:
        print(f"✗ 遷移失敗:{str(e)}")
        return False

2. 資料模型設計

關鍵發現:適當的索引和關聯設計可以大幅提升查詢效能。

# models.py - 最佳實踐
class TempSpec(db.Model):
    __tablename__ = 'ts_temp_spec'
    
    # 主鍵
    id = db.Column(db.Integer, primary_key=True)
    
    # 業務鍵,建立索引
    spec_code = db.Column(db.String(20), nullable=False, index=True)
    
    # 常用查詢欄位,建立索引
    status = db.Column(db.Enum(...), nullable=False, index=True)
    end_date = db.Column(db.Date, index=True)  # 用於到期查詢
    
    # 新功能擴展欄位
    notification_emails = db.Column(db.Text, nullable=True)
    
    # 正確的關聯設置
    uploads = db.relationship('Upload', back_populates='spec', 
                             cascade='all, delete-orphan')

🔄 Flask 應用架構最佳實踐

1. 藍圖Blueprint組織

關鍵發現:良好的模組分離有助於維護和擴展。

# 推薦的路由組織結構
routes/
├── __init__.py          # 藍圖註冊
├── auth.py             # 認證相關
├── api.py              # API 介面
├── temp_spec.py        # 核心業務邏輯
├── admin.py            # 管理功能
└── upload.py           # 檔案處理

2. 錯誤處理策略

# app.py - 全局錯誤處理
@app.errorhandler(403)
def forbidden(error):
    return render_template('403.html'), 403

@app.errorhandler(404)
def not_found(error):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_error(error):
    db.session.rollback()
    return render_template('500.html'), 500

3. 配置管理

# config.py - 環境配置分離
class DevelopmentConfig(Config):
    DEBUG = True
    TESTING = False

class ProductionConfig(Config):
    DEBUG = False
    TESTING = False
    # 生產環境特定設定

class TestingConfig(Config):
    TESTING = True
    # 測試環境設定

🏗️ 前端整合最佳實踐

1. ONLYOFFICE 整合要點

關鍵發現Docker 環境下的網路配置是最大的挑戰。

# routes/temp_spec.py - URL 修正邏輯
def edit_spec(spec_id):
    doc_url = get_file_uri(doc_filename)
    callback_url = url_for('temp_spec.onlyoffice_callback', spec_id=spec_id, _external=True)
    
    # Docker 環境 URL 修正
    if '127.0.0.1' in doc_url or 'localhost' in doc_url:
        doc_url = doc_url.replace('127.0.0.1', 'host.docker.internal')
        doc_url = doc_url.replace('localhost', 'host.docker.internal')
        callback_url = callback_url.replace('127.0.0.1', 'host.docker.internal')
        callback_url = callback_url.replace('localhost', 'host.docker.internal')

2. 前端元件最佳化

關鍵發現Tom Select 元件需要正確配置才能提供良好的使用者體驗。

// 推薦的 Tom Select 配置
const recipientSelect = new TomSelect('#recipients', {
    valueField: 'value',
    labelField: 'text',
    searchField: 'text',
    placeholder: '請輸入姓名或 Email 來搜尋...',
    plugins: ['remove_button'],
    maxItems: null,
    create: false,
    load: function(query, callback) {
        if (!query || query.length < 2) {
            callback();
            return;
        }
        
        // 實作搜尋邏輯...
    }
});

🚀 部署最佳實踐

1. Docker 配置優化

# docker-compose.yml - 生產環境配置
version: '3.8'

services:
  app:
    build: .
    environment:
      - FLASK_ENV=production
      - PYTHONUNBUFFERED=1
    volumes:
      - ./uploads:/app/uploads
      - ./logs:/app/logs
    restart: unless-stopped
    
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_NAME}
    volumes:
      - mysql_data:/var/lib/mysql
    restart: unless-stopped

volumes:
  mysql_data:

2. 日誌管理

# app.py - 生產環境日誌配置
if not app.debug:
    if not os.path.exists('logs'):
        os.mkdir('logs')
    
    file_handler = RotatingFileHandler('logs/tempspec.log', 
                                      maxBytes=10240000, backupCount=10)
    file_handler.setFormatter(logging.Formatter(
        '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
    ))
    file_handler.setLevel(logging.INFO)
    app.logger.addHandler(file_handler)

3. 安全性配置

# 推薦的安全標頭設置
@app.after_request
def after_request(response):
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-XSS-Protection'] = '1; mode=block'
    return response

🐛 除錯與監控

1. 開發階段除錯

# 推薦的除錯配置
DEBUG_LDAP = os.getenv('DEBUG_LDAP', 'false').lower() == 'true'
DEBUG_EMAIL = os.getenv('DEBUG_EMAIL', 'false').lower() == 'true'
DEBUG_DATABASE = os.getenv('DEBUG_DATABASE', 'false').lower() == 'true'

def debug_log(category, message):
    if category == 'ldap' and DEBUG_LDAP:
        print(f"[LDAP DEBUG] {message}")
    elif category == 'email' and DEBUG_EMAIL:
        print(f"[EMAIL DEBUG] {message}")
    # ...

2. 生產環境監控

# tasks.py - 健康檢查任務
@scheduler.task('cron', id='health_check', hour='*/1')
def health_check():
    try:
        # 檢查資料庫連接
        db.session.execute(text('SELECT 1'))
        
        # 檢查 LDAP 連接
        test_ldap_connection()
        
        # 檢查 SMTP 連接
        test_smtp_connection()
        
        app.logger.info("Health check passed")
    except Exception as e:
        app.logger.error(f"Health check failed: {e}")

📊 效能優化要點

1. 資料庫查詢優化

# 推薦的查詢模式
def get_active_specs_expiring_soon():
    return TempSpec.query.filter(
        TempSpec.status == 'active',
        TempSpec.end_date <= datetime.now().date() + timedelta(days=7)
    ).options(
        joinedload(TempSpec.uploads)  # 預載關聯資料
    ).all()

2. 快取策略

# 推薦使用 Flask-Caching
from flask_caching import Cache

cache = Cache(app, config={'CACHE_TYPE': 'simple'})

@cache.memoize(timeout=300)  # 5分鐘快取
def get_ldap_group_members(group_name):
    # LDAP 查詢邏輯...

🔧 維護與升級

1. 版本控制策略

# 推薦的版本標籤格式
git tag v3.2.0-rc1  # 發布候選版本
git tag v3.2.0      # 正式版本
git tag v3.2.1      # 修正版本

2. 備份策略

#!/bin/bash
# backup.sh - 定期備份腳本
DATE=$(date +%Y%m%d_%H%M%S)

# 資料庫備份
mysqldump -u $DB_USER -p$DB_PASSWORD $DB_NAME > backup_${DATE}.sql

# 檔案備份
tar -czf uploads_${DATE}.tar.gz uploads/

📝 總結

本文件記錄了開發暫時規範管理系統 V3 過程中的關鍵技術決策和最佳實踐。這些經驗可以幫助後續開發者:

  1. 避免常見陷阱:特別是 LDAP 配置和 SMTP 設定
  2. 提升開發效率:使用經過驗證的架構模式
  3. 確保系統穩定性:採用完整的錯誤處理和監控機制
  4. 簡化部署流程:使用 Docker 和自動化腳本

重要提醒:本文件包含敏感資訊,請勿外洩或提交至公開版本控制系統。


最後更新2025年1月
文件版本V1.0