更新数据截止日2026.01.08
This commit is contained in:
49
api/__init__.py
Normal file
49
api/__init__.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from flask import Flask, request, render_template, Blueprint
|
||||
from .v1 import v1
|
||||
from .v2 import v2
|
||||
from .v3 import v3
|
||||
app = Flask(__name__)
|
||||
app.register_blueprint(v1, url_prefix='/v1')
|
||||
app.register_blueprint(v2, url_prefix='/v2')
|
||||
app.register_blueprint(v3, url_prefix='/v3')
|
||||
app.template_folder = '../templates'
|
||||
app.static_folder = '../static'
|
||||
|
||||
|
||||
# 页面路由
|
||||
@app.route('/')
|
||||
@app.route('/v1/')
|
||||
@app.route('/v2/')
|
||||
def index():
|
||||
"""主页面 - 聊天界面"""
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@app.route('/qa')
|
||||
@app.route('/v1/qa')
|
||||
@app.route('/v2/qa')
|
||||
def qa_page():
|
||||
"""问答管理页面"""
|
||||
return render_template('qa.html')
|
||||
|
||||
|
||||
@app.route('/weekly_report')
|
||||
@app.route('/v1/weekly_report')
|
||||
@app.route('/v2/weekly_report')
|
||||
def weekly_report_page():
|
||||
"""周报页面"""
|
||||
return render_template('weekly_report.html')
|
||||
|
||||
|
||||
@app.route('/daily_report')
|
||||
@app.route('/v1/daily_report')
|
||||
@app.route('/v2/daily_report')
|
||||
def daily_report_page():
|
||||
"""日报页面"""
|
||||
return render_template('daily_report.html')
|
||||
|
||||
|
||||
@app.route('/markdown')
|
||||
def markdown_page():
|
||||
"""markdown页面"""
|
||||
return render_template('markdown.html')
|
||||
40
api/db.py
Normal file
40
api/db.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from . import app, api_response
|
||||
|
||||
import time
|
||||
|
||||
from lib import chat_session, db_manager
|
||||
|
||||
|
||||
@app.route('/api/db/datetime')
|
||||
def db_status():
|
||||
return api_response(success=True, data={'message': chat_session.status()})
|
||||
|
||||
|
||||
@app.route('/api/db/update')
|
||||
def db_update():
|
||||
current_time = time.localtime(time.time() - 24 * 60 * 60)
|
||||
day = current_time.tm_mday
|
||||
month = current_time.tm_mon
|
||||
year = current_time.tm_year
|
||||
if chat_session.status() == f"{year}-{month:02d}-{day:02d}-23":
|
||||
return api_response(success=False, error="数据库已更新", status_code=201)
|
||||
for hour in range(24):
|
||||
chat_session.process_chat_session(year, month, day, hour)
|
||||
start = f"{year}-{month:02d}-{day:02d} 00:00:00"
|
||||
end = f"{year}-{month:02d}-{day:02d} 23:59:59"
|
||||
|
||||
chat_session.update_qa_data(start, end)
|
||||
db_manager.upload_db()
|
||||
return api_response(success=True, data={'message': '数据库更新成功'})
|
||||
|
||||
|
||||
@app.route('/api/db/upload')
|
||||
def db_upload():
|
||||
db_manager.upload_db()
|
||||
return api_response(success=True, data={'message': '数据库上传成功'})
|
||||
|
||||
|
||||
@app.route('/api/db/download')
|
||||
def db_download():
|
||||
db_manager.download_db()
|
||||
return api_response(success=True, data={'message': '数据库下载成功'})
|
||||
645
api/lib.py
Normal file
645
api/lib.py
Normal file
@@ -0,0 +1,645 @@
|
||||
import json
|
||||
from chat_session import ChatSessionManager
|
||||
from file import ObjectFileManager
|
||||
from tools import from_string, from_datetime, format_timestamp, DatabaseManager
|
||||
from flask import jsonify
|
||||
|
||||
|
||||
class MessageRender:
|
||||
"""消息处理类"""
|
||||
|
||||
@staticmethod
|
||||
def format_timestamp(timestamp_str):
|
||||
"""格式化时间戳"""
|
||||
if not timestamp_str:
|
||||
return '未知时间'
|
||||
|
||||
try:
|
||||
timestamp = int(timestamp_str[:-3])
|
||||
return format_timestamp(timestamp)
|
||||
except ValueError:
|
||||
return '未知时间'
|
||||
|
||||
@staticmethod
|
||||
def process_message_content(message_data):
|
||||
"""处理消息内容"""
|
||||
content = message_data['content']
|
||||
msg_type = message_data['type']
|
||||
|
||||
if msg_type == 'text':
|
||||
message_data['display_content'] = content
|
||||
elif msg_type in ['image', 'file', 'video', 'voice']:
|
||||
message_data['display_content'] = f'[{msg_type}] {content}'
|
||||
elif msg_type == 'mixed':
|
||||
try:
|
||||
mixed_content = json.loads(content)
|
||||
display_parts = []
|
||||
for item in mixed_content:
|
||||
msg_type, item_content = item
|
||||
if msg_type == 'text':
|
||||
display_parts.append(item_content)
|
||||
else:
|
||||
display_parts.append(f'[{msg_type}]')
|
||||
message_data['display_content'] = ' '.join(display_parts)
|
||||
except ValueError:
|
||||
message_data['display_content'] = '[混合消息]'
|
||||
elif msg_type == 'chatrecord':
|
||||
try:
|
||||
chatrecord_data = json.loads(content)
|
||||
if 'chatrecord' in chatrecord_data and 'title' in chatrecord_data['chatrecord']:
|
||||
message_data['display_content'] = f'[聊天记录] {chatrecord_data["chatrecord"]["title"]}'
|
||||
message_data['chatrecord_data'] = chatrecord_data
|
||||
else:
|
||||
message_data['display_content'] = '[聊天记录]'
|
||||
except ValueError:
|
||||
message_data['display_content'] = '[聊天记录]'
|
||||
else:
|
||||
message_data['display_content'] = f'[{msg_type}] {content}'
|
||||
|
||||
return message_data
|
||||
|
||||
|
||||
class ReportService:
|
||||
"""报告服务类"""
|
||||
|
||||
def __init__(self, db: DatabaseManager):
|
||||
self.db = db
|
||||
self.execute_query = self.db.execute_query
|
||||
|
||||
def get_message_count_by_time_range(self, start_timestamp, end_timestamp):
|
||||
"""获取指定时间范围内的消息数量"""
|
||||
result = self.execute_query('''
|
||||
SELECT COUNT(*) as count
|
||||
FROM message
|
||||
WHERE time >= ?
|
||||
AND time <= ?
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999"), fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_active_users_by_time_range(self, start_timestamp, end_timestamp):
|
||||
"""获取指定时间范围内的活跃用户数量"""
|
||||
result = self.execute_query('''
|
||||
SELECT COUNT(DISTINCT sender) as count
|
||||
FROM message
|
||||
WHERE time >= ?
|
||||
AND time <= ?
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999"), fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_active_sub_users_by_time_range(self, start_timestamp, end_timestamp):
|
||||
"""获取指定时间范围内的活跃用户数量"""
|
||||
result = self.execute_query('''
|
||||
SELECT COUNT(DISTINCT sender) as count
|
||||
FROM message
|
||||
WHERE time >= ?
|
||||
AND time <= ?
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999"), fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_qa_count_by_time_range(self, start_timestamp, end_timestamp):
|
||||
"""获取指定时间范围内的QA记录数量"""
|
||||
result = self.execute_query('''
|
||||
SELECT COUNT(*) as count
|
||||
FROM qa
|
||||
WHERE start_time >= ?
|
||||
AND end_time <= ?
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999"), fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_non_answer_qa_count_by_time_range(self, start_timestamp, end_timestamp):
|
||||
"""获取指定时间范围内的QA记录数量"""
|
||||
result = self.execute_query('''
|
||||
SELECT COUNT(*) as count
|
||||
FROM qa
|
||||
WHERE start_time >= ?
|
||||
AND end_time <= ?
|
||||
AND answer = '[]'
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999"), fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_qa_count_by_time_range_sub(self, start_timestamp, end_timestamp):
|
||||
"""获取指定时间范围内的QA记录数量"""
|
||||
result = self.execute_query('''
|
||||
SELECT COUNT(*) as count
|
||||
FROM qa as q
|
||||
inner join subscribe as s on q.groupId = s.id
|
||||
WHERE start_time >= ?
|
||||
AND end_time <= ?
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999"), fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_non_answer_qa_count_by_time_range_sub(self, start_timestamp, end_timestamp):
|
||||
"""获取指定时间范围内的QA记录数量"""
|
||||
result = self.execute_query('''
|
||||
SELECT COUNT(*) as count
|
||||
FROM qa as q
|
||||
inner join subscribe as s on q.groupId = s.id
|
||||
WHERE start_time >= ?
|
||||
AND end_time <= ?
|
||||
AND answer = '[]'
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999"), fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_group_activity_data(self, start_timestamp, end_timestamp):
|
||||
"""获取群组活跃度数据"""
|
||||
return self.execute_query('''
|
||||
SELECT g.id, g.name, COUNT(m.id) as message_count
|
||||
FROM "group" g
|
||||
LEFT JOIN message m ON g.id = m.receiver AND m.time >= ? AND m.time <= ?
|
||||
GROUP BY g.id, g.name
|
||||
ORDER BY message_count DESC
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999"))
|
||||
|
||||
def get_subscribe_activity_data(self, start_timestamp, end_timestamp):
|
||||
"""获取订阅活跃度数据"""
|
||||
return self.execute_query('''
|
||||
SELECT s.id, s.name, COUNT(m.id) as message_count
|
||||
FROM subscribe s
|
||||
LEFT JOIN message m ON s.id = m.receiver AND m.time >= ? AND m.time <= ?
|
||||
GROUP BY s.id, s.name
|
||||
ORDER BY message_count DESC
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999"))
|
||||
|
||||
def get_recent_messages_by_time_range(self, start_timestamp, end_timestamp, limit=100):
|
||||
"""获取指定时间范围内的最近消息"""
|
||||
return self.execute_query('''
|
||||
SELECT content
|
||||
FROM message
|
||||
WHERE time >= ?
|
||||
AND time <= ?
|
||||
AND type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
ORDER BY time
|
||||
LIMIT ?
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999", limit))
|
||||
|
||||
def get_hourly_activity_data(self, start_timestamp, end_timestamp):
|
||||
"""获取按小时统计的消息量"""
|
||||
return self.execute_query('''
|
||||
SELECT strftime('%H',
|
||||
datetime(substr(time, 0, 11), 'unixepoch', '+8 hours')) as hour,
|
||||
COUNT(*) as count
|
||||
FROM message
|
||||
WHERE time >= ?
|
||||
AND time <= ?
|
||||
GROUP BY hour
|
||||
ORDER BY hour
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999"))
|
||||
|
||||
def get_group_count(self):
|
||||
"""获取群组总数"""
|
||||
result = self.execute_query('SELECT COUNT(*) as count FROM "group"', fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_member_count(self):
|
||||
"""获取成员总数"""
|
||||
result = self.execute_query('SELECT COUNT(*) as count FROM user', fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_sub_member_count(self):
|
||||
"""获取成员总数"""
|
||||
result = self.execute_query(
|
||||
'SELECT COUNT(distinct m.userId) as count FROM member as m inner join subscribe as s on m.groupId = s.id ',
|
||||
fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_subscribe_count(self):
|
||||
"""获取订阅总数"""
|
||||
result = self.execute_query('SELECT COUNT(*) as count FROM subscribe', fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_qa_count(self):
|
||||
"""获取指定时间范围内的QA记录总数"""
|
||||
result = self.execute_query('''
|
||||
SELECT COUNT(*) as count
|
||||
FROM qa
|
||||
|
||||
''', fetch_all=False)
|
||||
return result["count"]
|
||||
|
||||
def get_weekly_messages(self, start_timestamp, end_timestamp, limit=200):
|
||||
"""获取本周消息用于分类分析"""
|
||||
return self.execute_query('''
|
||||
SELECT content
|
||||
FROM message
|
||||
WHERE time >= ?
|
||||
AND time <= ?
|
||||
AND type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
ORDER BY time
|
||||
LIMIT ?
|
||||
''', (f"{start_timestamp}000", f"{end_timestamp}999", limit))
|
||||
|
||||
# 通用数据查询方法 (整合剩余数据库查询)
|
||||
def get_messages_by_ids(self, message_ids):
|
||||
"""根据消息ID列表获取消息内容"""
|
||||
if not message_ids:
|
||||
return []
|
||||
|
||||
placeholders = ','.join(['?'] * len(message_ids))
|
||||
query = f"""
|
||||
SELECT m.id, m.content, m.type, m.sender, m.receiver,u.name as sender_name,
|
||||
datetime(substr(time,0,11), 'unixepoch','+8 hours') as formatted_time
|
||||
FROM message m
|
||||
left join user u on m.sender = u.id
|
||||
WHERE m.id IN ({placeholders})
|
||||
AND type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
ORDER BY formatted_time
|
||||
"""
|
||||
return self.execute_query(query, message_ids)
|
||||
|
||||
def get_messages(self, limit=100, offset=0, group_id=None):
|
||||
"""获取消息列表"""
|
||||
query = '''
|
||||
SELECT m.*,
|
||||
u.name as sender_name,
|
||||
g.name as group_name,
|
||||
datetime(substr(time, 0, 11), 'unixepoch', '+8 hours') as formatted_time
|
||||
FROM message m
|
||||
LEFT JOIN user u ON m.sender = u.id
|
||||
LEFT JOIN "group" g ON m.receiver = g.id
|
||||
WHERE m.type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
AND m.toType = 1
|
||||
'''
|
||||
params = []
|
||||
|
||||
if group_id:
|
||||
query += ' AND m.receiver = ?'
|
||||
params.append(group_id)
|
||||
|
||||
query += ' ORDER BY formatted_time DESC LIMIT ? OFFSET ?'
|
||||
params.extend([limit, offset])
|
||||
|
||||
return self.execute_query(query, params)
|
||||
|
||||
def get_all_users(self):
|
||||
"""获取所有用户"""
|
||||
return self.execute_query('SELECT * FROM user ORDER BY name')
|
||||
|
||||
def get_all_groups(self):
|
||||
"""获取所有群组"""
|
||||
return self.execute_query('SELECT * FROM "group" ORDER BY id')
|
||||
|
||||
def get_all_subscribes(self):
|
||||
"""获取所有订阅"""
|
||||
return self.execute_query('SELECT * FROM subscribe ORDER BY id')
|
||||
|
||||
def get_subscribe_messages(self, subscribe_id, limit=100, offset=0):
|
||||
"""获取指定订阅的消息"""
|
||||
if not subscribe_id:
|
||||
query = '''
|
||||
SELECT m.*,
|
||||
u.name as sender_name,
|
||||
datetime(substr(m.time, 0, 11), 'unixepoch', '+8 hours') as formatted_time
|
||||
FROM message m
|
||||
LEFT JOIN user u ON m.sender = u.id
|
||||
WHERE m.toType = 1
|
||||
AND m.type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
ORDER BY formatted_time DESC
|
||||
LIMIT ? OFFSET ? \
|
||||
'''
|
||||
return self.execute_query(query, (limit, offset))
|
||||
else:
|
||||
query = '''
|
||||
SELECT m.*,
|
||||
u.name as sender_name,
|
||||
datetime(substr(m.time, 0, 11), 'unixepoch', '+8 hours') as formatted_time
|
||||
FROM message m
|
||||
LEFT JOIN user u ON m.sender = u.id
|
||||
WHERE m.receiver = ?
|
||||
AND m.type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
ORDER BY formatted_time DESC
|
||||
LIMIT ? OFFSET ? \
|
||||
'''
|
||||
return self.execute_query(query, (subscribe_id, limit, offset))
|
||||
|
||||
def get_subscribe_message_count(self, subscribe_id):
|
||||
"""获取指定订阅的消息统计"""
|
||||
subscribe_messages_total = self.execute_query('''
|
||||
SELECT COUNT(*) as count
|
||||
FROM message
|
||||
WHERE receiver = ?
|
||||
AND type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
''', (subscribe_id,), fetch_all=False)['count']
|
||||
lastest_time = self.execute_query('''
|
||||
SELECT datetime(substr(time, 0, 11), 'unixepoch', '+8 hours') as formatted_time
|
||||
FROM message
|
||||
WHERE receiver = ?
|
||||
AND type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
ORDER BY formatted_time DESC
|
||||
LIMIT 1
|
||||
''', (subscribe_id,), fetch_all=False)
|
||||
lastest_time = lastest_time['formatted_time'] if lastest_time else ""
|
||||
return {'total': subscribe_messages_total, 'lastest_time': lastest_time}
|
||||
|
||||
def get_group_messages(self, group_id, limit=100, offset=0):
|
||||
"""获取指定群组的消息"""
|
||||
if not group_id:
|
||||
query = '''
|
||||
SELECT m.*,
|
||||
u.name as sender_name,
|
||||
datetime(substr(m.time, 0, 11), 'unixepoch', '+8 hours') as formatted_time
|
||||
FROM message m
|
||||
LEFT JOIN user u ON m.sender = u.id
|
||||
WHERE m.toType = 1
|
||||
AND m.type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
ORDER BY formatted_time DESC
|
||||
LIMIT ? OFFSET ? \
|
||||
'''
|
||||
return self.execute_query(query, (limit, offset))
|
||||
else:
|
||||
query = '''
|
||||
SELECT m.*,
|
||||
u.name as sender_name,
|
||||
datetime(substr(m.time, 0, 11), 'unixepoch', '+8 hours') as formatted_time
|
||||
FROM message m
|
||||
LEFT JOIN user u ON m.sender = u.id
|
||||
WHERE m.receiver = ?
|
||||
AND m.type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
ORDER BY formatted_time DESC
|
||||
LIMIT ? OFFSET ? \
|
||||
'''
|
||||
return self.execute_query(query, (group_id, limit, offset))
|
||||
|
||||
def get_group_message_count(self, group_id):
|
||||
"""获取指定群组的消息"""
|
||||
|
||||
group_messages_total = self.execute_query('''
|
||||
SELECT COUNT(*) as count
|
||||
FROM message
|
||||
WHERE receiver = ?
|
||||
AND type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
''', (group_id,), fetch_all=False)['count']
|
||||
lastest_time = self.execute_query('''
|
||||
SELECT datetime(substr(time, 0, 11), 'unixepoch', '+8 hours') as formatted_time
|
||||
FROM message
|
||||
WHERE receiver = ?
|
||||
AND type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
ORDER BY formatted_time DESC
|
||||
LIMIT 1
|
||||
''', (group_id,), fetch_all=False)
|
||||
lastest_time = lastest_time['formatted_time'] if lastest_time else ""
|
||||
return {'total': group_messages_total, 'lastest_time': lastest_time}
|
||||
|
||||
def get_statistics(self):
|
||||
"""获取统计数据"""
|
||||
user_count = self.execute_query('SELECT COUNT(*) as count FROM user', fetch_all=False)['count']
|
||||
group_count = self.execute_query('SELECT COUNT(*) as count FROM "group"', fetch_all=False)['count']
|
||||
message_count = self.execute_query('SELECT COUNT(*) as count FROM message', fetch_all=False)['count']
|
||||
file_count = self.execute_query('SELECT COUNT(*) as count FROM file', fetch_all=False)['count']
|
||||
latest_time_result = self.execute_query('SELECT MAX(time) as latest_time FROM message', fetch_all=False)
|
||||
latest_time = latest_time_result['latest_time'] if latest_time_result else None
|
||||
|
||||
return {
|
||||
'user_count': user_count,
|
||||
'group_count': group_count,
|
||||
'message_count': message_count,
|
||||
'file_count': file_count,
|
||||
'latest_time': latest_time
|
||||
}
|
||||
|
||||
def get_v2_statistics(self):
|
||||
"""获取v2版本统计数据(包含订阅信息)"""
|
||||
user_count = self.execute_query(
|
||||
'SELECT COUNT(*) as count FROM user as u inner join member as m on u.id = m.userId inner join subscribe as s on m.groupId = s.id',
|
||||
fetch_all=False)['count']
|
||||
subscribe_count = self.execute_query('SELECT COUNT(*) as count FROM subscribe', fetch_all=False)['count']
|
||||
message_count = \
|
||||
self.execute_query(
|
||||
'SELECT COUNT(*) as count FROM message as m inner join subscribe as s on m.receiver = s.id',
|
||||
fetch_all=False)['count']
|
||||
file_count = self.execute_query(
|
||||
'SELECT COUNT(*) as count FROM file as f inner join message as m on f.id = m.content inner join subscribe as s on m.receiver = s.id',
|
||||
fetch_all=False)['count']
|
||||
latest_time_result = self.execute_query('SELECT MAX(time) as latest_time FROM message', fetch_all=False)
|
||||
latest_time = latest_time_result['latest_time'] if latest_time_result else None
|
||||
|
||||
return {
|
||||
'user_count': user_count,
|
||||
'group_count': subscribe_count,
|
||||
'message_count': message_count,
|
||||
'file_count': file_count,
|
||||
'latest_time': latest_time
|
||||
}
|
||||
|
||||
def search_messages(self, keyword, limit=100, group_id=None):
|
||||
"""搜索消息"""
|
||||
if group_id:
|
||||
query = '''
|
||||
SELECT m.*,
|
||||
u.name as sender_name,
|
||||
g.name as group_name,
|
||||
datetime(substr(m.time, 0, 11), 'unixepoch', '+8 hours') as formatted_time
|
||||
FROM message m
|
||||
LEFT JOIN user u ON m.sender = u.id
|
||||
LEFT JOIN "group" g ON m.receiver = g.id
|
||||
WHERE m.content LIKE ?
|
||||
AND m.type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
AND m.receiver = ?
|
||||
ORDER BY formatted_time
|
||||
LIMIT ? \
|
||||
'''
|
||||
return self.execute_query(query, (f'%{keyword}%', group_id, limit))
|
||||
else:
|
||||
query = '''
|
||||
SELECT m.*,
|
||||
u.name as sender_name,
|
||||
g.name as group_name,
|
||||
datetime(substr(m.time, 0, 11), 'unixepoch', '+8 hours') as formatted_time
|
||||
FROM message m
|
||||
LEFT JOIN user u ON m.sender = u.id
|
||||
LEFT JOIN "group" g ON m.receiver = g.id
|
||||
WHERE m.content LIKE ?
|
||||
AND m.type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
ORDER BY formatted_time
|
||||
LIMIT ? \
|
||||
'''
|
||||
return self.execute_query(query, (f'%{keyword}%', limit))
|
||||
|
||||
def search_subscribe_messages(self, keyword, limit=100, subscribe_id=None):
|
||||
"""搜索订阅消息"""
|
||||
if subscribe_id:
|
||||
query = '''
|
||||
SELECT m.*,
|
||||
u.name as sender_name,
|
||||
s.name as subscribe_name,
|
||||
datetime(substr(m.time, 0, 11), 'unixepoch', '+8 hours') as formatted_time
|
||||
FROM message m
|
||||
LEFT JOIN user u ON m.sender = u.id
|
||||
LEFT JOIN subscribe s ON m.receiver = s.id
|
||||
WHERE m.content LIKE ?
|
||||
AND m.type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
AND m.receiver = ?
|
||||
ORDER BY formatted_time
|
||||
LIMIT ? \
|
||||
'''
|
||||
return self.execute_query(query, (f'%{keyword}%', subscribe_id, limit))
|
||||
else:
|
||||
query = '''
|
||||
SELECT m.*,
|
||||
u.name as sender_name,
|
||||
s.name as subscribe_name,
|
||||
datetime(substr(m.time, 0, 11), 'unixepoch', '+8 hours') as formatted_time
|
||||
FROM message m
|
||||
LEFT JOIN user u ON m.sender = u.id
|
||||
LEFT JOIN subscribe s ON m.receiver = s.id
|
||||
WHERE m.content LIKE ?
|
||||
AND m.type NOT IN ('disagree', 'agree', 'weapp', 'link')
|
||||
ORDER BY formatted_time
|
||||
LIMIT ? \
|
||||
'''
|
||||
return self.execute_query(query, (f'%{keyword}%', limit))
|
||||
|
||||
def get_all_qa(self):
|
||||
"""获取所有QA记录"""
|
||||
return self.execute_query('SELECT * FROM qa ORDER BY id DESC')
|
||||
|
||||
def get_qa_by_id(self, qa_id):
|
||||
"""根据ID获取QA记录"""
|
||||
return self.execute_query('SELECT * FROM qa WHERE id = ?', (qa_id,), fetch_all=False)
|
||||
|
||||
def check_qa_exists(self, qa_id):
|
||||
"""检查QA记录是否存在"""
|
||||
result = self.execute_query('SELECT id FROM qa WHERE id = ?', (qa_id,), fetch_all=False)
|
||||
return result is not None
|
||||
|
||||
# 报告生成方法 (原ReportGenerator功能)
|
||||
@staticmethod
|
||||
def get_date_range(date_str):
|
||||
"""获取日期范围"""
|
||||
target_date = from_string(date_str).to_datetime()
|
||||
start_timestamp = int(from_string(date_str).start_of_day().to_timestamp())
|
||||
end_timestamp = int(from_string(date_str).end_of_day().to_timestamp())
|
||||
return target_date, start_timestamp, end_timestamp
|
||||
|
||||
@staticmethod
|
||||
def get_previous_day_range(target_date):
|
||||
"""获取前一天日期范围"""
|
||||
yesterday_date = from_datetime(target_date).days_ago(1)
|
||||
yesterday_start = int(yesterday_date.start_of_day().to_timestamp())
|
||||
yesterday_end = int(yesterday_date.end_of_day().to_timestamp())
|
||||
return yesterday_start, yesterday_end
|
||||
|
||||
@staticmethod
|
||||
def calculate_change_rate(current, previous):
|
||||
"""计算变化率"""
|
||||
if previous > 0:
|
||||
return (current - previous) / previous * 100
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def analyze_hot_topics(messages):
|
||||
"""分析热点话题"""
|
||||
keyword_patterns = ['季度报告', 'ESG', '股东大会', '披露', '系统', '操作', '政策']
|
||||
topic_counts = {}
|
||||
|
||||
for msg in messages:
|
||||
content = msg['content']
|
||||
for keyword in keyword_patterns:
|
||||
if keyword in content:
|
||||
topic_counts[keyword] = topic_counts.get(keyword, 0) + 1
|
||||
|
||||
# 排序并取前3个热点
|
||||
sorted_topics = sorted(topic_counts.items(), key=lambda x: x[1], reverse=True)
|
||||
return [f"{topic} (提及{count}次)" for topic, count in sorted_topics[:3]]
|
||||
|
||||
@staticmethod
|
||||
def analyze_category_breakdown(messages):
|
||||
"""分析问题分类"""
|
||||
category_counts = {
|
||||
'信息披露类': 0.0,
|
||||
'公司治理与法规': 0.0,
|
||||
'业务操作与系统': 0.0,
|
||||
'其他': 0.0
|
||||
}
|
||||
|
||||
category_keywords = {
|
||||
'信息披露类': ['披露', '报告', '公告', '季报', '年报', 'ESG'],
|
||||
'公司治理与法规': ['法规', '合规', '治理', '政策', '规定', '要求'],
|
||||
'业务操作与系统': ['系统', '操作', '填报', '上传', '下载', '登录']
|
||||
}
|
||||
|
||||
for msg in messages:
|
||||
content = msg['content']
|
||||
categorized = False
|
||||
for category, keywords in category_keywords.items():
|
||||
for keyword in keywords:
|
||||
if keyword in content:
|
||||
category_counts[category] += 1
|
||||
categorized = True
|
||||
break
|
||||
if categorized:
|
||||
break
|
||||
if not categorized:
|
||||
category_counts['其他'] += 1
|
||||
|
||||
# 计算百分比
|
||||
total_categorized = sum(category_counts.values())
|
||||
if total_categorized > 0:
|
||||
for category in category_counts:
|
||||
category_counts[category] = round(category_counts[category] / total_categorized * 100, 1)
|
||||
|
||||
return category_counts
|
||||
|
||||
@staticmethod
|
||||
def generate_group_activity_chart(group_activity_data):
|
||||
"""生成分群活跃度直方图"""
|
||||
if not group_activity_data:
|
||||
return "暂无数据"
|
||||
|
||||
# 获取前10个群组的数据
|
||||
top_groups = group_activity_data[:10]
|
||||
max_count = max(group['message_count'] for group in top_groups) if top_groups else 1
|
||||
|
||||
chart = ""
|
||||
for group in top_groups:
|
||||
group_name = group['name'] # 限制名称长度
|
||||
count = group['message_count']
|
||||
# 计算柱状图长度(最大50个字符)
|
||||
bar_length = int((count / max_count) * 50) if max_count > 0 else 0
|
||||
bar = "█" * bar_length
|
||||
chart += f"{group_name:<15} | {bar} {count}\n"
|
||||
|
||||
return chart
|
||||
|
||||
def generate_hourly_activity_chart(self, start_timestamp, end_timestamp):
|
||||
"""生成活跃时段直方图"""
|
||||
# 获取按小时统计的消息量
|
||||
hourly_data = self.get_hourly_activity_data(start_timestamp, end_timestamp)
|
||||
|
||||
if not hourly_data:
|
||||
return "暂无数据"
|
||||
|
||||
# 创建24小时的完整数据
|
||||
hour_counts = {str(i).zfill(2): 0 for i in range(24)}
|
||||
for data in hourly_data:
|
||||
hour_counts[data['hour']] = data['count']
|
||||
|
||||
max_count = max(hour_counts.values()) if hour_counts else 1
|
||||
|
||||
chart = ""
|
||||
for hour in sorted(hour_counts.keys()):
|
||||
count = hour_counts[hour]
|
||||
# 计算柱状图长度(最大30个字符)
|
||||
bar_length = int((count / max_count) * 30) if max_count > 0 else 0
|
||||
bar = "█" * bar_length
|
||||
chart += f"{hour}:00 | {bar} {count}\n"
|
||||
|
||||
return chart
|
||||
|
||||
|
||||
def api_response(success=True, data=None, error=None, status_code=200, content=None):
|
||||
"""统一的API响应格式"""
|
||||
response = {'success': success}
|
||||
if error:
|
||||
response['error'] = str(error)
|
||||
if data:
|
||||
response.update(data)
|
||||
if content:
|
||||
response["data"] = content
|
||||
return jsonify(response), status_code
|
||||
|
||||
|
||||
ofm = ObjectFileManager()
|
||||
db_manager = DatabaseManager('db/data', online_db_uri="zysl-log/wework/chat_msg/db/data.db")
|
||||
report_service = ReportService(db_manager)
|
||||
message_processor = MessageRender()
|
||||
chat_session = ChatSessionManager(db_manager)
|
||||
677
api/v1.py
Normal file
677
api/v1.py
Normal file
@@ -0,0 +1,677 @@
|
||||
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)
|
||||
641
api/v2.py
Normal file
641
api/v2.py
Normal file
@@ -0,0 +1,641 @@
|
||||
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
|
||||
|
||||
v2 = Blueprint('v2', __name__)
|
||||
|
||||
|
||||
# API路由 - 消息相关
|
||||
@v2.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)
|
||||
|
||||
|
||||
# v2 API路由 - 使用与v1相同命名但使用subscribe表
|
||||
@v2.route('/api/groups')
|
||||
def get_groups():
|
||||
"""获取订阅列表API (v2版本,使用subscribe表)"""
|
||||
try:
|
||||
subscribes_data = report_service.get_all_subscribes()
|
||||
groups = [dict(subscribe) for subscribe in subscribes_data]
|
||||
return api_response(data={'groups': groups})
|
||||
except Exception as e:
|
||||
return api_response(success=False, error=e, status_code=500)
|
||||
|
||||
|
||||
@v2.route('/api/group_messages')
|
||||
def get_group_messages():
|
||||
"""获取指定订阅的消息API (v2版本,使用subscribe表)"""
|
||||
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_subscribe_messages(group_id, limit, offset)
|
||||
group_messages_info = report_service.get_subscribe_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)
|
||||
|
||||
|
||||
# v2 API路由 - 统计信息
|
||||
@v2.route('/api/statistics')
|
||||
def get_statistics():
|
||||
"""获取统计数据API (v2版本,使用subscribe表)"""
|
||||
try:
|
||||
statistics = report_service.get_v2_statistics()
|
||||
return api_response(data=statistics)
|
||||
|
||||
except Exception as e:
|
||||
return api_response(success=False, error=e, status_code=500)
|
||||
|
||||
|
||||
# v2 API路由 - 搜索
|
||||
@v2.route('/api/search_messages')
|
||||
def search_messages():
|
||||
"""搜索消息API (v2版本,使用subscribe表)"""
|
||||
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_subscribe_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路由 - 搜索建议
|
||||
@v2.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)
|
||||
|
||||
|
||||
# v2 API路由 - QA管理 (使用subscribe表)
|
||||
@v2.route('/api/qa')
|
||||
def get_qa_list():
|
||||
"""获取QA列表API (v2版本,使用subscribe表)"""
|
||||
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("a.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("a.start_time >= ? AND a.end_time <= ?")
|
||||
query_params.extend([start_timestamp, end_timestamp])
|
||||
except ValueError:
|
||||
# 时间格式错误时,忽略时间条件
|
||||
pass
|
||||
|
||||
# 构建查询语句
|
||||
base_query = "SELECT a.* FROM subscribe as s inner join qa as a on a.groupId = s.id"
|
||||
if query_conditions:
|
||||
base_query += " WHERE " + " AND ".join(query_conditions)
|
||||
base_query += " ORDER BY a.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)
|
||||
|
||||
|
||||
@v2.route('/api/qa/<int:qa_id>')
|
||||
def get_qa_detail(qa_id):
|
||||
"""获取单个QA详情API (v2版本,使用subscribe表)"""
|
||||
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)
|
||||
|
||||
|
||||
@v2.route('/api/qa', methods=['POST'])
|
||||
def create_qa():
|
||||
"""创建QA记录API (v2版本,使用subscribe表)"""
|
||||
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)
|
||||
|
||||
|
||||
@v2.route('/api/qa/<int:qa_id>', methods=['PUT'])
|
||||
def update_qa(qa_id):
|
||||
"""更新QA记录API (v2版本,使用subscribe表)"""
|
||||
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)
|
||||
|
||||
|
||||
@v2.route('/api/qa/<int:qa_id>', methods=['DELETE'])
|
||||
def delete_qa(qa_id):
|
||||
"""删除单个QA记录API (v2版本,使用subscribe表)"""
|
||||
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)
|
||||
|
||||
|
||||
@v2.route('/api/qa/batch', methods=['DELETE'])
|
||||
def batch_delete_qa():
|
||||
"""批量删除QA记录API (v2版本,使用subscribe表)"""
|
||||
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(data={'message': '批量删除QA记录成功'})
|
||||
|
||||
except Exception as e:
|
||||
return api_response(success=False, error=e, status_code=500)
|
||||
|
||||
|
||||
# v2 API路由 - 报告生成
|
||||
@v2.route('/api/reports/daily')
|
||||
def generate_daily_report():
|
||||
"""生成日报API (v2版本,使用subscribe表)"""
|
||||
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_sub_users_by_time_range(start_timestamp, end_timestamp)
|
||||
yesterday_active_users = report_service.get_active_sub_users_by_time_range(yesterday_start, yesterday_end)
|
||||
|
||||
user_change_rate = report_service.calculate_change_rate(
|
||||
today_active_users, yesterday_active_users
|
||||
)
|
||||
|
||||
# 获取订阅和成员数量
|
||||
subscribe_count = report_service.get_subscribe_count()
|
||||
member_count = report_service.get_member_count()
|
||||
|
||||
# 获取QA记录数量
|
||||
today_qa_count = report_service.get_qa_count_by_time_range_sub(start_timestamp, end_timestamp)
|
||||
yesterday_qa_count = report_service.get_qa_count_by_time_range_sub(yesterday_start, yesterday_end)
|
||||
non_qa_count = report_service.get_non_answer_qa_count_by_time_range_sub(start_timestamp, end_timestamp)
|
||||
qa_change_rate = report_service.calculate_change_rate(
|
||||
today_qa_count, yesterday_qa_count
|
||||
)
|
||||
|
||||
# 获取订阅活跃度
|
||||
subscribe_activity_data = report_service.get_subscribe_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)
|
||||
# 生成直方图
|
||||
subscribe_activity_chart = report_service.generate_group_activity_chart(subscribe_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}
|
||||
**覆盖范围**:{subscribe_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. 分订阅活跃度对比**
|
||||
```
|
||||
{subscribe_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': subscribe_count,
|
||||
'member_count': member_count
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return api_response(success=False, error=e, status_code=500)
|
||||
|
||||
|
||||
@v2.route('/api/reports/weekly')
|
||||
def generate_weekly_report():
|
||||
"""生成周报API (v2版本,使用subscribe表)"""
|
||||
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_sub_users_by_time_range(start_timestamp, end_timestamp)
|
||||
|
||||
# 获取订阅和成员数量
|
||||
subscribe_count = report_service.get_subscribe_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': subscribe_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)
|
||||
276
api/v3.py
Normal file
276
api/v3.py
Normal file
@@ -0,0 +1,276 @@
|
||||
"""
|
||||
主程序入口 - 集成周报生成器
|
||||
"""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
from . import Blueprint
|
||||
|
||||
from api.lib import db_manager
|
||||
from weekly_report_generator import WeeklyReportGenerator
|
||||
from flask import request, jsonify, render_template
|
||||
|
||||
v3 = Blueprint('v3', __name__)
|
||||
|
||||
# 初始化周报生成器
|
||||
weekly_report_generator = None
|
||||
|
||||
|
||||
def init_weekly_report_generator():
|
||||
"""初始化周报生成器"""
|
||||
global weekly_report_generator
|
||||
if weekly_report_generator is None:
|
||||
weekly_report_generator = WeeklyReportGenerator(db_manager)
|
||||
|
||||
|
||||
@v3.route('/api/weekly_report/generate', methods=['GET', 'POST'])
|
||||
def generate_weekly_report_api():
|
||||
"""生成周报API接口 (v3版本,基于需求文档样式)"""
|
||||
try:
|
||||
init_weekly_report_generator()
|
||||
|
||||
# 获取参数
|
||||
if request.method == 'POST':
|
||||
data = request.get_json()
|
||||
week_str = data.get('week')
|
||||
use_deepseek = data.get('use_deepseek', True)
|
||||
else:
|
||||
week_str = request.args.get('week')
|
||||
use_deepseek = request.args.get('use_deepseek', 'true').lower() == 'true'
|
||||
|
||||
# 如果没有提供周次,使用当前周
|
||||
if not week_str:
|
||||
current_date = datetime.now()
|
||||
year = current_date.isocalendar()[0]
|
||||
week_num = current_date.isocalendar()[1]
|
||||
week_str = f"{year}-{week_num:02d}"
|
||||
|
||||
# 生成周报
|
||||
result = weekly_report_generator.generate_weekly_report(week_str, use_deepseek)
|
||||
|
||||
if result['success']:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': {
|
||||
'report': result['report'],
|
||||
'week': result['week'],
|
||||
'statistics': result.get('statistics', {})
|
||||
}
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': result['error']
|
||||
}), 500
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f"生成周报失败: {str(e)}"
|
||||
}), 500
|
||||
|
||||
|
||||
@v3.route('/api/weekly_report/weeks', methods=['GET'])
|
||||
def get_available_weeks_v3():
|
||||
"""获取可用的周次列表 (v3版本)"""
|
||||
try:
|
||||
init_weekly_report_generator()
|
||||
|
||||
# 获取数据库中最早和最晚的消息时间
|
||||
result = db_manager.execute_query('''
|
||||
SELECT MIN(time) as min_time, MAX(time) as max_time
|
||||
FROM message
|
||||
WHERE LENGTH(time) >= 10
|
||||
''', fetch_all=False)
|
||||
|
||||
if result and result['min_time'] and result['max_time']:
|
||||
min_timestamp = int(result['min_time'][:10])
|
||||
max_timestamp = int(result['max_time'][:10])
|
||||
|
||||
min_date = datetime.fromtimestamp(min_timestamp)
|
||||
max_date = datetime.fromtimestamp(max_timestamp)
|
||||
|
||||
# 生成可用的周次列表
|
||||
weeks = []
|
||||
current_date = min_date
|
||||
|
||||
while current_date <= max_date:
|
||||
year = current_date.isocalendar()[0]
|
||||
week_num = current_date.isocalendar()[1]
|
||||
week_str = f"{year}-{week_num:02d}"
|
||||
|
||||
# 计算周的开始和结束日期
|
||||
start_of_week = current_date - timedelta(days=current_date.weekday())
|
||||
end_of_week = start_of_week + timedelta(days=6)
|
||||
|
||||
weeks.append({
|
||||
'week': week_str,
|
||||
'start_date': start_of_week.strftime('%Y-%m-%d'),
|
||||
'end_date': end_of_week.strftime('%Y-%m-%d'),
|
||||
'display': f"{year}年第{week_num}周 ({start_of_week.strftime('%m.%d')}-{end_of_week.strftime('%m.%d')})"
|
||||
})
|
||||
|
||||
current_date += timedelta(weeks=1)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': {
|
||||
'weeks': weeks[-20:] # 返回最近20周
|
||||
}
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': '无法获取时间范围数据'
|
||||
}), 500
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f"获取周次列表失败: {str(e)}"
|
||||
}), 500
|
||||
|
||||
|
||||
@v3.route('/api/weekly_report/preview', methods=['POST'])
|
||||
def preview_weekly_report():
|
||||
"""预览周报API接口 (v3版本)"""
|
||||
try:
|
||||
init_weekly_report_generator()
|
||||
|
||||
data = request.get_json()
|
||||
week_str = data.get('week')
|
||||
|
||||
if not week_str:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': '请提供周次参数'
|
||||
}), 400
|
||||
|
||||
# 生成预览数据(不调用DeepSeek API)
|
||||
result = weekly_report_generator.generate_weekly_report(week_str, use_deepseek=False)
|
||||
|
||||
if result['success']:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': {
|
||||
'preview': result['report'],
|
||||
'statistics': result.get('statistics', {})
|
||||
}
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': result['error']
|
||||
}), 500
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f"预览周报失败: {str(e)}"
|
||||
}), 500
|
||||
|
||||
|
||||
@v3.route('/api/weekly_report/export', methods=['POST'])
|
||||
def export_weekly_report():
|
||||
"""导出周报API接口 (v3版本)"""
|
||||
try:
|
||||
init_weekly_report_generator()
|
||||
|
||||
data = request.get_json()
|
||||
week_str = data.get('week')
|
||||
export_format = data.get('format', 'markdown') # markdown, html, pdf
|
||||
|
||||
if not week_str:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': '请提供周次参数'
|
||||
}), 400
|
||||
|
||||
# 生成完整周报
|
||||
result = weekly_report_generator.generate_weekly_report(week_str, use_deepseek=True)
|
||||
|
||||
if result['success']:
|
||||
report_content = result['report']
|
||||
|
||||
# 根据格式处理内容
|
||||
if export_format == 'html':
|
||||
# 简单的Markdown转HTML(实际项目中可以使用专业库)
|
||||
html_content = markdown_to_html(report_content)
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': {
|
||||
'content': html_content,
|
||||
'filename': f"周报_{week_str}.html",
|
||||
'mime_type': 'text/html'
|
||||
}
|
||||
})
|
||||
elif export_format == 'pdf':
|
||||
# PDF导出(需要额外库支持)
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'PDF导出功能暂未实现'
|
||||
}), 501
|
||||
else: # markdown
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': {
|
||||
'content': report_content,
|
||||
'filename': f"周报_{week_str}.md",
|
||||
'mime_type': 'text/markdown'
|
||||
}
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': result['error']
|
||||
}), 500
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f"导出周报失败: {str(e)}"
|
||||
}), 500
|
||||
|
||||
|
||||
def markdown_to_html(markdown_text: str) -> str:
|
||||
"""简单的Markdown转HTML函数"""
|
||||
# 基本转换规则
|
||||
html = markdown_text
|
||||
|
||||
# 标题转换
|
||||
html = html.replace('\n# ', '\n<h1>').replace('\n## ', '\n<h2>').replace('\n### ', '\n<h3>')
|
||||
html = html.replace('\n#### ', '\n<h4>').replace('\n##### ', '\n<h5>').replace('\n###### ', '\n<h6>')
|
||||
|
||||
# 表格处理
|
||||
html = html.replace('\t', '</td><td>')
|
||||
html = html.replace('\n', '</td></tr>\n<tr><td>')
|
||||
|
||||
# 基本的HTML包装
|
||||
html = f"""<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>周报</title>
|
||||
<style>
|
||||
body {{ font-family: Arial, sans-serif; margin: 20px; }}
|
||||
table {{ border-collapse: collapse; width: 100%; }}
|
||||
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
|
||||
th {{ background-color: #f2f2f2; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{html}
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
return html
|
||||
|
||||
|
||||
# v3周报页面路由
|
||||
@v3.route('/weekly_report')
|
||||
def weekly_report_page():
|
||||
"""周报页面 v3版本"""
|
||||
return render_template('weekly_report_v3.html')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user