Hai Lin 2 недель назад
Родитель
Сommit
806ccef6e6
3 измененных файлов с 399 добавлено и 11 удалено
  1. 75 11
      app.py
  2. 321 0
      templates/home.html
  3. 3 0
      templates/layout.html

+ 75 - 11
app.py

@@ -68,9 +68,9 @@ try:
         maxcached=5,        # 最大空闲连接数
         maxshared=3,        # 最大共享连接数
         blocking=True,      # 连接池满时是否阻塞等待
-        maxusage=None,      # 一个连接最多被重复使用的次数
+        maxusage=1000,      # 一个连接最多被重复使用的次数,防止连接长时间使用失效
         setsession=[],      # 开始会话前执行的命令列表
-        ping=0,             # 用ping命令检查连接是否可用的频率
+        ping=1,             # 每次获取连接时都检查连接是否可用
         **DB_CONFIG
     )
     
@@ -89,6 +89,33 @@ except ImportError:
     
     print("[Database] DBUtils not available, using regular database connections")
 
+def verify_connection(conn):
+    """Verify database connection is still alive"""
+    try:
+        cursor = conn.cursor()
+        cursor.execute("SELECT 1")
+        cursor.fetchone()
+        cursor.close()
+        return True
+    except Exception as e:
+        print(f"[Database] Connection verification failed: {e}")
+        return False
+
+def safe_commit(conn):
+    """Safely commit transaction with error handling"""
+    try:
+        conn.commit()
+        print(f"[Database] Transaction committed successfully")
+        return True
+    except Exception as e:
+        print(f"[Database] Commit failed: {e}")
+        try:
+            conn.rollback()
+            print(f"[Database] Rollback completed")
+        except Exception as rollback_err:
+            print(f"[Database] Rollback also failed: {rollback_err}")
+        return False
+
 def format_timestamp(ts):
     if not ts: return '未知'
     try:
@@ -1520,14 +1547,18 @@ def add_member():
                         print(f"[Add Member] Error updating AI content status: {e}")
 
                 print(f"[Add Member] Committing transaction")
-                conn.commit()
-                print(f"[Add Member] Transaction committed successfully")
-                
-                if request.headers.get('X-Requested-With') == 'XMLHttpRequest' or request.is_json:
-                    return jsonify({"success": True, "message": "成员录入成功", "member_id": member_id})
-
-                flash('成员录入成功')
-                return redirect(url_for('members'))
+                if safe_commit(conn):
+                    print(f"[Add Member] Transaction committed successfully")
+                    if request.headers.get('X-Requested-With') == 'XMLHttpRequest' or request.is_json:
+                        return jsonify({"success": True, "message": "成员录入成功", "member_id": member_id})
+                    flash('成员录入成功')
+                    return redirect(url_for('members'))
+                else:
+                    print(f"[Add Member] Transaction commit failed!")
+                    if request.headers.get('X-Requested-With') == 'XMLHttpRequest' or request.is_json:
+                        return jsonify({"success": False, "message": "成员录入失败,事务提交失败"}), 500
+                    flash('成员录入失败,事务提交失败')
+                    return redirect(url_for('add_member'))
         
         with conn.cursor() as cursor:
             cursor.execute("SELECT id, name FROM family_member_info ORDER BY name")
@@ -1806,6 +1837,39 @@ def delete_member(member_id):
     finally:
         conn.close()
 
