This commit is contained in:
beabigegg
2025-08-28 08:59:46 +08:00
parent b9557250a4
commit 4f7f46b07a
42 changed files with 4992 additions and 494 deletions

204
功能升級規劃.txt Normal file
View File

@@ -0,0 +1,204 @@
功能升級計畫:智慧通知模組
1. 專案願景
本次升級旨在將系統的郵件通知功能從一個靜態的範例,全面進化為一個智慧化、高彈性、自動化的通知中樞。完成後,系統將具備以下三大核心能力:
互動式通知:在所有關鍵操作(啟用、展延、終止)中,提供類似 Outlook 的動態收件人搜尋介面。
全流程覆蓋:確保暫規生命週期中的每一個重要節點,都能觸發對應的事件通知。
前瞻性提醒:從被動通知轉向主動預警,自動發送即將到期的提醒郵件,防止暫規失效。
2. 專案藍圖 (Roadmap)
本計畫將分為三個獨立但環環相扣的模組進行開發,建議依序實施。
模組 核心功能 開發重點 狀態
模組 A 動態收件人介面 建置 LDAP 搜尋 API、前端 UI 元件整合 全新開發
模組 B 全流程事件通知 將模組 A 的成果應用於「展延」與「終止」流程 功能擴展
模組 C 自動化排程提醒 引入排程器、建立每日檢查任務、定義提醒規則 全新開發
匯出到試算表
3. 模組 A動態收件人介面 (互動式通知)
此模組是整個計畫的基石,目標是打造一個可複用的收件人選擇器。
A.1. 後端:建置 LDAP 搜尋 API
強化 ldap_utils.py新增 search_ldap_principals 函式,使用服務帳號安全地查詢 AD 中符合條件的使用者,並回傳其姓名與 Email。
建立 API 路由 routes/api.py建立一個新的 Blueprint提供 /api/ldap-search 端點,並使用 @login_required 保護,僅供登入者呼叫。
註冊 API 路由:在 app.py 中註冊 api_bp使 API 生效。
(詳細程式碼請參考前次對話中的計畫,此處不再贅述)
A.2. 前端:整合 Tom Select 動態搜尋元件
修改 templates/base.html透過 CDN 引入 Tom Select 的 CSS 與 JavaScript 函式庫。
修改 templates/activate_spec.html
新增一個 <select multiple> 輸入框,用於選擇收件人。
編寫 JavaScript初始化 Tom Select 元件,並設定其 load 事件去呼叫後端的 /api/ldap-search API實現使用者輸入時的動態搜尋與載入功能。
A.3. 邏輯:更新啟用流程
修改 routes/temp_spec.py 中的 activate_spec 函式:
移除原先寫死的 get_ldap_group_members('TempSpec_Approvers') 邏輯。
改為從 request.form.get('recipients') 獲取由前端 Tom Select 傳來、以逗號分隔的 Email 字串。
解析字串為 Email 列表,並傳遞給 send_email 函式。
4. 模組 B全流程事件通知 (全流程覆蓋)
此模組的目標是將 模組 A 的成果,擴展應用到「展延」與「終止」這兩個操作上。
B.1. 修改 templates/extend_spec.html
複製 activate_spec.html 中新增的「郵件通知對象」HTML 區塊。
將其貼到 extend_spec.html 的表單中,<button type="submit"> 之前。
複製 activate_spec.html 中 {% block scripts %} 內的 Tom Select 初始化腳本,並貼到 extend_spec.html 的 scripts 區塊中。
B.2. 修改 routes/temp_spec.py 中的 extend_spec 函式
Python
# In: routes/temp_spec.py
@temp_spec_bp.route('/extend/<int:spec_id>', methods=['GET', 'POST'])
@editor_or_admin_required
def extend_spec(spec_id):
spec = TempSpec.query.get_or_404(spec_id)
if request.method == 'POST':
# ... (原有的檔案上傳、日期更新等邏輯不變) ...
# ===== START: 新增郵件通知邏輯 =====
recipients_str = request.form.get('recipients')
if recipients_str:
recipients = [email.strip() for email in recipients_str.split(',')]
subject = f"[暫規通知] 規範 '{spec.spec_code}' 已展延"
body = f"""
<html><body>
<p>您好,</p>
<p>暫時規範 <b>{spec.spec_code} - {spec.title}</b> 已成功展延。</p>
<p>新的結束日期為: <b>{spec.end_date.strftime('%Y-%m-%d')}</b></p>
<p>詳細資訊請登入系統查看。</p>
</body></html>
"""
send_email(recipients, subject, body)
# ===== END: 新增郵件通知邏輯 =====
db.session.commit()
flash(f"規範 '{spec.spec_code}' 已成功展延!", 'success')
return redirect(url_for('temp_spec.spec_list'))
default_new_end_date = spec.end_date + timedelta(days=30)
return render_template('extend_spec.html', spec=spec, default_new_end_date=default_new_end_date)
B.3. 針對 terminate_spec 重複 B.1 與 B.2 的步驟
對 templates/terminate_spec.html 和 routes/temp_spec.py 中的 terminate_spec 函式執行相同的修改,調整郵件的 subject 和 body 以符合「終止」的情境。
5. 模組 C自動化排程提醒 (前瞻性提醒)
此模組將為系統引入大腦,使其能夠主動進行管理。
C.1. 安裝與設定排程器
安裝套件:
Bash
pip install Flask-APScheduler
並將 Flask-APScheduler 新增到 requirements.txt 檔案中。
在 app.py 中初始化排程器:
Python
# In: app.py
from flask_apscheduler import APScheduler # <-- 新增
# ...
app = Flask(__name__)
app.config.from_object('config.Config')
# ===== START: 初始化排程器 =====
scheduler = APScheduler()
scheduler.init_app(app)
scheduler.start()
# ===== END: 初始化排程器 =====
db.init_app(app)
# ...
C.2. 建立排程任務 (tasks.py)
為了保持程式碼的模組化,我們建立一個新檔案來存放排程任務。
建立新檔案 tasks.py:
Python
# 檔案位置: beabigegg/temp_spec_system_v3/TEMP_spec_system_V3-b9557250a410cf778a51ece25ffe28543f494ffb/tasks.py (新檔案)
from datetime import date, timedelta
from models import TempSpec
from utils import send_email
from ldap_utils import get_ldap_group_members
def check_expiring_specs(app):
"""
每日執行的排程任務:檢查即將到期的暫規並發送提醒郵件。
"""
with app.app_context():
print("Running scheduled task: Checking for expiring specs...")
today = date.today()
seven_days_later = today + timedelta(days=7)
three_days_later = today + timedelta(days=3)
# 找出 7 天後 和 3 天後到期的暫規
expiring_soon = TempSpec.query.filter(
TempSpec.status == 'active',
TempSpec.end_date.in_([seven_days_later, three_days_later])
).all()
if not expiring_soon:
print("No specs expiring in 3 or 7 days.")
return
# **重要**: 定義預設的通知對象,例如某個管理群組
# 您需要將 'TempSpec_Admins' 替換為實際的 AD 群組名稱
default_recipients = get_ldap_group_members('TempSpec_Admins')
if not default_recipients:
print("Warning: Could not find default recipients in AD group 'TempSpec_Admins'.")
return
for spec in expiring_soon:
remaining_days = (spec.end_date - today).days
# 組合通知郵件
subject = f"[暫規到期提醒] 規範 '{spec.spec_code}' 將於 {remaining_days} 天後到期"
body = f"""
<html><body>
<p>您好,</p>
<p>此為自動提醒郵件。</p>
<p>暫時規範 <b>{spec.spec_code} - {spec.title}</b> 即將到期。</p>
<p><b>結束日期: {spec.end_date.strftime('%Y-%m-%d')} (剩餘 {remaining_days} 天)</b></p>
<p>申請人: {spec.applicant}</p>
<p>請及時處理,如需展延請登入系統操作。</p>
</body></html>
"""
# 發送郵件給預設群組
send_email(default_recipients, subject, body)
print(f"Sent expiry reminder for spec {spec.spec_code} to {len(default_recipients)} recipients.")
C.3. 在 app.py 中註冊與啟動任務
最後,我們需要告訴排程器有這個任務,並設定它每天執行。
Python
# In: app.py
# ... (在 from ... import ... 之後)
from tasks import check_expiring_specs
# ... (在 scheduler.start() 之後)
# 註冊排程任務:每天凌晨 2:00 執行一次
@scheduler.task('cron', id='check_expiring_specs_job', hour=2, minute=0)
def scheduled_job():
check_expiring_specs(app)
# ... (Flask app 的其他部分)