first commit
This commit is contained in:
236
logs/app.log
236
logs/app.log
@@ -1900,3 +1900,239 @@ werkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'meal_p
|
||||
2025-10-17 23:09:02,391 INFO: 应用启动 [in /home/renjianbo/aitsc/config/base.py:82]
|
||||
2025-10-17 23:11:02,368 INFO: 应用启动 [in /home/renjianbo/aitsc/config/base.py:82]
|
||||
2025-10-17 23:17:26,078 INFO: 应用启动 [in /home/renjianbo/aitsc/config/base.py:82]
|
||||
2025-10-18 08:48:09,480 WARNING: 慢查询检测: 1.75s - SELECT admin_user.id AS admin_user_id, admin_user.username AS admin_user_username, admin_user.passwo... [in /home/renjianbo/aitsc/src/flask_prompt_master/utils/performance_monitor.py:35]
|
||||
2025-10-27 11:27:36,580 ERROR: LLM API调用失败 (尝试 1/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-27 11:27:45,432 ERROR: LLM API调用失败 (尝试 2/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-27 11:27:56,356 ERROR: LLM API调用失败 (尝试 3/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-27 11:27:56,361 ERROR: Exception on / [POST] [in /home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py:1414]
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 644, in connect
|
||||
sock = socket.create_connection(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/socket.py", line 865, in create_connection
|
||||
raise exceptions[0]
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/socket.py", line 850, in create_connection
|
||||
sock.connect(sa)
|
||||
ConnectionRefusedError: [Errno 111] Connection refused
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py", line 208, in index
|
||||
conn = pymysql.connect(
|
||||
^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 358, in __init__
|
||||
self.connect()
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 711, in connect
|
||||
raise exc
|
||||
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 2190, in wsgi_app
|
||||
response = self.full_dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1486, in full_dispatch_request
|
||||
rv = self.handle_user_exception(e)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask_cors/extension.py", line 176, in wrapped_function
|
||||
return cors_after_request(app.make_response(f(*args, **kwargs)))
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1484, in full_dispatch_request
|
||||
rv = self.dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1469, in dispatch_request
|
||||
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py", line 225, in index
|
||||
print(f"获取用户ID失败: {str(e)}")
|
||||
OSError: [Errno 5] Input/output error
|
||||
2025-10-27 11:28:15,195 ERROR: LLM API调用失败 (尝试 1/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-27 11:28:24,273 ERROR: LLM API调用失败 (尝试 2/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-27 11:28:37,182 ERROR: LLM API调用失败 (尝试 3/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-27 11:28:37,184 ERROR: Exception on / [POST] [in /home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py:1414]
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 644, in connect
|
||||
sock = socket.create_connection(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/socket.py", line 865, in create_connection
|
||||
raise exceptions[0]
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/socket.py", line 850, in create_connection
|
||||
sock.connect(sa)
|
||||
ConnectionRefusedError: [Errno 111] Connection refused
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py", line 208, in index
|
||||
conn = pymysql.connect(
|
||||
^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 358, in __init__
|
||||
self.connect()
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 711, in connect
|
||||
raise exc
|
||||
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 2190, in wsgi_app
|
||||
response = self.full_dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1486, in full_dispatch_request
|
||||
rv = self.handle_user_exception(e)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask_cors/extension.py", line 176, in wrapped_function
|
||||
return cors_after_request(app.make_response(f(*args, **kwargs)))
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1484, in full_dispatch_request
|
||||
rv = self.dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1469, in dispatch_request
|
||||
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py", line 225, in index
|
||||
print(f"获取用户ID失败: {str(e)}")
|
||||
OSError: [Errno 5] Input/output error
|
||||
2025-10-28 07:52:24,409 ERROR: LLM API调用失败 (尝试 1/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 07:52:39,998 ERROR: LLM API调用失败 (尝试 2/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 07:52:55,022 ERROR: LLM API调用失败 (尝试 3/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 07:52:55,024 ERROR: Exception on / [POST] [in /home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py:1414]
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 644, in connect
|
||||
sock = socket.create_connection(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/socket.py", line 865, in create_connection
|
||||
raise exceptions[0]
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/socket.py", line 850, in create_connection
|
||||
sock.connect(sa)
|
||||
ConnectionRefusedError: [Errno 111] Connection refused
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py", line 208, in index
|
||||
conn = pymysql.connect(
|
||||
^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 358, in __init__
|
||||
self.connect()
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 711, in connect
|
||||
raise exc
|
||||
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 2190, in wsgi_app
|
||||
response = self.full_dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1486, in full_dispatch_request
|
||||
rv = self.handle_user_exception(e)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask_cors/extension.py", line 176, in wrapped_function
|
||||
return cors_after_request(app.make_response(f(*args, **kwargs)))
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1484, in full_dispatch_request
|
||||
rv = self.dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1469, in dispatch_request
|
||||
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py", line 225, in index
|
||||
print(f"获取用户ID失败: {str(e)}")
|
||||
OSError: [Errno 5] Input/output error
|
||||
2025-10-28 08:00:33,784 ERROR: LLM API调用失败 (尝试 1/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 08:00:54,337 ERROR: LLM API调用失败 (尝试 2/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 08:01:15,922 ERROR: LLM API调用失败 (尝试 3/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 08:01:58,631 ERROR: LLM API调用失败 (尝试 1/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 08:02:10,952 ERROR: LLM API调用失败 (尝试 2/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 08:02:19,800 ERROR: LLM API调用失败 (尝试 1/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 08:02:30,316 ERROR: LLM API调用失败 (尝试 3/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 08:02:30,319 ERROR: Exception on / [POST] [in /home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py:1414]
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 644, in connect
|
||||
sock = socket.create_connection(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/socket.py", line 865, in create_connection
|
||||
raise exceptions[0]
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/socket.py", line 850, in create_connection
|
||||
sock.connect(sa)
|
||||
ConnectionRefusedError: [Errno 111] Connection refused
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py", line 208, in index
|
||||
get_template_icon=get_template_icon, industries=industries,
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 358, in __init__
|
||||
self.connect()
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 711, in connect
|
||||
raise exc
|
||||
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 2190, in wsgi_app
|
||||
response = self.full_dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1486, in full_dispatch_request
|
||||
rv = self.handle_user_exception(e)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask_cors/extension.py", line 176, in wrapped_function
|
||||
return cors_after_request(app.make_response(f(*args, **kwargs)))
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1484, in full_dispatch_request
|
||||
rv = self.dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1469, in dispatch_request
|
||||
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py", line 225, in index
|
||||
OSError: [Errno 5] Input/output error
|
||||
2025-10-28 08:02:33,775 ERROR: LLM API调用失败 (尝试 2/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 08:02:46,133 ERROR: LLM API调用失败 (尝试 3/3): [Errno 5] Input/output error [in /home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py:100]
|
||||
2025-10-28 08:02:46,135 ERROR: Exception on / [POST] [in /home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py:1414]
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 644, in connect
|
||||
sock = socket.create_connection(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/socket.py", line 865, in create_connection
|
||||
raise exceptions[0]
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/socket.py", line 850, in create_connection
|
||||
sock.connect(sa)
|
||||
ConnectionRefusedError: [Errno 111] Connection refused
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py", line 208, in index
|
||||
get_template_icon=get_template_icon, industries=industries,
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 358, in __init__
|
||||
self.connect()
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/pymysql/connections.py", line 711, in connect
|
||||
raise exc
|
||||
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 2190, in wsgi_app
|
||||
response = self.full_dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1486, in full_dispatch_request
|
||||
rv = self.handle_user_exception(e)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask_cors/extension.py", line 176, in wrapped_function
|
||||
return cors_after_request(app.make_response(f(*args, **kwargs)))
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1484, in full_dispatch_request
|
||||
rv = self.dispatch_request()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/miniconda3/envs/myenv/lib/python3.12/site-packages/flask/app.py", line 1469, in dispatch_request
|
||||
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/renjianbo/aitsc/src/flask_prompt_master/routes/routes.py", line 225, in index
|
||||
OSError: [Errno 5] Input/output error
|
||||
2025-10-28 08:04:36,598 INFO: 应用启动 [in /home/renjianbo/aitsc/config/base.py:82]
|
||||
|
||||
@@ -1 +1 @@
|
||||
11425
|
||||
24197
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,463 +0,0 @@
|
||||
"""
|
||||
优化历史功能API接口
|
||||
使用腾讯云prompt表
|
||||
"""
|
||||
|
||||
from flask import Blueprint, request, jsonify, current_app, render_template
|
||||
from flask_login import login_required, current_user
|
||||
from datetime import datetime, date, timedelta
|
||||
from sqlalchemy import func, desc, or_
|
||||
import time
|
||||
import pymysql
|
||||
|
||||
# 导入数据库模型
|
||||
from ..models.models import db, Prompt, User, WxUser
|
||||
|
||||
# 创建蓝图
|
||||
optimization_history_bp = Blueprint('optimization_history', __name__)
|
||||
|
||||
def get_current_user_id():
|
||||
"""获取当前用户ID,如果没有登录用户则返回默认用户ID"""
|
||||
try:
|
||||
return current_user.id if current_user.is_authenticated else 1
|
||||
except:
|
||||
return 1
|
||||
|
||||
def get_tencent_db_connection():
|
||||
"""获取腾讯云数据库连接"""
|
||||
try:
|
||||
conn = pymysql.connect(
|
||||
host='gz-cynosdbmysql-grp-d26pzce5.sql.tencentcdb.com',
|
||||
port=24936,
|
||||
user='root',
|
||||
password='!Rjb12191',
|
||||
database='pro_db',
|
||||
charset='utf8mb4'
|
||||
)
|
||||
return conn
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"连接腾讯云数据库失败: {str(e)}")
|
||||
return None
|
||||
|
||||
@optimization_history_bp.route('/optimization-history')
|
||||
# @login_required # 暂时注释掉登录要求,用于测试
|
||||
def optimization_history_page():
|
||||
"""优化历史页面"""
|
||||
return render_template('optimization_history.html')
|
||||
|
||||
@optimization_history_bp.route('/api/optimization-history', methods=['GET'])
|
||||
# @login_required # 暂时注释掉登录要求,用于测试
|
||||
def get_optimization_history():
|
||||
"""获取用户优化历史记录 - 使用腾讯云prompt表"""
|
||||
try:
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 20, type=int)
|
||||
search = request.args.get('search', '')
|
||||
date_filter = request.args.get('date_filter', '')
|
||||
|
||||
# 获取腾讯云数据库连接
|
||||
conn = get_tencent_db_connection()
|
||||
if not conn:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '数据库连接失败'
|
||||
}), 500
|
||||
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 构建查询条件
|
||||
where_conditions = []
|
||||
params = []
|
||||
|
||||
# 用户ID条件
|
||||
user_id = get_current_user_id()
|
||||
where_conditions.append("(user_id = %s OR wx_user_id = %s)")
|
||||
params.extend([user_id, user_id])
|
||||
|
||||
# 搜索条件
|
||||
if search:
|
||||
where_conditions.append("(input_text LIKE %s OR generated_text LIKE %s)")
|
||||
search_param = f"%{search}%"
|
||||
params.extend([search_param, search_param])
|
||||
|
||||
# 日期过滤
|
||||
if date_filter:
|
||||
if date_filter == 'today':
|
||||
where_conditions.append("DATE(created_at) = CURDATE()")
|
||||
elif date_filter == 'week':
|
||||
where_conditions.append("created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)")
|
||||
elif date_filter == 'month':
|
||||
where_conditions.append("created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)")
|
||||
|
||||
# 构建完整查询
|
||||
where_clause = " AND ".join(where_conditions) if where_conditions else "1=1"
|
||||
|
||||
# 获取总数
|
||||
count_sql = f"SELECT COUNT(*) FROM prompt WHERE {where_clause}"
|
||||
cursor.execute(count_sql, params)
|
||||
total = cursor.fetchone()[0]
|
||||
|
||||
# 计算分页
|
||||
offset = (page - 1) * per_page
|
||||
total_pages = (total + per_page - 1) // per_page
|
||||
|
||||
# 获取数据
|
||||
data_sql = f"""
|
||||
SELECT id, input_text, generated_text, created_at, user_id, wx_user_id
|
||||
FROM prompt
|
||||
WHERE {where_clause}
|
||||
ORDER BY created_at DESC
|
||||
LIMIT %s OFFSET %s
|
||||
"""
|
||||
cursor.execute(data_sql, params + [per_page, offset])
|
||||
results = cursor.fetchall()
|
||||
|
||||
# 格式化数据
|
||||
history_list = []
|
||||
for row in results:
|
||||
history_dict = {
|
||||
'id': row[0],
|
||||
'original_text': row[1],
|
||||
'optimized_text': row[2],
|
||||
'created_at': row[3].strftime('%Y-%m-%d %H:%M:%S') if row[3] else None,
|
||||
'user_id': row[4],
|
||||
'wx_user_id': row[5],
|
||||
'optimization_type': '提示词优化',
|
||||
'satisfaction_rating': None,
|
||||
'is_favorite': False
|
||||
}
|
||||
history_list.append(history_dict)
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': {
|
||||
'history': history_list,
|
||||
'pagination': {
|
||||
'page': page,
|
||||
'per_page': per_page,
|
||||
'total': total,
|
||||
'pages': total_pages,
|
||||
'has_next': page < total_pages,
|
||||
'has_prev': page > 1
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"获取优化历史失败: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '获取历史记录失败'
|
||||
}), 500
|
||||
|
||||
|
||||
@optimization_history_bp.route('/api/optimization-history', methods=['POST'])
|
||||
# @login_required # 暂时注释掉登录要求,用于测试
|
||||
def add_optimization_history():
|
||||
"""添加优化历史记录 - 使用腾讯云prompt表"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
|
||||
# 验证必需字段
|
||||
required_fields = ['original_text', 'optimized_text']
|
||||
for field in required_fields:
|
||||
if field not in data or not data[field]:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'缺少必需字段: {field}'
|
||||
}), 400
|
||||
|
||||
# 获取腾讯云数据库连接
|
||||
conn = get_tencent_db_connection()
|
||||
if not conn:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '数据库连接失败'
|
||||
}), 500
|
||||
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 获取用户ID
|
||||
user_id = get_current_user_id()
|
||||
|
||||
# 插入数据到prompt表
|
||||
insert_sql = """
|
||||
INSERT INTO prompt (input_text, generated_text, user_id, wx_user_id, created_at)
|
||||
VALUES (%s, %s, %s, %s, NOW())
|
||||
"""
|
||||
|
||||
cursor.execute(insert_sql, [
|
||||
data['original_text'],
|
||||
data['optimized_text'],
|
||||
user_id,
|
||||
data.get('wx_user_id', user_id) # 如果有微信用户ID则使用,否则使用普通用户ID
|
||||
])
|
||||
|
||||
# 获取插入的记录ID
|
||||
prompt_id = cursor.lastrowid
|
||||
|
||||
conn.commit()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
# 返回结果
|
||||
result_data = {
|
||||
'id': prompt_id,
|
||||
'original_text': data['original_text'],
|
||||
'optimized_text': data['optimized_text'],
|
||||
'user_id': user_id,
|
||||
'wx_user_id': data.get('wx_user_id', user_id),
|
||||
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'optimization_type': '提示词优化',
|
||||
'satisfaction_rating': None,
|
||||
'is_favorite': False
|
||||
}
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': result_data,
|
||||
'message': '历史记录添加成功'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
current_app.logger.error(f"添加优化历史失败: {str(e)}")
|
||||
current_app.logger.error(f"错误堆栈: {traceback.format_exc()}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'添加历史记录失败: {str(e)}'
|
||||
}), 500
|
||||
|
||||
|
||||
@optimization_history_bp.route('/api/optimization-history/<int:history_id>/rating', methods=['PUT'])
|
||||
# @login_required # 暂时注释掉登录要求,用于测试
|
||||
def update_rating(history_id):
|
||||
"""更新满意度评分 - 简化版本,仅返回成功"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
rating = data.get('rating')
|
||||
|
||||
if not rating or not isinstance(rating, int) or rating < 1 or rating > 5:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '评分必须在1-5之间'
|
||||
}), 400
|
||||
|
||||
# 由于prompt表没有评分字段,这里仅返回成功
|
||||
# 在实际应用中,可以考虑添加评分字段到prompt表
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': '评分记录成功(注意:当前版本暂不支持评分存储)'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"更新评分失败: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '更新评分失败'
|
||||
}), 500
|
||||
|
||||
|
||||
@optimization_history_bp.route('/api/optimization-history/<int:history_id>', methods=['DELETE'])
|
||||
# @login_required # 暂时注释掉登录要求,用于测试
|
||||
def delete_optimization_history(history_id):
|
||||
"""删除优化历史记录 - 使用腾讯云prompt表"""
|
||||
try:
|
||||
# 获取腾讯云数据库连接
|
||||
conn = get_tencent_db_connection()
|
||||
if not conn:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '数据库连接失败'
|
||||
}), 500
|
||||
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 检查记录是否存在
|
||||
check_sql = "SELECT id FROM prompt WHERE id = %s"
|
||||
cursor.execute(check_sql, [history_id])
|
||||
if not cursor.fetchone():
|
||||
cursor.close()
|
||||
conn.close()
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '历史记录不存在'
|
||||
}), 404
|
||||
|
||||
# 删除记录
|
||||
delete_sql = "DELETE FROM prompt WHERE id = %s"
|
||||
cursor.execute(delete_sql, [history_id])
|
||||
|
||||
conn.commit()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': '历史记录删除成功'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"删除优化历史失败: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '删除历史记录失败'
|
||||
}), 500
|
||||
|
||||
|
||||
@optimization_history_bp.route('/api/optimization-history/clear', methods=['DELETE'])
|
||||
# @login_required # 暂时注释掉登录要求,用于测试
|
||||
def clear_optimization_history():
|
||||
"""清空用户优化历史记录 - 使用腾讯云prompt表"""
|
||||
try:
|
||||
# 获取腾讯云数据库连接
|
||||
conn = get_tencent_db_connection()
|
||||
if not conn:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '数据库连接失败'
|
||||
}), 500
|
||||
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 获取用户ID
|
||||
user_id = get_current_user_id()
|
||||
|
||||
# 删除用户的所有记录
|
||||
delete_sql = "DELETE FROM prompt WHERE user_id = %s OR wx_user_id = %s"
|
||||
cursor.execute(delete_sql, [user_id, user_id])
|
||||
deleted_count = cursor.rowcount
|
||||
|
||||
conn.commit()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f'历史记录清空成功,共删除 {deleted_count} 条记录'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"清空优化历史失败: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '清空历史记录失败'
|
||||
}), 500
|
||||
|
||||
|
||||
@optimization_history_bp.route('/api/optimization-history/stats', methods=['GET'])
|
||||
# @login_required # 暂时注释掉登录要求,用于测试
|
||||
def get_user_stats():
|
||||
"""获取用户使用统计 - 简化版本"""
|
||||
try:
|
||||
# 获取腾讯云数据库连接
|
||||
conn = get_tencent_db_connection()
|
||||
if not conn:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '数据库连接失败'
|
||||
}), 500
|
||||
|
||||
cursor = conn.cursor()
|
||||
user_id = get_current_user_id()
|
||||
|
||||
# 获取基本统计信息
|
||||
stats_sql = """
|
||||
SELECT
|
||||
COUNT(*) as total_count,
|
||||
COUNT(CASE WHEN DATE(created_at) = CURDATE() THEN 1 END) as today_count,
|
||||
COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) THEN 1 END) as week_count,
|
||||
COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as month_count
|
||||
FROM prompt
|
||||
WHERE user_id = %s OR wx_user_id = %s
|
||||
"""
|
||||
cursor.execute(stats_sql, [user_id, user_id])
|
||||
stats = cursor.fetchone()
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': {
|
||||
'total_count': stats[0] or 0,
|
||||
'today_count': stats[1] or 0,
|
||||
'week_count': stats[2] or 0,
|
||||
'month_count': stats[3] or 0
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"获取用户统计失败: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '获取统计信息失败'
|
||||
}), 500
|
||||
|
||||
|
||||
@optimization_history_bp.route('/api/optimization-history/export', methods=['GET'])
|
||||
# @login_required # 暂时注释掉登录要求,用于测试
|
||||
def export_history():
|
||||
"""导出历史记录 - 使用腾讯云prompt表"""
|
||||
try:
|
||||
format_type = request.args.get('format', 'json')
|
||||
|
||||
# 获取腾讯云数据库连接
|
||||
conn = get_tencent_db_connection()
|
||||
if not conn:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '数据库连接失败'
|
||||
}), 500
|
||||
|
||||
cursor = conn.cursor()
|
||||
user_id = get_current_user_id()
|
||||
|
||||
# 获取所有历史记录
|
||||
export_sql = """
|
||||
SELECT id, input_text, generated_text, created_at, user_id, wx_user_id
|
||||
FROM prompt
|
||||
WHERE user_id = %s OR wx_user_id = %s
|
||||
ORDER BY created_at DESC
|
||||
"""
|
||||
cursor.execute(export_sql, [user_id, user_id])
|
||||
results = cursor.fetchall()
|
||||
|
||||
# 格式化数据
|
||||
history_list = []
|
||||
for row in results:
|
||||
history_dict = {
|
||||
'id': row[0],
|
||||
'original_text': row[1],
|
||||
'optimized_text': row[2],
|
||||
'created_at': row[3].strftime('%Y-%m-%d %H:%M:%S') if row[3] else None,
|
||||
'user_id': row[4],
|
||||
'wx_user_id': row[5],
|
||||
'optimization_type': '提示词优化'
|
||||
}
|
||||
history_list.append(history_dict)
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
if format_type == 'json':
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': history_list,
|
||||
'count': len(history_list)
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '不支持的导出格式'
|
||||
}), 400
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"导出历史记录失败: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '导出历史记录失败'
|
||||
}), 500
|
||||
@@ -56,11 +56,11 @@ def generate_with_llm(input_text, template_id=None, max_retries=3):
|
||||
start_time = time.time() # 记录开始时间
|
||||
|
||||
# 记录参数到日志
|
||||
logger.info("=== API 调用参数 ===")
|
||||
logger.info(f"模板ID: {template_id}")
|
||||
logger.info(f"输入文本: {input_text}")
|
||||
logger.info(f"系统提示: {system_prompt}")
|
||||
logger.info("==================")
|
||||
current_app.logger.info("=== API 调用参数 ===")
|
||||
current_app.logger.info(f"模板ID: {template_id}")
|
||||
current_app.logger.info(f"输入文本: {input_text}")
|
||||
current_app.logger.info(f"系统提示: {system_prompt}")
|
||||
current_app.logger.info("==================")
|
||||
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
@@ -80,10 +80,10 @@ def generate_with_llm(input_text, template_id=None, max_retries=3):
|
||||
|
||||
# 打印响应
|
||||
generated_text = response.choices[0].message.content.strip()
|
||||
print("\n=== API 响应结果 ===")
|
||||
print(f"生成的提示词: {generated_text}")
|
||||
print(f"生成耗时: {generation_time}ms")
|
||||
print("==================\n")
|
||||
current_app.logger.info("=== API 响应结果 ===")
|
||||
current_app.logger.info(f"生成的提示词: {generated_text}")
|
||||
current_app.logger.info(f"生成耗时: {generation_time}ms")
|
||||
current_app.logger.info("==================")
|
||||
|
||||
# 保存到历史记录
|
||||
save_to_history(
|
||||
@@ -197,46 +197,69 @@ def index():
|
||||
sub_categories = sorted(set(t.sub_category for t in all_templates if t.sub_category))
|
||||
|
||||
if form.validate_on_submit():
|
||||
template_id = request.form.get('template_id')
|
||||
generated_text = generate_with_llm(form.input_text.data, template_id)
|
||||
|
||||
# 获取搜索状态
|
||||
search_state = request.form.get('search_state', '')
|
||||
|
||||
# 获取默认用户的 uid
|
||||
try:
|
||||
conn = pymysql.connect(
|
||||
host='localhost',
|
||||
user='root',
|
||||
password='123456',
|
||||
database='pro_db',
|
||||
charset='utf8mb4'
|
||||
template_id = request.form.get('template_id')
|
||||
input_text = form.input_text.data.strip()
|
||||
|
||||
# 验证输入
|
||||
if not input_text:
|
||||
flash('请输入您的需求描述', 'error')
|
||||
return render_template('generate.html', form=form, prompt=None, templates=templates,
|
||||
get_template_icon=get_template_icon, industries=industries,
|
||||
professions=professions, categories=categories,
|
||||
sub_categories=sub_categories)
|
||||
|
||||
# 调用LLM生成提示词
|
||||
generated_text = generate_with_llm(input_text, template_id)
|
||||
|
||||
# 检查生成结果
|
||||
if not generated_text or generated_text.startswith("提示词生成失败"):
|
||||
flash(f'生成失败: {generated_text}', 'error')
|
||||
return render_template('generate.html', form=form, prompt=None, templates=templates,
|
||||
get_template_icon=get_template_icon, industries=industries,
|
||||
professions=professions, categories=categories,
|
||||
sub_categories=sub_categories)
|
||||
|
||||
# 获取搜索状态
|
||||
search_state = request.form.get('search_state', '')
|
||||
|
||||
# 获取默认用户的 uid - 使用SQLAlchemy而不是直接连接
|
||||
try:
|
||||
# 尝试从现有用户表中获取admin用户
|
||||
admin_user = User.query.filter_by(login_name='admin').first()
|
||||
if admin_user:
|
||||
user_id = admin_user.uid
|
||||
else:
|
||||
# 如果没有admin用户,使用默认值
|
||||
user_id = 1
|
||||
except Exception as e:
|
||||
current_app.logger.warning(f"获取用户ID失败: {str(e)}")
|
||||
user_id = 1 # 如果查询失败,使用默认值
|
||||
|
||||
# 保存到数据库
|
||||
prompt = Prompt(
|
||||
input_text=input_text,
|
||||
generated_text=generated_text,
|
||||
user_id=user_id
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT uid FROM user WHERE login_name = 'admin' LIMIT 1")
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
user_id = result[0]
|
||||
else:
|
||||
user_id = 1 # 如果没有找到用户,使用默认值
|
||||
cursor.close()
|
||||
conn.close()
|
||||
db.session.add(prompt)
|
||||
db.session.commit()
|
||||
|
||||
flash('提示词生成成功!', 'success')
|
||||
return render_template('generate.html', form=form, prompt=prompt, templates=templates,
|
||||
get_template_icon=get_template_icon, industries=industries,
|
||||
professions=professions, categories=categories,
|
||||
sub_categories=sub_categories, selected_template_id=template_id,
|
||||
search_state=search_state)
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取用户ID失败: {str(e)}")
|
||||
user_id = 1 # 如果查询失败,使用默认值
|
||||
|
||||
prompt = Prompt(
|
||||
input_text=form.input_text.data,
|
||||
generated_text=generated_text,
|
||||
user_id=user_id # 使用查询到的用户ID
|
||||
)
|
||||
db.session.add(prompt)
|
||||
db.session.commit()
|
||||
return render_template('generate.html', form=form, prompt=prompt, templates=templates,
|
||||
get_template_icon=get_template_icon, industries=industries,
|
||||
professions=professions, categories=categories,
|
||||
sub_categories=sub_categories, selected_template_id=template_id,
|
||||
search_state=search_state)
|
||||
current_app.logger.error(f"生成提示词时发生错误: {str(e)}")
|
||||
db.session.rollback()
|
||||
flash(f'生成提示词时发生错误: {str(e)}', 'error')
|
||||
return render_template('generate.html', form=form, prompt=None, templates=templates,
|
||||
get_template_icon=get_template_icon, industries=industries,
|
||||
professions=professions, categories=categories,
|
||||
sub_categories=sub_categories)
|
||||
return render_template('generate.html', form=form, prompt=None, templates=templates,
|
||||
get_template_icon=get_template_icon, industries=industries,
|
||||
professions=professions, categories=categories,
|
||||
|
||||
@@ -1,846 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}优化历史 - AI应用{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
/* 优化历史页面样式 - 参考收藏页面设计 */
|
||||
.history-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #1E3A8A 0%, #3B82F6 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.history-header {
|
||||
text-align: center;
|
||||
color: white;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.history-header h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.history-header p {
|
||||
font-size: 1.2rem;
|
||||
opacity: 0.9;
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.history-content {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* 搜索和筛选区域 */
|
||||
.search-filters {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.search-filters input,
|
||||
.search-filters select {
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e1e5e9;
|
||||
border-radius: 10px;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.search-filters input:focus,
|
||||
.search-filters select:focus {
|
||||
outline: none;
|
||||
border-color: #3B82F6;
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
background: linear-gradient(135deg, #3B82F6, #1E3A8A);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
/* 历史记录列表 */
|
||||
.history-list {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
background: #f8f9fa;
|
||||
border: 2px solid #e1e5e9;
|
||||
border-radius: 15px;
|
||||
padding: 25px;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.history-item:hover {
|
||||
border-color: #3B82F6;
|
||||
box-shadow: 0 5px 15px rgba(59, 130, 246, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.history-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.history-title {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.history-type {
|
||||
color: #3B82F6;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.history-meta {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.history-time {
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.history-industry {
|
||||
background: #e3f2fd;
|
||||
color: #1976d2;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.history-profession {
|
||||
background: #f3e5f5;
|
||||
color: #7b1fa2;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.history-content {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.history-original,
|
||||
.history-optimized {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.history-original h4,
|
||||
.history-optimized h4 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.history-original h4::before {
|
||||
content: "📝";
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.history-optimized h4::before {
|
||||
content: "✨";
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.history-original p,
|
||||
.history-optimized p {
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
background: white;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #e1e5e9;
|
||||
}
|
||||
|
||||
.history-optimized p {
|
||||
border-left-color: #3B82F6;
|
||||
background: #f8faff;
|
||||
}
|
||||
|
||||
/* 标签区域 */
|
||||
.history-tags {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 15px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tag {
|
||||
background: linear-gradient(135deg, #6366F1, #8B5CF6);
|
||||
color: white;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 评分区域 */
|
||||
.history-rating {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.stars {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.stars i {
|
||||
color: #ddd;
|
||||
font-size: 1.1rem;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.stars i.active {
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
.history-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: #10B981;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: #059669;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.favorite-btn {
|
||||
background: #EF4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.favorite-btn:hover {
|
||||
background: #DC2626;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.favorite-btn.favorited {
|
||||
background: #F59E0B;
|
||||
}
|
||||
|
||||
.favorite-btn.favorited:hover {
|
||||
background: #D97706;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background: #6B7280;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background: #4B5563;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* 分页 */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-top: 30px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.pagination button {
|
||||
padding: 10px 16px;
|
||||
border: 2px solid #e1e5e9;
|
||||
background: white;
|
||||
color: #333;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pagination button:hover {
|
||||
border-color: #3B82F6;
|
||||
color: #3B82F6;
|
||||
}
|
||||
|
||||
.pagination button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.pagination .current-page {
|
||||
background: #3B82F6;
|
||||
color: white;
|
||||
border-color: #3B82F6;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #3B82F6;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.no-history {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.no-history i {
|
||||
font-size: 4rem;
|
||||
color: #ddd;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.no-history h3 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.no-history p {
|
||||
font-size: 1rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 统计卡片 */
|
||||
.stats-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: linear-gradient(135deg, #3B82F6, #1E3A8A);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
box-shadow: 0 5px 15px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.stat-card h3 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.stat-card p {
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.9;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.history-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.history-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.search-filters {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.history-header {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.history-meta {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.history-actions {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.stats-cards {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.stats-cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.history-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="history-container">
|
||||
<div class="history-header">
|
||||
<h1><i class="fas fa-history"></i> 优化历史</h1>
|
||||
<p>查看和管理您的AI优化历史记录,随时回顾精彩内容</p>
|
||||
</div>
|
||||
|
||||
<div class="history-content">
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stats-cards" id="statsCards">
|
||||
<div class="stat-card">
|
||||
<h3 id="totalCount">0</h3>
|
||||
<p>总记录数</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="todayCount">0</h3>
|
||||
<p>今日生成</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="avgRating">0</h3>
|
||||
<p>平均评分</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3 id="timeSaved">0</h3>
|
||||
<p>节省时间(分钟)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索和筛选 -->
|
||||
<div class="search-filters">
|
||||
<input type="text" id="searchInput" placeholder="搜索历史记录...">
|
||||
<select id="typeFilter">
|
||||
<option value="">所有类型</option>
|
||||
<option value="提示词优化">提示词优化</option>
|
||||
<option value="产品文案">产品文案</option>
|
||||
<option value="商务邮件">商务邮件</option>
|
||||
<option value="技术文档">技术文档</option>
|
||||
<option value="营销文案">营销文案</option>
|
||||
</select>
|
||||
<select id="dateFilter">
|
||||
<option value="">所有时间</option>
|
||||
<option value="today">今天</option>
|
||||
<option value="week">本周</option>
|
||||
<option value="month">本月</option>
|
||||
</select>
|
||||
<select id="ratingFilter">
|
||||
<option value="">所有评分</option>
|
||||
<option value="5">5星</option>
|
||||
<option value="4">4星</option>
|
||||
<option value="3">3星</option>
|
||||
<option value="2">2星</option>
|
||||
<option value="1">1星</option>
|
||||
</select>
|
||||
<button id="searchBtn" class="search-btn">
|
||||
<i class="fas fa-search"></i> 搜索
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 历史记录列表 -->
|
||||
<div id="historyList" class="history-list">
|
||||
<div class="loading">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>正在加载历史记录...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div id="pagination" class="pagination" style="display: none;">
|
||||
<!-- 分页内容 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 历史记录详情模态框 -->
|
||||
<div class="modal fade" id="historyDetailModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
<i class="fas fa-history"></i> 历史记录详情
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="history-detail-content">
|
||||
<!-- 详情内容 -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
||||
<button type="button" class="btn btn-primary" id="copy-detail-btn">
|
||||
<i class="fas fa-copy"></i> 复制结果
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning" id="rate-detail-btn">
|
||||
<i class="fas fa-star"></i> 评分
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" id="delete-detail-btn">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 评分模态框 -->
|
||||
<div class="modal fade" id="ratingModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
<i class="fas fa-star"></i> 满意度评分
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="text-center">
|
||||
<p class="mb-3">请为这次优化结果评分:</p>
|
||||
<div class="rating-stars" id="ratingStars">
|
||||
<i class="fas fa-star" data-rating="1"></i>
|
||||
<i class="fas fa-star" data-rating="2"></i>
|
||||
<i class="fas fa-star" data-rating="3"></i>
|
||||
<i class="fas fa-star" data-rating="4"></i>
|
||||
<i class="fas fa-star" data-rating="5"></i>
|
||||
</div>
|
||||
<p class="mt-3 text-muted" id="ratingText">点击星星进行评分</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="save-rating-btn" disabled>保存评分</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/optimization_history_db.js') }}"></script>
|
||||
<script>
|
||||
let currentPage = 1;
|
||||
let currentHistoryId = null;
|
||||
let currentRating = 0;
|
||||
|
||||
// 页面加载完成后执行
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('🚀 优化历史页面开始初始化...');
|
||||
|
||||
try {
|
||||
// 初始化优化历史数据库对象
|
||||
window.optimizationHistoryDB = new OptimizationHistoryDB();
|
||||
console.log('✅ OptimizationHistoryDB对象创建成功');
|
||||
|
||||
loadStats();
|
||||
console.log('✅ 统计信息加载完成');
|
||||
|
||||
loadHistory();
|
||||
console.log('✅ 历史记录加载完成');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 初始化失败:', error);
|
||||
document.getElementById('historyList').innerHTML = `
|
||||
<div class="error-message">
|
||||
<h3>初始化失败</h3>
|
||||
<p>错误信息: ${error.message}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 搜索按钮点击事件
|
||||
document.getElementById('searchBtn').addEventListener('click', function() {
|
||||
currentPage = 1;
|
||||
loadHistory();
|
||||
});
|
||||
|
||||
// 回车搜索
|
||||
document.getElementById('searchInput').addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
currentPage = 1;
|
||||
loadHistory();
|
||||
}
|
||||
});
|
||||
|
||||
// 筛选器变化
|
||||
['typeFilter', 'dateFilter', 'ratingFilter'].forEach(id => {
|
||||
document.getElementById(id).addEventListener('change', function() {
|
||||
currentPage = 1;
|
||||
loadHistory();
|
||||
});
|
||||
});
|
||||
|
||||
// 评分星星点击事件
|
||||
document.querySelectorAll('#ratingStars i').forEach(star => {
|
||||
star.addEventListener('click', function() {
|
||||
currentRating = parseInt(this.dataset.rating);
|
||||
updateRatingDisplay();
|
||||
});
|
||||
|
||||
star.addEventListener('mouseenter', function() {
|
||||
const rating = parseInt(this.dataset.rating);
|
||||
highlightStars(rating);
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('ratingStars').addEventListener('mouseleave', function() {
|
||||
highlightStars(currentRating);
|
||||
});
|
||||
|
||||
// 保存评分
|
||||
document.getElementById('save-rating-btn').addEventListener('click', function() {
|
||||
if (currentRating > 0 && currentHistoryId) {
|
||||
optimizationHistoryDB.updateRating(currentHistoryId, currentRating)
|
||||
.then(success => {
|
||||
if (success) {
|
||||
$('#ratingModal').modal('hide');
|
||||
loadHistory();
|
||||
loadStats();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 复制详情
|
||||
document.getElementById('copy-detail-btn').addEventListener('click', function() {
|
||||
const optimizedText = document.querySelector('#history-detail-content .optimized-text').textContent;
|
||||
navigator.clipboard.writeText(optimizedText).then(() => {
|
||||
showToast('内容已复制到剪贴板', 'success');
|
||||
});
|
||||
});
|
||||
|
||||
// 删除详情
|
||||
document.getElementById('delete-detail-btn').addEventListener('click', function() {
|
||||
if (confirm('确定要删除这条历史记录吗?')) {
|
||||
optimizationHistoryDB.deleteHistory(currentHistoryId)
|
||||
.then(success => {
|
||||
if (success) {
|
||||
$('#historyDetailModal').modal('hide');
|
||||
loadHistory();
|
||||
loadStats();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 加载统计信息
|
||||
function loadStats() {
|
||||
optimizationHistoryDB.getUserStats(30).then(stats => {
|
||||
if (stats) {
|
||||
document.getElementById('totalCount').textContent = stats.period.total_generations || 0;
|
||||
document.getElementById('todayCount').textContent = stats.today.generation_count || 0;
|
||||
document.getElementById('avgRating').textContent = stats.period.avg_rating || 0;
|
||||
document.getElementById('timeSaved').textContent = stats.period.total_time_saved || 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 加载历史记录
|
||||
function loadHistory() {
|
||||
console.log('🔄 开始加载历史记录...');
|
||||
|
||||
const search = document.getElementById('searchInput').value;
|
||||
const typeFilter = document.getElementById('typeFilter').value;
|
||||
const dateFilter = document.getElementById('dateFilter').value;
|
||||
const ratingFilter = document.getElementById('ratingFilter').value;
|
||||
|
||||
console.log('📋 搜索参数:', { search, typeFilter, dateFilter, ratingFilter });
|
||||
|
||||
optimizationHistoryDB.getHistory({
|
||||
page: currentPage,
|
||||
search: search,
|
||||
typeFilter: typeFilter,
|
||||
dateFilter: dateFilter,
|
||||
ratingFilter: ratingFilter
|
||||
});
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
function viewDetail(historyId) {
|
||||
currentHistoryId = historyId;
|
||||
|
||||
// 这里需要从当前显示的历史记录中获取数据
|
||||
// 实际实现中应该通过API获取完整数据
|
||||
const historyItem = document.querySelector(`[data-id="${historyId}"]`);
|
||||
if (historyItem) {
|
||||
const originalText = historyItem.querySelector('.history-original p').textContent;
|
||||
const optimizedText = historyItem.querySelector('.history-optimized p').textContent;
|
||||
const type = historyItem.querySelector('.history-type').textContent;
|
||||
const time = historyItem.querySelector('.history-time').textContent;
|
||||
|
||||
let html = `
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h6><i class="fas fa-edit"></i> 原始输入</h6>
|
||||
<div class="border rounded p-3 bg-light original-text">
|
||||
${escapeHtml(originalText)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6><i class="fas fa-magic"></i> 优化结果</h6>
|
||||
<div class="border rounded p-3 bg-light optimized-text">
|
||||
<pre class="mb-0">${escapeHtml(optimizedText)}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-6">
|
||||
<strong>类型:</strong> ${type}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<strong>时间:</strong> ${time}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.getElementById('history-detail-content').innerHTML = html;
|
||||
$('#historyDetailModal').modal('show');
|
||||
}
|
||||
}
|
||||
|
||||
// 评分功能
|
||||
function rateHistory(historyId) {
|
||||
currentHistoryId = historyId;
|
||||
currentRating = 0;
|
||||
updateRatingDisplay();
|
||||
$('#ratingModal').modal('show');
|
||||
}
|
||||
|
||||
// 更新评分显示
|
||||
function updateRatingDisplay() {
|
||||
highlightStars(currentRating);
|
||||
document.getElementById('save-rating-btn').disabled = currentRating === 0;
|
||||
|
||||
const texts = ['', '很差', '一般', '还行', '很好', '优秀'];
|
||||
document.getElementById('ratingText').textContent = texts[currentRating] || '点击星星进行评分';
|
||||
}
|
||||
|
||||
// 高亮星星
|
||||
function highlightStars(rating) {
|
||||
document.querySelectorAll('#ratingStars i').forEach((star, index) => {
|
||||
if (index < rating) {
|
||||
star.classList.add('active');
|
||||
} else {
|
||||
star.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 跳转到指定页面
|
||||
function goToPage(page) {
|
||||
currentPage = page;
|
||||
loadHistory();
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function showToast(message, type) {
|
||||
// 使用优化历史DB类中的消息显示功能
|
||||
if (window.optimizationHistoryDB) {
|
||||
if (type === 'success') {
|
||||
window.optimizationHistoryDB.showSuccess(message);
|
||||
} else {
|
||||
window.optimizationHistoryDB.showError(message);
|
||||
}
|
||||
} else {
|
||||
alert(message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -193,3 +193,4 @@ if __name__ == "__main__":
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
125
test_prompt_generation.py
Normal file
125
test_prompt_generation.py
Normal file
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
测试提示词生成功能的脚本
|
||||
"""
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
|
||||
def test_prompt_generation():
|
||||
"""测试提示词生成功能"""
|
||||
base_url = "http://localhost:5002"
|
||||
|
||||
# 测试数据
|
||||
test_data = {
|
||||
"input_text": "帮我写一个关于人工智能的演讲稿",
|
||||
"template_id": "1"
|
||||
}
|
||||
|
||||
print("🧪 开始测试提示词生成功能...")
|
||||
print(f"测试URL: {base_url}")
|
||||
print(f"测试数据: {test_data}")
|
||||
print("-" * 50)
|
||||
|
||||
try:
|
||||
# 发送POST请求
|
||||
response = requests.post(
|
||||
base_url,
|
||||
data=test_data,
|
||||
timeout=120, # 2分钟超时
|
||||
headers={
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'User-Agent': 'TestScript/1.0'
|
||||
}
|
||||
)
|
||||
|
||||
print(f"📊 响应状态码: {response.status_code}")
|
||||
print(f"📊 响应头: {dict(response.headers)}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ 请求成功!")
|
||||
|
||||
# 检查响应内容
|
||||
if "生成的提示词" in response.text or "提示词生成成功" in response.text:
|
||||
print("✅ 提示词生成功能正常!")
|
||||
elif "提示词生成失败" in response.text:
|
||||
print("❌ 提示词生成失败")
|
||||
print(f"错误信息: {response.text[:500]}...")
|
||||
else:
|
||||
print("⚠️ 响应内容异常")
|
||||
print(f"响应内容: {response.text[:500]}...")
|
||||
|
||||
elif response.status_code == 500:
|
||||
print("❌ 服务器内部错误")
|
||||
print(f"错误信息: {response.text[:500]}...")
|
||||
else:
|
||||
print(f"❌ 请求失败,状态码: {response.status_code}")
|
||||
print(f"响应内容: {response.text[:500]}...")
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
print("❌ 请求超时")
|
||||
except requests.exceptions.ConnectionError:
|
||||
print("❌ 连接失败,请检查服务器是否运行")
|
||||
except Exception as e:
|
||||
print(f"❌ 测试过程中发生错误: {str(e)}")
|
||||
|
||||
print("-" * 50)
|
||||
print("🏁 测试完成")
|
||||
|
||||
def test_api_endpoint():
|
||||
"""测试API端点"""
|
||||
base_url = "http://localhost:5002"
|
||||
|
||||
print("🧪 测试API端点...")
|
||||
|
||||
# 测试GET请求
|
||||
try:
|
||||
response = requests.get(base_url, timeout=30)
|
||||
if response.status_code == 200:
|
||||
print("✅ GET请求正常")
|
||||
else:
|
||||
print(f"❌ GET请求失败: {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f"❌ GET请求错误: {str(e)}")
|
||||
|
||||
# 测试微信API端点
|
||||
wx_url = f"{base_url}/api/wx/generate"
|
||||
wx_data = {
|
||||
"input_text": "测试微信API",
|
||||
"uid": 1
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
wx_url,
|
||||
json=wx_data,
|
||||
timeout=60,
|
||||
headers={'Content-Type': 'application/json'}
|
||||
)
|
||||
print(f"📊 微信API响应: {response.status_code}")
|
||||
if response.status_code == 200:
|
||||
print("✅ 微信API正常")
|
||||
else:
|
||||
print(f"❌ 微信API失败: {response.text[:200]}...")
|
||||
except Exception as e:
|
||||
print(f"❌ 微信API错误: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("🚀 启动提示词生成系统测试")
|
||||
print("=" * 60)
|
||||
|
||||
# 测试API端点
|
||||
test_api_endpoint()
|
||||
print()
|
||||
|
||||
# 测试提示词生成
|
||||
test_prompt_generation()
|
||||
|
||||
print("=" * 60)
|
||||
print("📋 测试总结:")
|
||||
print("1. 检查服务器是否运行在 localhost:5002")
|
||||
print("2. 检查数据库连接是否正常")
|
||||
print("3. 检查LLM API配置是否正确")
|
||||
print("4. 查看日志文件获取详细错误信息")
|
||||
|
||||
|
||||
@@ -170,3 +170,4 @@ if __name__ == "__main__":
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
162
启动和停止.txt
162
启动和停止.txt
@@ -1,162 +0,0 @@
|
||||
# 提示词大师项目 - 服务器启动和停止指南
|
||||
|
||||
## 🚀 **启动服务器**
|
||||
|
||||
### 方法一:使用完整命令启动
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd /home/renjianbo/aitsc
|
||||
|
||||
# 激活conda环境并启动服务
|
||||
eval "$(/home/renjianbo/miniconda3/bin/conda shell.bash hook)" && conda activate myenv && gunicorn -c gunicorn.conf.py run_dev:app
|
||||
```
|
||||
|
||||
### 方法二:分步启动
|
||||
```bash
|
||||
# 1. 进入项目目录
|
||||
cd /home/renjianbo/aitsc
|
||||
|
||||
# 2. 激活conda环境
|
||||
eval "$(/home/renjianbo/miniconda3/bin/conda shell.bash hook)"
|
||||
conda activate myenv
|
||||
|
||||
# 3. 启动Gunicorn服务
|
||||
gunicorn -c gunicorn.conf.py run_dev:app
|
||||
```
|
||||
|
||||
### 方法三:后台启动
|
||||
```bash
|
||||
# 后台启动服务
|
||||
nohup gunicorn -c gunicorn.conf.py run_dev:app > logs/gunicorn.log 2>&1 &
|
||||
```
|
||||
|
||||
## 🛑 **停止服务器**
|
||||
|
||||
### 方法一:使用PID文件停止
|
||||
```bash
|
||||
# 停止服务(如果存在PID文件)
|
||||
kill -TERM $(cat logs/gunicorn.pid)
|
||||
```
|
||||
|
||||
### 方法二:强制停止所有Gunicorn进程
|
||||
```bash
|
||||
# 强制停止所有gunicorn进程
|
||||
pkill -9 -f gunicorn
|
||||
```
|
||||
|
||||
### 方法三:停止特定项目进程
|
||||
```bash
|
||||
# 停止run_dev:app相关进程
|
||||
pkill -f "run_dev:app"
|
||||
```
|
||||
|
||||
## 🔍 **检查服务状态**
|
||||
|
||||
### 检查进程状态
|
||||
```bash
|
||||
# 查看Gunicorn进程
|
||||
ps aux | grep "run_dev:app" | grep -v grep
|
||||
|
||||
# 查看所有Gunicorn进程
|
||||
ps aux | grep gunicorn | grep -v grep
|
||||
```
|
||||
|
||||
### 检查端口状态
|
||||
```bash
|
||||
# 检查5002端口是否被监听
|
||||
ss -tlnp | grep :5002
|
||||
|
||||
# 或者使用netstat
|
||||
netstat -tlnp | grep :5002
|
||||
```
|
||||
|
||||
### 检查服务响应
|
||||
```bash
|
||||
# 测试服务是否正常响应
|
||||
curl -s http://localhost:5002/ | head -10
|
||||
|
||||
# 测试特定页面
|
||||
curl -s -o /dev/null -w "%{http_code}" http://localhost:5002/admin/analytics_admin/
|
||||
```
|
||||
|
||||
## 📊 **查看日志**
|
||||
|
||||
### 查看错误日志
|
||||
```bash
|
||||
# 查看最新的错误日志
|
||||
tail -f logs/gunicorn_error.log
|
||||
|
||||
# 查看应用日志
|
||||
tail -f logs/app.log
|
||||
```
|
||||
|
||||
### 查看访问日志
|
||||
```bash
|
||||
# 查看访问日志
|
||||
tail -f logs/gunicorn_access.log
|
||||
```
|
||||
|
||||
## 🔧 **故障排除**
|
||||
|
||||
### 清理和重启
|
||||
```bash
|
||||
# 1. 停止所有相关进程
|
||||
pkill -f gunicorn
|
||||
|
||||
# 2. 删除PID文件
|
||||
rm -f logs/gunicorn.pid
|
||||
|
||||
# 3. 重新启动
|
||||
eval "$(/home/renjianbo/miniconda3/bin/conda shell.bash hook)" && conda activate myenv && gunicorn -c gunicorn.conf.py run_dev:app
|
||||
```
|
||||
|
||||
### 检查依赖
|
||||
```bash
|
||||
# 检查Python环境
|
||||
which python
|
||||
python --version
|
||||
|
||||
# 检查已安装的包
|
||||
pip list | grep -E "(flask|gunicorn|openai)"
|
||||
```
|
||||
|
||||
## 🌐 **访问地址**
|
||||
|
||||
启动成功后,可以访问以下地址:
|
||||
|
||||
- **主页**:`http://101.43.95.130:5002/`
|
||||
- **饭菜规划**:`http://101.43.95.130:5002/meal-planning`
|
||||
- **古诗词解析**:`http://101.43.95.130:5002/poetry/`
|
||||
- **古诗词示例**:`http://101.43.95.130:5002/poetry/examples`
|
||||
- **后台管理**:`http://101.43.95.130:5002/admin`
|
||||
- **数据分析**:`http://101.43.95.130:5002/admin/analytics_admin/`
|
||||
|
||||
## ✅ **启动成功的标志**
|
||||
|
||||
看到以下信息表示启动成功:
|
||||
- `[INFO] 应用启动`
|
||||
- `[INFO] 工作进程 X 已启动`
|
||||
- `[INFO] 工作进程 X 初始化完成`
|
||||
- 端口5002开始监听
|
||||
|
||||
## 📝 **项目信息**
|
||||
|
||||
- **项目名称**:提示词大师
|
||||
- **功能描述**:智能生成高质量提示词,提升您的工作效率
|
||||
- **技术栈**:Python 3.12 + Flask + Gunicorn + MySQL
|
||||
- **端口**:5002
|
||||
- **环境**:conda myenv
|
||||
- **配置文件**:gunicorn.conf.py
|
||||
- **启动文件**:run_dev.py
|
||||
|
||||
## 🚨 **注意事项**
|
||||
|
||||
1. 确保conda环境已正确激活
|
||||
2. 确保所有依赖包已安装
|
||||
3. 确保数据库连接正常
|
||||
4. 确保5002端口未被占用
|
||||
5. 定期检查日志文件大小,避免磁盘空间不足
|
||||
|
||||
---
|
||||
*最后更新:2025-09-14*
|
||||
*维护人员:系统管理员*
|
||||
79
模板初始化快速参考.md
79
模板初始化快速参考.md
@@ -1,79 +0,0 @@
|
||||
# 模板初始化快速参考
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 一键初始化腾讯云数据库
|
||||
```bash
|
||||
python direct_insert_templates.py
|
||||
```
|
||||
|
||||
### 选择数据库类型初始化
|
||||
```bash
|
||||
python init_tencent_db.py
|
||||
```
|
||||
|
||||
## 📊 当前状态
|
||||
|
||||
- ✅ **腾讯云数据库**: 176个模板已成功插入
|
||||
- ✅ **数据完整性**: 验证通过
|
||||
- ✅ **连接状态**: 正常
|
||||
|
||||
## 🔧 常用命令
|
||||
|
||||
```bash
|
||||
# 强制重新插入所有模板
|
||||
python direct_insert_templates.py
|
||||
|
||||
# 初始化本地数据库
|
||||
python init_tencent_db.py local
|
||||
|
||||
# 初始化腾讯云数据库
|
||||
python init_tencent_db.py tencent
|
||||
|
||||
# 验证数据库连接
|
||||
python -c "
|
||||
import pymysql
|
||||
conn = pymysql.connect(
|
||||
host='gz-cynosdbmysql-grp-d26pzce5.sql.tencentcdb.com',
|
||||
port=24936, user='root', password='!Rjb12191',
|
||||
database='pro_db', charset='utf8mb4'
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SELECT COUNT(*) FROM prompt_template')
|
||||
print(f'模板总数: {cursor.fetchone()[0]}')
|
||||
conn.close()
|
||||
"
|
||||
```
|
||||
|
||||
## 📋 数据库配置
|
||||
|
||||
### 腾讯云数据库
|
||||
- **主机**: gz-cynosdbmysql-grp-d26pzce5.sql.tencentcdb.com:24936
|
||||
- **用户**: root
|
||||
- **密码**: !Rjb12191
|
||||
- **数据库**: pro_db
|
||||
|
||||
### 本地数据库
|
||||
- **主机**: localhost:3306
|
||||
- **用户**: root
|
||||
- **密码**: 123456
|
||||
- **数据库**: pro_db
|
||||
|
||||
## 🛠️ 故障排除
|
||||
|
||||
| 问题 | 解决方案 |
|
||||
|------|----------|
|
||||
| 连接失败 | 检查网络和防火墙设置 |
|
||||
| 权限错误 | 验证数据库用户权限 |
|
||||
| 数据不完整 | 使用 `direct_insert_templates.py` 重新初始化 |
|
||||
| 模块导入错误 | 确保在项目根目录运行脚本 |
|
||||
|
||||
## 📁 相关文件
|
||||
|
||||
- `direct_insert_templates.py` - 推荐使用
|
||||
- `init_tencent_db.py` - 通用初始化
|
||||
- `src/flask_prompt_master/promptsTemplates.py` - 模板源文件
|
||||
- `模板初始化完整文档.md` - 详细文档
|
||||
|
||||
---
|
||||
**最后更新**: 2025年1月6日
|
||||
504
生成专业提示词代码逻辑分析.md
504
生成专业提示词代码逻辑分析.md
@@ -1,504 +0,0 @@
|
||||
# 🎯 生成专业提示词代码逻辑分析
|
||||
|
||||
## 📋 系统概述
|
||||
|
||||
生成专业提示词系统是一个基于Flask的Web应用,采用前后端分离架构,集成了DeepSeek LLM API,实现了智能化的提示词生成功能。
|
||||
|
||||
## 🏗️ 系统架构
|
||||
|
||||
### 1. 技术栈
|
||||
- **后端**: Flask + SQLAlchemy + PyMySQL
|
||||
- **前端**: HTML5 + CSS3 + JavaScript + Bootstrap
|
||||
- **数据库**: MySQL (本地 + 腾讯云)
|
||||
- **LLM API**: DeepSeek Chat API
|
||||
- **部署**: Gunicorn + Nginx
|
||||
|
||||
### 2. 核心组件
|
||||
- **路由层**: Flask Blueprint路由管理
|
||||
- **模型层**: SQLAlchemy ORM模型
|
||||
- **服务层**: LLM API集成服务
|
||||
- **视图层**: Jinja2模板渲染
|
||||
- **静态资源**: CSS/JS资源管理
|
||||
|
||||
## 🔄 完整生成流程
|
||||
|
||||
### 第一阶段:用户交互层
|
||||
|
||||
#### 1.1 前端界面 (`generate.html`)
|
||||
```html
|
||||
<!-- 模板选择区域 -->
|
||||
<div class="template-grid">
|
||||
{% for template in templates %}
|
||||
<div class="template-card" data-template-id="{{ template.id }}">
|
||||
<input type="radio" name="template_id" value="{{ template.id }}">
|
||||
<label>{{ template.name }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- 需求输入区域 -->
|
||||
<div class="input-section">
|
||||
<textarea name="input_text" placeholder="请详细描述您的需求..."></textarea>
|
||||
</div>
|
||||
|
||||
<!-- 生成按钮 -->
|
||||
<button type="submit" class="btn-generate">生成专业提示词</button>
|
||||
```
|
||||
|
||||
#### 1.2 JavaScript交互逻辑
|
||||
```javascript
|
||||
// 模板选择处理
|
||||
function handleTemplateSelection(radio) {
|
||||
const card = radio.closest('.template-card');
|
||||
const templateId = card.dataset.templateId;
|
||||
|
||||
// 添加选择动画
|
||||
card.classList.add('selecting');
|
||||
|
||||
// 更新选择状态
|
||||
updateSelectionStatus();
|
||||
|
||||
// 添加到选择历史
|
||||
addToSelectionHistory(templateId, templateName);
|
||||
}
|
||||
|
||||
// 表单提交处理
|
||||
document.getElementById('promptForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(this);
|
||||
const templateId = formData.get('template_id');
|
||||
const inputText = formData.get('input_text');
|
||||
|
||||
// 发送AJAX请求
|
||||
fetch('/', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
// 更新页面内容
|
||||
document.body.innerHTML = html;
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 第二阶段:后端处理层
|
||||
|
||||
#### 2.1 路由处理 (`routes.py`)
|
||||
```python
|
||||
@main_bp.route('/', methods=['GET', 'POST'])
|
||||
def index():
|
||||
form = PromptForm()
|
||||
templates = PromptTemplate.query.all()
|
||||
|
||||
if form.validate_on_submit():
|
||||
# 获取用户输入和模板ID
|
||||
template_id = request.form.get('template_id')
|
||||
input_text = form.input_text.data
|
||||
|
||||
# 调用LLM生成提示词
|
||||
generated_text = generate_with_llm(input_text, template_id)
|
||||
|
||||
# 保存到数据库
|
||||
prompt = Prompt(
|
||||
input_text=input_text,
|
||||
generated_text=generated_text,
|
||||
user_id=get_user_id()
|
||||
)
|
||||
db.session.add(prompt)
|
||||
db.session.commit()
|
||||
|
||||
# 返回结果页面
|
||||
return render_template('generate.html',
|
||||
form=form,
|
||||
prompt=prompt,
|
||||
templates=templates)
|
||||
|
||||
return render_template('generate.html',
|
||||
form=form,
|
||||
templates=templates)
|
||||
```
|
||||
|
||||
#### 2.2 模板系统逻辑
|
||||
```python
|
||||
def get_system_prompt(template_id=None):
|
||||
"""获取系统提示词模板"""
|
||||
if template_id:
|
||||
# 根据模板ID获取特定模板
|
||||
template = PromptTemplate.query.get(template_id)
|
||||
if template:
|
||||
return template.system_prompt
|
||||
|
||||
# 获取默认模板
|
||||
default_template = PromptTemplate.query.filter_by(is_default=True).first()
|
||||
if default_template:
|
||||
return default_template.system_prompt
|
||||
|
||||
# 硬编码默认模板
|
||||
return """你是一个专业的提示词工程师,擅长将普通的描述转换为结构化、专业的 Prompt。
|
||||
|
||||
你需要:
|
||||
1. 分析用户的需求和意图
|
||||
2. 将其转换为清晰、详细的提示词
|
||||
3. 添加必要的上下文和约束条件
|
||||
4. 使用专业的术语和格式
|
||||
5. 确保生成的提示词能够获得最佳的 AI 响应
|
||||
|
||||
请直接返回优化后的提示词,不要添加任何解释或其他内容。"""
|
||||
```
|
||||
|
||||
### 第三阶段:LLM集成层
|
||||
|
||||
#### 3.1 API配置
|
||||
```python
|
||||
# OpenAI兼容客户端配置
|
||||
client = OpenAI(
|
||||
api_key='sk-fdf7cc1c73504e628ec0119b7e11b8cc',
|
||||
base_url='https://api.deepseek.com/v1'
|
||||
)
|
||||
```
|
||||
|
||||
#### 3.2 LLM调用逻辑
|
||||
```python
|
||||
def generate_with_llm(input_text, template_id=None, max_retries=3):
|
||||
"""调用大模型API生成提示词,带重试机制"""
|
||||
system_prompt = get_system_prompt(template_id)
|
||||
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = client.chat.completions.create(
|
||||
model="deepseek-chat",
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": input_text}
|
||||
],
|
||||
temperature=0.7,
|
||||
max_tokens=500,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
generated_text = response.choices[0].message.content.strip()
|
||||
return generated_text
|
||||
|
||||
except Exception as e:
|
||||
if attempt == max_retries - 1:
|
||||
current_app.logger.error(f'LLM API调用失败: {str(e)}')
|
||||
return "提示词生成失败,请稍后重试"
|
||||
time.sleep(2 ** attempt) # 指数退避
|
||||
```
|
||||
|
||||
### 第四阶段:专家模式生成
|
||||
|
||||
#### 4.1 两阶段专家系统
|
||||
```python
|
||||
@main_bp.route('/api/wx/generate/expert', methods=['POST'])
|
||||
def wx_generate_expert_prompt():
|
||||
"""两阶段专家提示词生成系统"""
|
||||
|
||||
# 第一阶段:意图识别专家
|
||||
intent_analyst_prompt = """你是一位资深的意图分析专家,请分析用户输入的意图和需求。
|
||||
|
||||
你必须严格按照以下JSON格式返回:
|
||||
{
|
||||
"core_intent": "技术", // 技术、创意、分析、咨询
|
||||
"domain": "web开发", // 具体的专业领域
|
||||
"key_requirements": [ // 2-4个关键需求
|
||||
"需求1", "需求2"
|
||||
],
|
||||
"expected_output": "期望输出的具体形式",
|
||||
"constraints": [ // 1-3个主要约束
|
||||
"约束1", "约束2"
|
||||
],
|
||||
"keywords": [ // 2-4个关键词
|
||||
"关键词1", "关键词2"
|
||||
]
|
||||
}"""
|
||||
|
||||
# 获取意图分析结果
|
||||
intent_response = client.chat.completions.create(
|
||||
model="deepseek-chat",
|
||||
messages=[
|
||||
{"role": "system", "content": intent_analyst_prompt},
|
||||
{"role": "user", "content": user_input}
|
||||
],
|
||||
temperature=0.1
|
||||
)
|
||||
|
||||
intent_analysis = json.loads(intent_response.choices[0].message.content.strip())
|
||||
|
||||
# 第二阶段:领域专家提示生成
|
||||
domain_expert_templates = {
|
||||
"技术": """你是一位专业的技术领域提示工程师。基于以下意图分析,生成一个专业的技术任务提示词:
|
||||
|
||||
意图分析:{analysis}
|
||||
|
||||
请生成的提示词包含:
|
||||
1. 明确的技术背景和上下文
|
||||
2. 具体的技术要求和规范
|
||||
3. 性能和质量标准
|
||||
4. 技术约束条件
|
||||
5. 预期交付成果
|
||||
6. 评估标准
|
||||
|
||||
使用专业技术术语,确保提示词的可执行性和可验证性。""",
|
||||
|
||||
"创意": """你是一位专业的创意领域提示工程师。基于以下意图分析,生成一个创意设计提示词:
|
||||
|
||||
意图分析:{analysis}
|
||||
|
||||
请生成的提示词包含:
|
||||
1. 创意方向和灵感来源
|
||||
2. 风格和氛围要求
|
||||
3. 目标受众定义
|
||||
4. 设计元素规范
|
||||
5. 创意表现形式
|
||||
6. 评估标准
|
||||
|
||||
使用专业创意术语,确保提示词的创新性和可执行性。"""
|
||||
}
|
||||
|
||||
# 选择领域专家模板
|
||||
expert_prompt = domain_expert_templates.get(
|
||||
intent_analysis['core_intent'],
|
||||
default_template
|
||||
)
|
||||
|
||||
# 生成最终提示词
|
||||
final_response = client.chat.completions.create(
|
||||
model="deepseek-chat",
|
||||
messages=[
|
||||
{"role": "system", "content": expert_prompt.format(
|
||||
analysis=json.dumps(intent_analysis, ensure_ascii=False, indent=2)
|
||||
)},
|
||||
{"role": "user", "content": user_input}
|
||||
],
|
||||
temperature=0.7
|
||||
)
|
||||
|
||||
generated_prompt = final_response.choices[0].message.content.strip()
|
||||
|
||||
return jsonify({
|
||||
'code': 200,
|
||||
'data': {
|
||||
'intent_analysis': intent_analysis,
|
||||
'generated_prompt': generated_prompt
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 🗄️ 数据库设计
|
||||
|
||||
### 1. 核心表结构
|
||||
|
||||
#### Prompt表 (主要数据表)
|
||||
```sql
|
||||
CREATE TABLE prompt (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
input_text TEXT NOT NULL,
|
||||
generated_text TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
user_id INT,
|
||||
wx_user_id INT,
|
||||
FOREIGN KEY (user_id) REFERENCES user(uid),
|
||||
FOREIGN KEY (wx_user_id) REFERENCES wx_user(id)
|
||||
);
|
||||
```
|
||||
|
||||
#### PromptTemplate表 (模板管理)
|
||||
```sql
|
||||
CREATE TABLE prompt_template (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(50),
|
||||
industry VARCHAR(50),
|
||||
profession VARCHAR(50),
|
||||
sub_category VARCHAR(50),
|
||||
system_prompt TEXT NOT NULL,
|
||||
is_default BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### 2. 数据流转
|
||||
```
|
||||
用户输入 → 模板选择 → LLM处理 → 结果生成 → 数据库存储 → 页面展示
|
||||
```
|
||||
|
||||
## 🎨 前端交互逻辑
|
||||
|
||||
### 1. 模板选择系统
|
||||
```javascript
|
||||
// 模板筛选逻辑
|
||||
function performSearchAndFilter() {
|
||||
const searchTerm = document.getElementById('templateSearch').value.toLowerCase();
|
||||
const selectedCategory = document.querySelector('.filter-tab.active')?.dataset.category;
|
||||
|
||||
document.querySelectorAll('.template-card').forEach(card => {
|
||||
const templateName = card.querySelector('h3').textContent.toLowerCase();
|
||||
const templateCategory = card.dataset.category;
|
||||
|
||||
const matchesSearch = templateName.includes(searchTerm);
|
||||
const matchesCategory = !selectedCategory || templateCategory === selectedCategory;
|
||||
|
||||
card.style.display = (matchesSearch && matchesCategory) ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// 选择状态管理
|
||||
function updateSelectionStatus() {
|
||||
const selectedTemplates = document.querySelectorAll('input[name="template_id"]:checked');
|
||||
const selectedCount = selectedTemplates.length;
|
||||
|
||||
document.getElementById('selectedCount').textContent = selectedCount;
|
||||
document.getElementById('selectionStatus').style.display = selectedCount > 0 ? 'block' : 'none';
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 用户体验优化
|
||||
```javascript
|
||||
// 现代交互功能
|
||||
function initializeModernInteractions() {
|
||||
// 平滑滚动
|
||||
initializeSmoothScroll();
|
||||
|
||||
// 焦点管理
|
||||
initializeFocusManagement();
|
||||
|
||||
// 悬停效果
|
||||
initializeHoverEffects();
|
||||
|
||||
// 键盘导航
|
||||
initializeKeyboardNavigation();
|
||||
|
||||
// 性能优化
|
||||
initializePerformanceOptimizations();
|
||||
}
|
||||
|
||||
// 防抖搜索
|
||||
let searchTimeout;
|
||||
document.getElementById('templateSearch').addEventListener('input', function() {
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(() => {
|
||||
performSearchAndFilter();
|
||||
}, 300);
|
||||
});
|
||||
```
|
||||
|
||||
## ⚙️ 配置管理
|
||||
|
||||
### 1. 环境配置
|
||||
```python
|
||||
class Config:
|
||||
# 数据库配置
|
||||
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:123456@localhost:3306/pro_db?charset=utf8mb4'
|
||||
TENCENT_SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:!Rjb12191@gz-cynosdbmysql-grp-d26pzce5.sql.tencentcdb.com:24936/pro_db?charset=utf8mb4'
|
||||
|
||||
# LLM API配置
|
||||
LLM_API_URL = 'https://api.deepseek.com/v1'
|
||||
LLM_API_KEY = 'sk-fdf7cc1c73504e628ec0119b7e11b8cc'
|
||||
|
||||
# 微信小程序配置
|
||||
WX_APPID = 'wx2c65877d37fc29bf'
|
||||
WX_SECRET = '89aa97dda3c1347c6ae3d6ab4627f1f4'
|
||||
```
|
||||
|
||||
### 2. 部署配置
|
||||
```python
|
||||
# Gunicorn配置 (gunicorn.conf.py)
|
||||
bind = "0.0.0.0:5002"
|
||||
workers = multiprocessing.cpu_count() * 2 + 1
|
||||
worker_class = "sync"
|
||||
timeout = 120
|
||||
accesslog = "logs/gunicorn_access.log"
|
||||
errorlog = "logs/gunicorn_error.log"
|
||||
```
|
||||
|
||||
## 🔧 核心功能实现
|
||||
|
||||
### 1. 模板管理系统
|
||||
- **模板分类**: 按行业、职业、领域分类
|
||||
- **模板选择**: 单选模式,支持默认模板
|
||||
- **模板搜索**: 实时搜索和筛选
|
||||
- **模板历史**: 记录用户选择历史
|
||||
|
||||
### 2. 生成引擎
|
||||
- **普通模式**: 单次LLM调用
|
||||
- **专家模式**: 两阶段专家系统
|
||||
- **重试机制**: 指数退避重试
|
||||
- **错误处理**: 完善的异常处理
|
||||
|
||||
### 3. 数据管理
|
||||
- **用户数据**: 支持普通用户和微信用户
|
||||
- **历史记录**: 完整的生成历史管理
|
||||
- **数据导出**: 支持JSON格式导出
|
||||
- **统计分析**: 使用统计和分析
|
||||
|
||||
## 🚀 性能优化
|
||||
|
||||
### 1. 前端优化
|
||||
- **懒加载**: 图片和资源懒加载
|
||||
- **防抖搜索**: 减少API调用频率
|
||||
- **缓存策略**: 模板数据缓存
|
||||
- **响应式设计**: 移动端适配
|
||||
|
||||
### 2. 后端优化
|
||||
- **连接池**: 数据库连接池管理
|
||||
- **重试机制**: LLM API调用重试
|
||||
- **日志记录**: 详细的日志追踪
|
||||
- **错误处理**: 优雅的错误处理
|
||||
|
||||
### 3. 数据库优化
|
||||
- **索引优化**: 关键字段索引
|
||||
- **查询优化**: 减少N+1查询
|
||||
- **分页查询**: 大数据量分页
|
||||
- **连接管理**: 连接池和超时设置
|
||||
|
||||
## 📊 监控和日志
|
||||
|
||||
### 1. 日志系统
|
||||
```python
|
||||
# 配置日志
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# API调用日志
|
||||
logger.info("=== API 调用参数 ===")
|
||||
logger.info(f"模板ID: {template_id}")
|
||||
logger.info(f"输入文本: {input_text}")
|
||||
logger.info(f"系统提示: {system_prompt}")
|
||||
|
||||
# 错误日志
|
||||
current_app.logger.error(f'LLM API调用失败: {str(e)}')
|
||||
```
|
||||
|
||||
### 2. 性能监控
|
||||
- **响应时间**: API调用响应时间
|
||||
- **成功率**: 生成成功率统计
|
||||
- **错误率**: 错误类型和频率
|
||||
- **用户行为**: 用户使用模式分析
|
||||
|
||||
## 🎯 总结
|
||||
|
||||
生成专业提示词系统采用了现代化的架构设计,具有以下特点:
|
||||
|
||||
### 优势
|
||||
1. **架构清晰**: 前后端分离,职责明确
|
||||
2. **功能完整**: 支持多种生成模式
|
||||
3. **用户体验**: 现代化的交互设计
|
||||
4. **扩展性强**: 支持模板和功能扩展
|
||||
5. **性能优化**: 多层次的性能优化
|
||||
|
||||
### 技术亮点
|
||||
1. **两阶段专家系统**: 意图识别 + 领域专家
|
||||
2. **智能模板选择**: 基于用户行为的推荐
|
||||
3. **多数据库支持**: 本地 + 腾讯云
|
||||
4. **完善的错误处理**: 重试机制和降级策略
|
||||
5. **现代化前端**: 响应式设计和交互优化
|
||||
|
||||
这个系统为用户提供了专业、高效的提示词生成服务,通过智能化的模板选择和专家级的生成逻辑,帮助用户快速生成高质量的AI提示词。
|
||||
|
||||
---
|
||||
*分析完成时间:2025年1月*
|
||||
*系统版本:v1.0*
|
||||
*维护人员:系统管理员*
|
||||
Reference in New Issue
Block a user