客户在自己主页提交申报 → 投顾工作台受理 → 自动跑既有推荐逻辑生成一份待审核草稿 → 投顾再走既有的「审核通过 → 发送给客户」。补上原先「客户只能被动等方案」的缺口。 后端 - 新增表 advisor_service_request(本轮新建,带 AUTO_INCREMENT)+ 迁移 20260916_advisor_service_request(幂等:先查表再建,兼容本库 alembic 指针滞后) - 新增 AdvisorServiceRequestService:create / list_mine / queue / review · 申报前置:风险测评必须存在且未失效(FM-03,12 个月),服务端判 · 队列复用 ProductRecommendationService._visible_customer_ids(本人 + 归属),待受理排前 · 受理即调用推荐逻辑生成草稿并把 content_id 回填;生成前置失败给出人话原因并保持待受理 · 「已发送客户」不落库,由关联方案 published_at 推导,避免两处状态各写各的 - 权限码 9071-9074(客户 write/read:self、投顾 read/review),已进种子与授权工具 前端 - 投顾工作台新增「客户申报」面板(受理 / 驳回,驳回理由客户可见) - 客户页新增申报表单(金额/期限/风险偏好/备注)与「我的申报」列表 客户自助路由刻意不挂投顾灰度闸门:那是投顾业务的灰度,客户提交自己的申请不该被它拦下。
42 lines
2.1 KiB
Python
42 lines
2.1 KiB
Python
"""客户主动申报投顾方案 → 投顾受理审批 的工单模型。"""
|
|
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
|
|
from sqlalchemy import BigInteger, DateTime, Numeric, String
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.model.base import Base
|
|
|
|
#: 状态机(`pending --受理--> accepted` / `pending --驳回--> rejected`)。
|
|
#: 「已发送给客户」不单独存状态:它由 `result_content_id` 指向的方案是否已发布**推导**出来
|
|
#: (见 `AdvisorServiceRequestService._view`),避免两处状态各写各的而对不上。
|
|
PENDING = "pending"
|
|
ACCEPTED = "accepted"
|
|
REJECTED = "rejected"
|
|
DELIVERED = "delivered"
|
|
|
|
|
|
class AdvisorServiceRequest(Base):
|
|
"""客户在「我的投顾方案」页发起的服务申请单。"""
|
|
|
|
__tablename__ = "advisor_service_request"
|
|
|
|
#: 本表是本轮新建的,**带上 AUTO_INCREMENT** —— 既有 `fin_*` 表缺自增
|
|
#: 导致"不给 id 就插不进去"的坑,新表不再重复踩。
|
|
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
|
|
request_no: Mapped[str] = mapped_column(String(32), unique=True, nullable=False)
|
|
customer_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
|
amount_wan: Mapped[Decimal] = mapped_column(Numeric(14, 2), nullable=False)
|
|
horizon: Mapped[str] = mapped_column(String(16), nullable=False)
|
|
risk_preference: Mapped[str] = mapped_column(String(16), nullable=False)
|
|
note: Mapped[str | None] = mapped_column(String(500))
|
|
status: Mapped[str] = mapped_column(String(24), nullable=False)
|
|
handled_by: Mapped[int | None] = mapped_column(BigInteger)
|
|
handled_at: Mapped[datetime | None] = mapped_column(DateTime)
|
|
advisor_note: Mapped[str | None] = mapped_column(String(500))
|
|
#: 受理时自动生成的方案(`client_facing_content.id`);驳回时为 None。
|
|
result_content_id: Mapped[int | None] = mapped_column(BigInteger)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|