1054 lines
37 KiB
Markdown
1054 lines
37 KiB
Markdown
# 📚 优化历史功能设计方案
|
||
|
||
## 🎯 功能概述
|
||
|
||
基于现有的生成专业提示词系统,开发一个完整的优化历史功能,让用户可以:
|
||
- 查看自己生成的所有提示词历史
|
||
- 搜索和筛选历史记录
|
||
- 重新使用历史提示词
|
||
- 收藏重要的提示词
|
||
- 导出历史数据
|
||
|
||
## 🏗️ 系统架构设计
|
||
|
||
### 1. 数据模型设计
|
||
|
||
#### 1.1 历史记录表 (prompt_history)
|
||
```sql
|
||
CREATE TABLE prompt_history (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
user_id INT NOT NULL,
|
||
wx_user_id INT,
|
||
original_input TEXT NOT NULL COMMENT '原始输入',
|
||
generated_prompt TEXT NOT NULL COMMENT '生成的提示词',
|
||
template_id INT COMMENT '使用的模板ID',
|
||
template_name VARCHAR(100) COMMENT '模板名称',
|
||
generation_time INT COMMENT '生成耗时(毫秒)',
|
||
satisfaction_rating TINYINT COMMENT '满意度评分(1-5)',
|
||
tags JSON COMMENT '标签列表',
|
||
is_favorite BOOLEAN DEFAULT FALSE COMMENT '是否收藏',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
|
||
INDEX idx_user_id (user_id),
|
||
INDEX idx_wx_user_id (wx_user_id),
|
||
INDEX idx_created_at (created_at),
|
||
INDEX idx_template_id (template_id),
|
||
INDEX idx_is_favorite (is_favorite)
|
||
);
|
||
```
|
||
|
||
#### 1.2 历史标签表 (history_tags)
|
||
```sql
|
||
CREATE TABLE history_tags (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
history_id INT NOT NULL,
|
||
tag_name VARCHAR(50) NOT NULL,
|
||
tag_type VARCHAR(20) DEFAULT 'custom',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
|
||
FOREIGN KEY (history_id) REFERENCES prompt_history(id) ON DELETE CASCADE,
|
||
INDEX idx_history_id (history_id),
|
||
INDEX idx_tag_name (tag_name)
|
||
);
|
||
```
|
||
|
||
#### 1.3 用户统计表 (user_statistics)
|
||
```sql
|
||
CREATE TABLE user_statistics (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
user_id INT NOT NULL,
|
||
total_generations INT DEFAULT 0,
|
||
favorite_count INT DEFAULT 0,
|
||
avg_rating DECIMAL(3,2) DEFAULT 0.00,
|
||
last_generation_at TIMESTAMP NULL,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
|
||
UNIQUE KEY uk_user_id (user_id)
|
||
);
|
||
```
|
||
|
||
### 2. API接口设计
|
||
|
||
#### 2.1 获取历史记录列表
|
||
```http
|
||
GET /api/history
|
||
参数:
|
||
- page: 页码 (默认1)
|
||
- per_page: 每页数量 (默认20)
|
||
- search: 搜索关键词
|
||
- template_id: 模板筛选
|
||
- date_from: 开始日期
|
||
- date_to: 结束日期
|
||
- is_favorite: 是否只显示收藏
|
||
- sort: 排序方式 (created_at, rating, generation_time)
|
||
```
|
||
|
||
#### 2.2 获取单个历史记录
|
||
```http
|
||
GET /api/history/{id}
|
||
```
|
||
|
||
#### 2.3 更新历史记录
|
||
```http
|
||
PUT /api/history/{id}
|
||
数据:
|
||
- satisfaction_rating: 满意度评分
|
||
- tags: 标签列表
|
||
- is_favorite: 是否收藏
|
||
```
|
||
|
||
#### 2.4 删除历史记录
|
||
```http
|
||
DELETE /api/history/{id}
|
||
```
|
||
|
||
#### 2.5 批量操作
|
||
```http
|
||
POST /api/history/batch
|
||
操作:
|
||
- delete: 批量删除
|
||
- favorite: 批量收藏
|
||
- unfavorite: 批量取消收藏
|
||
```
|
||
|
||
#### 2.6 导出历史记录
|
||
```http
|
||
GET /api/history/export
|
||
参数:
|
||
- format: 导出格式 (json, csv, txt)
|
||
- date_from: 开始日期
|
||
- date_to: 结束日期
|
||
```
|
||
|
||
### 3. 前端页面设计
|
||
|
||
#### 3.1 历史记录列表页面
|
||
- **搜索栏**: 支持关键词搜索
|
||
- **筛选器**: 按模板、日期、评分筛选
|
||
- **排序选项**: 按时间、评分、生成时间排序
|
||
- **列表展示**: 卡片式展示历史记录
|
||
- **分页导航**: 支持分页浏览
|
||
|
||
#### 3.2 历史记录详情页面
|
||
- **完整展示**: 原始输入和生成结果
|
||
- **操作按钮**: 复制、收藏、评分、删除
|
||
- **相关信息**: 生成时间、使用模板、耗时等
|
||
|
||
#### 3.3 统计面板
|
||
- **总生成数**: 历史生成总数
|
||
- **收藏数量**: 收藏的提示词数量
|
||
- **平均评分**: 用户满意度统计
|
||
- **使用趋势**: 生成时间趋势图
|
||
|
||
## 🔧 技术实现方案
|
||
|
||
### 1. 后端实现
|
||
|
||
#### 1.1 数据模型 (models.py)
|
||
```python
|
||
class PromptHistory(db.Model):
|
||
__tablename__ = 'prompt_history'
|
||
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, nullable=False)
|
||
wx_user_id = db.Column(db.Integer)
|
||
original_input = db.Column(db.Text, nullable=False)
|
||
generated_prompt = db.Column(db.Text, nullable=False)
|
||
template_id = db.Column(db.Integer)
|
||
template_name = db.Column(db.String(100))
|
||
generation_time = db.Column(db.Integer)
|
||
satisfaction_rating = db.Column(db.SmallInteger)
|
||
tags = db.Column(db.JSON)
|
||
is_favorite = db.Column(db.Boolean, default=False)
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
|
||
# 关联关系
|
||
template = db.relationship('PromptTemplate', backref='histories')
|
||
history_tags = db.relationship('HistoryTag', backref='history', lazy='dynamic', cascade='all, delete-orphan')
|
||
|
||
def to_dict(self):
|
||
return {
|
||
'id': self.id,
|
||
'original_input': self.original_input,
|
||
'generated_prompt': self.generated_prompt,
|
||
'template_id': self.template_id,
|
||
'template_name': self.template_name,
|
||
'generation_time': self.generation_time,
|
||
'satisfaction_rating': self.satisfaction_rating,
|
||
'tags': self.tags or [],
|
||
'is_favorite': self.is_favorite,
|
||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||
'updated_at': self.updated_at.isoformat() if self.updated_at else None
|
||
}
|
||
|
||
class HistoryTag(db.Model):
|
||
__tablename__ = 'history_tags'
|
||
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
history_id = db.Column(db.Integer, db.ForeignKey('prompt_history.id'), nullable=False)
|
||
tag_name = db.Column(db.String(50), nullable=False)
|
||
tag_type = db.Column(db.String(20), default='custom')
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
class UserStatistics(db.Model):
|
||
__tablename__ = 'user_statistics'
|
||
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, unique=True, nullable=False)
|
||
total_generations = db.Column(db.Integer, default=0)
|
||
favorite_count = db.Column(db.Integer, default=0)
|
||
avg_rating = db.Column(db.Numeric(3, 2), default=0.00)
|
||
last_generation_at = db.Column(db.DateTime)
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
```
|
||
|
||
#### 1.2 API路由 (history_routes.py)
|
||
```python
|
||
from flask import Blueprint, request, jsonify, current_app
|
||
from sqlalchemy import desc, asc, and_, or_
|
||
from datetime import datetime, timedelta
|
||
import json
|
||
|
||
history_bp = Blueprint('history', __name__)
|
||
|
||
@history_bp.route('/api/history', methods=['GET'])
|
||
def get_history_list():
|
||
"""获取历史记录列表"""
|
||
try:
|
||
# 获取参数
|
||
page = request.args.get('page', 1, type=int)
|
||
per_page = min(request.args.get('per_page', 20, type=int), 100)
|
||
search = request.args.get('search', '').strip()
|
||
template_id = request.args.get('template_id', type=int)
|
||
date_from = request.args.get('date_from')
|
||
date_to = request.args.get('date_to')
|
||
is_favorite = request.args.get('is_favorite', type=bool)
|
||
sort = request.args.get('sort', 'created_at')
|
||
|
||
# 获取用户ID
|
||
user_id = get_current_user_id()
|
||
|
||
# 构建查询
|
||
query = PromptHistory.query.filter(
|
||
or_(PromptHistory.user_id == user_id, PromptHistory.wx_user_id == user_id)
|
||
)
|
||
|
||
# 搜索条件
|
||
if search:
|
||
query = query.filter(
|
||
or_(
|
||
PromptHistory.original_input.contains(search),
|
||
PromptHistory.generated_prompt.contains(search)
|
||
)
|
||
)
|
||
|
||
# 模板筛选
|
||
if template_id:
|
||
query = query.filter(PromptHistory.template_id == template_id)
|
||
|
||
# 日期筛选
|
||
if date_from:
|
||
query = query.filter(PromptHistory.created_at >= datetime.fromisoformat(date_from))
|
||
if date_to:
|
||
query = query.filter(PromptHistory.created_at <= datetime.fromisoformat(date_to))
|
||
|
||
# 收藏筛选
|
||
if is_favorite is not None:
|
||
query = query.filter(PromptHistory.is_favorite == is_favorite)
|
||
|
||
# 排序
|
||
if sort == 'rating':
|
||
query = query.order_by(desc(PromptHistory.satisfaction_rating))
|
||
elif sort == 'generation_time':
|
||
query = query.order_by(asc(PromptHistory.generation_time))
|
||
else:
|
||
query = query.order_by(desc(PromptHistory.created_at))
|
||
|
||
# 分页
|
||
pagination = query.paginate(page=page, per_page=per_page, error_out=False)
|
||
|
||
# 格式化结果
|
||
history_list = [history.to_dict() for history in pagination.items]
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'data': {
|
||
'history': history_list,
|
||
'pagination': {
|
||
'page': pagination.page,
|
||
'per_page': pagination.per_page,
|
||
'total': pagination.total,
|
||
'pages': pagination.pages,
|
||
'has_next': pagination.has_next,
|
||
'has_prev': pagination.has_prev
|
||
}
|
||
}
|
||
})
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"获取历史记录失败: {str(e)}")
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '获取历史记录失败'
|
||
}), 500
|
||
|
||
@history_bp.route('/api/history/<int:history_id>', methods=['GET'])
|
||
def get_history_detail(history_id):
|
||
"""获取单个历史记录详情"""
|
||
try:
|
||
user_id = get_current_user_id()
|
||
history = PromptHistory.query.filter(
|
||
PromptHistory.id == history_id,
|
||
or_(PromptHistory.user_id == user_id, PromptHistory.wx_user_id == user_id)
|
||
).first()
|
||
|
||
if not history:
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '历史记录不存在'
|
||
}), 404
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'data': history.to_dict()
|
||
})
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"获取历史记录详情失败: {str(e)}")
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '获取历史记录详情失败'
|
||
}), 500
|
||
|
||
@history_bp.route('/api/history/<int:history_id>', methods=['PUT'])
|
||
def update_history(history_id):
|
||
"""更新历史记录"""
|
||
try:
|
||
user_id = get_current_user_id()
|
||
history = PromptHistory.query.filter(
|
||
PromptHistory.id == history_id,
|
||
or_(PromptHistory.user_id == user_id, PromptHistory.wx_user_id == user_id)
|
||
).first()
|
||
|
||
if not history:
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '历史记录不存在'
|
||
}), 404
|
||
|
||
data = request.get_json()
|
||
|
||
# 更新字段
|
||
if 'satisfaction_rating' in data:
|
||
history.satisfaction_rating = data['satisfaction_rating']
|
||
if 'tags' in data:
|
||
history.tags = data['tags']
|
||
if 'is_favorite' in data:
|
||
history.is_favorite = data['is_favorite']
|
||
|
||
history.updated_at = datetime.utcnow()
|
||
db.session.commit()
|
||
|
||
# 更新用户统计
|
||
update_user_statistics(user_id)
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'data': history.to_dict(),
|
||
'message': '更新成功'
|
||
})
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"更新历史记录失败: {str(e)}")
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '更新历史记录失败'
|
||
}), 500
|
||
|
||
@history_bp.route('/api/history/<int:history_id>', methods=['DELETE'])
|
||
def delete_history(history_id):
|
||
"""删除历史记录"""
|
||
try:
|
||
user_id = get_current_user_id()
|
||
history = PromptHistory.query.filter(
|
||
PromptHistory.id == history_id,
|
||
or_(PromptHistory.user_id == user_id, PromptHistory.wx_user_id == user_id)
|
||
).first()
|
||
|
||
if not history:
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '历史记录不存在'
|
||
}), 404
|
||
|
||
db.session.delete(history)
|
||
db.session.commit()
|
||
|
||
# 更新用户统计
|
||
update_user_statistics(user_id)
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'message': '删除成功'
|
||
})
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"删除历史记录失败: {str(e)}")
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '删除历史记录失败'
|
||
}), 500
|
||
|
||
@history_bp.route('/api/history/export', methods=['GET'])
|
||
def export_history():
|
||
"""导出历史记录"""
|
||
try:
|
||
user_id = get_current_user_id()
|
||
format_type = request.args.get('format', 'json')
|
||
date_from = request.args.get('date_from')
|
||
date_to = request.args.get('date_to')
|
||
|
||
# 构建查询
|
||
query = PromptHistory.query.filter(
|
||
or_(PromptHistory.user_id == user_id, PromptHistory.wx_user_id == user_id)
|
||
)
|
||
|
||
if date_from:
|
||
query = query.filter(PromptHistory.created_at >= datetime.fromisoformat(date_from))
|
||
if date_to:
|
||
query = query.filter(PromptHistory.created_at <= datetime.fromisoformat(date_to))
|
||
|
||
histories = query.order_by(desc(PromptHistory.created_at)).all()
|
||
|
||
if format_type == 'json':
|
||
data = [history.to_dict() for history in histories]
|
||
return jsonify({
|
||
'success': True,
|
||
'data': data,
|
||
'count': len(data)
|
||
})
|
||
elif format_type == 'csv':
|
||
# 实现CSV导出
|
||
pass
|
||
else:
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '不支持的导出格式'
|
||
}), 400
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"导出历史记录失败: {str(e)}")
|
||
return jsonify({
|
||
'success': False,
|
||
'message': '导出历史记录失败'
|
||
}), 500
|
||
|
||
def get_current_user_id():
|
||
"""获取当前用户ID"""
|
||
# 实现用户ID获取逻辑
|
||
return 1 # 临时返回默认用户ID
|
||
|
||
def update_user_statistics(user_id):
|
||
"""更新用户统计信息"""
|
||
try:
|
||
# 获取用户历史统计
|
||
total_generations = PromptHistory.query.filter(
|
||
or_(PromptHistory.user_id == user_id, PromptHistory.wx_user_id == user_id)
|
||
).count()
|
||
|
||
favorite_count = PromptHistory.query.filter(
|
||
or_(PromptHistory.user_id == user_id, PromptHistory.wx_user_id == user_id),
|
||
PromptHistory.is_favorite == True
|
||
).count()
|
||
|
||
# 计算平均评分
|
||
avg_rating_result = db.session.query(db.func.avg(PromptHistory.satisfaction_rating)).filter(
|
||
or_(PromptHistory.user_id == user_id, PromptHistory.wx_user_id == user_id),
|
||
PromptHistory.satisfaction_rating.isnot(None)
|
||
).scalar()
|
||
|
||
avg_rating = float(avg_rating_result) if avg_rating_result else 0.0
|
||
|
||
# 获取最后生成时间
|
||
last_generation = PromptHistory.query.filter(
|
||
or_(PromptHistory.user_id == user_id, PromptHistory.wx_user_id == user_id)
|
||
).order_by(desc(PromptHistory.created_at)).first()
|
||
|
||
last_generation_at = last_generation.created_at if last_generation else None
|
||
|
||
# 更新或创建统计记录
|
||
stats = UserStatistics.query.filter_by(user_id=user_id).first()
|
||
if not stats:
|
||
stats = UserStatistics(user_id=user_id)
|
||
db.session.add(stats)
|
||
|
||
stats.total_generations = total_generations
|
||
stats.favorite_count = favorite_count
|
||
stats.avg_rating = avg_rating
|
||
stats.last_generation_at = last_generation_at
|
||
stats.updated_at = datetime.utcnow()
|
||
|
||
db.session.commit()
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"更新用户统计失败: {str(e)}")
|
||
db.session.rollback()
|
||
```
|
||
|
||
### 2. 前端实现
|
||
|
||
#### 2.1 历史记录页面 (history.html)
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>优化历史 - AI提示词生成器</title>
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||
<style>
|
||
.history-card {
|
||
border: 1px solid #e0e0e0;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
.history-card:hover {
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||
transform: translateY(-2px);
|
||
}
|
||
.history-card.favorite {
|
||
border-color: #ff6b6b;
|
||
background-color: #fff5f5;
|
||
}
|
||
.search-section {
|
||
background: #f8f9fa;
|
||
padding: 20px;
|
||
border-radius: 8px;
|
||
margin-bottom: 30px;
|
||
}
|
||
.stats-card {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.tag {
|
||
display: inline-block;
|
||
background: #e3f2fd;
|
||
color: #1976d2;
|
||
padding: 4px 8px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
margin: 2px;
|
||
}
|
||
.rating-stars {
|
||
color: #ffc107;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container-fluid">
|
||
<div class="row">
|
||
<!-- 侧边栏 -->
|
||
<div class="col-md-3">
|
||
<div class="search-section">
|
||
<h5><i class="fas fa-search"></i> 搜索和筛选</h5>
|
||
<div class="mb-3">
|
||
<input type="text" class="form-control" id="searchInput" placeholder="搜索历史记录...">
|
||
</div>
|
||
<div class="mb-3">
|
||
<select class="form-select" id="templateFilter">
|
||
<option value="">所有模板</option>
|
||
</select>
|
||
</div>
|
||
<div class="mb-3">
|
||
<select class="form-select" id="dateFilter">
|
||
<option value="">所有时间</option>
|
||
<option value="today">今天</option>
|
||
<option value="week">本周</option>
|
||
<option value="month">本月</option>
|
||
</select>
|
||
</div>
|
||
<div class="mb-3">
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="checkbox" id="favoriteFilter">
|
||
<label class="form-check-label" for="favoriteFilter">
|
||
只显示收藏
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="mb-3">
|
||
<select class="form-select" id="sortSelect">
|
||
<option value="created_at">按时间排序</option>
|
||
<option value="rating">按评分排序</option>
|
||
<option value="generation_time">按生成时间排序</option>
|
||
</select>
|
||
</div>
|
||
<button class="btn btn-primary w-100" onclick="searchHistory()">
|
||
<i class="fas fa-search"></i> 搜索
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 统计信息 -->
|
||
<div class="stats-card">
|
||
<h6><i class="fas fa-chart-bar"></i> 统计信息</h6>
|
||
<div class="row text-center">
|
||
<div class="col-6">
|
||
<div class="h4" id="totalGenerations">0</div>
|
||
<small>总生成数</small>
|
||
</div>
|
||
<div class="col-6">
|
||
<div class="h4" id="favoriteCount">0</div>
|
||
<small>收藏数</small>
|
||
</div>
|
||
</div>
|
||
<div class="mt-3">
|
||
<small>平均评分: <span id="avgRating">0.0</span></small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 主内容区 -->
|
||
<div class="col-md-9">
|
||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||
<h2><i class="fas fa-history"></i> 优化历史</h2>
|
||
<div>
|
||
<button class="btn btn-outline-primary" onclick="exportHistory()">
|
||
<i class="fas fa-download"></i> 导出
|
||
</button>
|
||
<button class="btn btn-primary" onclick="refreshHistory()">
|
||
<i class="fas fa-refresh"></i> 刷新
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 历史记录列表 -->
|
||
<div id="historyList">
|
||
<!-- 动态加载内容 -->
|
||
</div>
|
||
|
||
<!-- 分页 -->
|
||
<nav aria-label="历史记录分页">
|
||
<ul class="pagination justify-content-center" id="pagination">
|
||
<!-- 动态生成分页 -->
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 历史记录详情模态框 -->
|
||
<div class="modal fade" id="historyModal" tabindex="-1">
|
||
<div class="modal-dialog modal-lg">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">历史记录详情</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="mb-3">
|
||
<label class="form-label">原始输入:</label>
|
||
<div class="border p-3 rounded" id="modalOriginalInput"></div>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">生成的提示词:</label>
|
||
<div class="border p-3 rounded" id="modalGeneratedPrompt"></div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<label class="form-label">模板:</label>
|
||
<span id="modalTemplateName"></span>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label">生成时间:</label>
|
||
<span id="modalCreatedAt"></span>
|
||
</div>
|
||
</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" onclick="copyPrompt()">
|
||
<i class="fas fa-copy"></i> 复制
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||
<script>
|
||
let currentPage = 1;
|
||
let currentFilters = {};
|
||
|
||
// 页面加载完成后初始化
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
loadHistory();
|
||
loadTemplates();
|
||
loadStatistics();
|
||
});
|
||
|
||
// 加载历史记录
|
||
function loadHistory(page = 1) {
|
||
const params = new URLSearchParams({
|
||
page: page,
|
||
per_page: 20,
|
||
...currentFilters
|
||
});
|
||
|
||
fetch(`/api/history?${params}`)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
displayHistory(data.data.history);
|
||
displayPagination(data.data.pagination);
|
||
currentPage = page;
|
||
} else {
|
||
alert('加载历史记录失败: ' + data.message);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
alert('加载历史记录失败');
|
||
});
|
||
}
|
||
|
||
// 显示历史记录
|
||
function displayHistory(historyList) {
|
||
const container = document.getElementById('historyList');
|
||
|
||
if (historyList.length === 0) {
|
||
container.innerHTML = `
|
||
<div class="text-center py-5">
|
||
<i class="fas fa-history fa-3x text-muted mb-3"></i>
|
||
<h5 class="text-muted">暂无历史记录</h5>
|
||
<p class="text-muted">开始生成您的第一个提示词吧!</p>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
container.innerHTML = historyList.map(history => `
|
||
<div class="history-card ${history.is_favorite ? 'favorite' : ''}" data-id="${history.id}">
|
||
<div class="row">
|
||
<div class="col-md-8">
|
||
<h6 class="mb-2">
|
||
${history.template_name || '默认模板'}
|
||
${history.is_favorite ? '<i class="fas fa-heart text-danger ms-2"></i>' : ''}
|
||
</h6>
|
||
<p class="text-muted mb-2">${history.original_input.substring(0, 100)}${history.original_input.length > 100 ? '...' : ''}</p>
|
||
<div class="mb-2">
|
||
${history.tags ? history.tags.map(tag => `<span class="tag">${tag}</span>`).join('') : ''}
|
||
</div>
|
||
<small class="text-muted">
|
||
<i class="fas fa-clock"></i> ${formatDate(history.created_at)}
|
||
${history.generation_time ? ` | <i class="fas fa-stopwatch"></i> ${history.generation_time}ms` : ''}
|
||
</small>
|
||
</div>
|
||
<div class="col-md-4 text-end">
|
||
<div class="mb-2">
|
||
${history.satisfaction_rating ? generateStars(history.satisfaction_rating) : ''}
|
||
</div>
|
||
<div class="btn-group">
|
||
<button class="btn btn-sm btn-outline-primary" onclick="viewHistory(${history.id})">
|
||
<i class="fas fa-eye"></i>
|
||
</button>
|
||
<button class="btn btn-sm btn-outline-success" onclick="copyPrompt('${history.generated_prompt.replace(/'/g, "\\'")}')">
|
||
<i class="fas fa-copy"></i>
|
||
</button>
|
||
<button class="btn btn-sm ${history.is_favorite ? 'btn-warning' : 'btn-outline-warning'}" onclick="toggleFavorite(${history.id})">
|
||
<i class="fas fa-heart"></i>
|
||
</button>
|
||
<button class="btn btn-sm btn-outline-danger" onclick="deleteHistory(${history.id})">
|
||
<i class="fas fa-trash"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// 搜索历史记录
|
||
function searchHistory() {
|
||
currentFilters = {
|
||
search: document.getElementById('searchInput').value,
|
||
template_id: document.getElementById('templateFilter').value,
|
||
date_filter: document.getElementById('dateFilter').value,
|
||
is_favorite: document.getElementById('favoriteFilter').checked,
|
||
sort: document.getElementById('sortSelect').value
|
||
};
|
||
|
||
loadHistory(1);
|
||
}
|
||
|
||
// 查看历史记录详情
|
||
function viewHistory(historyId) {
|
||
fetch(`/api/history/${historyId}`)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
const history = data.data;
|
||
document.getElementById('modalOriginalInput').textContent = history.original_input;
|
||
document.getElementById('modalGeneratedPrompt').textContent = history.generated_prompt;
|
||
document.getElementById('modalTemplateName').textContent = history.template_name || '默认模板';
|
||
document.getElementById('modalCreatedAt').textContent = formatDate(history.created_at);
|
||
|
||
const modal = new bootstrap.Modal(document.getElementById('historyModal'));
|
||
modal.show();
|
||
} else {
|
||
alert('获取历史记录详情失败: ' + data.message);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
alert('获取历史记录详情失败');
|
||
});
|
||
}
|
||
|
||
// 复制提示词
|
||
function copyPrompt(text) {
|
||
if (text) {
|
||
navigator.clipboard.writeText(text).then(() => {
|
||
alert('提示词已复制到剪贴板');
|
||
});
|
||
}
|
||
}
|
||
|
||
// 切换收藏状态
|
||
function toggleFavorite(historyId) {
|
||
fetch(`/api/history/${historyId}`, {
|
||
method: 'PUT',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
is_favorite: !document.querySelector(`[data-id="${historyId}"]`).classList.contains('favorite')
|
||
})
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
loadHistory(currentPage);
|
||
loadStatistics();
|
||
} else {
|
||
alert('操作失败: ' + data.message);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
alert('操作失败');
|
||
});
|
||
}
|
||
|
||
// 删除历史记录
|
||
function deleteHistory(historyId) {
|
||
if (confirm('确定要删除这条历史记录吗?')) {
|
||
fetch(`/api/history/${historyId}`, {
|
||
method: 'DELETE'
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
loadHistory(currentPage);
|
||
loadStatistics();
|
||
} else {
|
||
alert('删除失败: ' + data.message);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
alert('删除失败');
|
||
});
|
||
}
|
||
}
|
||
|
||
// 导出历史记录
|
||
function exportHistory() {
|
||
const params = new URLSearchParams({
|
||
format: 'json',
|
||
...currentFilters
|
||
});
|
||
|
||
window.open(`/api/history/export?${params}`, '_blank');
|
||
}
|
||
|
||
// 刷新历史记录
|
||
function refreshHistory() {
|
||
loadHistory(currentPage);
|
||
loadStatistics();
|
||
}
|
||
|
||
// 加载模板列表
|
||
function loadTemplates() {
|
||
fetch('/api/templates')
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
const select = document.getElementById('templateFilter');
|
||
select.innerHTML = '<option value="">所有模板</option>' +
|
||
data.data.templates.map(template =>
|
||
`<option value="${template.id}">${template.name}</option>`
|
||
).join('');
|
||
}
|
||
});
|
||
}
|
||
|
||
// 加载统计信息
|
||
function loadStatistics() {
|
||
fetch('/api/history/statistics')
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
document.getElementById('totalGenerations').textContent = data.data.total_generations;
|
||
document.getElementById('favoriteCount').textContent = data.data.favorite_count;
|
||
document.getElementById('avgRating').textContent = data.data.avg_rating.toFixed(1);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 工具函数
|
||
function formatDate(dateString) {
|
||
const date = new Date(dateString);
|
||
return date.toLocaleString('zh-CN');
|
||
}
|
||
|
||
function generateStars(rating) {
|
||
return '★'.repeat(rating) + '☆'.repeat(5 - rating);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
### 3. 集成到现有系统
|
||
|
||
#### 3.1 修改生成逻辑
|
||
在现有的生成提示词逻辑中添加历史记录保存:
|
||
|
||
```python
|
||
def save_to_history(user_input, generated_text, template_id=None, generation_time=None):
|
||
"""保存到历史记录"""
|
||
try:
|
||
user_id = get_current_user_id()
|
||
template = PromptTemplate.query.get(template_id) if template_id else None
|
||
|
||
history = PromptHistory(
|
||
user_id=user_id,
|
||
original_input=user_input,
|
||
generated_prompt=generated_text,
|
||
template_id=template_id,
|
||
template_name=template.name if template else None,
|
||
generation_time=generation_time
|
||
)
|
||
|
||
db.session.add(history)
|
||
db.session.commit()
|
||
|
||
# 更新用户统计
|
||
update_user_statistics(user_id)
|
||
|
||
return history.id
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"保存历史记录失败: {str(e)}")
|
||
db.session.rollback()
|
||
return None
|
||
```
|
||
|
||
#### 3.2 修改路由注册
|
||
在应用初始化时注册历史记录蓝图:
|
||
|
||
```python
|
||
from src.flask_prompt_master.routes.history_routes import history_bp
|
||
|
||
def create_app():
|
||
app = Flask(__name__)
|
||
|
||
# 注册蓝图
|
||
app.register_blueprint(main_bp)
|
||
app.register_blueprint(history_bp) # 新增历史记录蓝图
|
||
|
||
return app
|
||
```
|
||
|
||
## 🎯 功能特性
|
||
|
||
### 1. 核心功能
|
||
- ✅ **历史记录管理**: 完整的CRUD操作
|
||
- ✅ **搜索和筛选**: 多维度搜索和筛选
|
||
- ✅ **收藏功能**: 重要提示词收藏管理
|
||
- ✅ **评分系统**: 用户满意度评分
|
||
- ✅ **标签系统**: 自定义标签管理
|
||
- ✅ **数据导出**: 支持多种格式导出
|
||
|
||
### 2. 用户体验
|
||
- ✅ **响应式设计**: 移动端适配
|
||
- ✅ **实时搜索**: 防抖搜索优化
|
||
- ✅ **分页浏览**: 大数据量分页处理
|
||
- ✅ **统计面板**: 使用统计和趋势分析
|
||
- ✅ **批量操作**: 批量删除和收藏
|
||
|
||
### 3. 性能优化
|
||
- ✅ **数据库索引**: 关键字段索引优化
|
||
- ✅ **分页查询**: 减少数据传输量
|
||
- ✅ **缓存策略**: 模板和统计数据缓存
|
||
- ✅ **异步加载**: 前端异步数据加载
|
||
|
||
## 📊 数据统计
|
||
|
||
### 1. 用户统计
|
||
- 总生成数量
|
||
- 收藏数量
|
||
- 平均满意度评分
|
||
- 最后生成时间
|
||
|
||
### 2. 使用趋势
|
||
- 每日生成数量
|
||
- 模板使用频率
|
||
- 用户活跃度分析
|
||
|
||
## 🚀 部署说明
|
||
|
||
### 1. 数据库迁移
|
||
```sql
|
||
-- 执行SQL脚本创建表结构
|
||
-- 运行数据库迁移脚本
|
||
```
|
||
|
||
### 2. 配置更新
|
||
```python
|
||
# 在配置文件中添加历史记录相关配置
|
||
HISTORY_PAGE_SIZE = 20
|
||
HISTORY_MAX_EXPORT = 1000
|
||
```
|
||
|
||
### 3. 前端资源
|
||
- 将历史记录页面添加到路由
|
||
- 更新导航菜单
|
||
- 添加相关静态资源
|
||
|
||
## 🎉 总结
|
||
|
||
这个优化历史功能设计完整,具有以下特点:
|
||
|
||
1. **功能完整**: 涵盖历史记录的完整生命周期管理
|
||
2. **用户体验**: 现代化的界面设计和交互体验
|
||
3. **性能优化**: 多层次的性能优化策略
|
||
4. **扩展性强**: 支持功能扩展和定制
|
||
5. **集成简单**: 与现有系统无缝集成
|
||
|
||
通过这个功能,用户可以更好地管理和回顾自己的提示词生成历史,提升使用体验和工作效率。
|
||
|
||
---
|
||
*设计完成时间:2025年1月*
|
||
*功能版本:v1.0*
|
||
*维护人员:系统管理员*
|