Browse Source

commit 优化疑似

Hai Lin 2 days ago
parent
commit
69721d7f34

+ 31 - 6
app.py

@@ -1051,12 +1051,37 @@ def suspected_errors():
     if 'user_id' not in session:
         return redirect(url_for('login'))
     
+    search_name = request.args.get('name', '').strip()
+    page = request.args.get('page', 1, type=int)
+    per_page = 20
+    offset = (page - 1) * per_page
+    
     conn = get_db_connection()
     try:
         with conn.cursor() as cursor:
-            # Get members with suspected errors
-            query = "SELECT id, name, simplified_name, sex, name_word_generation, birthday, suspected_error FROM family_member_info WHERE suspected_error IS NOT NULL AND suspected_error != '' ORDER BY name"
-            cursor.execute(query)
+            # Base query with condition for non-empty suspected_error (using TRIM to remove whitespace)
+            base_query = "SELECT id, name, simplified_name, sex, name_word_generation, birthday, suspected_error FROM family_member_info WHERE suspected_error IS NOT NULL AND TRIM(suspected_error) != ''"
+            count_query = "SELECT COUNT(*) as count FROM family_member_info WHERE suspected_error IS NOT NULL AND TRIM(suspected_error) != ''"
+            
+            # Add search condition if provided
+            params = []
+            if search_name:
+                # Support both traditional and simplified name search
+                base_query += " AND (name LIKE %s OR simplified_name LIKE %s)"
+                count_query += " AND (name LIKE %s OR simplified_name LIKE %s)"
+                search_param = f"%{search_name}%"
+                params.extend([search_param, search_param])
+            
+            # Get total count
+            cursor.execute(count_query, params)
+            result = cursor.fetchone()
+            total = result['count'] if result else 0
+            total_pages = (total + per_page - 1) // per_page
+            
+            # Get members with pagination
+            base_query += " ORDER BY name LIMIT %s OFFSET %s"
+            params.extend([per_page, offset])
+            cursor.execute(base_query, params)
             members = cursor.fetchall()
             
             # Format birthday for display
@@ -1069,7 +1094,7 @@ def suspected_errors():
     finally:
         conn.close()
         
-    return render_template('suspected_errors.html', members=members)
+    return render_template('suspected_errors.html', members=members, search_name=search_name, page=page, total_pages=total_pages, total=total)
 
 @app.route('/manager/tree')
 def tree():
@@ -1411,7 +1436,7 @@ def add_member():
                 'family_rank': request.form.get('family_rank'),
                 'tags': request.form.get('tags'),
                 'notes': request.form.get('notes'),
-                'suspected_error': request.form.get('suspected_error') or '',
+                'suspected_error': request.form.get('suspected_error').strip() if request.form.get('suspected_error') else '',
                 'source_record_id': request.form.get('source_record_id') or None,  # Save source record ID
                 'create_uid': session['user_id']  # 记录当前操作人
             }
@@ -1571,7 +1596,7 @@ def edit_member(member_id):
                 'family_rank': request.form.get('family_rank'),
                 'tags': request.form.get('tags'),
                 'notes': request.form.get('notes'),
-                'suspected_error': request.form.get('suspected_error') or '',
+                'suspected_error': request.form.get('suspected_error').strip() if request.form.get('suspected_error') else '',
                 'source_record_id': request.form.get('source_record_id') or None,
                 'create_uid': session['user_id']  # 记录当前操作人
             }

+ 61 - 0
check_empty_suspected_errors.py

