feat(modules): 帮助中心、工单、工作流与多渠道接入

补齐 FAQ、帮助中心、工单、可视化工作流及 Telegram/WhatsApp/Slack 等渠道模块。
This commit is contained in:
WangShen007 committed 2026-10-17 22:05:00 +08:00
1 parent ba91abc4b2
commit 83a16ed07d
225 files changed
+59337

No files matched your search

+185
View File
@@ -0,0 +1,185 @@
"""
Copyright 2024-2026 ChatterMate
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from typing import Optional
from uuid import UUID
from sqlalchemy.orm import Session
from app.repositories.workflow import WorkflowRepository
from app.repositories.agent import AgentRepository
from app.models.workflow import Workflow
from app.models.schemas.workflow import WorkflowCreate
from app.core.logger import get_logger
logger = get_logger(__name__)
class WorkflowService:
def __init__(self, db: Session):
self.db = db
self.workflow_repo = WorkflowRepository(db)
self.agent_repo = AgentRepository(db)
def create_workflow(self, workflow_data: WorkflowCreate, created_by: UUID, organization_id: UUID) -> Workflow:
"""Create a new workflow with validation"""
# Validate agent exists and belongs to organization
agent = self.agent_repo.get_by_id(workflow_data.agent_id)
if not agent:
raise ValueError("Agent not found")
if agent.organization_id != organization_id:
raise ValueError("Agent does not belong to your organization")
# Check if workflow already exists for this agent
existing_workflow = self.workflow_repo.get_workflows_by_agent(workflow_data.agent_id)
if existing_workflow:
raise ValueError("Agent already has a workflow. Only one workflow per agent is allowed.")
# Check if workflow name already exists in organization
existing_workflow_name = self.workflow_repo.get_by_name(
workflow_data.name,
organization_id
)
if existing_workflow_name:
raise ValueError(f"Workflow with name '{workflow_data.name}' already exists")
# Create workflow data
workflow_dict = workflow_data.model_dump()
workflow_dict.update({
"created_by": created_by,
"organization_id": organization_id
})
# Create workflow
workflow = self.workflow_repo.create_workflow(**workflow_dict)
logger.info(f"Created workflow {workflow.id} for agent {workflow_data.agent_id}")
return workflow
def get_workflow_by_agent_id(self, agent_id: UUID, organization_id: UUID) -> Optional[Workflow]:
"""Get workflow by agent ID with organization validation"""
# Validate agent exists and belongs to organization
agent = self.agent_repo.get_by_id(agent_id)
if not agent:
raise ValueError("Agent not found")
if agent.organization_id != organization_id:
raise ValueError("Agent does not belong to your organization")
# Get workflows for this agent
workflows = self.workflow_repo.get_workflows_by_agent(agent_id)
# Return the first workflow (should be only one due to our constraint)
return workflows[0] if workflows else None
def update_workflow(self, workflow_id: UUID, workflow_data: dict, organization_id: UUID) -> Optional[Workflow]:
"""Update workflow with validation"""
# Get existing workflow
workflow = self.workflow_repo.get_by_id(workflow_id)
if not workflow:
raise ValueError("Workflow not found")
# Verify organization access
if workflow.organization_id != organization_id:
raise ValueError("Workflow does not belong to your organization")
# If name is being updated, check for duplicates
if 'name' in workflow_data and workflow_data['name'] != workflow.name:
existing_workflow = self.workflow_repo.get_by_name(
workflow_data['name'],
organization_id
)
if existing_workflow and existing_workflow.id != workflow_id:
raise ValueError(f"Workflow with name '{workflow_data['name']}' already exists")
# If agent_id is being updated, validate the new agent and check constraint
if 'agent_id' in workflow_data and workflow_data['agent_id'] != workflow.agent_id:
# Validate new agent exists and belongs to organization
agent = self.agent_repo.get_by_id(workflow_data['agent_id'])
if not agent:
raise ValueError("Agent not found")
if agent.organization_id != organization_id:
raise ValueError("Agent does not belong to your organization")
# Check if new agent already has a workflow
existing_workflows = self.workflow_repo.get_workflows_by_agent(workflow_data['agent_id'])
if existing_workflows:
raise ValueError("Agent already has a workflow. Only one workflow per agent is allowed.")
# Check if status is being updated to published
if 'status' in workflow_data and workflow_data['status'] == 'published':
# When publishing, update the agent's active_workflow_id and use_workflow
agent = self.agent_repo.get_by_id(workflow.agent_id)
if agent:
self.agent_repo.update_agent(
workflow.agent_id,
active_workflow_id=workflow_id,
use_workflow=True
)
logger.info(f"Updated agent {workflow.agent_id} active_workflow_id to {workflow_id}")
# Check if status is being updated to draft (unpublished)
if 'status' in workflow_data and workflow_data['status'] == 'draft':
# When unpublishing, clear the agent's active_workflow_id and disable workflow
agent = self.agent_repo.get_by_id(workflow.agent_id)
if agent and agent.active_workflow_id == workflow_id:
self.agent_repo.update_agent(
workflow.agent_id,
active_workflow_id=None,
use_workflow=False
)
logger.info(f"Cleared agent {workflow.agent_id} active_workflow_id")
# Update workflow
updated_workflow = self.workflow_repo.update_workflow(workflow_id, **workflow_data)
logger.info(f"Updated workflow {workflow_id}")
return updated_workflow
def delete_workflow(self, workflow_id: UUID, organization_id: UUID) -> bool:
"""Delete workflow with validation"""
# Get existing workflow
workflow = self.workflow_repo.get_by_id(workflow_id)
if not workflow:
raise ValueError("Workflow not found")
# Verify organization access
if workflow.organization_id != organization_id:
raise ValueError("Workflow does not belong to your organization")
# Clear agent's active_workflow_id if this workflow is active
agent = self.agent_repo.get_by_id(workflow.agent_id)
if agent and agent.active_workflow_id == workflow_id:
self.agent_repo.update_agent(
workflow.agent_id,
active_workflow_id=None,
use_workflow=False
)
logger.info(f"Cleared agent {workflow.agent_id} active_workflow_id before deleting workflow")
# Delete workflow
success = self.workflow_repo.delete_workflow(workflow_id)
if success:
logger.info(f"Deleted workflow {workflow_id}")
return success