Files
SmartChat/api/v1.py
2026-01-08 01:06:27 +08:00

678 lines
25 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from datetime import datetime, timedelta
from api.lib import report_service, message_processor, db_manager
from . import request, Blueprint
from .lib import api_response
import json
from chat_session import agent
from tools import TimeUtils, now, from_datetime
v1 = Blueprint('v1', __name__)
# API路由 - 消息相关
@v1.route('/api/messages_by_ids', methods=['POST'])
def get_messages_by_ids():
"""根据消息ID列表获取消息内容"""
try:
data = request.get_json()
message_ids = data.get('message_ids', [])
if not message_ids:
return api_response(data={'messages': []})
messages_data = report_service.get_messages_by_ids(message_ids)
formatted_messages = []
for msg in messages_data:
message = dict(msg)
message = message_processor.process_message_content(message)
formatted_messages.append(message)
return api_response(data={'messages': formatted_messages})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
@v1.route('/api/messages')
def get_messages():
"""获取消息列表API"""
try:
limit = request.args.get('limit', 100, type=int)
offset = request.args.get('offset', 0, type=int)
group_id = request.args.get('group_id', type=str)
messages_data = report_service.get_messages(limit, offset, group_id)
formatted_messages = []
for msg in messages_data:
message = dict(msg)
message = message_processor.process_message_content(message)
formatted_messages.append(message)
return api_response(data={
'messages': formatted_messages,
'total': len(formatted_messages)
})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
# API路由 - 用户和群组
@v1.route('/api/users')
def get_users():
"""获取用户列表API"""
try:
users_data = report_service.get_all_users()
users = [dict(user) for user in users_data]
return api_response(data={'users': users})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
@v1.route('/api/groups')
def get_groups():
"""获取群组列表API"""
try:
groups_data = report_service.get_all_groups()
groups = [dict(group) for group in groups_data]
return api_response(data={'groups': groups})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
@v1.route('/api/group_messages')
def get_group_messages():
"""获取指定群组的消息API"""
try:
group_id = request.args.get('group_id', type=str)
limit = request.args.get('limit', 100, type=int)
offset = request.args.get('offset', 0, type=int)
if not group_id:
return api_response(success=False, error='缺少群组ID参数', status_code=400)
messages_data = report_service.get_group_messages(group_id, limit, offset)
group_messages_info = report_service.get_group_message_count(group_id)
formatted_messages = []
for msg in messages_data:
message = dict(msg)
message = message_processor.process_message_content(message)
formatted_messages.append(message)
return api_response(data={'messages': formatted_messages, **group_messages_info})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
# API路由 - 统计信息
@v1.route('/api/statistics')
def get_statistics():
"""获取统计数据API"""
try:
statistics = report_service.get_statistics()
return api_response(data=statistics)
except Exception as e:
return api_response(success=False, error=e, status_code=500)
# API路由 - 搜索
@v1.route('/api/search_messages')
def search_messages():
"""搜索消息API"""
try:
keyword = request.args.get('keyword', '')
limit = request.args.get('limit', 100, type=int)
search_type = request.args.get('search_type', 'all') # all, question, answer
if not keyword:
return api_response(success=False, error='请输入搜索关键词', status_code=400)
group_id = request.args.get('group_id', type=str)
# 智能搜索:支持模糊匹配和精确匹配
messages_data = report_service.search_messages(keyword, limit, group_id)
# 根据搜索类型过滤消息
if search_type == 'question':
messages_data = [msg for msg in messages_data if msg.get('type') in ['text', 'mixed']]
elif search_type == 'answer':
messages_data = [msg for msg in messages_data if msg.get('type') in ['text', 'mixed']]
formatted_messages = []
for msg in messages_data:
message = dict(msg)
message = message_processor.process_message_content(message)
# 添加搜索关键词高亮
if keyword:
content = message.get('content', '')
if keyword.lower() in content.lower():
# 简单的关键词高亮(前端可以进一步处理)
highlighted_content = content.replace(keyword, f'<mark>{keyword}</mark>')
message['highlighted_content'] = highlighted_content
formatted_messages.append(message)
return api_response(data={'messages': formatted_messages, 'total': len(formatted_messages)})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
# API路由 - 搜索建议
@v1.route('/api/search_suggestions')
def search_suggestions():
"""搜索建议API"""
try:
keyword = request.args.get('keyword', '')
if not keyword:
return api_response(data={'suggestions': []})
# 获取热门搜索关键词
suggestions = []
# 1. 从消息内容中提取相关关键词
try:
messages = report_service.search_messages(keyword, 10, None)
for msg in messages:
content = msg.get('content', '')
# 简单的关键词提取逻辑
words = content.split()
for word in words:
if len(word) > 2 and keyword.lower() in word.lower():
if word not in suggestions:
suggestions.append(word)
if len(suggestions) >= 5:
break
if len(suggestions) >= 5:
break
except:
pass
# 2. 添加常见搜索关键词
common_keywords = ['信息披露', '公司治理', '业务操作', '系统填报', '政策法规', '季度报告', '股东大会']
for kw in common_keywords:
if keyword.lower() in kw.lower() and kw not in suggestions:
suggestions.append(kw)
if len(suggestions) >= 8:
break
return api_response(data={'suggestions': suggestions[:8]})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
# API路由 - QA管理
@v1.route('/api/qa')
def get_qa_list():
"""获取QA列表API"""
try:
# 获取查询参数
group_id = request.args.get('group_id', '')
keyword = request.args.get('keyword', '')
start_time = request.args.get('start_time', '')
end_time = request.args.get('end_time', '')
# 构建查询条件
query_conditions = []
query_params = []
if group_id:
query_conditions.append("groupId = ?")
query_params.append(group_id)
if start_time and end_time:
try:
# 将时间字符串转换为时间戳(毫秒级)
start_timestamp = int(TimeUtils(start_time + ' 00:00:00').to_timestamp() * 1000)
end_timestamp = int(TimeUtils(end_time + ' 23:59:59').to_timestamp() * 1000)
query_conditions.append("start_time >= ? AND end_time <= ?")
query_params.extend([start_timestamp, end_timestamp])
except ValueError:
# 时间格式错误时,忽略时间条件
pass
# 构建查询语句
base_query = "SELECT * FROM qa"
if query_conditions:
base_query += " WHERE " + " AND ".join(query_conditions)
base_query += " ORDER BY id DESC"
# 执行查询
conn = db_manager.get_connection()
cursor = conn.cursor()
cursor.execute(base_query, query_params)
qa_data = cursor.fetchall()
conn.close()
qa_list = [dict(qa) for qa in qa_data]
# 如果有关键词搜索,进行内容过滤
if keyword:
filtered_qa_list = []
for qa in qa_list:
# 检查群组ID是否匹配
if group_id != qa.get('groupId') and group_id != "":
continue
# 检查问题内容是否匹配
try:
question_ids = json.loads(qa.get('question', '[]')) + json.loads(qa.get('answer', '[]'))
if question_ids:
# 获取问题消息内容进行匹配
messages = report_service.get_messages_by_ids(question_ids)
for msg in messages:
if keyword.lower() in (msg['content'] or '').lower():
filtered_qa_list.append(qa)
break
except Exception as e:
print(e)
pass
qa_list = filtered_qa_list
return api_response(data={'qa_list': qa_list, 'total': len(qa_list)})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
@v1.route('/api/qa/<int:qa_id>')
def get_qa_detail(qa_id):
"""获取单个QA详情API"""
try:
qa_data = report_service.get_qa_by_id(qa_id)
if not qa_data:
return api_response(success=False, error='QA记录不存在', status_code=404)
qa_data = dict(qa_data)
try:
qa_data['question_list'] = json.loads(qa_data['question'])
qa_data['answer_list'] = json.loads(qa_data['answer'])
except:
qa_data['question_list'] = []
qa_data['answer_list'] = []
return api_response(data={'qa': qa_data})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
@v1.route('/api/qa', methods=['POST'])
def create_qa():
"""创建QA记录API"""
try:
data = request.get_json()
group_id = data.get('groupId', '')
question_ids = data.get('question', [])
answer_ids = data.get('answer', [])
# 处理时间字符串,转换为时间戳(毫秒级)
start_time = data.get('start_time', '')
end_time = data.get('end_time', '')
try:
if start_time:
start_time = int(TimeUtils(start_time).to_timestamp() * 1000)
if end_time:
end_time = int(TimeUtils(end_time).to_timestamp() * 1000)
except ValueError:
# 时间格式错误时,使用空字符串
start_time = ''
end_time = ''
conn = db_manager.get_connection()
cursor = conn.cursor()
index_id = cursor.execute_query(f"SELECT id FROM qa order by id desc limit 1", fetch_all=False)[0]
index_id += 1
cursor.execute('''
INSERT INTO qa ("id", "groupId", "question", "answer", "start_time", "end_time")
VALUES (?, ?, ?, ?, ?, ?)
''', (index_id, group_id, json.dumps(question_ids), json.dumps(answer_ids),
start_time, end_time))
conn.commit()
qa_id = cursor.lastrowid
conn.close()
return api_response(data={
'id': qa_id,
'message': 'QA记录创建成功'
})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
@v1.route('/api/qa/<int:qa_id>', methods=['PUT'])
def update_qa(qa_id):
"""更新QA记录API"""
try:
data = request.get_json()
group_id = data.get('groupId', '')
question_ids = data.get('question', [])
answer_ids = data.get('answer', [])
# 处理时间字符串,转换为时间戳(毫秒级)
start_time = data.get('start_time', '')
end_time = data.get('end_time', '')
try:
if start_time:
start_time = int(TimeUtils(start_time).to_timestamp() * 1000)
if end_time:
end_time = int(TimeUtils(end_time).to_timestamp() * 1000)
except ValueError:
# 时间格式错误时,保持原值不更新
pass
# 检查记录是否存在
if not report_service.check_qa_exists(qa_id):
return api_response(success=False, error='QA记录不存在', status_code=404)
conn = db_manager.get_connection()
cursor = conn.cursor()
# 如果提供了时间参数,则更新时间字段
if start_time and end_time:
cursor.execute('''
UPDATE qa
SET groupId = ?,
question = ?,
answer = ?,
start_time = ?,
end_time = ?
WHERE id = ?
''',
(group_id, json.dumps(question_ids), json.dumps(answer_ids), start_time, end_time, qa_id))
else:
cursor.execute('''
UPDATE qa
SET groupId = ?,
question = ?,
answer = ?
WHERE id = ?
''', (group_id, json.dumps(question_ids), json.dumps(answer_ids), qa_id))
conn.commit()
conn.close()
return api_response(data={'message': 'QA记录更新成功'})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
@v1.route('/api/qa/<int:qa_id>', methods=['DELETE'])
def delete_qa(qa_id):
"""删除单个QA记录API"""
try:
# 检查记录是否存在
if not report_service.check_qa_exists(qa_id):
return api_response(success=False, error='QA记录不存在', status_code=404)
conn = db_manager.get_connection()
cursor = conn.cursor()
cursor.execute('DELETE FROM qa WHERE id = ?', (qa_id,))
conn.commit()
conn.close()
return api_response(data={'message': 'QA记录删除成功'})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
@v1.route('/api/qa/batch', methods=['DELETE'])
def batch_delete_qa():
"""批量删除QA记录API"""
try:
data = request.get_json()
qa_ids = data.get('qa_ids', [])
if not qa_ids:
return api_response(success=False, error='请提供要删除的QA记录ID列表', status_code=400)
# 检查所有记录是否存在
for qa_id in qa_ids:
if not report_service.check_qa_exists(qa_id):
return api_response(success=False, error=f'QA记录 {qa_id} 不存在', status_code=404)
conn = db_manager.get_connection()
cursor = conn.cursor()
# 构建批量删除语句
placeholders = ','.join(['?'] * len(qa_ids))
query = f'DELETE FROM qa WHERE id IN ({placeholders})'
cursor.execute(query, qa_ids)
conn.commit()
conn.close()
return api_response(status_code=404)
except Exception as e:
return api_response(success=False, error=e, status_code=500)
# API路由 - 报告生成
@v1.route('/api/reports/daily')
def generate_daily_report():
"""生成日报API"""
try:
date_str = request.args.get('date', now().to_string(TimeUtils.FORMAT_DATE))
target_date, start_timestamp, end_timestamp = report_service.get_date_range(date_str)
yesterday_start, yesterday_end = report_service.get_previous_day_range(target_date)
# 使用ReportService类获取数据
today_message_count = report_service.get_message_count_by_time_range(start_timestamp, end_timestamp)
yesterday_message_count = report_service.get_message_count_by_time_range(yesterday_start, yesterday_end)
message_change_rate = report_service.calculate_change_rate(
today_message_count, yesterday_message_count
)
# 获取活跃用户数
today_active_users = report_service.get_active_users_by_time_range(start_timestamp, end_timestamp)
yesterday_active_users = report_service.get_active_users_by_time_range(yesterday_start, yesterday_end)
user_change_rate = report_service.calculate_change_rate(
today_active_users, yesterday_active_users
)
# 获取群组和成员数量
group_count = report_service.get_group_count()
member_count = report_service.get_member_count()
# 获取QA记录数量
today_qa_count = report_service.get_qa_count_by_time_range(start_timestamp, end_timestamp)
yesterday_qa_count = report_service.get_qa_count_by_time_range(yesterday_start, yesterday_end)
non_qa_count = report_service.get_non_answer_qa_count_by_time_range(start_timestamp, end_timestamp)
qa_change_rate = report_service.calculate_change_rate(
today_qa_count, yesterday_qa_count
)
# 获取群组活跃度
group_activity_data = report_service.get_group_activity_data(start_timestamp, end_timestamp)
# 获取热点话题
recent_messages = report_service.get_recent_messages_by_time_range(start_timestamp, end_timestamp, 100)
hot_topics = report_service.analyze_hot_topics(recent_messages)
# 生成直方图
group_activity_chart = report_service.generate_group_activity_chart(group_activity_data)
hourly_activity_chart = report_service.generate_hourly_activity_chart(start_timestamp, end_timestamp)
# 生成日报
report_date = from_datetime(target_date).format_custom('%Y年%m月%d日 (%A)')
daily_report = f"""# **致远速联高端社群运营日报**
**报告周期**:{report_date}
**覆盖范围**:{group_count}个群,总计成员 {member_count} 人
## **1. 核心数据概览**
| 指标 | 今日数据 | 昨日数据 | 变化 |
| :--- | :--- | :--- | :--- |
| 总消息量 | {today_message_count} 条 | {yesterday_message_count} 条 | {'↑' if message_change_rate >= 0 else '↓'}{abs(message_change_rate):.1f}% |
| 活跃人数 | {today_active_users} 人 | {yesterday_active_users} 人 | {'↑' if user_change_rate >= 0 else '↓'}{abs(user_change_rate):.1f}% |
| 问题新增记录 | {today_qa_count} 条 | {yesterday_qa_count} 条 | {'↑' if qa_change_rate >= 0 else '↓'}{abs(qa_change_rate):.1f}% |
## **2. 分群活跃度对比**
```
{group_activity_chart}
```
## **3. 今日热点与时段**
### **热点问题TOP 3**:
"""
for i, topic in enumerate(hot_topics, 1):
daily_report += f" {i}. {topic}\n"
daily_report += f"""### **活跃时段**:消息高峰分布
```
{hourly_activity_chart}
```
## **4. 待办事项**
{"有" + str(non_qa_count) + "条未被回答的问题,关注活跃群组的后续讨论,确保所有疑问被解答。" if non_qa_count > 0 else "无未被回答的问题,继续保持活跃群组的讨论。"}
明日重点关注:根据今日热点预测明日可能的问题。
"""
return api_response(data={
'report': daily_report,
'date': date_str,
'statistics': {
'today_message_count': today_message_count,
'yesterday_message_count': yesterday_message_count,
'message_change_rate': message_change_rate,
'today_active_users': today_active_users,
'yesterday_active_users': yesterday_active_users,
'user_change_rate': user_change_rate,
'today_qa_count': today_qa_count,
'yesterday_qa_count': yesterday_qa_count,
'qa_change_rate': qa_change_rate,
'group_count': group_count,
'member_count': member_count
}
})
except Exception as e:
return api_response(success=False, error=e, status_code=500)
@v1.route('/api/reports/weekly')
def generate_weekly_report():
"""生成周报API"""
try:
week_str = request.args.get('week', now().format_custom('%Y-%W'))
# 解析week_str参数,格式为YYYY-WW
year, week_num = map(int, week_str.split('-'))
# 根据周数计算时间范围
# 获取指定年份的第一天
first_day_of_year = datetime(year, 1, 1)
# 计算第一周的周一(ISO周从周一开始)
first_monday = first_day_of_year + timedelta(days=(7 - first_day_of_year.weekday()) % 7)
if first_day_of_year.weekday() <= 3: # 如果1月1日是周一到周四,则属于第一周
first_monday = first_day_of_year - timedelta(days=first_day_of_year.weekday())
# 计算指定周的周一
week_monday = first_monday + timedelta(weeks=week_num - 1)
# 计算指定周的周日
week_sunday = week_monday + timedelta(days=6)
start_date = week_monday.replace(hour=0, minute=0, second=0, microsecond=0)
end_date = week_sunday.replace(hour=23, minute=59, second=59, microsecond=999999)
start_timestamp = int(from_datetime(start_date).to_timestamp())
end_timestamp = int(from_datetime(end_date).to_timestamp())
# 获取上周同期数据
last_week_start = start_date - timedelta(weeks=1)
last_week_end = end_date - timedelta(weeks=1)
last_week_start_ts = int(from_datetime(last_week_start).to_timestamp())
last_week_end_ts = int(from_datetime(last_week_end).to_timestamp())
# 使用ReportService类获取数据
week_message_count = report_service.get_message_count_by_time_range(start_timestamp, end_timestamp)
last_week_message_count = report_service.get_message_count_by_time_range(last_week_start_ts, last_week_end_ts)
message_change_rate = report_service.calculate_change_rate(
week_message_count, last_week_message_count
)
# 获取活跃用户数
week_active_users = report_service.get_active_users_by_time_range(start_timestamp, end_timestamp)
# 获取群组和成员数量
group_count = report_service.get_group_count()
member_count = report_service.get_member_count()
# 获取QA记录数量
qa_count = report_service.get_qa_count_by_time_range(start_timestamp, end_timestamp)
non_answer_qa_count = report_service.get_non_answer_qa_count_by_time_range(start_timestamp, end_timestamp)
# 获取本周消息用于分类分析
week_messages = report_service.get_weekly_messages(start_timestamp, end_timestamp, 200)
category_breakdown = report_service.analyze_category_breakdown(week_messages)
# 生成周报
week_period = f"{from_datetime(start_date).format_custom('%Y年%m月%d日')} - {from_datetime(end_date).format_custom('%Y年%m月%d日')} (第{week_str.split('-')[1]}周)"
weekly_report = f"""# **致远速联高端社群运营周报**
**报告周期**:{week_period}
## **1. 本周摘要**
- 本周社群总消息量 **{week_message_count}条**,{'环比上升' if message_change_rate >= 0 else '环比下降'} **{abs(message_change_rate):.1f}%**。
- 活跃用户数达到 **{week_active_users}人**,社群参与度良好。
- 新增问题解答记录 **{qa_count}条**,知识沉淀效果显著。
- 其中,有 **{non_answer_qa_count}条** 未被回答,值得注意。
## **2. 深度洞察:上市公司提问分类**
"""
# 添加问题分类信息
for category, percentage in category_breakdown.items():
weekly_report += f"- {category}:{percentage}%\n"
weekly_report += f"""\n**洞察**:问题集中在信披和法规,说明成员对合规性高度重视。
## **3. 内容价值与知识沉淀**
- **本周高价值问答**:系统自动识别并记录了{qa_count}条有价值的问答记录。
- **建议**:将高频问答整理至知识库,便于后续参考。
"""
result = agent.chat_deepseek(weekly_report,
"根据下面的内容给出总结和建议,格式如下:\n## **4. 总结与建议**\n**总结**:\n**建议**:\n")
return api_response(data={
'report': weekly_report + "\n" + result,
'week': week_str,
'statistics': {
'week_message_count': week_message_count,
'last_week_message_count': last_week_message_count,
'message_change_rate': message_change_rate,
'week_active_users': week_active_users,
'group_count': group_count,
'member_count': member_count,
'qa_count': qa_count,
'category_breakdown': category_breakdown
}
})
except Exception as e:
return api_response(success=False, error=e, status_code=500)