@@ -0,0 +1,61 @@
+import pymysql
+
+print('检查列表中显示为空的疑似错误标注...')
+try:
+    conn = pymysql.connect(
+        host='rm-f8ze60yirdj8786u2wo.mysql.rds.aliyuncs.com',
+        port=3306,
+        user='root',
+        password='csqz@20255',
+        db='csqz-client',
+        charset='utf8mb4',
+        cursorclass=pymysql.cursors.DictCursor
+    )
+    print('数据库连接成功')
+    
+    cursor = conn.cursor()
+    
+    # 查询所有suspected_error字段不为NULL的成员
+    query = """
+        SELECT id, name, simplified_name, suspected_error 
+        FROM family_member_info 
+        WHERE suspected_error IS NOT NULL 
+        ORDER BY name
+    """
+    cursor.execute(query)
+    members = cursor.fetchall()
+    
+    print(f'\n找到 {len(members)} 个成员的suspected_error字段不为NULL')
+    print('\n检查suspected_error字段值:')
+    print('-' * 80)
+    
+    empty_count = 0
+    for member in members:
+        se = member['suspected_error']
+        se_type = type(se)
+        se_repr = repr(se)
+        se_len = len(se) if isinstance(se, str) else 0
+        se_trimmed = se.strip() if isinstance(se, str) else ''
+        se_trimmed_len = len(se_trimmed)
+        
+        # 检查是否为空或只包含空白字符
+        if not se or se_trimmed == '':
+            empty_count += 1
+            print(f'ID: {member["id"]}, 姓名: {member["name"]}, 疑似错误标注: {se_repr}, 类型: {se_type}, 长度: {se_len}, 去除空白后长度: {se_trimmed_len}')
+    
+    print('-' * 80)
+    print(f'\n其中 {empty_count} 个成员的疑似错误标注为空')
+    
+    # 检查前端显示为空的具体记录
+    print('\n检查前端显示为空的记录:')
+    front_end_empty_ids = [2929, 1831, 1835, 1907, 1802, 1857, 1989, 1941, 1967, 1972]
+    for member_id in front_end_empty_ids:
+        cursor.execute("SELECT id, name, suspected_error FROM family_member_info WHERE id = %s", (member_id,))
+        record = cursor.fetchone()
+        if record:
+            se = record['suspected_error']
+            print(f'ID: {record["id"]}, 姓名: {record["name"]}, 疑似错误标注: {repr(se)}, 长度: {len(se) if isinstance(se, str) else 0}')
+    
+    conn.close()
+except Exception as e:
+    print(f'Error: {e}')

+ 48 - 0
check_suspected_errors.py

@@ -0,0 +1,48 @@
+import pymysql
+
+print('检查疑似错误标注为空的成员...')
+try:
+    conn = pymysql.connect(
+        host='rm-f8ze60yirdj8786u2wo.mysql.rds.aliyuncs.com',
+        port=3306,
+        user='root',
+        password='csqz@20255',
+        db='csqz-client',
+        charset='utf8mb4',
+        cursorclass=pymysql.cursors.DictCursor
+    )
+    print('数据库连接成功')
+    
+    cursor = conn.cursor()
+    
+    # 查询所有suspected_error字段不为NULL但可能为空的成员
+    query = """
+        SELECT id, name, simplified_name, suspected_error 
+        FROM family_member_info 
+        WHERE suspected_error IS NOT NULL 
+        ORDER BY name
+    """
+    cursor.execute(query)
+    members = cursor.fetchall()
+    
+    print(f'\n找到 {len(members)} 个成员的suspected_error字段不为NULL')
+    print('\n检查suspected_error字段值:')
+    print('-' * 80)
+    
+    empty_count = 0
+    for member in members:
+        se = member['suspected_error']
+        se_type = type(se)
+        se_repr = repr(se)
+        se_len = len(se) if isinstance(se, str) else 0
+        
+        if not se or se.strip() == '':
+            empty_count += 1
+            print(f'ID: {member["id"]}, 姓名: {member["name"]}, 疑似错误标注: {se_repr}, 类型: {se_type}, 长度: {se_len}')
+    
+    print('-' * 80)
+    print(f'\n其中 {empty_count} 个成员的疑似错误标注为空')
+    
+    conn.close()
+except Exception as e:
+    print(f'Error: {e}')

+ 59 - 0
clean_all_empty_suspected_errors.py

@@ -0,0 +1,59 @@
+import pymysql
+
+print('清理数据库中所有只包含空格和换行符的疑似错误标注...')
+try:
+    conn = pymysql.connect(
+        host='rm-f8ze60yirdj8786u2wo.mysql.rds.aliyuncs.com',
+        port=3306,
+        user='root',
+        password='csqz@20255',
+        db='csqz-client',
+        charset='utf8mb4',
+        cursorclass=pymysql.cursors.DictCursor
+    )
+    print('数据库连接成功')
+    
+    cursor = conn.cursor()
+    
+    # 1. 先查询需要清理的记录
+    query = """
+        SELECT id, name, suspected_error 
+        FROM family_member_info 
+        WHERE suspected_error IS NOT NULL AND TRIM(suspected_error) = ''
+    """
+    cursor.execute(query)
+    records = cursor.fetchall()
+    
+    print(f'\n找到 {len(records)} 条需要清理的记录')
+    
+    if records:
+        # 2. 清理这些记录,将suspected_error设置为NULL
+        update_query = """
+            UPDATE family_member_info 
+            SET suspected_error = NULL 
+            WHERE id = %s
+        """
+        
+        count = 0
+        for record in records:
+            cursor.execute(update_query, (record['id'],))
+            count += 1
+        
+        conn.commit()
+        print(f'\n成功清理 {count} 条记录')
+    else:
+        print('\n没有需要清理的记录')
+    
+    # 3. 验证清理结果
+    cursor.execute(query)
+    remaining_records = cursor.fetchall()
+    print(f'\n清理后剩余 {len(remaining_records)} 条需要清理的记录')
+    
+    # 4. 检查清理后的数据
+    cursor.execute("SELECT COUNT(*) as count FROM family_member_info WHERE suspected_error IS NOT NULL")
+    remaining_non_null = cursor.fetchone()['count']
+    print(f'\n清理后suspected_error不为NULL的记录数: {remaining_non_null}')
+    
+    conn.close()
+except Exception as e:
+    print(f'Error: {e}')

