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)