2026-09-12 20:42:33 +08:00
|
|
|
|
"""草稿代理 + 本地镜像 + 发送终审(投顾工作台核心闭环)。
|
|
|
|
|
|
|
|
|
|
|
|
- 草稿 list/detail/save/discard 代理到投顾Agent(错误码映射见 agent_client);
|
|
|
|
|
|
- save 时本地镜像一份 advisor_report(send_status=draft),实现 A5「以最后保存快照为准」;
|
|
|
|
|
|
- send 为工作台本地实现:签约兜底 + 三检终审(敏感词/适当性/免责)+ 写 sys_message
|
|
|
|
|
|
与 advisor_report(sent) 同事务;Agent 不感知 sent。
|
|
|
|
|
|
"""
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
import uuid
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
from typing import Any
|
|
|
|
|
|
|
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
|
|
|
|
|
from common_const import (
|
|
|
|
|
|
AGENT_INTENT_RECOMMEND,
|
|
|
|
|
|
CUSTOMER_REL_STATUS_SIGNED,
|
|
|
|
|
|
INTENT_TO_MSG_TYPE,
|
|
|
|
|
|
MSG_TYPE_RECOMMEND,
|
|
|
|
|
|
REPORT_SEND_STATUS_DISCARDED,
|
|
|
|
|
|
REPORT_SEND_STATUS_DRAFT,
|
|
|
|
|
|
REPORT_SEND_STATUS_SENT,
|
|
|
|
|
|
)
|
|
|
|
|
|
from model.advisor_report import AdvisorReport
|
|
|
|
|
|
from model.sys_message import SysMessage
|
|
|
|
|
|
from model.sys_user import SysUser
|
|
|
|
|
|
from repositories.advisor_report import AdvisorReportRepo
|
|
|
|
|
|
from repositories.product import ProductRepo
|
|
|
|
|
|
from repositories.risk_assessment import CustomerProfileRepo
|
|
|
|
|
|
from repositories.sensitive_word import SensitiveWordRepo
|
2026-09-13 16:19:24 +08:00
|
|
|
|
from repositories.sys_message import SysMessageRepo
|
2026-09-12 20:42:33 +08:00
|
|
|
|
from schemas.advisor import DraftSaveReq, RebalanceRunReq, TalkScriptReq
|
|
|
|
|
|
from service.advisor.agent_client import get_agent_client
|
|
|
|
|
|
from service.advisor.permissions import ensure_customer_owned, require_owned_relation
|
|
|
|
|
|
from service.advisor.send_review import review_send
|
|
|
|
|
|
from utils.exceptions import ForbiddenError, ParamError
|
|
|
|
|
|
|
|
|
|
|
|
# 建议清单中「低配申购」侧键(Agent 侧 suggestions 结构,文档未给字段级定义,按合理约定)
|
|
|
|
|
|
# 假设:suggestions 为 dict,申购清单在 buy/purchase/低配申购/申购 任一键下,项含 product_code。
|
|
|
|
|
|
_BUY_KEYS = ("buy", "purchase", "低配申购", "申购")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def gen_report_id() -> str:
|
|
|
|
|
|
"""生成本地报告唯一 ID(advisor_report.report_id)。"""
|
|
|
|
|
|
return "rpt-" + uuid.uuid4().hex
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _build_report_from_detail(detail: dict, advisor_id: int) -> AdvisorReport:
|
|
|
|
|
|
"""由 Agent 草稿详情构建本地报告镜像(未 save 直接 send 的兜底,D6)。"""
|
|
|
|
|
|
return AdvisorReport(
|
|
|
|
|
|
report_id=gen_report_id(),
|
|
|
|
|
|
draft_id=str(detail.get("draft_id") or ""),
|
|
|
|
|
|
customer_id=int(detail.get("customer_id") or 0),
|
|
|
|
|
|
advisor_id=advisor_id,
|
|
|
|
|
|
intent=detail.get("intent") or AGENT_INTENT_RECOMMEND,
|
|
|
|
|
|
title=detail.get("title"),
|
|
|
|
|
|
content=detail.get("content"),
|
|
|
|
|
|
edit_history=[],
|
|
|
|
|
|
send_status=REPORT_SEND_STATUS_DRAFT,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-13 16:19:24 +08:00
|
|
|
|
def _extract_buy_codes(detail: Any) -> list[str]:
|
|
|
|
|
|
"""从 Agent 草稿详情提取发送终审需要校验的产品代码。
|
2026-09-12 20:42:33 +08:00
|
|
|
|
|
2026-09-13 16:19:24 +08:00
|
|
|
|
兼容旧的顶层 ``suggestions``,以及 Agent 当前的 ``structured_data``:调仓只取
|
|
|
|
|
|
``buy``,推荐取 ``items``。解析失败返回空列表,交由其它终审规则继续处理。
|
2026-09-12 20:42:33 +08:00
|
|
|
|
"""
|
2026-09-13 16:19:24 +08:00
|
|
|
|
if not isinstance(detail, dict):
|
2026-09-12 20:42:33 +08:00
|
|
|
|
return []
|
2026-09-13 16:19:24 +08:00
|
|
|
|
|
|
|
|
|
|
structured_data = detail.get("structured_data")
|
|
|
|
|
|
data = structured_data if isinstance(structured_data, dict) else detail
|
|
|
|
|
|
suggestions = data.get("suggestions")
|
|
|
|
|
|
if isinstance(suggestions, dict):
|
|
|
|
|
|
data = suggestions
|
|
|
|
|
|
|
2026-09-12 20:42:33 +08:00
|
|
|
|
items: list | None = None
|
|
|
|
|
|
for key in _BUY_KEYS:
|
2026-09-13 16:19:24 +08:00
|
|
|
|
value = data.get(key)
|
2026-09-12 20:42:33 +08:00
|
|
|
|
if isinstance(value, list):
|
|
|
|
|
|
items = value
|
|
|
|
|
|
break
|
2026-09-13 16:19:24 +08:00
|
|
|
|
if items is None and isinstance(data.get("items"), list):
|
|
|
|
|
|
items = data["items"]
|
|
|
|
|
|
|
2026-09-12 20:42:33 +08:00
|
|
|
|
codes: list[str] = []
|
|
|
|
|
|
for it in items or []:
|
|
|
|
|
|
if isinstance(it, dict):
|
|
|
|
|
|
code = it.get("product_code") or it.get("code") or it.get("fund_code")
|
|
|
|
|
|
if code:
|
|
|
|
|
|
codes.append(str(code))
|
|
|
|
|
|
return codes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _get_customer_risk(db: AsyncSession, customer_id: int) -> str | None:
|
|
|
|
|
|
"""客户当前风险等级(适当性硬依据)。
|
|
|
|
|
|
|
|
|
|
|
|
假设:取 fin_customer_profile.risk_level(问卷服务在风评后回写,与 fin_risk_assessment
|
|
|
|
|
|
保持一致),作为「当前有效 C 级」。
|
|
|
|
|
|
"""
|
|
|
|
|
|
profile = await CustomerProfileRepo(db).get_by_customer(customer_id)
|
|
|
|
|
|
return profile.risk_level if profile else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _resolve_product_risks(
|
|
|
|
|
|
db: AsyncSession, draft_id: str, auth_header: str, trace_id: str
|
|
|
|
|
|
) -> list[str | None]:
|
|
|
|
|
|
"""适当性输入:取草稿建议清单的申购产品,查 fin_product 得 R 级。
|
|
|
|
|
|
|
|
|
|
|
|
本地镜像不存 suggestions,故发送时向 Agent 实时取一次结构化清单;Agent 不可用则
|
|
|
|
|
|
发送被 fail-closed 阻断(合规优先,符合 PRD「不可完全依赖 Agent」+ 双重防护)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
result = await get_agent_client().draft_detail(
|
|
|
|
|
|
draft_id, auth_header=auth_header, trace_id=trace_id
|
|
|
|
|
|
)
|
2026-09-13 16:19:24 +08:00
|
|
|
|
codes = _extract_buy_codes(result["data"] or {})
|
2026-09-12 20:42:33 +08:00
|
|
|
|
product_repo = ProductRepo(db)
|
|
|
|
|
|
risks: list[str | None] = []
|
|
|
|
|
|
for code in codes:
|
|
|
|
|
|
product = await product_repo.get_by_code(code)
|
|
|
|
|
|
risks.append(product.risk_level if product else None)
|
|
|
|
|
|
return risks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _sent_payload(report: AdvisorReport) -> dict:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"report_id": report.report_id,
|
|
|
|
|
|
"send_time": report.send_time.isoformat() if report.send_time else None,
|
|
|
|
|
|
"msg_id": report.msg_id,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# 草稿代理(读取/编辑/废弃)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
async def list_drafts(
|
|
|
|
|
|
db: AsyncSession,
|
|
|
|
|
|
user: SysUser,
|
|
|
|
|
|
*,
|
|
|
|
|
|
auth_header: str,
|
|
|
|
|
|
trace_id: str,
|
|
|
|
|
|
customer_id: int | None = None,
|
|
|
|
|
|
status: str | None = None,
|
|
|
|
|
|
page: int = 1,
|
2026-09-13 22:21:49 +08:00
|
|
|
|
page_size: int = 10,
|
2026-09-12 20:42:33 +08:00
|
|
|
|
) -> dict:
|
|
|
|
|
|
result = await get_agent_client().draft_list(
|
|
|
|
|
|
auth_header=auth_header,
|
|
|
|
|
|
trace_id=trace_id,
|
|
|
|
|
|
advisor_id=user.id,
|
|
|
|
|
|
customer_id=customer_id,
|
|
|
|
|
|
status=status,
|
|
|
|
|
|
page=page,
|
|
|
|
|
|
page_size=page_size,
|
|
|
|
|
|
)
|
|
|
|
|
|
data = result["data"] or {}
|
|
|
|
|
|
if result["warning"]:
|
|
|
|
|
|
data["warning"] = result["warning"]
|
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_draft(
|
|
|
|
|
|
db: AsyncSession, user: SysUser, *, auth_header: str, trace_id: str, draft_id: str
|
|
|
|
|
|
) -> dict:
|
|
|
|
|
|
result = await get_agent_client().draft_detail(
|
|
|
|
|
|
draft_id, auth_header=auth_header, trace_id=trace_id
|
|
|
|
|
|
)
|
|
|
|
|
|
data = result["data"] or {}
|
|
|
|
|
|
# 数据权限:草稿归属客户必须属于当前投顾;无法确认归属时拒绝(fail-closed)
|
|
|
|
|
|
customer_id = data.get("customer_id")
|
|
|
|
|
|
if customer_id is None:
|
|
|
|
|
|
raise ForbiddenError("无法校验草稿归属客户")
|
|
|
|
|
|
await ensure_customer_owned(db, user.id, int(customer_id))
|
|
|
|
|
|
if result["warning"]:
|
|
|
|
|
|
data["warning"] = result["warning"]
|
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _upsert_report_mirror(
|
|
|
|
|
|
db: AsyncSession, user: SysUser, draft_id: str, detail: dict, req: DraftSaveReq
|
|
|
|
|
|
) -> None:
|
|
|
|
|
|
"""save 后本地镜像快照(send_status=draft),记录编辑留痕。
|
|
|
|
|
|
|
|
|
|
|
|
已发送(sent)的快照不回退为 draft,避免覆盖已交付内容。
|
|
|
|
|
|
"""
|
|
|
|
|
|
repo = AdvisorReportRepo(db)
|
|
|
|
|
|
report = await repo.get_by_draft_id(draft_id)
|
|
|
|
|
|
edit_entry = {
|
|
|
|
|
|
"editor": user.id,
|
|
|
|
|
|
"time": datetime.now().isoformat(),
|
|
|
|
|
|
"title": req.title,
|
|
|
|
|
|
}
|
|
|
|
|
|
if report is None:
|
|
|
|
|
|
report = AdvisorReport(
|
|
|
|
|
|
report_id=gen_report_id(),
|
|
|
|
|
|
draft_id=draft_id,
|
|
|
|
|
|
customer_id=int(detail["customer_id"]),
|
|
|
|
|
|
advisor_id=user.id,
|
|
|
|
|
|
intent=detail.get("intent") or AGENT_INTENT_RECOMMEND,
|
|
|
|
|
|
title=req.title,
|
|
|
|
|
|
content=req.content,
|
|
|
|
|
|
edit_history=[edit_entry],
|
|
|
|
|
|
send_status=REPORT_SEND_STATUS_DRAFT,
|
|
|
|
|
|
)
|
|
|
|
|
|
db.add(report)
|
|
|
|
|
|
elif report.send_status != REPORT_SEND_STATUS_SENT:
|
|
|
|
|
|
report.title = req.title
|
|
|
|
|
|
report.content = req.content
|
|
|
|
|
|
report.edit_history = list(report.edit_history or []) + [edit_entry]
|
|
|
|
|
|
report.send_status = REPORT_SEND_STATUS_DRAFT
|
|
|
|
|
|
await db.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def save_draft(
|
|
|
|
|
|
db: AsyncSession,
|
|
|
|
|
|
user: SysUser,
|
|
|
|
|
|
*,
|
|
|
|
|
|
auth_header: str,
|
|
|
|
|
|
trace_id: str,
|
|
|
|
|
|
draft_id: str,
|
|
|
|
|
|
req: DraftSaveReq,
|
|
|
|
|
|
) -> dict:
|
|
|
|
|
|
# 1) 先取草稿确认归属(避免对无权限草稿执行写操作),并拿到 intent/customer_id
|
|
|
|
|
|
detail = await get_draft(db, user, auth_header=auth_header, trace_id=trace_id, draft_id=draft_id)
|
|
|
|
|
|
# 2) 调 Agent 保存(Agent 重新适当性校验,违规 40020;缺免责仅告警不阻断)
|
2026-09-13 16:19:24 +08:00
|
|
|
|
payload = {
|
|
|
|
|
|
"title": req.title,
|
|
|
|
|
|
"content": req.content,
|
|
|
|
|
|
"structured_data": req.suggestions,
|
|
|
|
|
|
}
|
2026-09-12 20:42:33 +08:00
|
|
|
|
result = await get_agent_client().draft_save(
|
|
|
|
|
|
draft_id, payload, auth_header=auth_header, trace_id=trace_id
|
|
|
|
|
|
)
|
|
|
|
|
|
# 3) 本地镜像快照
|
|
|
|
|
|
await _upsert_report_mirror(db, user, draft_id, detail, req)
|
|
|
|
|
|
data = result["data"] or {}
|
|
|
|
|
|
if result["warning"]:
|
|
|
|
|
|
data["warning"] = result["warning"]
|
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def discard_draft(
|
|
|
|
|
|
db: AsyncSession, user: SysUser, *, auth_header: str, trace_id: str, draft_id: str
|
|
|
|
|
|
) -> dict:
|
|
|
|
|
|
# 1) 确认归属(避免越权废弃)
|
|
|
|
|
|
await get_draft(db, user, auth_header=auth_header, trace_id=trace_id, draft_id=draft_id)
|
|
|
|
|
|
# 2) 回写 Agent 归档(Agent 不可用则废弃失败,本地不翻转,保持一致)
|
|
|
|
|
|
result = await get_agent_client().draft_operate(
|
|
|
|
|
|
draft_id, "discard", auth_header=auth_header, trace_id=trace_id
|
|
|
|
|
|
)
|
|
|
|
|
|
# 3) 本地镜像翻转 discarded(已 sent 的终态不回退)
|
|
|
|
|
|
report = await AdvisorReportRepo(db).get_by_draft_id(draft_id)
|
|
|
|
|
|
if report is not None and report.send_status != REPORT_SEND_STATUS_SENT:
|
|
|
|
|
|
report.send_status = REPORT_SEND_STATUS_DISCARDED
|
|
|
|
|
|
await db.commit()
|
|
|
|
|
|
data = result["data"] or {}
|
|
|
|
|
|
if result["warning"]:
|
|
|
|
|
|
data["warning"] = result["warning"]
|
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# 发送(工作台本地,Agent 不感知)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
async def send_draft(
|
|
|
|
|
|
db: AsyncSession, user: SysUser, *, auth_header: str, trace_id: str, draft_id: str
|
|
|
|
|
|
) -> dict:
|
|
|
|
|
|
repo = AdvisorReportRepo(db)
|
|
|
|
|
|
report = await repo.get_by_draft_id(draft_id)
|
|
|
|
|
|
|
|
|
|
|
|
# 无快照:未 save 直接发送,先拉 Agent 草稿建快照(D6 fallback)
|
|
|
|
|
|
if report is None:
|
|
|
|
|
|
detail = await get_draft(db, user, auth_header=auth_header, trace_id=trace_id, draft_id=draft_id)
|
|
|
|
|
|
report = _build_report_from_detail(detail, user.id)
|
|
|
|
|
|
elif report.advisor_id != user.id:
|
|
|
|
|
|
raise ForbiddenError("无权操作该客户数据")
|
|
|
|
|
|
|
2026-09-13 16:19:24 +08:00
|
|
|
|
if report.send_status == REPORT_SEND_STATUS_DISCARDED:
|
|
|
|
|
|
raise ParamError("已废弃的报告不可发送")
|
|
|
|
|
|
|
2026-09-12 20:42:33 +08:00
|
|
|
|
# 幂等:已发送直接返回(重复点击不重复发站内信)
|
|
|
|
|
|
if report.send_status == REPORT_SEND_STATUS_SENT:
|
|
|
|
|
|
return {**_sent_payload(report), "duplicated": True}
|
|
|
|
|
|
|
|
|
|
|
|
# 数据权限 + 签约状态实时兜底(发送前实时查询,PRD §4.5.2)
|
|
|
|
|
|
rel = await require_owned_relation(db, user.id, report.customer_id)
|
|
|
|
|
|
if rel.status != CUSTOMER_REL_STATUS_SIGNED:
|
|
|
|
|
|
raise ForbiddenError("客户尚未签约,禁止发送报告")
|
|
|
|
|
|
|
|
|
|
|
|
# 发送终审(三检全过才放行;适当性需 Agent 侧结构化建议清单)
|
|
|
|
|
|
customer_risk = await _get_customer_risk(db, report.customer_id)
|
|
|
|
|
|
product_risks = await _resolve_product_risks(db, draft_id, auth_header, trace_id)
|
|
|
|
|
|
sensitive_words = [w.word for w in await SensitiveWordRepo(db).list_active()]
|
|
|
|
|
|
review_send(
|
|
|
|
|
|
content=report.content,
|
|
|
|
|
|
title=report.title,
|
|
|
|
|
|
customer_risk=customer_risk,
|
|
|
|
|
|
product_risks=product_risks,
|
|
|
|
|
|
sensitive_words=sensitive_words,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-09-13 16:19:24 +08:00
|
|
|
|
# 提交结果不确定后重试时,先复用已经写入的站内信,避免重复触达客户。
|
|
|
|
|
|
existing_message = await SysMessageRepo(db).get_by_biz_id(
|
|
|
|
|
|
report.report_id, user_id=report.customer_id
|
|
|
|
|
|
)
|
|
|
|
|
|
if existing_message is not None:
|
|
|
|
|
|
report.send_status = REPORT_SEND_STATUS_SENT
|
|
|
|
|
|
report.send_time = report.send_time or existing_message.create_time
|
|
|
|
|
|
report.send_by = report.send_by or user.id
|
|
|
|
|
|
report.msg_id = existing_message.id
|
|
|
|
|
|
db.add(report)
|
|
|
|
|
|
await db.commit()
|
|
|
|
|
|
return {**_sent_payload(report), "duplicated": True}
|
|
|
|
|
|
|
2026-09-12 20:42:33 +08:00
|
|
|
|
# 写站内信 + 报告置 sent,同事务(失败可重试,避免假送达)
|
|
|
|
|
|
msg_type = INTENT_TO_MSG_TYPE.get(report.intent, MSG_TYPE_RECOMMEND)
|
|
|
|
|
|
message = SysMessage(
|
|
|
|
|
|
user_id=report.customer_id,
|
|
|
|
|
|
msg_type=msg_type,
|
|
|
|
|
|
title=report.title or msg_type,
|
|
|
|
|
|
content=(report.content or "")[:512],
|
|
|
|
|
|
biz_id=report.report_id,
|
|
|
|
|
|
)
|
|
|
|
|
|
report.send_status = REPORT_SEND_STATUS_SENT
|
|
|
|
|
|
report.send_time = datetime.now()
|
|
|
|
|
|
report.send_by = user.id
|
|
|
|
|
|
db.add(message)
|
|
|
|
|
|
db.add(report) # 已跟踪对象时无副作用,新对象时入 session
|
2026-09-13 16:19:24 +08:00
|
|
|
|
try:
|
|
|
|
|
|
await db.flush() # 生成 message.id,供回填 msg_id
|
|
|
|
|
|
report.msg_id = message.id
|
|
|
|
|
|
await db.commit()
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
await db.rollback()
|
|
|
|
|
|
raise
|
2026-09-12 20:42:33 +08:00
|
|
|
|
return _sent_payload(report)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# 调仓触发 / 话术(代理 Agent)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
async def run_rebalance(
|
|
|
|
|
|
db: AsyncSession, user: SysUser, *, auth_header: str, trace_id: str, req: RebalanceRunReq
|
|
|
|
|
|
) -> dict:
|
|
|
|
|
|
# 本地先校验归属 + 签约,快速失败(Agent 侧 40030 为兜底);未签约不生成待办
|
|
|
|
|
|
rel = await require_owned_relation(db, user.id, req.customer_id)
|
|
|
|
|
|
if rel.status != CUSTOMER_REL_STATUS_SIGNED:
|
|
|
|
|
|
raise ParamError("该客户尚未签约,不支持生成调仓建议")
|
|
|
|
|
|
result = await get_agent_client().rebalance_run(
|
|
|
|
|
|
req.customer_id, auth_header=auth_header, trace_id=trace_id
|
|
|
|
|
|
)
|
|
|
|
|
|
return result["data"] or {"accepted": True}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def generate_talk_script(
|
|
|
|
|
|
db: AsyncSession, user: SysUser, *, auth_header: str, trace_id: str, req: TalkScriptReq
|
|
|
|
|
|
) -> dict:
|
|
|
|
|
|
await ensure_customer_owned(db, user.id, req.customer_id)
|
|
|
|
|
|
result = await get_agent_client().generate_talk_script(
|
|
|
|
|
|
req.customer_id, req.scene_type, auth_header=auth_header, trace_id=trace_id
|
|
|
|
|
|
)
|
|
|
|
|
|
data = result["data"] or {}
|
|
|
|
|
|
if result["warning"]:
|
|
|
|
|
|
data["warning"] = result["warning"]
|
|
|
|
|
|
return data
|