|
|
@@ -21,36 +21,184 @@ class CustomerOptionsBody(BaseModel):
|
|
|
limit: int = Field(default=50, ge=1, le=200, description="返回条数上限")
|
|
|
|
|
|
|
|
|
-# ── 客户列表 ──────────────────────────────────────────────────
|
|
|
+# ── 客户列头筛选字段(客户列表 / 公海共用)──────────────────────
|
|
|
+
|
|
|
|
|
|
+class CustomerColumnFilterMixin(BaseModel):
|
|
|
+ """列头漏斗参数:前端 camelCase ↔ 后端 snake_case,供 build_customer_column_filters。"""
|
|
|
|
|
|
-class CustomerListBody(BaseModel):
|
|
|
model_config = ConfigDict(populate_by_name=True)
|
|
|
|
|
|
- page: int = Field(default=1, ge=1, description="页码")
|
|
|
- pageSize: int = Field(default=10, ge=1, le=500, description="每页条数")
|
|
|
- keyword: Optional[str] = Field(default=None, description="搜索关键词(客户名称/编号/手机号)")
|
|
|
- search_keyword: Optional[str] = Field(
|
|
|
- default=None, alias="searchKeyword", description="快速搜索关键词(与 keyword 等价)"
|
|
|
- )
|
|
|
+ # 文本
|
|
|
customer_name: Optional[str] = Field(default=None, alias="customerName", description="客户名称")
|
|
|
customer_no: Optional[str] = Field(default=None, alias="customerNo", description="客户编号")
|
|
|
+ short_name: Optional[str] = Field(default=None, alias="shortName", description="客户简称")
|
|
|
+ mobile: Optional[str] = Field(default=None, description="手机号")
|
|
|
+ telephone: Optional[str] = Field(default=None, description="固定电话")
|
|
|
+ email: Optional[str] = Field(default=None, description="邮箱")
|
|
|
+ website: Optional[str] = Field(default=None, description="客户网站")
|
|
|
+ address: Optional[str] = Field(default=None, description="详细地址")
|
|
|
+ province: Optional[str] = Field(default=None, description="省")
|
|
|
+ city: Optional[str] = Field(default=None, description="市")
|
|
|
+ district: Optional[str] = Field(default=None, description="区")
|
|
|
+ owner_user_name: Optional[str] = Field(default=None, alias="ownerUserName", description="负责人姓名")
|
|
|
+ remark: Optional[str] = Field(default=None, description="备注")
|
|
|
+
|
|
|
+ # 枚举单值(兼容旧入参)+ 多选 *In
|
|
|
customer_type: Optional[str] = Field(default=None, alias="customerType", description="客户类型")
|
|
|
+ customer_type_in: Optional[str] = Field(default=None, alias="customerTypeIn", description="客户类型多选(逗号)")
|
|
|
level: Optional[str] = Field(default=None, description="客户级别")
|
|
|
+ level_in: Optional[str] = Field(default=None, alias="levelIn", description="客户级别多选(逗号)")
|
|
|
source: Optional[str] = Field(default=None, description="客户来源")
|
|
|
+ source_in: Optional[str] = Field(default=None, alias="sourceIn", description="客户来源多选(逗号)")
|
|
|
industry: Optional[str] = Field(default=None, description="所属行业")
|
|
|
+ industry_in: Optional[str] = Field(default=None, alias="industryIn", description="所属行业多选(逗号)")
|
|
|
park_area: Optional[str] = Field(default=None, alias="parkArea", description="所在园区")
|
|
|
+ park_area_in: Optional[str] = Field(default=None, alias="parkAreaIn", description="所在园区多选(逗号)")
|
|
|
+ scale: Optional[str] = Field(default=None, description="企业规模")
|
|
|
+ scale_in: Optional[str] = Field(default=None, alias="scaleIn", description="企业规模多选(逗号)")
|
|
|
status: Optional[str] = Field(default=None, description="客户状态")
|
|
|
+ status_in: Optional[str] = Field(default=None, alias="statusIn", description="客户状态多选(逗号)")
|
|
|
deal_status: Optional[str] = Field(default=None, alias="dealStatus", description="成交状态")
|
|
|
+ deal_status_in: Optional[str] = Field(default=None, alias="dealStatusIn", description="成交状态多选(逗号)")
|
|
|
+
|
|
|
owner_user_id: Optional[str] = Field(default=None, alias="ownerUserId", description="负责人ID")
|
|
|
+
|
|
|
+ # 创建时间(begin/end 与 createTimeFrom/To 并存;range 由 CustomerListBody 解析)
|
|
|
+ begin_time: Optional[str] = Field(default=None, alias="beginTime", description="创建时间起")
|
|
|
+ end_time: Optional[str] = Field(default=None, alias="endTime", description="创建时间止")
|
|
|
+ create_time_from: Optional[str] = Field(default=None, alias="createTimeFrom", description="创建时间起")
|
|
|
+ create_time_to: Optional[str] = Field(default=None, alias="createTimeTo", description="创建时间止")
|
|
|
+
|
|
|
+ # 其它日期 From/To
|
|
|
+ last_time_from: Optional[str] = Field(default=None, alias="lastTimeFrom")
|
|
|
+ last_time_to: Optional[str] = Field(default=None, alias="lastTimeTo")
|
|
|
+ next_time_from: Optional[str] = Field(default=None, alias="nextTimeFrom")
|
|
|
+ next_time_to: Optional[str] = Field(default=None, alias="nextTimeTo")
|
|
|
+ deal_time_from: Optional[str] = Field(default=None, alias="dealTimeFrom")
|
|
|
+ deal_time_to: Optional[str] = Field(default=None, alias="dealTimeTo")
|
|
|
+ into_pool_time_from: Optional[str] = Field(default=None, alias="intoPoolTimeFrom")
|
|
|
+ into_pool_time_to: Optional[str] = Field(default=None, alias="intoPoolTimeTo")
|
|
|
+ claim_time_from: Optional[str] = Field(default=None, alias="claimTimeFrom")
|
|
|
+ claim_time_to: Optional[str] = Field(default=None, alias="claimTimeTo")
|
|
|
+ protect_time_from: Optional[str] = Field(default=None, alias="protectTimeFrom")
|
|
|
+ protect_time_to: Optional[str] = Field(default=None, alias="protectTimeTo")
|
|
|
+ update_time_from: Optional[str] = Field(default=None, alias="updateTimeFrom")
|
|
|
+ update_time_to: Optional[str] = Field(default=None, alias="updateTimeTo")
|
|
|
+
|
|
|
+ # 数字 Op / Min / Max / 单值
|
|
|
+ deal_amount_op: Optional[str] = Field(default=None, alias="dealAmountOp")
|
|
|
+ deal_amount: Optional[str] = Field(default=None, alias="dealAmount")
|
|
|
+ deal_amount_min: Optional[str] = Field(default=None, alias="dealAmountMin")
|
|
|
+ deal_amount_max: Optional[str] = Field(default=None, alias="dealAmountMax")
|
|
|
+ follow_count_op: Optional[str] = Field(default=None, alias="followCountOp")
|
|
|
+ follow_count: Optional[str] = Field(default=None, alias="followCount")
|
|
|
+ follow_count_min: Optional[str] = Field(default=None, alias="followCountMin")
|
|
|
+ follow_count_max: Optional[str] = Field(default=None, alias="followCountMax")
|
|
|
+ contact_count_op: Optional[str] = Field(default=None, alias="contactCountOp")
|
|
|
+ contact_count: Optional[str] = Field(default=None, alias="contactCount")
|
|
|
+ contact_count_min: Optional[str] = Field(default=None, alias="contactCountMin")
|
|
|
+ contact_count_max: Optional[str] = Field(default=None, alias="contactCountMax")
|
|
|
+ opportunity_count_op: Optional[str] = Field(default=None, alias="opportunityCountOp")
|
|
|
+ opportunity_count: Optional[str] = Field(default=None, alias="opportunityCount")
|
|
|
+ opportunity_count_min: Optional[str] = Field(default=None, alias="opportunityCountMin")
|
|
|
+ opportunity_count_max: Optional[str] = Field(default=None, alias="opportunityCountMax")
|
|
|
+ contract_count_op: Optional[str] = Field(default=None, alias="contractCountOp")
|
|
|
+ contract_count: Optional[str] = Field(default=None, alias="contractCount")
|
|
|
+ contract_count_min: Optional[str] = Field(default=None, alias="contractCountMin")
|
|
|
+ contract_count_max: Optional[str] = Field(default=None, alias="contractCountMax")
|
|
|
+
|
|
|
+ def column_filter_kwargs(self) -> dict:
|
|
|
+ """snake_case 列筛参数,供 CRUD / build_customer_column_filters。"""
|
|
|
+ return {
|
|
|
+ "customer_name": self.customer_name,
|
|
|
+ "customer_no": self.customer_no,
|
|
|
+ "short_name": self.short_name,
|
|
|
+ "mobile": self.mobile,
|
|
|
+ "telephone": self.telephone,
|
|
|
+ "email": self.email,
|
|
|
+ "website": self.website,
|
|
|
+ "address": self.address,
|
|
|
+ "province": self.province,
|
|
|
+ "city": self.city,
|
|
|
+ "district": self.district,
|
|
|
+ "owner_user_name": self.owner_user_name,
|
|
|
+ "remark": self.remark,
|
|
|
+ "customer_type": self.customer_type,
|
|
|
+ "customer_type_in": self.customer_type_in,
|
|
|
+ "level": self.level,
|
|
|
+ "level_in": self.level_in,
|
|
|
+ "source": self.source,
|
|
|
+ "source_in": self.source_in,
|
|
|
+ "industry": self.industry,
|
|
|
+ "industry_in": self.industry_in,
|
|
|
+ "park_area": self.park_area,
|
|
|
+ "park_area_in": self.park_area_in,
|
|
|
+ "scale": self.scale,
|
|
|
+ "scale_in": self.scale_in,
|
|
|
+ "status": self.status,
|
|
|
+ "status_in": self.status_in,
|
|
|
+ "deal_status": self.deal_status,
|
|
|
+ "deal_status_in": self.deal_status_in,
|
|
|
+ "owner_user_id": self.owner_user_id,
|
|
|
+ "begin_time": self.begin_time,
|
|
|
+ "end_time": self.end_time,
|
|
|
+ "create_time_from": self.create_time_from,
|
|
|
+ "create_time_to": self.create_time_to,
|
|
|
+ "last_time_from": self.last_time_from,
|
|
|
+ "last_time_to": self.last_time_to,
|
|
|
+ "next_time_from": self.next_time_from,
|
|
|
+ "next_time_to": self.next_time_to,
|
|
|
+ "deal_time_from": self.deal_time_from,
|
|
|
+ "deal_time_to": self.deal_time_to,
|
|
|
+ "into_pool_time_from": self.into_pool_time_from,
|
|
|
+ "into_pool_time_to": self.into_pool_time_to,
|
|
|
+ "claim_time_from": self.claim_time_from,
|
|
|
+ "claim_time_to": self.claim_time_to,
|
|
|
+ "protect_time_from": self.protect_time_from,
|
|
|
+ "protect_time_to": self.protect_time_to,
|
|
|
+ "update_time_from": self.update_time_from,
|
|
|
+ "update_time_to": self.update_time_to,
|
|
|
+ "deal_amount_op": self.deal_amount_op,
|
|
|
+ "deal_amount": self.deal_amount,
|
|
|
+ "deal_amount_min": self.deal_amount_min,
|
|
|
+ "deal_amount_max": self.deal_amount_max,
|
|
|
+ "follow_count_op": self.follow_count_op,
|
|
|
+ "follow_count": self.follow_count,
|
|
|
+ "follow_count_min": self.follow_count_min,
|
|
|
+ "follow_count_max": self.follow_count_max,
|
|
|
+ "contact_count_op": self.contact_count_op,
|
|
|
+ "contact_count": self.contact_count,
|
|
|
+ "contact_count_min": self.contact_count_min,
|
|
|
+ "contact_count_max": self.contact_count_max,
|
|
|
+ "opportunity_count_op": self.opportunity_count_op,
|
|
|
+ "opportunity_count": self.opportunity_count,
|
|
|
+ "opportunity_count_min": self.opportunity_count_min,
|
|
|
+ "opportunity_count_max": self.opportunity_count_max,
|
|
|
+ "contract_count_op": self.contract_count_op,
|
|
|
+ "contract_count": self.contract_count,
|
|
|
+ "contract_count_min": self.contract_count_min,
|
|
|
+ "contract_count_max": self.contract_count_max,
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+# ── 客户列表 ──────────────────────────────────────────────────
|
|
|
+
|
|
|
+
|
|
|
+class CustomerListBody(CustomerColumnFilterMixin):
|
|
|
+ model_config = ConfigDict(populate_by_name=True)
|
|
|
+
|
|
|
+ page: int = Field(default=1, ge=1, description="页码")
|
|
|
+ pageSize: int = Field(default=10, ge=1, le=500, description="每页条数")
|
|
|
+ keyword: Optional[str] = Field(default=None, description="搜索关键词(客户名称/编号/手机号)")
|
|
|
+ search_keyword: Optional[str] = Field(
|
|
|
+ default=None, alias="searchKeyword", description="快速搜索关键词(与 keyword 等价)"
|
|
|
+ )
|
|
|
view_type: Optional[str] = Field(default=None, alias="viewType", description="视图类型(mine/all/toFollow/key)")
|
|
|
view_mode: Optional[str] = Field(default=None, alias="viewMode", description="视图模式(mine/all/toFollow/key)")
|
|
|
create_time_range: Optional[List[str]] = Field(
|
|
|
default=None, alias="createTimeRange", description="创建时间范围 [start, end]"
|
|
|
)
|
|
|
- begin_time: Optional[str] = Field(default=None, alias="beginTime", description="创建时间起")
|
|
|
- end_time: Optional[str] = Field(default=None, alias="endTime", description="创建时间止")
|
|
|
-
|
|
|
|
|
|
ids: Optional[List[str]] = Field(default=None, description="导出指定ID列表")
|
|
|
columns: Optional[List[str]] = Field(default=None, description="导出列(前端 visibleColumns)")
|
|
|
@@ -58,31 +206,19 @@ class CustomerListBody(BaseModel):
|
|
|
def to_list_kwargs(self, *, view_mode_override: Optional[str] = None) -> dict:
|
|
|
"""解析列表查询参数,供 API 与 CRUD 共用。"""
|
|
|
keyword = (self.keyword or self.search_keyword or "").strip() or None
|
|
|
- create_time_start = None
|
|
|
- create_time_end = None
|
|
|
- if self.create_time_range and len(self.create_time_range) == 2:
|
|
|
- create_time_start = self.create_time_range[0]
|
|
|
- create_time_end = self.create_time_range[1]
|
|
|
- else:
|
|
|
- create_time_start = self.begin_time
|
|
|
- create_time_end = self.end_time
|
|
|
view_mode = view_mode_override or self.view_mode or self.view_type or "all"
|
|
|
- return {
|
|
|
+ kwargs = {
|
|
|
"keyword": keyword,
|
|
|
"view_mode": view_mode,
|
|
|
- "customer_name": self.customer_name,
|
|
|
- "customer_no": self.customer_no,
|
|
|
- "customer_type": self.customer_type,
|
|
|
- "level": self.level,
|
|
|
- "source": self.source,
|
|
|
- "industry": self.industry,
|
|
|
- "park_area": self.park_area,
|
|
|
- "status": self.status,
|
|
|
- "deal_status": self.deal_status,
|
|
|
- "owner_user_id": self.owner_user_id,
|
|
|
- "create_time_start": create_time_start,
|
|
|
- "create_time_end": create_time_end,
|
|
|
+ **self.column_filter_kwargs(),
|
|
|
}
|
|
|
+ # createTimeRange 优先于 begin/end / createTimeFrom/To
|
|
|
+ if self.create_time_range and len(self.create_time_range) == 2:
|
|
|
+ kwargs["begin_time"] = self.create_time_range[0]
|
|
|
+ kwargs["end_time"] = self.create_time_range[1]
|
|
|
+ kwargs["create_time_from"] = None
|
|
|
+ kwargs["create_time_to"] = None
|
|
|
+ return kwargs
|
|
|
|
|
|
|
|
|
# ── 客户新增 ──────────────────────────────────────────────────
|