feat: 完善 DDD 架构 - 添加领域分析器服务适配层

- 添加 src/__init__.py 作为源码包入口
- 添加 domain/services 分析器适配层:
  - topic_analyzer.py: 话题分析领域服务
  - user_title_analyzer.py: 用户称号分析领域服务
  - golden_quote_analyzer.py: 金句分析领域服务
- 更新 domain/entities/__init__.py 导出更多实体类
- 更新 shared/constants.py 添加枚举类 (Platform, TaskStatus, ContentType, ReportFormat)
- 所有新代码使用中文注释
This commit is contained in:
SXP-Simon
2026-02-08 15:12:52 +08:00
parent 39e5d6169d
commit 39cc318d9b
7 changed files with 520 additions and 4 deletions
+19
View File
@@ -0,0 +1,19 @@
"""
QQ群日常分析插件 - 源代码包
本包包含插件的核心实现,采用 DDD (领域驱动设计) 架构:
- application: 应用层 - 编排领域服务,处理用例
- domain: 领域层 - 核心业务逻辑,平台无关
- infrastructure: 基础设施层 - 外部服务适配
- shared: 共享组件 - 跨层使用的工具和常量
遗留模块(渐进式迁移中):
- analysis: 分析器实现
- core: 核心组件
- reports: 报告生成
- scheduler: 定时任务
- utils: 工具函数
- visualization: 可视化组件
"""
__version__ = "2.0.0"
+35 -4
View File
@@ -1,5 +1,36 @@
# 领域实体
from .analysis_task import AnalysisTask, TaskStatus
from .analysis_result import GroupAnalysisResult
"""
领域实体
__all__ = ["AnalysisTask", "TaskStatus", "GroupAnalysisResult"]
该模块导出所有领域实体类,包括:
- AnalysisTask: 分析任务聚合根
- GroupAnalysisResult: 群聊分析结果实体
"""
from .analysis_task import AnalysisTask, TaskStatus
from .analysis_result import (
GroupAnalysisResult,
SummaryTopic,
UserTitle,
GoldenQuote,
TokenUsage,
EmojiStatistics,
ActivityVisualization,
GroupStatistics,
)
# 别名,保持向后兼容
AnalysisResult = GroupAnalysisResult
__all__ = [
"AnalysisTask",
"TaskStatus",
"GroupAnalysisResult",
"AnalysisResult", # 别名
"SummaryTopic",
"UserTitle",
"GoldenQuote",
"TokenUsage",
"EmojiStatistics",
"ActivityVisualization",
"GroupStatistics",
]
+18
View File
@@ -3,12 +3,30 @@
该模块导出所有封装核心业务逻辑的领域服务,
用于分析群聊数据。这些服务是平台无关的。
服务分类:
- 分析器服务: 话题分析、用户称号分析、金句分析
- 计算服务: 统计计算
- 生成服务: 报告生成
"""
from .statistics_calculator import StatisticsCalculator
from .report_generator import ReportGenerator
from .topic_analyzer import ITopicAnalyzer, TopicAnalyzerAdapter
from .user_title_analyzer import IUserTitleAnalyzer, UserTitleAnalyzerAdapter
from .golden_quote_analyzer import IGoldenQuoteAnalyzer, GoldenQuoteAnalyzerAdapter
__all__ = [
# 统计与报告服务
"StatisticsCalculator",
"ReportGenerator",
# 话题分析服务
"ITopicAnalyzer",
"TopicAnalyzerAdapter",
# 用户称号分析服务
"IUserTitleAnalyzer",
"UserTitleAnalyzerAdapter",
# 金句分析服务
"IGoldenQuoteAnalyzer",
"GoldenQuoteAnalyzerAdapter",
]
@@ -0,0 +1,132 @@
"""
金句分析领域服务
该模块提供平台无关的金句分析服务接口。
实际分析逻辑委托给 infrastructure 层的具体实现。
架构说明:
- 本文件定义领域服务接口和数据转换逻辑
- 具体的 LLM 调用和消息处理在 src/analysis/analyzers/golden_quote_analyzer.py 中实现
- 采用渐进式迁移策略,保持与现有代码的兼容性
"""
from abc import ABC, abstractmethod
from typing import List, Tuple, TYPE_CHECKING
from ..value_objects.golden_quote import GoldenQuote
from ..value_objects.unified_message import UnifiedMessage
if TYPE_CHECKING:
from ..value_objects.statistics import TokenUsage
class IGoldenQuoteAnalyzer(ABC):
"""
金句分析服务接口
定义平台无关的金句分析契约。
所有平台的金句分析都应该实现此接口。
"""
@abstractmethod
async def analyze(
self,
messages: List[UnifiedMessage],
unified_msg_origin: str = None,
) -> Tuple[List[GoldenQuote], "TokenUsage"]:
"""
分析消息中的金句
参数:
messages: 统一格式的消息列表
unified_msg_origin: 消息来源标识,用于选择 LLM 提供商
返回:
(金句列表, Token 使用统计)
"""
pass
class GoldenQuoteAnalyzerAdapter(IGoldenQuoteAnalyzer):
"""
金句分析服务适配器
将现有的 GoldenQuoteAnalyzer 实现适配为领域服务接口。
负责 UnifiedMessage 与原始消息格式之间的转换。
"""
def __init__(self, legacy_analyzer):
"""
初始化适配器
参数:
legacy_analyzer: 现有的 GoldenQuoteAnalyzer 实例
"""
self._analyzer = legacy_analyzer
async def analyze(
self,
messages: List[UnifiedMessage],
unified_msg_origin: str = None,
) -> Tuple[List[GoldenQuote], "TokenUsage"]:
"""
分析消息中的金句
将 UnifiedMessage 转换为原始格式,调用现有分析器,
然后将结果转换为领域值对象。
参数:
messages: 统一格式的消息列表
unified_msg_origin: 消息来源标识
返回:
(金句列表, Token 使用统计)
"""
# 将 UnifiedMessage 转换为原始消息格式
raw_messages = [self._to_raw_message(msg) for msg in messages]
# 调用现有分析器
legacy_quotes, token_usage = await self._analyzer.analyze_golden_quotes(
raw_messages, unified_msg_origin
)
# 将结果转换为领域值对象
quotes = [
GoldenQuote(
content=q.content,
sender_name=q.sender,
sender_id=str(q.qq) if hasattr(q, 'qq') and q.qq else None,
reason=q.reason,
)
for q in legacy_quotes
]
return quotes, token_usage
def _to_raw_message(self, msg: UnifiedMessage) -> dict:
"""
将 UnifiedMessage 转换为原始消息格式
参数:
msg: 统一消息对象
返回:
原始消息字典
"""
# 构建消息内容列表
message_content = []
if msg.text_content:
message_content.append({
"type": "text",
"data": {"text": msg.text_content}
})
return {
"message_id": msg.message_id,
"time": int(msg.timestamp.timestamp()) if msg.timestamp else 0,
"sender": {
"user_id": msg.sender_id,
"nickname": msg.sender_name,
},
"message": message_content,
}
+131
View File
@@ -0,0 +1,131 @@
"""
话题分析领域服务
该模块提供平台无关的话题分析服务接口。
实际分析逻辑委托给 infrastructure 层的具体实现。
架构说明:
- 本文件定义领域服务接口和数据转换逻辑
- 具体的 LLM 调用和消息处理在 src/analysis/analyzers/topic_analyzer.py 中实现
- 采用渐进式迁移策略,保持与现有代码的兼容性
"""
from abc import ABC, abstractmethod
from typing import List, Tuple, TYPE_CHECKING
from ..value_objects.topic import Topic
from ..value_objects.unified_message import UnifiedMessage
if TYPE_CHECKING:
from ..value_objects.statistics import TokenUsage
class ITopicAnalyzer(ABC):
"""
话题分析服务接口
定义平台无关的话题分析契约。
所有平台的话题分析都应该实现此接口。
"""
@abstractmethod
async def analyze(
self,
messages: List[UnifiedMessage],
unified_msg_origin: str = None,
) -> Tuple[List[Topic], "TokenUsage"]:
"""
分析消息中的话题
参数:
messages: 统一格式的消息列表
unified_msg_origin: 消息来源标识,用于选择 LLM 提供商
返回:
(话题列表, Token 使用统计)
"""
pass
class TopicAnalyzerAdapter(ITopicAnalyzer):
"""
话题分析服务适配器
将现有的 TopicAnalyzer 实现适配为领域服务接口。
负责 UnifiedMessage 与原始消息格式之间的转换。
"""
def __init__(self, legacy_analyzer):
"""
初始化适配器
参数:
legacy_analyzer: 现有的 TopicAnalyzer 实例
"""
self._analyzer = legacy_analyzer
async def analyze(
self,
messages: List[UnifiedMessage],
unified_msg_origin: str = None,
) -> Tuple[List[Topic], "TokenUsage"]:
"""
分析消息中的话题
将 UnifiedMessage 转换为原始格式,调用现有分析器,
然后将结果转换为领域值对象。
参数:
messages: 统一格式的消息列表
unified_msg_origin: 消息来源标识
返回:
(话题列表, Token 使用统计)
"""
# 将 UnifiedMessage 转换为原始消息格式
raw_messages = [self._to_raw_message(msg) for msg in messages]
# 调用现有分析器
legacy_topics, token_usage = await self._analyzer.analyze_topics(
raw_messages, unified_msg_origin
)
# 将结果转换为领域值对象
topics = [
Topic(
name=t.topic,
contributors=t.contributors,
detail=t.detail,
)
for t in legacy_topics
]
return topics, token_usage
def _to_raw_message(self, msg: UnifiedMessage) -> dict:
"""
将 UnifiedMessage 转换为原始消息格式
参数:
msg: 统一消息对象
返回:
原始消息字典
"""
# 构建消息内容列表
message_content = []
if msg.text_content:
message_content.append({
"type": "text",
"data": {"text": msg.text_content}
})
return {
"message_id": msg.message_id,
"time": int(msg.timestamp.timestamp()) if msg.timestamp else 0,
"sender": {
"user_id": msg.sender_id,
"nickname": msg.sender_name,
},
"message": message_content,
}
+141
View File
@@ -0,0 +1,141 @@
"""
用户称号分析领域服务
该模块提供平台无关的用户称号分析服务接口。
实际分析逻辑委托给 infrastructure 层的具体实现。
架构说明:
- 本文件定义领域服务接口和数据转换逻辑
- 具体的 LLM 调用和消息处理在 src/analysis/analyzers/user_title_analyzer.py 中实现
- 采用渐进式迁移策略,保持与现有代码的兼容性
"""
from abc import ABC, abstractmethod
from typing import List, Dict, Tuple, Any, TYPE_CHECKING
from ..value_objects.user_title import UserTitle
from ..value_objects.unified_message import UnifiedMessage
if TYPE_CHECKING:
from ..value_objects.statistics import TokenUsage
class IUserTitleAnalyzer(ABC):
"""
用户称号分析服务接口
定义平台无关的用户称号分析契约。
所有平台的用户称号分析都应该实现此接口。
"""
@abstractmethod
async def analyze(
self,
messages: List[UnifiedMessage],
user_analysis: Dict[str, Any],
unified_msg_origin: str = None,
top_users: List[Dict] = None,
) -> Tuple[List[UserTitle], "TokenUsage"]:
"""
分析用户称号
参数:
messages: 统一格式的消息列表
user_analysis: 用户分析统计数据
unified_msg_origin: 消息来源标识,用于选择 LLM 提供商
top_users: 活跃用户列表(可选)
返回:
(用户称号列表, Token 使用统计)
"""
pass
class UserTitleAnalyzerAdapter(IUserTitleAnalyzer):
"""
用户称号分析服务适配器
将现有的 UserTitleAnalyzer 实现适配为领域服务接口。
负责 UnifiedMessage 与原始消息格式之间的转换。
"""
def __init__(self, legacy_analyzer):
"""
初始化适配器
参数:
legacy_analyzer: 现有的 UserTitleAnalyzer 实例
"""
self._analyzer = legacy_analyzer
async def analyze(
self,
messages: List[UnifiedMessage],
user_analysis: Dict[str, Any],
unified_msg_origin: str = None,
top_users: List[Dict] = None,
) -> Tuple[List[UserTitle], "TokenUsage"]:
"""
分析用户称号
将 UnifiedMessage 转换为原始格式,调用现有分析器,
然后将结果转换为领域值对象。
参数:
messages: 统一格式的消息列表
user_analysis: 用户分析统计数据
unified_msg_origin: 消息来源标识
top_users: 活跃用户列表
返回:
(用户称号列表, Token 使用统计)
"""
# 将 UnifiedMessage 转换为原始消息格式
raw_messages = [self._to_raw_message(msg) for msg in messages]
# 调用现有分析器
legacy_titles, token_usage = await self._analyzer.analyze_user_titles(
raw_messages, user_analysis, unified_msg_origin, top_users
)
# 将结果转换为领域值对象
titles = [
UserTitle(
user_id=str(t.qq),
user_name=t.name,
title=t.title,
mbti=t.mbti,
reason=t.reason,
)
for t in legacy_titles
]
return titles, token_usage
def _to_raw_message(self, msg: UnifiedMessage) -> dict:
"""
将 UnifiedMessage 转换为原始消息格式
参数:
msg: 统一消息对象
返回:
原始消息字典
"""
# 构建消息内容列表
message_content = []
if msg.text_content:
message_content.append({
"type": "text",
"data": {"text": msg.text_content}
})
return {
"message_id": msg.message_id,
"time": int(msg.timestamp.timestamp()) if msg.timestamp else 0,
"sender": {
"user_id": msg.sender_id,
"nickname": msg.sender_name,
},
"message": message_content,
}
+44
View File
@@ -2,6 +2,50 @@
常量 - 插件中使用的共享常量
"""
from enum import Enum
class Platform(str, Enum):
"""平台枚举类"""
ONEBOT = "onebot"
AIOCQHTTP = "aiocqhttp"
TELEGRAM = "telegram"
DISCORD = "discord"
SLACK = "slack"
LARK = "lark"
class TaskStatus(str, Enum):
"""任务状态枚举类"""
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
class ContentType(str, Enum):
"""消息内容类型枚举类"""
TEXT = "text"
IMAGE = "image"
EMOJI = "emoji"
STICKER = "sticker"
FILE = "file"
AUDIO = "audio"
VIDEO = "video"
REPLY = "reply"
AT = "at"
UNKNOWN = "unknown"
class ReportFormat(str, Enum):
"""报告格式枚举类"""
TEXT = "text"
MARKDOWN = "markdown"
IMAGE = "image"
HTML = "html"
# 插件元数据
PLUGIN_NAME = "astrbot_plugin_qq_group_daily_analysis"
PLUGIN_VERSION = "2.0.0"