feat: 补齐 ppt_history 的 DB-backed 檔案回填並保留開啟/下載
All checks were successful
CD Pipeline / deploy (push) Successful in 59s

This commit is contained in:
OoO
2026-05-15 14:14:07 +08:00
parent 40d4d01007
commit 0b415d965b
2 changed files with 72 additions and 5 deletions

View File

@@ -2235,6 +2235,7 @@ def ppt_audit_history():
if not os.path.isdir(reports_dir):
error = f'{reports_dir} 目錄不存在'
else:
files_by_name = {}
for f in os.listdir(reports_dir):
if not f.lower().endswith('.pptx'):
continue
@@ -2247,14 +2248,68 @@ def ppt_audit_history():
try:
mtime = os.path.getmtime(full)
if month_start_ts <= mtime < month_end_ts:
files.append({
files_by_name[f] = {
'source': 'filesystem',
'name': f,
'size_kb': round(os.path.getsize(full) / 1024, 1),
'mtime': datetime.fromtimestamp(mtime).strftime('%Y-%m-%d %H:%M'),
'mtime_ts': mtime,
})
'file_exists': True,
'file_path': full,
}
except OSError:
continue
# 補充:若 188 主機僅保留 DB 快取紀錄或掃描過程漏掉,仍可回補當月報表清單
try:
session = get_session()
try:
sql = """
SELECT report_type, file_path, file_size, generated_at
FROM ppt_reports
WHERE generated_at >= :month_start
AND generated_at < :month_end
"""
params = {'month_start': month_start, 'month_end': month_end}
if report_type != 'all':
sql += " AND report_type = :report_type"
params['report_type'] = report_type
rows = session.execute(sa_text(sql), params).fetchall()
for rpt_type, file_path, file_size, generated_at in rows:
if not file_path:
continue
name = os.path.basename(file_path)
if not name.lower().endswith('.pptx'):
continue
if report_prefix != 'all' and not name.startswith(report_prefix):
continue
if name in files_by_name:
files_by_name[name]['source'] = 'both'
continue
if not generated_at:
continue
mtime = generated_at.timestamp()
if not (month_start_ts <= mtime < month_end_ts):
continue
candidate_path = os.path.join(reports_dir, name)
exists = os.path.isfile(candidate_path)
files_by_name[name] = {
'source': 'database',
'name': name,
'size_kb': round((file_size or 0) / 1024, 1),
'mtime': generated_at.strftime('%Y-%m-%d %H:%M'),
'mtime_ts': mtime,
'file_exists': exists,
'file_path': candidate_path if exists else file_path,
'report_type': rpt_type,
}
finally:
session.close()
except Exception:
pass
files = list(files_by_name.values())
files.sort(key=lambda x: x['mtime_ts'], reverse=True)
except Exception as e:
error = f'{type(e).__name__}: {str(e)[:200]}'

View File

@@ -274,7 +274,7 @@
</div>
</div>
<div class="table-responsive">
<table class="table table-sm mb-0">
<table class="table table-sm mb-0">
<thead class="table-light">
<tr><th>檔名</th><th class="text-end">KB</th><th>修改時間</th><th>狀態</th><th>操作</th></tr>
</thead>
@@ -282,17 +282,29 @@
{% for f in files %}
<tr>
<td><code>{{ f.name }}</code></td>
<td class="text-end">{{ f.size_kb }}</td>
<td class="text-end">{{ f.size_kb if f.size_kb is not none else '—' }}</td>
<td><small>{{ f.mtime }}</small></td>
<td><small class="text-muted">22:00 排程掃描</small></td>
<td>
{% if f.source == 'database' %}
<span class="text-muted">{% if f.file_exists %}資料庫快取 + 檔案可存取{% else %}資料庫快取(檔案未落盤){% endif %}</span>
{% elif f.source == 'both' %}
<span class="text-success">檔案 + 資料庫</span>
{% else %}
<span class="text-muted">22:00 掃描落盤</span>
{% endif %}
</td>
<td>
<div class="ppt-file-actions">
{% if f.file_exists %}
<a class="btn btn-outline-primary btn-sm" href="{{ url_for('admin_observability.ppt_audit_file', filename=f.name) }}" target="_blank" rel="noopener">
<i class="fas fa-file-powerpoint me-1"></i>開啟
</a>
<a class="btn btn-outline-secondary btn-sm" href="{{ url_for('admin_observability.ppt_audit_file', filename=f.name, action='download') }}">
<i class="fas fa-download me-1"></i>下載
</a>
{% else %}
<span class="small text-muted">本機暫無檔案,請先至來源回補</span>
{% endif %}
</div>
</td>
</tr>