+@app.route('/manager/home')
+def home():
+    """Home page - Dashboard for the genealogy management system"""
+    if 'user_id' not in session:
+        return redirect(url_for('login'))
+    
+    conn = get_db_connection()
+    try:
+        with conn.cursor() as cursor:
+            # Get member count
+            cursor.execute("SELECT COUNT(*) as count FROM family_member_info")
+            member_count = cursor.fetchone()['count']
+            
+            # Get record count
+            cursor.execute("SELECT COUNT(*) as count FROM genealogy_records")
+            record_count = cursor.fetchone()['count']
+            
+            # Get PDF count
+            cursor.execute("SELECT COUNT(*) as count FROM genealogy_pdfs")
+            pdf_count = cursor.fetchone()['count']
+            
+            # Get suspected error count
+            cursor.execute("SELECT COUNT(*) as count FROM family_member_info WHERE suspected_error IS NOT NULL AND TRIM(suspected_error) != ''")
+            error_count = cursor.fetchone()['count']
+    finally:
+        conn.close()
+    
+    return render_template('home.html', 
+                           member_count=member_count,
+                           record_count=record_count,
+                           pdf_count=pdf_count,
+                           error_count=error_count)
+
 @app.route('/manager/login', methods=['GET', 'POST'])
 def login():
     if request.method == 'POST':
@@ -1821,7 +1885,7 @@ def login():
                     if user:
                         session['user_id'] = user['id']
                         session['username'] = user['username']
-                        return redirect(url_for('index'))
+                        return redirect(url_for('home'))
                     else:
                         flash('用户名或密码错误')
             finally:

+ 321 - 0
templates/home.html

