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

646 lines
30 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.

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)