feat(send): async acceptance and send_result event

This commit is contained in:
2026-07-10 12:18:54 +08:00
parent 1d2948677e
commit 1641553d31
4 changed files with 185 additions and 22 deletions

View File

@@ -25,12 +25,16 @@ class SendTextRequest(BaseModel):
account_id: str
conversation_id: str = Field(..., description="目标会话/群 ID")
text: str = Field(..., min_length=1, description="消息内容")
record_id: str = Field(..., description="mci-server 发送记录 ID")
tenant_code: str = Field("default", description="业务系统租户编码")
class SendImageRequest(BaseModel):
account_id: str
conversation_id: str
image_url: str
record_id: str = Field(..., description="mci-server 发送记录 ID")
tenant_code: str = Field("default", description="业务系统租户编码")
@router.post("/login/start")
@@ -55,9 +59,21 @@ def expire_login(account_id: str, request: LoginConfirmRequest):
@router.post("/send/text")
def send_text(request: SendTextRequest):
return send_service.send_text(request.account_id, request.conversation_id, request.text)
return send_service.send_text(
request.account_id,
request.conversation_id,
request.text,
request.record_id,
request.tenant_code,
)
@router.post("/send/image")
def send_image(request: SendImageRequest):
return send_service.send_image(request.account_id, request.conversation_id, request.image_url)
return send_service.send_image(
request.account_id,
request.conversation_id,
request.image_url,
request.record_id,
request.tenant_code,
)

View File

@@ -1,33 +1,116 @@
from __future__ import annotations
import uuid
from typing import Optional
from app.clients.pywechat_client import ClientRegistry, OnlineStatus, registry
from app.models.event import EventType, OutboundEvent
from app.repositories.event_store import EventStore, event_store
class SendService:
def __init__(self, registry: ClientRegistry):
def __init__(self, registry: ClientRegistry, store: EventStore):
self.registry = registry
self.store = store
def send_text(self, account_id: str, conversation_id: str, text: str) -> dict:
def send_text(
self,
account_id: str,
conversation_id: str,
text: str,
record_id: str,
tenant_code: str = "default",
) -> dict:
client = self.registry.get(account_id)
if client is None:
return {"success": False, "error": f"account {account_id} not registered"}
return self._fail(account_id, record_id, tenant_code, conversation_id, f"account {account_id} not registered")
if client.state.online_status != OnlineStatus.ONLINE:
return {"success": False, "error": "account not online"}
return client.send_text(conversation_id, text)
return self._fail(account_id, record_id, tenant_code, conversation_id, "account not online")
if client.state.risk_level >= 80:
return self._fail(
account_id,
record_id,
tenant_code,
conversation_id,
"account risk level too high",
error_code="RISK_CONTROL_BLOCKED",
)
def send_image(self, account_id: str, conversation_id: str, image_url: str) -> dict:
# Placeholder for image sending via pywechat.
message_id = f"mock-msg-{uuid.uuid4().hex[:16]}"
channel_result = client.send_text(conversation_id, text)
self._emit_send_result(account_id, record_id, tenant_code, conversation_id, True, message_id, channel_result)
return {"accepted": True, "message_id": message_id}
def send_image(
self,
account_id: str,
conversation_id: str,
image_url: str,
record_id: str,
tenant_code: str = "default",
) -> dict:
client = self.registry.get(account_id)
if client is None:
return {"success": False, "error": f"account {account_id} not registered"}
return self._fail(account_id, record_id, tenant_code, conversation_id, f"account {account_id} not registered")
if client.state.online_status != OnlineStatus.ONLINE:
return {"success": False, "error": "account not online"}
return {
"success": True,
"message_id": f"mock-img-{image_url.split('/')[-1]}",
"conversation_id": conversation_id,
"type": "image",
}
return self._fail(account_id, record_id, tenant_code, conversation_id, "account not online")
message_id = f"mock-img-{image_url.split('/')[-1]}"
self._emit_send_result(
account_id,
record_id,
tenant_code,
conversation_id,
True,
message_id,
{"image_url": image_url},
)
return {"accepted": True, "message_id": message_id}
def _fail(
self,
account_id: str,
record_id: str,
tenant_code: str,
conversation_id: str,
error: str,
error_code: str = "SEND_FAILED",
) -> dict:
self._emit_send_result(
account_id,
record_id,
tenant_code,
conversation_id,
False,
None,
{"error": error, "error_code": error_code},
)
return {"accepted": False, "error": error}
def _emit_send_result(
self,
account_id: str,
record_id: str,
tenant_code: str,
conversation_id: str,
success: bool,
message_id: Optional[str],
channel_result: dict,
) -> None:
event = OutboundEvent(
event_type=EventType.SEND_RESULT,
account_id=account_id,
tenant_code=tenant_code,
data={
"record_id": record_id,
"conversation_id": conversation_id,
"success": success,
"message_id": message_id,
"channel_result": channel_result,
"error": channel_result.get("error"),
},
)
self.store.save(event)
send_service = SendService(registry)
send_service = SendService(registry, event_store)