# 暫時規範管理系統 V3 - 開發者最佳實踐指南
> **⚠️ 重要提醒**:本文件包含敏感的系統配置和最佳實踐資訊,僅供開發團隊內部使用。
> 此文件已在 .gitignore 中排除,請勿提交至版本控制系統。
## 🎯 文件目的
本文件記錄在開發暫時規範管理系統 V3 過程中遇到的技術難點及最佳解決方案,避免後續開發者重複踩坑。
---
## 🔐 LDAP/Active Directory 整合最佳實踐
### 1. LDAP 連接配置
**關鍵發現**:LDAP 連接的穩定性很大程度取決於正確的配置參數組合。
#### 正確的配置模式
```python
# 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` 作為登入屬性
```python
# ❌ 錯誤做法
LDAP_USER_LOGIN_ATTR = "sAMAccountName"
# ✅ 正確做法
LDAP_USER_LOGIN_ATTR = "userPrincipalName"
```
**錯誤 2**:服務帳號權限不足
```python
# 服務帳號至少需要以下權限:
# - Read permission on the search base
# - List Contents permission
# - Read All Properties permission
```
### 2. LDAP 搜尋最佳化
**關鍵發現**:正確的搜尋篩選器可以大幅提升效能並避免權限問題。
#### 用戶搜尋最佳實踐
```python
# 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. 限制搜尋結果數量避免效能問題
#### 群組搜尋最佳實踐
```python
# 同時支援 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 連接穩定性
**關鍵發現**:連接池和重試機制對生產環境至關重要。
```python
# 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 配置需求,系統必須具備彈性。
#### 配置架構設計
```python
# 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', '')
```
#### 智能連接邏輯
```python
# 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. 郵件發送可靠性
**關鍵發現**:詳細的日誌和錯誤處理對於診斷郵件問題至關重要。
```python
# 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 格式郵件必須考慮各種郵件客戶端的相容性。
```python
# 推薦的 HTML 郵件格式
def create_email_body(spec, action):
body = f"""
"""
return body
```
---
## 🗄️ 資料庫設計最佳實踐
### 1. 資料庫遷移策略
**關鍵發現**:平滑的資料庫升級對於生產環境至關重要。
#### 遷移腳本模板
```python
# 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. 資料模型設計
**關鍵發現**:適當的索引和關聯設計可以大幅提升查詢效能。
```python
# 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)組織
**關鍵發現**:良好的模組分離有助於維護和擴展。
```python
# 推薦的路由組織結構
routes/
├── __init__.py # 藍圖註冊
├── auth.py # 認證相關
├── api.py # API 介面
├── temp_spec.py # 核心業務邏輯
├── admin.py # 管理功能
└── upload.py # 檔案處理
```
### 2. 錯誤處理策略
```python
# 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. 配置管理
```python
# config.py - 環境配置分離
class DevelopmentConfig(Config):
DEBUG = True
TESTING = False
class ProductionConfig(Config):
DEBUG = False
TESTING = False
# 生產環境特定設定
class TestingConfig(Config):
TESTING = True
# 測試環境設定
```
---
## 🏗️ 前端整合最佳實踐
### 1. ONLYOFFICE 整合要點
**關鍵發現**:Docker 環境下的網路配置是最大的挑戰。
```python
# 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 元件需要正確配置才能提供良好的使用者體驗。
```javascript
// 推薦的 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 配置優化
```yaml
# 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. 日誌管理
```python
# 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. 安全性配置
```python
# 推薦的安全標頭設置
@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. 開發階段除錯
```python
# 推薦的除錯配置
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. 生產環境監控
```python
# 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. 資料庫查詢優化
```python
# 推薦的查詢模式
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. 快取策略
```python
# 推薦使用 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. 版本控制策略
```bash
# 推薦的版本標籤格式
git tag v3.2.0-rc1 # 發布候選版本
git tag v3.2.0 # 正式版本
git tag v3.2.1 # 修正版本
```
### 2. 備份策略
```bash
#!/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*