Files
group_fqcd_jr/app/model/advisor_service_request.py
Windows 2fe7d0c506 feat(投顾): 客户主动申报投顾方案 + 投顾受理自动生成方案草稿
客户在自己主页提交申报 → 投顾工作台受理 → 自动跑既有推荐逻辑生成一份待审核草稿 →
投顾再走既有的「审核通过 → 发送给客户」。补上原先「客户只能被动等方案」的缺口。

后端
- 新增表 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),已进种子与授权工具

前端
- 投顾工作台新增「客户申报」面板(受理 / 驳回,驳回理由客户可见)
- 客户页新增申报表单(金额/期限/风险偏好/备注)与「我的申报」列表

客户自助路由刻意不挂投顾灰度闸门:那是投顾业务的灰度,客户提交自己的申请不该被它拦下。
2026-09-16 18:17:46 +08:00

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)