2026-10-19 06:44:15 -04:00
|
|
|
|
"""工作台的请求/响应模型(M4 ⑧)。
|
|
|
|
|
|
|
2026-10-23 04:15:09 -04:00
|
|
|
|
聚合端点一次返回三区块(前端一次请求渲染整页,避免多次往返):
|
|
|
|
|
|
经营绩效总览 / 待办事项 / 风险预警。金额一律 Decimal
|
2026-10-19 06:44:15 -04:00
|
|
|
|
(序列化为字符串),前端不做数值运算。
|
|
|
|
|
|
|
2026-10-23 04:15:09 -04:00
|
|
|
|
**最近动态独立成分页端点**:它可能有很长(操作日志全量),塞进聚合
|
|
|
|
|
|
响应既撑爆页面又只能截断前 20 条;拆开后翻页只重取这张列表,不必
|
|
|
|
|
|
重算总览/待办/预警。列表与模块筛选项同源返回(一次请求给全)。
|
|
|
|
|
|
|
2026-10-19 06:44:15 -04:00
|
|
|
|
待办与预警为**查询时实时派生**(不落库、不引调度器,见计划决策);
|
2026-11-02 22:20:55 -05:00
|
|
|
|
同步记录、读取开关和自动采集开关分别返回,不用当前开关覆盖历史采集结果。
|
2026-10-19 06:44:15 -04:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from datetime import date, datetime
|
|
|
|
|
|
from decimal import Decimal
|
|
|
|
|
|
|
2026-10-23 04:15:09 -04:00
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
|
|
|
|
|
|
|
|
from app.core.response import PageQuery
|
2026-10-19 06:44:15 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TrendPoint(BaseModel):
|
|
|
|
|
|
"""近 6 个月趋势点(绩效按台账归属月,回款按到账月)。"""
|
|
|
|
|
|
|
|
|
|
|
|
month: str
|
|
|
|
|
|
performance_total: Decimal | None = None
|
|
|
|
|
|
receipt_total: Decimal | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SettlementBrief(BaseModel):
|
|
|
|
|
|
"""当月月结状态(工作台总览角标)。"""
|
|
|
|
|
|
|
|
|
|
|
|
exists: bool = False
|
|
|
|
|
|
status: int | None = Field(default=None, description="1草稿 2待审批 3已审定 4已驳回")
|
|
|
|
|
|
total_performance: Decimal | None = None
|
|
|
|
|
|
total_payable: Decimal | None = None
|
|
|
|
|
|
locked_at: datetime | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OverviewBlock(BaseModel):
|
|
|
|
|
|
"""经营绩效总览(FR-033):口径与台账/月结一致。"""
|
|
|
|
|
|
|
|
|
|
|
|
month: str
|
|
|
|
|
|
performance_total: Decimal | None = Field(default=None, description="当月单笔绩效合计")
|
|
|
|
|
|
payable_total: Decimal | None = Field(default=None, description="当月应付绩效合计")
|
|
|
|
|
|
person_count: int = Field(default=0, description="当月有台账的人员数(拆行去重)")
|
|
|
|
|
|
team_count: int = Field(default=0, description="在职营销经理数(团队数)")
|
|
|
|
|
|
settlement: SettlementBrief
|
|
|
|
|
|
trend: list[TrendPoint] = Field(default_factory=list, description="近 6 个月")
|
|
|
|
|
|
note: str = Field(default="", description="口径说明(页面展示)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ApprovalTodoItem(BaseModel):
|
|
|
|
|
|
"""待审批条目。"""
|
|
|
|
|
|
|
|
|
|
|
|
order_id: int
|
|
|
|
|
|
order_no: str
|
|
|
|
|
|
order_type: int
|
|
|
|
|
|
submitter_name: str | None = None
|
|
|
|
|
|
create_time: datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ReceiptTodoItem(BaseModel):
|
|
|
|
|
|
"""回款未指定发货批次条目。"""
|
|
|
|
|
|
|
|
|
|
|
|
receipt_id: int
|
|
|
|
|
|
receipt_batch_no: str
|
|
|
|
|
|
business_id: int
|
|
|
|
|
|
business_no: str | None = None
|
|
|
|
|
|
amount: Decimal
|
|
|
|
|
|
receipt_date: date
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TodoBlock(BaseModel):
|
|
|
|
|
|
"""待办事项(FR-034,澄清 #11 起步范围)。"""
|
|
|
|
|
|
|
|
|
|
|
|
pending_approval_total: int = 0
|
|
|
|
|
|
pending_approvals: list[ApprovalTodoItem] = Field(default_factory=list)
|
|
|
|
|
|
unassigned_receipt_total: int = 0
|
|
|
|
|
|
unassigned_receipts: list[ReceiptTodoItem] = Field(default_factory=list)
|
2026-11-02 22:20:55 -05:00
|
|
|
|
sync_available: bool = Field(default=False, description="同步记录是否可查询")
|
2026-10-19 06:44:15 -04:00
|
|
|
|
sync_failure_total: int = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WarningItem(BaseModel):
|
|
|
|
|
|
"""风险预警条目(实时派生,不改变业务数据)。"""
|
|
|
|
|
|
|
|
|
|
|
|
code: str = Field(description="规则编码:receipt_overdue / approval_timeout")
|
|
|
|
|
|
level: str = Field(description="级别:warning / danger")
|
|
|
|
|
|
object_type: str
|
|
|
|
|
|
object_id: int
|
|
|
|
|
|
title: str
|
|
|
|
|
|
detail: str
|
|
|
|
|
|
suggest: str
|
|
|
|
|
|
link: str | None = Field(default=None, description="前端跳转路径")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WarningBlock(BaseModel):
|
|
|
|
|
|
"""风险预警(FR-035,首批两条可算规则;同步规则待 ⑥)。"""
|
|
|
|
|
|
|
|
|
|
|
|
enabled: bool = True
|
|
|
|
|
|
receipt_overdue_days: int = 90
|
|
|
|
|
|
approval_timeout_days: int = 3
|
|
|
|
|
|
items: list[WarningItem] = Field(default_factory=list)
|
2026-10-19 21:45:03 -04:00
|
|
|
|
total: int = Field(default=0, description="全部规则命中总数(items 仅为展示上限内的子集)")
|
2026-10-19 06:44:15 -04:00
|
|
|
|
receipt_overdue_total: int = 0
|
|
|
|
|
|
approval_timeout_total: int = 0
|
|
|
|
|
|
sync_available: bool = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ActivityItem(BaseModel):
|
|
|
|
|
|
"""最近动态条目(来源 operation_logs,只读聚合)。"""
|
|
|
|
|
|
|
|
|
|
|
|
id: int
|
|
|
|
|
|
module: str
|
|
|
|
|
|
action: str
|
|
|
|
|
|
summary: str
|
|
|
|
|
|
username: str | None = None
|
|
|
|
|
|
success: int
|
|
|
|
|
|
create_time: datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-10-23 04:15:09 -04:00
|
|
|
|
class ActivityQuery(PageQuery):
|
|
|
|
|
|
"""最近动态查询条件:分页 + 模块筛选。
|
|
|
|
|
|
|
|
|
|
|
|
只支持模块筛选(工作台只需要这一个维度);其余筛选项见日志查询页。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
module: str | None = Field(default=None, max_length=64, description="模块,精确匹配")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ActivityPageOut(BaseModel):
|
|
|
|
|
|
"""最近动态分页结果:当前页 + 总数 + 模块筛选项。
|
|
|
|
|
|
|
|
|
|
|
|
分页字段名沿用全站规范(list/total/pageNum/pageSize),与
|
|
|
|
|
|
`PageResult` 同形;额外带 `modules`(全量模块名),
|
|
|
|
|
|
供前端模块下拉使用 —— 不随当前页变化,避免翻页后选项增减。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
model_config = ConfigDict(populate_by_name=True)
|
|
|
|
|
|
|
|
|
|
|
|
records: list[ActivityItem] = Field(
|
|
|
|
|
|
default_factory=list, alias="list", serialization_alias="list", description="当前页数据"
|
|
|
|
|
|
)
|
|
|
|
|
|
total: int = Field(default=0, description="总记录数")
|
|
|
|
|
|
page_num: int = Field(
|
|
|
|
|
|
default=1, alias="pageNum", serialization_alias="pageNum", description="当前页码"
|
|
|
|
|
|
)
|
|
|
|
|
|
page_size: int = Field(
|
|
|
|
|
|
default=10, alias="pageSize", serialization_alias="pageSize", description="每页条数"
|
|
|
|
|
|
)
|
|
|
|
|
|
modules: list[str] = Field(default_factory=list, description="全部模块名(筛选项)")
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-11-02 22:20:55 -05:00
|
|
|
|
class SyncBlock(BaseModel):
|
|
|
|
|
|
"""源采集历史与当前运行配置,仅公开汇总状态。"""
|
|
|
|
|
|
|
|
|
|
|
|
available: bool = False
|
|
|
|
|
|
enabled: bool = False
|
|
|
|
|
|
automation_enabled: bool = False
|
|
|
|
|
|
latest_status: str | None = None
|
|
|
|
|
|
last_success_at: datetime | None = None
|
|
|
|
|
|
historical_failure_total: int = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-10-19 06:44:15 -04:00
|
|
|
|
class DashboardOut(BaseModel):
|
2026-10-23 04:15:09 -04:00
|
|
|
|
"""工作台三区块一次性响应(最近动态走独立分页端点)。"""
|
2026-10-19 06:44:15 -04:00
|
|
|
|
|
|
|
|
|
|
month: str
|
|
|
|
|
|
overview: OverviewBlock
|
|
|
|
|
|
todos: TodoBlock
|
|
|
|
|
|
warnings: WarningBlock
|
2026-11-02 22:20:55 -05:00
|
|
|
|
sync: SyncBlock = Field(default_factory=SyncBlock)
|