+ 74 - 0
clean_empty_suspected_errors_python.py

@@ -0,0 +1,74 @@
+import pymysql
+
+print('使用Python清理数据库中所有只包含空格和换行符的疑似错误标注...')
+try:
+    conn = pymysql.connect(
+        host='rm-f8ze60yirdj8786u2wo.mysql.rds.aliyuncs.com',
+        port=3306,
+        user='root',
+        password='csqz@20255',
+        db='csqz-client',
+        charset='utf8mb4',
+        cursorclass=pymysql.cursors.DictCursor
+    )
+    print('数据库连接成功')
+    
+    cursor = conn.cursor()
+    
+    # 1. 查询所有suspected_error字段不为NULL的成员
+    query = """
+        SELECT id, name, suspected_error 
+        FROM family_member_info 
+        WHERE suspected_error IS NOT NULL 
+        ORDER BY name
+    """
+    cursor.execute(query)
+    members = cursor.fetchall()
+    
+    print(f'\n找到 {len(members)} 个成员的suspected_error字段不为NULL')
+    
+    # 2. 检查并清理只包含空白字符的记录
+    to_clean = []
+    for member in members:
+        se = member['suspected_error']
+        if isinstance(se, str):
+            # 使用Python的strip()函数来去除空白字符
+            if se.strip() == '':
+                to_clean.append(member['id'])
+                print(f'需要清理: ID={member["id"]}, 姓名={member["name"]}, 疑似错误标注={repr(se)}')
+    
+    print(f'\n找到 {len(to_clean)} 条需要清理的记录')
+    
+    if to_clean:
+        # 3. 清理这些记录,将suspected_error设置为NULL
+        update_query = """
+            UPDATE family_member_info 
+            SET suspected_error = NULL 
+            WHERE id = %s
+        """
+        
+        count = 0
+        for member_id in to_clean:
+            cursor.execute(update_query, (member_id,))
+            count += 1
+        
+        conn.commit()
+        print(f'\n成功清理 {count} 条记录')
+    else:
+        print('\n没有需要清理的记录')
+    
+    # 4. 验证清理结果
+    cursor.execute(query)
+    remaining_members = cursor.fetchall()
+    remaining_empty = 0
+    for member in remaining_members:
+        se = member['suspected_error']
+        if isinstance(se, str) and se.strip() == '':
+            remaining_empty += 1
+    
+    print(f'\n清理后剩余 {remaining_empty} 条需要清理的记录')
+    print(f'清理后suspected_error不为NULL的记录数: {len(remaining_members)}')
+    
+    conn.close()
+except Exception as e:
+    print(f'Error: {e}')

+ 63 - 0
clean_suspected_errors.py

@@ -0,0 +1,63 @@
+import pymysql
+
+print('清理数据库中只包含空格和换行符的疑似错误标注...')
+try:
+    conn = pymysql.connect(
+        host='rm-f8ze60yirdj8786u2wo.mysql.rds.aliyuncs.com',
+        port=3306,
+        user='root',
+        password='csqz@20255',
+        db='csqz-client',
+        charset='utf8mb4',
+        cursorclass=pymysql.cursors.DictCursor
+    )
+    print('数据库连接成功')
+    
+    cursor = conn.cursor()
+    
+    # 1. 先查询需要清理的记录
+    query = """
+        SELECT id, name, suspected_error 
+        FROM family_member_info 
+        WHERE suspected_error IS NOT NULL AND TRIM(suspected_error) = ''
+    """
+    cursor.execute(query)
+    records = cursor.fetchall()
+    
+    print(f'\n找到 {len(records)} 条需要清理的记录')
+    
+    if records:
+        print('\n详细信息:')
+        for record in records:
+            print(f'ID: {record["id"]}, 姓名: {record["name"]}, 疑似错误标注: {repr(record["suspected_error"])}')
+        
+        # 2. 清理这些记录,将suspected_error设置为NULL
+        update_query = """
+            UPDATE family_member_info 
+            SET suspected_error = NULL 
+            WHERE id = %s
+        """
+        
+        count = 0
+        for record in records:
+            cursor.execute(update_query, (record['id'],))
+            count += 1
+        
+        conn.commit()
+        print(f'\n成功清理 {count} 条记录')
+    else:
+        print('\n没有需要清理的记录')
+    
+    # 3. 验证清理结果
+    cursor.execute(query)
+    remaining_records = cursor.fetchall()
+    print(f'\n清理后剩余 {len(remaining_records)} 条需要清理的记录')
+    
+    if remaining_records:
+        print('\n剩余记录详细信息:')
+        for record in remaining_records:
+            print(f'ID: {record["id"]}, 姓名: {record["name"]}, 疑似错误标注: {repr(record["suspected_error"])}')
+    
+    conn.close()
+except Exception as e:
+    print(f'Error: {e}')

