Merge branch 'develop' of http://47.106.207.27:3000/AI260626/Mutual_Fund into develop_feature_customer

# Conflicts:
#	api/routers/advisor_agent.py
This commit is contained in:
2026-09-13 23:37:04 +08:00
45 changed files with 1606 additions and 129 deletions
+1 -1
View File
@@ -138,7 +138,7 @@ class AdvisorAgentClient:
customer_id: int | None = None,
status: str | None = None,
page: int = 1,
page_size: int = 20,
page_size: int = 10,
) -> dict:
params = {"advisor_id": advisor_id, "page": page, "page_size": page_size}
if customer_id is not None:
+7 -3
View File
@@ -9,6 +9,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from model.sys_user import SysUser
from repositories.audit_log import AuditLogRepo
from utils.pagination import normalize_pagination, pagination_result
def _audit_item(a) -> dict:
@@ -34,17 +35,20 @@ async def list_ledger(
start: datetime | None = None,
end: datetime | None = None,
page: int = 1,
page_size: int = 20,
page_size: int = 10,
) -> dict:
page, page_size, offset = normalize_pagination(page, page_size)
repo = AuditLogRepo(db)
items = await repo.list_by_advisor(
user_id=user.id, action=action, customer_id=customer_id, keyword=keyword, start=start, end=end,
limit=page_size, offset=(page - 1) * page_size,
limit=page_size, offset=offset,
)
total = await repo.count_by_advisor(
user_id=user.id, action=action, keyword=keyword, start=start, end=end
)
return {"total": total, "page": page, "page_size": page_size, "items": [_audit_item(a) for a in items]}
return pagination_result(
[_audit_item(a) for a in items], total, page=page, page_size=page_size
)
def _to_csv(rows: list[dict]) -> str:
+14 -9
View File
@@ -28,6 +28,7 @@ from service.advisor.audit_writer import write_audit
from service.advisor.masking import mask_name, mask_phone
from service.advisor.permissions import ensure_customer_owned, require_owned_relation
from utils.exceptions import ForbiddenError, NotFoundError
from utils.pagination import normalize_pagination, pagination_result
# 持仓中状态(与 service/holdings.py 口径一致)
_HOLDING_STATUS = "持有中"
@@ -57,12 +58,13 @@ async def list_customers(
status: str | None = None,
keyword: str | None = None,
page: int = 1,
page_size: int = 20,
page_size: int = 10,
) -> dict:
page, page_size, offset = normalize_pagination(page, page_size)
repo = CustomerRelationRepo(db)
rows = await repo.list_customer_rows(
advisor_id=user.id, status=status, keyword=keyword,
limit=page_size, offset=(page - 1) * page_size,
limit=page_size, offset=offset,
)
total = await repo.count_customer_rows(advisor_id=user.id, status=status, keyword=keyword)
items = []
@@ -80,7 +82,7 @@ async def list_customers(
else None,
}
)
return {"total": total, "page": page, "page_size": page_size, "items": items}
return pagination_result(items, total, page=page, page_size=page_size)
async def get_customer(
@@ -133,19 +135,19 @@ async def get_customer_holdings(
async def get_customer_reports(
db: AsyncSession, user: SysUser, customer_id: int, *, page: int = 1, page_size: int = 20
db: AsyncSession, user: SysUser, customer_id: int, *, page: int = 1, page_size: int = 10
) -> dict:
page, page_size, offset = normalize_pagination(page, page_size)
await ensure_customer_owned(db, user.id, customer_id)
repo = AdvisorReportRepo(db)
items = await repo.list_by_customer(
customer_id,
advisor_id=user.id,
limit=page_size,
offset=(page - 1) * page_size,
offset=offset,
)
return {
"total": await repo.count_by_advisor(advisor_id=user.id, customer_id=customer_id),
"items": [
return pagination_result(
[
{
"report_id": r.report_id,
"intent": r.intent,
@@ -155,7 +157,10 @@ async def get_customer_reports(
}
for r in items
],
}
await repo.count_by_advisor(advisor_id=user.id, customer_id=customer_id),
page=page,
page_size=page_size,
)
async def update_relation(
+1 -1
View File
@@ -144,7 +144,7 @@ async def list_drafts(
customer_id: int | None = None,
status: str | None = None,
page: int = 1,
page_size: int = 20,
page_size: int = 10,
) -> dict:
result = await get_agent_client().draft_list(
auth_header=auth_header,
+7 -3
View File
@@ -11,6 +11,7 @@ from model.sys_user import SysUser
from repositories.advisor_todo import AdvisorTodoRepo
from schemas.advisor import TodoHandleReq
from utils.exceptions import ForbiddenError, NotFoundError, ParamError
from utils.pagination import normalize_pagination, pagination_result
def _todo_item(t: AdvisorTodo) -> dict:
@@ -36,15 +37,18 @@ async def list_todos(
status: str | None = None,
todo_type: str | None = None,
page: int = 1,
page_size: int = 20,
page_size: int = 10,
) -> dict:
page, page_size, offset = normalize_pagination(page, page_size)
repo = AdvisorTodoRepo(db)
items = await repo.list_by_advisor(
advisor_id=user.id, status=status, todo_type=todo_type,
limit=page_size, offset=(page - 1) * page_size,
limit=page_size, offset=offset,
)
total = await repo.count_by_advisor(advisor_id=user.id, status=status, todo_type=todo_type)
return {"total": total, "page": page, "page_size": page_size, "items": [_todo_item(t) for t in items]}
return pagination_result(
[_todo_item(t) for t in items], total, page=page, page_size=page_size
)
async def handle_todo(db: AsyncSession, user: SysUser, todo_id: int, req: TodoHandleReq) -> dict:
+11 -6
View File
@@ -15,6 +15,7 @@ from schemas.advisor import VisitCreateReq, VisitUpdateReq
from service.advisor.permissions import ensure_customer_owned
from service.advisor.audit_writer import write_audit
from utils.exceptions import NotFoundError, ParamError
from utils.pagination import normalize_pagination, pagination_result
# 合规话术库(内置,标准化投教/市场解读/调仓沟通)。
# 假设:V1.0 内置常量,后续可迁移 sys_config 运营化;内容不含承诺收益等敏感词。
@@ -40,17 +41,20 @@ def _visit_item(v: AdvisorVisitRecord) -> dict:
async def list_visits(
db: AsyncSession, user: SysUser, *, customer_id: int | None = None,
page: int = 1, page_size: int = 20,
page: int = 1, page_size: int = 10,
) -> dict:
page, page_size, offset = normalize_pagination(page, page_size)
if customer_id is not None:
await ensure_customer_owned(db, user.id, customer_id)
repo = AdvisorVisitRecordRepo(db)
items = await repo.list_by_advisor(
advisor_id=user.id, customer_id=customer_id,
limit=page_size, offset=(page - 1) * page_size,
limit=page_size, offset=offset,
)
total = await repo.count_by_advisor(advisor_id=user.id, customer_id=customer_id)
return {"total": total, "page": page, "page_size": page_size, "items": [_visit_item(v) for v in items]}
return pagination_result(
[_visit_item(v) for v in items], total, page=page, page_size=page_size
)
async def create_visit(db: AsyncSession, user: SysUser, req: VisitCreateReq) -> dict:
@@ -114,19 +118,20 @@ def list_talk_templates() -> list[dict]:
async def list_touch_logs(
db: AsyncSession, user: SysUser, customer_id: int, *, page: int = 1, page_size: int = 20
db: AsyncSession, user: SysUser, customer_id: int, *, page: int = 1, page_size: int = 10
) -> dict:
"""触达留痕:站内信(发送触达)+ 回访记录(人工沟通)聚合。
假设:V1.0 触达通道仅站内信与回访;短信/企微/电话统一落在回访记录。
"""
page, page_size, offset = normalize_pagination(page, page_size)
await ensure_customer_owned(db, user.id, customer_id)
messages = await SysMessageRepo(db).list_by_user(
customer_id, limit=page_size, offset=(page - 1) * page_size
customer_id, limit=page_size, offset=offset
)
visits = await AdvisorVisitRecordRepo(db).list_by_advisor(
advisor_id=user.id, customer_id=customer_id,
limit=page_size, offset=(page - 1) * page_size,
limit=page_size, offset=offset,
)
return {
"messages": [