@@ -0,0 +1,321 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>留氏(衢州)族谱信息管理系统2026</title>
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
+    <style>
+        body {
+            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
+            min-height: 100vh;
+            font-family: 'Noto Serif SC', 'STSong', serif;
+        }
+        
+        .hero-section {
+            padding: 80px 0;
+            position: relative;
+        }
+        
+        .title-container {
+            position: relative;
+            z-index: 1;
+        }
+        
+        .main-title {
+            font-size: 2.5rem;
+            font-weight: 700;
+            color: #ffd700;
+            text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
+            margin-bottom: 0.5rem;
+        }
+        
+        .subtitle {
+            font-size: 1.1rem;
+            color: rgba(255,255,255,0.8);
+            letter-spacing: 0.1em;
+        }
+        
+        .card-custom {
+            background: rgba(255,255,255,0.05);
+            backdrop-filter: blur(10px);
+            border: 1px solid rgba(255,255,255,0.1);
+            border-radius: 16px;
+            transition: transform 0.3s, box-shadow 0.3s;
+        }
+        
+        .card-custom:hover {
+            transform: translateY(-5px);
+            box-shadow: 0 20px 40px rgba(0,0,0,0.3);
+        }
+        
+        .section-title {
+            font-size: 1.25rem;
+            color: #ffd700;
+            margin-bottom: 1.5rem;
+            position: relative;
+            padding-bottom: 10px;
+        }
+        
+        .section-title::after {
+            content: '';
+            position: absolute;
+            bottom: 0;
+            left: 0;
+            width: 60px;
+            height: 2px;
+            background: linear-gradient(90deg, #ffd700, transparent);
+        }
+        
+        .member-name {
+            font-size: 1.1rem;
+            color: #fff;
+            font-weight: 500;
+        }
+        
+        .member-role {
+            font-size: 0.9rem;
+            color: rgba(255,255,255,0.6);
+        }
+        
+        .nav-btn {
+            background: linear-gradient(135deg, #ffd700, #ffb700);
+            border: none;
+            color: #1a1a2e;
+            font-weight: 600;
+            padding: 12px 30px;
+            border-radius: 50px;
+            transition: all 0.3s;
+        }
+        
+        .nav-btn:hover {
+            transform: scale(1.05);
+            box-shadow: 0 10px 30px rgba(255,215,0,0.4);
+        }
+        
+        .nav-btn-secondary {
+            background: transparent;
+            border: 1px solid rgba(255,255,255,0.3);
+            color: #fff;
+            font-weight: 500;
+            padding: 12px 30px;
+            border-radius: 50px;
+            transition: all 0.3s;
+        }
+        
+        .nav-btn-secondary:hover {
+            background: rgba(255,255,255,0.1);
+            border-color: rgba(255,255,255,0.5);
+        }
+        
+        .stat-card {
+            background: linear-gradient(135deg, rgba(255,215,0,0.1), rgba(255,183,0,0.05));
+            border: 1px solid rgba(255,215,0,0.2);
+        }
+        
+        .stat-number {
+            font-size: 2.5rem;
+            font-weight: 700;
+            color: #ffd700;
+        }
+        
+        .stat-label {
+            color: rgba(255,255,255,0.7);
+            font-size: 0.9rem;
+        }
+        
+        .footer {
+            padding: 30px 0;
+            border-top: 1px solid rgba(255,255,255,0.1);
+        }
+        
+        .footer-text {
+            color: rgba(255,255,255,0.5);
+            font-size: 0.9rem;
+        }
+        
+        .logo-img {
+            width: 120px;
+            height: 120px;
+            border-radius: 50%;
+            border: 3px solid rgba(255,215,0,0.5);
+            box-shadow: 0 10px 30px rgba(255,215,0,0.3);
+        }
+    </style>
+</head>
+<body>
+    <!-- Hero Section -->
+    <section class="hero-section text-center">
+        <div class="container title-container">
+            <div class="mb-6">
+                <img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=Chinese%20character%20%22Liu%22%20%E7%95%99%20gold%20calligraphy%20traditional%20Chinese%20style%20elegant%20on%20black%20background%20simple&image_size=square" 
+                     alt="留氏族徽" class="logo-img mb-4">
+            </div>
+            <h1 class="main-title">留氏(衢州)族谱信息管理系统</h1>
+            <p class="subtitle">二〇二六年度版</p>
+            <div class="mt-6">
+                <a href="{{ url_for('members') }}" class="nav-btn me-3">
+                    <i class="bi bi-people me-2"></i>成员管理
+                </a>
+                <a href="{{ url_for('index') }}" class="nav-btn-secondary">
+                    <i class="bi bi-images me-2"></i>扫描件管理
+                </a>
+            </div>
+        </div>
+    </section>
+
+    <!-- Stats Section -->
+    <section class="py-8">
+        <div class="container">
+            <div class="row justify-content-center">
+                <div class="col-md-3 col-sm-6 mb-4">
+                    <div class="card stat-card text-center p-4 rounded-16">
+                        <div class="stat-number">{{ member_count }}</div>
+                        <div class="stat-label">录入成员</div>
+                    </div>
+                </div>
+                <div class="col-md-3 col-sm-6 mb-4">
+                    <div class="card stat-card text-center p-4 rounded-16">
+                        <div class="stat-number">{{ record_count }}</div>
+                        <div class="stat-label">扫描件记录</div>
+                    </div>
+                </div>
+                <div class="col-md-3 col-sm-6 mb-4">
+                    <div class="card stat-card text-center p-4 rounded-16">
+                        <div class="stat-number">{{ pdf_count }}</div>
+                        <div class="stat-label">家谱PDF</div>
+                    </div>
+                </div>
+                <div class="col-md-3 col-sm-6 mb-4">
+                    <div class="card stat-card text-center p-4 rounded-16">
+                        <div class="stat-number">{{ error_count }}</div>
+                        <div class="stat-label">疑似错误</div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </section>
+
+    <!-- Editorial Board Section -->
+    <section class="py-12">
+        <div class="container">
+            <div class="card-custom p-6">
+                <h2 class="section-title">编委会成员</h2>
+                
+                <!-- Step 1: Chief Editor -->
+                <div style="position: relative; padding-left: 24px; padding-top: 12px; padding-bottom: 12px; margin-bottom: 8px;">
+                    <div style="position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: linear-gradient(to bottom, #ffd700, #ffb700); border-radius: 0 4px 4px 0;"></div>
+                    <div style="display: flex; align-items: center; gap: 12px;">
+                        <i class="bi bi-star" style="color: #ffd700; font-size: 18px;"></i>
+                        <span style="color: #ffd700; font-weight: 600; font-size: 16px;">主编:</span>
+                        <span style="color: #ffffff; font-weight: 700; font-size: 18px;">留文正</span>
+                    </div>
+                </div>
+                
+                <!-- Step 2: Editorial Committee -->
+                <div style="position: relative; padding-left: 72px; padding-top: 12px; padding-bottom: 12px; margin-bottom: 8px;">
+                    <div style="position: absolute; left: 48px; top: 0; bottom: 0; width: 4px; background: linear-gradient(to bottom, #4285F4, #3367D6); border-radius: 0 4px 4px 0;"></div>
+                    <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 8px;">
+                        <i class="bi bi-users" style="color: #4285F4; font-size: 18px;"></i>
+                        <span style="color: #4285F4; font-weight: 600; font-size: 16px;">编委成员:</span>
+                    </div>
+                    <div style="display: flex; flex-wrap: wrap; gap: 8px; margin-left: 48px;">
+                        <span style="padding: 4px 12px; background: rgba(255,215,0,0.2); border-radius: 20px; color: #ffd700; font-weight: 500; font-size: 14px;">留忠德</span>
+                        <span style="padding: 4px 12px; background: rgba(255,215,0,0.2); border-radius: 20px; color: #ffd700; font-weight: 500; font-size: 14px;">留文正</span>
+                        <span style="padding: 4px 12px; background: rgba(255,215,0,0.2); border-radius: 20px; color: #ffd700; font-weight: 500; font-size: 14px;">留越</span>
+                        <span style="padding: 4px 12px; background: rgba(255,215,0,0.2); border-radius: 20px; color: #ffd700; font-weight: 500; font-size: 14px;">留良吾</span>
+                        <span style="padding: 4px 12px; background: rgba(255,215,0,0.2); border-radius: 20px; color: #ffd700; font-weight: 500; font-size: 14px;">留如藩</span>
+                        <span style="padding: 4px 12px; background: rgba(255,215,0,0.2); border-radius: 20px; color: #ffd700; font-weight: 500; font-size: 14px;">留强</span>
+                        <span style="padding: 4px 12px; background: rgba(255,255,255,0.1); border-radius: 20px; color: rgba(255,255,255,0.6); font-size: 14px;">……</span>
+                    </div>
+                </div>
+                
+                <!-- Step 3: External Advisor -->
+                <div style="position: relative; padding-left: 120px; padding-top: 12px; padding-bottom: 12px; margin-bottom: 8px;">
+                    <div style="position: absolute; left: 96px; top: 0; bottom: 0; width: 4px; background: linear-gradient(to bottom, #34A853, #2E7D32); border-radius: 0 4px 4px 0;"></div>
+                    <div style="display: flex; align-items: center; gap: 12px;">
+                        <i class="bi bi-chat-left" style="color: #34A853; font-size: 18px;"></i>
+                        <span style="color: #34A853; font-weight: 600; font-size: 16px;">外支顾问:</span>
+                        <span style="color: #ffffff; font-weight: 500; font-size: 16px;">留朝信</span>
+                    </div>
+                </div>
+                
+                <!-- Step 4: System Planner -->
+                <div style="position: relative; padding-left: 168px; padding-top: 12px; padding-bottom: 12px; margin-bottom: 8px;">
+                    <div style="position: absolute; left: 144px; top: 0; bottom: 0; width: 4px; background: linear-gradient(to bottom, #9C27B0, #7B1FA2); border-radius: 0 4px 4px 0;"></div>
+                    <div style="display: flex; align-items: center; gap: 12px;">
+                        <i class="bi bi-layout-grid" style="color: #9C27B0; font-size: 18px;"></i>
+                        <span style="color: #9C27B0; font-weight: 600; font-size: 16px;">系统规划:</span>
+                        <span style="color: #ffffff; font-weight: 500; font-size: 16px;">留越</span>
+                    </div>
+                </div>
+                
+                <!-- Step 5: Technical Implementation -->
+                <div style="position: relative; padding-left: 216px; padding-top: 12px; padding-bottom: 12px;">
+                    <div style="position: absolute; left: 192px; top: 0; bottom: 0; width: 4px; background: linear-gradient(to bottom, #FB8C00, #EF6C00); border-radius: 0 4px 4px 0;"></div>
+                    <div style="display: flex; align-items: center; gap: 12px;">
+                        <i class="bi bi-code" style="color: #FB8C00; font-size: 18px;"></i>
+                        <span style="color: #FB8C00; font-weight: 600; font-size: 16px;">技术实现:</span>
+                        <span style="color: #ffffff; font-weight: 500; font-size: 16px;">春笋秋竹(杭州)科技有限公司</span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </section>
+
+    <!-- Quick Actions -->
+    <section class="py-12">
+        <div class="container">
+            <h2 class="section-title text-center mb-8">快速导航</h2>
+            <div class="row g-4">
+                <div class="col-lg-2 col-md-3 col-sm-4 mb-4">
+                    <a href="{{ url_for('members') }}" class="card-custom p-4 text-center text-decoration-none">
+                        <i class="bi bi-people text-3xl text-yellow-400 mb-3"></i>
+                        <div class="text-white font-medium">成员列表</div>
+                    </a>
+                </div>
+                <div class="col-lg-2 col-md-3 col-sm-4 mb-4">
+                    <a href="{{ url_for('add_member') }}" class="card-custom p-4 text-center text-decoration-none">
+                        <i class="bi bi-user-plus text-3xl text-green-400 mb-3"></i>
+                        <div class="text-white font-medium">录入新成员</div>
+                    </a>
+                </div>
+                <div class="col-lg-2 col-md-3 col-sm-4 mb-4">
+                    <a href="{{ url_for('tree') }}" class="card-custom p-4 text-center text-decoration-none">
+                        <i class="bi bi-tree text-3xl text-blue-400 mb-3"></i>
+                        <div class="text-white font-medium">关系树状图</div>
+                    </a>
+                </div>
+                <div class="col-lg-2 col-md-3 col-sm-4 mb-4">
+                    <a href="{{ url_for('index') }}" class="card-custom p-4 text-center text-decoration-none">
+                        <i class="bi bi-images text-3xl text-purple-400 mb-3"></i>
+                        <div class="text-white font-medium">扫描件管理</div>
+                    </a>
+                </div>
+                <div class="col-lg-2 col-md-3 col-sm-4 mb-4">
+                    <a href="{{ url_for('pdf_management') }}" class="card-custom p-4 text-center text-decoration-none">
+                        <i class="bi bi-file-pdf text-3xl text-red-400 mb-3"></i>
+                        <div class="text-white font-medium">家谱管理</div>
+                    </a>
+                </div>
+                <div class="col-lg-2 col-md-3 col-sm-4 mb-4">
+                    <a href="{{ url_for('suspected_errors') }}" class="card-custom p-4 text-center text-decoration-none">
+                        <i class="bi bi-alert-circle text-3xl text-orange-400 mb-3"></i>
+                        <div class="text-white font-medium">疑似错误汇总</div>
+                    </a>
+                </div>
+            </div>
+        </div>
+    </section>
+
+    <!-- Footer -->
+    <footer class="footer text-center">
+        <div class="container">
+            <p class="footer-text">© 2026 留氏(衢州)族谱信息管理系统 | 传承家族文化,弘扬民族精神</p>
+        </div>
+    </footer>
+
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+</body>
+</html>

+ 3 - 0
templates/layout.html

@@ -28,6 +28,9 @@
                     <h4>家谱管理</h4>
                 </div>
                 <nav>
+                    <a href="{{ url_for('home') }}" class="{% if request.endpoint == 'home' %}active{% endif %}">
+                        <i class="bi bi-house me-2"></i> 系统首页
+                    </a>
                     <a href="{{ url_for('pdf_management') }}" class="{% if request.endpoint == 'pdf_management' %}active{% endif %}">
                         <i class="bi bi-book me-2"></i> 家谱管理
                     </a>