+ 2 - 2
templates/add_member.html

@@ -238,8 +238,8 @@
                     <div class="section-title">疑似错误标注</div>
                     <div class="row g-3 mb-4">
                         <div class="col-md-12">
-                            <textarea name="suspected_error" class="form-control" rows="2">
-                                {{ '' if not member or not member.suspected_error or member.suspected_error in ['None', 'none', 'NULL', 'null'] else member.suspected_error }}
+                            <textarea name="suspected_error" class="form-control" rows="2" onblur="this.value = this.value.trim()">
+                                {{ '' if not member or not member.suspected_error or member.suspected_error in ['None', 'none', 'NULL', 'null'] else member.suspected_error.strip() }}
                             </textarea>
                             <div class="form-text mt-1">请在此标注该成员信息中可能存在的错误,例如出生日期矛盾、关系冲突等</div>
                         </div>

+ 55 - 1
templates/suspected_errors.html

@@ -5,7 +5,18 @@
 {% block content %}
 <div class="d-flex justify-content-between align-items-center mb-4">
     <h2><i class="bi bi-exclamation-triangle"></i> 疑似错误汇总</h2>
-    <a href="{{ url_for('members') }}" class="btn btn-outline-secondary"><i class="bi bi-arrow-left"></i> 返回成员列表</a>
+    <div class="d-flex gap-2">
+        <form class="d-flex" action="{{ url_for('suspected_errors') }}" method="GET">
+            <div class="input-group">
+                <input type="text" name="name" class="form-control" placeholder="输入姓名搜索..." value="{{ search_name or '' }}">
+                <button class="btn btn-outline-primary" type="submit"><i class="bi bi-search"></i></button>
+                {% if search_name %}
+                    <a href="{{ url_for('suspected_errors') }}" class="btn btn-outline-secondary">重置</a>
+                {% endif %}
+            </div>
+        </form>
+        <a href="{{ url_for('members') }}" class="btn btn-outline-secondary"><i class="bi bi-arrow-left"></i> 返回成员列表</a>
+    </div>
 </div>
 
 <div class="card shadow-sm border-0">
@@ -51,4 +62,47 @@
         </div>
     </div>
 </div>
+
+<!-- 分页 -->
+{% if total_pages > 1 %}
+<nav class="mt-4" aria-label="Page navigation">
+    <ul class="pagination justify-content-center">
+        {% if page > 1 %}
+        <li class="page-item">
+            <a class="page-link" href="{{ url_for('suspected_errors', page=page-1, name=search_name) }}" aria-label="Previous">
+                <span aria-hidden="true">&laquo;</span>
+            </a>
+        </li>
+        {% else %}
+        <li class="page-item disabled">
+            <span class="page-link" aria-hidden="true">&laquo;</span>
+        </li>
+        {% endif %}
+        
+        {% for p in range(1, total_pages + 1) %}
+            {% if p == page %}
+            <li class="page-item active">
+                <span class="page-link">{{ p }}</span>
+            </li>
+            {% else %}
+            <li class="page-item">
+                <a class="page-link" href="{{ url_for('suspected_errors', page=p, name=search_name) }}">{{ p }}</a>
+            </li>
+            {% endif %}
+        {% endfor %}
+        
+        {% if page < total_pages %}
+        <li class="page-item">
+            <a class="page-link" href="{{ url_for('suspected_errors', page=page+1, name=search_name) }}" aria-label="Next">
+                <span aria-hidden="true">&raquo;</span>
+            </a>
+        </li>
+        {% else %}
+        <li class="page-item disabled">
+            <span class="page-link" aria-hidden="true">&raquo;</span>
+        </li>
+        {% endif %}
+    </ul>
+</nav>
+{% endif %}
 {% endblock %}