bgufix
This commit is contained in:
commit
7f25e71ff3
94
README.md
Normal file
94
README.md
Normal file
@ -0,0 +1,94 @@
|
||||
# 客户管理模块 (Customer Management)
|
||||
|
||||
## 模块概述
|
||||
客户管理模块提供完整的客户档案管理、交接管理和公海池功能,支持360度客户视图和自动化工作流程。
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 2.2 客户管理模块
|
||||
|
||||
#### 2.2.1 客户档案管理
|
||||
- **基础信息**:客户类型(个人/企业)、联系方式、所属行业、分级标签(重要/普通/潜在)
|
||||
- **360度视图**:整合商机记录、合同历史、服务工单、回款情况,参考纷享销客客户档案体系
|
||||
- **数据校验**:手机号/企业税号唯一性校验,避免重复建档
|
||||
|
||||
#### 2.2.2 客户交接管理
|
||||
- **触发场景**:人员离职/岗位调整自动触发交接流程
|
||||
- **交接内容**:含客户基本信息、未结商机、历史合同、未解决问题等核心数据
|
||||
- **交接流程**:
|
||||
a. 准备阶段:系统自动生成交接清单,原负责人补充完善
|
||||
b. 审核阶段:负责人审核清单完整性
|
||||
c. 确认阶段:接手人确认接收,系统自动更新客户负责人
|
||||
- **客户通知**:交接完成后自动发送短信/邮件告知客户对接人变更
|
||||
|
||||
#### 2.2.3 客户公海池
|
||||
- **规则配置**:自动回收N天未跟进的客户
|
||||
- **分配机制**:手动分配或销售自主领取
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
### customers (客户档案表)
|
||||
- `id`: 客户ID (主键)
|
||||
- `customer_name`: 客户名称 (必填)
|
||||
- `customer_type`: 客户类型 (individual=个人, enterprise=企业)
|
||||
- `phone`: 手机号 (唯一性约束)
|
||||
- `tax_id`: 企业税号 (唯一性约束,仅企业客户)
|
||||
- `industry`: 所属行业
|
||||
- `customer_level`: 分级标签 (important=重要, normal=普通, potential=潜在)
|
||||
- `owner_id`: 负责人ID
|
||||
- `last_follow_up`: 最后跟进时间
|
||||
- `status`: 状态 (active=活跃, inactive=非活跃, in_pool=公海)
|
||||
|
||||
### customer_handover (客户交接表)
|
||||
- `id`: 交接ID (主键)
|
||||
- `customer_id`: 客户ID
|
||||
- `from_owner_id`: 原负责人ID
|
||||
- `to_owner_id`: 新负责人ID
|
||||
- `handover_reason`: 交接原因 (resignation=离职, position_change=岗位调整)
|
||||
- `current_stage`: 当前阶段 (preparation/review/confirmation/completed)
|
||||
|
||||
### customer_handover_items (交接项目明细表)
|
||||
- `id`: 项目ID (主键)
|
||||
- `handover_id`: 交接ID
|
||||
- `item_type`: 项目类型 (basic_info/opportunities/contracts/service_tickets/payment_issues)
|
||||
- `item_description`: 项目描述
|
||||
- `is_completed`: 是否完成
|
||||
|
||||
### customer_pool (客户公海池表)
|
||||
- `id`: 公海记录ID (主键)
|
||||
- `customer_id`: 客户ID
|
||||
- `original_owner_id`: 原负责人ID
|
||||
- `recycle_reason`: 回收原因 (inactive_days=未跟进超限, manual=手动)
|
||||
- `pool_status`: 公海状态 (available/assigned/claimed)
|
||||
|
||||
## API接口
|
||||
|
||||
### 客户管理
|
||||
- `create_customer()`: 创建客户档案(包含唯一性校验)
|
||||
- `get_customer_360_view()`: 获取客户360度视图
|
||||
|
||||
### 交接管理
|
||||
- `initiate_handover()`: 发起交接流程(自动生成清单)
|
||||
- `complete_handover_preparation()`: 完成准备阶段
|
||||
- `approve_handover()`: 审核交接清单
|
||||
- `confirm_handover()`: 确认接收(自动更新负责人并发送通知)
|
||||
|
||||
### 公海管理
|
||||
- `recycle_to_pool()`: 回收客户到公海池
|
||||
- `claim_from_pool()`: 从公海池认领客户
|
||||
|
||||
## 前端界面
|
||||
- 客户档案CRUD界面
|
||||
- 客户交接管理界面
|
||||
- 客户公海池界面
|
||||
- 客户360度视图查询面板
|
||||
|
||||
## 安装部署
|
||||
1. 将模块目录复制到 `~/repos/` 目录下
|
||||
2. 运行主应用的 `build.sh` 脚本,自动处理数据库表创建和前端资源链接
|
||||
3. 模块将自动集成到系统中
|
||||
|
||||
## 依赖要求
|
||||
- ahserver >= 1.0.0
|
||||
- sqlor-database-module >= 1.0.0
|
||||
- bricks-framework >= 1.0.0
|
||||
0
customer_management/__init__.py
Normal file
0
customer_management/__init__.py
Normal file
453
customer_management/core.py
Normal file
453
customer_management/core.py
Normal file
@ -0,0 +1,453 @@
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Dict, Optional
|
||||
import uuid
|
||||
import re
|
||||
|
||||
from ahserver.serverenv import ServerEnv
|
||||
from appPublic.worker import awaitify
|
||||
from sqlor.dbp import DBP
|
||||
|
||||
|
||||
async def create_customer(
|
||||
customer_name: str,
|
||||
customer_type: str,
|
||||
phone: str = None,
|
||||
email: str = None,
|
||||
tax_id: str = None,
|
||||
industry: str = None,
|
||||
customer_level: str = "potential",
|
||||
address: str = None,
|
||||
owner_id: str = None,
|
||||
region: str = None
|
||||
) -> Dict:
|
||||
"""创建客户档案"""
|
||||
dbp = DBP()
|
||||
customer_id = str(uuid.uuid4()).replace('-', '')
|
||||
|
||||
# 数据校验
|
||||
await validate_customer_data(dbp, phone, tax_id, customer_type)
|
||||
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
customer_data = {
|
||||
"id": customer_id,
|
||||
"customer_name": customer_name,
|
||||
"customer_type": customer_type,
|
||||
"phone": phone,
|
||||
"email": email,
|
||||
"tax_id": tax_id,
|
||||
"industry": industry,
|
||||
"customer_level": customer_level,
|
||||
"address": address,
|
||||
"owner_id": owner_id or get_current_user_id(),
|
||||
"region": region,
|
||||
"last_follow_up": now,
|
||||
"created_at": now,
|
||||
"updated_at": now,
|
||||
"status": "active"
|
||||
}
|
||||
|
||||
await dbp.insert("customers", customer_data)
|
||||
return customer_data
|
||||
|
||||
|
||||
async def validate_customer_data(dbp, phone: str, tax_id: str, customer_type: str):
|
||||
"""验证客户数据唯一性"""
|
||||
# 手机号唯一性校验
|
||||
if phone:
|
||||
existing_phone = await dbp.select_one("customers", {"phone": phone})
|
||||
if existing_phone:
|
||||
raise ValueError(f"手机号 {phone} 已存在,不能重复建档")
|
||||
|
||||
# 企业税号唯一性校验(仅对企业客户)
|
||||
if customer_type == "enterprise" and tax_id:
|
||||
existing_tax = await dbp.select_one("customers", {"tax_id": tax_id})
|
||||
if existing_tax:
|
||||
raise ValueError(f"企业税号 {tax_id} 已存在,不能重复建档")
|
||||
|
||||
|
||||
async def initiate_handover(
|
||||
customer_id: str,
|
||||
to_owner_id: str,
|
||||
handover_reason: str,
|
||||
reviewer_id: str = None
|
||||
) -> Dict:
|
||||
"""发起客户交接流程"""
|
||||
dbp = DBP()
|
||||
|
||||
# 获取客户信息
|
||||
customer = await dbp.select_one("customers", {"id": customer_id})
|
||||
if not customer:
|
||||
raise ValueError("客户不存在")
|
||||
|
||||
if customer["status"] != "active":
|
||||
raise ValueError("只能交接活跃状态的客户")
|
||||
|
||||
# 创建交接记录
|
||||
handover_id = str(uuid.uuid4()).replace('-', '')
|
||||
handover_data = {
|
||||
"id": handover_id,
|
||||
"customer_id": customer_id,
|
||||
"from_owner_id": customer["owner_id"],
|
||||
"to_owner_id": to_owner_id,
|
||||
"handover_reason": handover_reason,
|
||||
"current_stage": "preparation",
|
||||
"reviewer_id": reviewer_id,
|
||||
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
|
||||
await dbp.insert("customer_handover", handover_data)
|
||||
|
||||
# 自动生成交接清单
|
||||
await generate_handover_items(dbp, handover_id, customer_id)
|
||||
|
||||
return handover_data
|
||||
|
||||
|
||||
async def generate_handover_items(dbp, handover_id: str, customer_id: str):
|
||||
"""自动生成交接清单"""
|
||||
items = []
|
||||
|
||||
# 基本信息
|
||||
items.append({
|
||||
"id": str(uuid.uuid4()).replace('-', ''),
|
||||
"handover_id": handover_id,
|
||||
"item_type": "basic_info",
|
||||
"item_description": "客户基本信息和联系记录",
|
||||
"is_completed": "0",
|
||||
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
})
|
||||
|
||||
# 未结商机
|
||||
opportunities = await dbp.query(
|
||||
"SELECT id, customer_name, estimated_amount, current_stage FROM opportunities WHERE customer_name = (SELECT customer_name FROM customers WHERE id = %(customer_id)s) AND status = 'active'",
|
||||
{"customer_id": customer_id}
|
||||
)
|
||||
if opportunities:
|
||||
for opp in opportunities:
|
||||
items.append({
|
||||
"id": str(uuid.uuid4()).replace('-', ''),
|
||||
"handover_id": handover_id,
|
||||
"item_type": "opportunities",
|
||||
"item_id": opp["id"],
|
||||
"item_description": f"商机: {opp['customer_name']} - 预估金额: {opp['estimated_amount']}, 阶段: {opp['current_stage']}",
|
||||
"is_completed": "0",
|
||||
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
})
|
||||
|
||||
# 历史合同(假设合同管理模块存在)
|
||||
contracts = await dbp.query(
|
||||
"SELECT id, contract_no, amount, status FROM contracts WHERE customer_id = %(customer_id)s",
|
||||
{"customer_id": customer_id}
|
||||
)
|
||||
if contracts:
|
||||
for contract in contracts:
|
||||
items.append({
|
||||
"id": str(uuid.uuid4()).replace('-', ''),
|
||||
"handover_id": handover_id,
|
||||
"item_type": "contracts",
|
||||
"item_id": contract["id"],
|
||||
"item_description": f"合同: {contract['contract_no']} - 金额: {contract['amount']}, 状态: {contract['status']}",
|
||||
"is_completed": "0",
|
||||
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
})
|
||||
|
||||
# 服务工单(假设服务模块存在)
|
||||
service_tickets = await dbp.query(
|
||||
"SELECT id, ticket_no, subject, status FROM service_tickets WHERE customer_id = %(customer_id)s AND status != 'closed'",
|
||||
{"customer_id": customer_id}
|
||||
)
|
||||
if service_tickets:
|
||||
for ticket in service_tickets:
|
||||
items.append({
|
||||
"id": str(uuid.uuid4()).replace('-', ''),
|
||||
"handover_id": handover_id,
|
||||
"item_type": "service_tickets",
|
||||
"item_id": ticket["id"],
|
||||
"item_description": f"服务工单: {ticket['ticket_no']} - 主题: {ticket['subject']}, 状态: {ticket['status']}",
|
||||
"is_completed": "0",
|
||||
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
})
|
||||
|
||||
# 未解决回款问题
|
||||
payment_issues = await dbp.query(
|
||||
"SELECT id, invoice_no, amount, due_date FROM payment_records WHERE customer_id = %(customer_id)s AND status = 'overdue'",
|
||||
{"customer_id": customer_id}
|
||||
)
|
||||
if payment_issues:
|
||||
for issue in payment_issues:
|
||||
items.append({
|
||||
"id": str(uuid.uuid4()).replace('-', ''),
|
||||
"handover_id": handover_id,
|
||||
"item_type": "payment_issues",
|
||||
"item_id": issue["id"],
|
||||
"item_description": f"回款问题: 发票 {issue['invoice_no']} - 金额: {issue['amount']}, 到期日: {issue['due_date']}",
|
||||
"is_completed": "0",
|
||||
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
})
|
||||
|
||||
# 批量插入交接项目
|
||||
for item in items:
|
||||
await dbp.insert("customer_handover_items", item)
|
||||
|
||||
|
||||
async def complete_handover_preparation(handover_id: str) -> Dict:
|
||||
"""完成交接准备阶段"""
|
||||
dbp = DBP()
|
||||
|
||||
handover = await dbp.select_one("customer_handover", {"id": handover_id})
|
||||
if not handover:
|
||||
raise ValueError("交接记录不存在")
|
||||
|
||||
if handover["current_stage"] != "preparation":
|
||||
raise ValueError("当前不在准备阶段")
|
||||
|
||||
# 更新为审核阶段
|
||||
await dbp.update(
|
||||
"customer_handover",
|
||||
{
|
||||
"current_stage": "review",
|
||||
"prepared_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
},
|
||||
{"id": handover_id}
|
||||
)
|
||||
|
||||
return {"handover_id": handover_id, "stage": "review"}
|
||||
|
||||
|
||||
async def approve_handover(handover_id: str, approver_id: str = None) -> Dict:
|
||||
"""审核交接清单"""
|
||||
dbp = DBP()
|
||||
approver_id = approver_id or get_current_user_id()
|
||||
|
||||
handover = await dbp.select_one("customer_handover", {"id": handover_id})
|
||||
if not handover:
|
||||
raise ValueError("交接记录不存在")
|
||||
|
||||
if handover["current_stage"] != "review":
|
||||
raise ValueError("当前不在审核阶段")
|
||||
|
||||
# 更新为确认阶段
|
||||
await dbp.update(
|
||||
"customer_handover",
|
||||
{
|
||||
"current_stage": "confirmation",
|
||||
"reviewed_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
},
|
||||
{"id": handover_id}
|
||||
)
|
||||
|
||||
return {"handover_id": handover_id, "stage": "confirmation"}
|
||||
|
||||
|
||||
async def confirm_handover(handover_id: str, confirm_by: str = None) -> Dict:
|
||||
"""确认接收客户"""
|
||||
dbp = DBP()
|
||||
confirm_by = confirm_by or get_current_user_id()
|
||||
|
||||
handover = await dbp.select_one("customer_handover", {"id": handover_id})
|
||||
if not handover:
|
||||
raise ValueError("交接记录不存在")
|
||||
|
||||
if handover["current_stage"] != "confirmation":
|
||||
raise ValueError("当前不在确认阶段")
|
||||
|
||||
# 更新客户负责人
|
||||
await dbp.update(
|
||||
"customers",
|
||||
{
|
||||
"owner_id": handover["to_owner_id"],
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
},
|
||||
{"id": handover["customer_id"]}
|
||||
)
|
||||
|
||||
# 完成交接流程
|
||||
await dbp.update(
|
||||
"customer_handover",
|
||||
{
|
||||
"current_stage": "completed",
|
||||
"confirmed_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"completed_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
},
|
||||
{"id": handover_id}
|
||||
)
|
||||
|
||||
# 发送客户通知(模拟)
|
||||
await send_customer_notification(handover["customer_id"], handover["to_owner_id"])
|
||||
|
||||
return {"handover_id": handover_id, "stage": "completed", "customer_id": handover["customer_id"]}
|
||||
|
||||
|
||||
async def send_customer_notification(customer_id: str, new_owner_id: str):
|
||||
"""发送客户对接人变更通知"""
|
||||
# 这里应该集成短信/邮件服务
|
||||
# 模拟实现
|
||||
dbp = DBP()
|
||||
customer = await dbp.select_one("customers", {"id": customer_id})
|
||||
new_owner = await dbp.select_one("users", {"id": new_owner_id}) # 假设用户表存在
|
||||
|
||||
notification_content = f"尊敬的{customer['customer_name']},您的客户经理已变更为{new_owner.get('name', '新经理')},联系方式:{new_owner.get('phone', '待更新')}。"
|
||||
|
||||
# 记录通知日志(实际应调用短信/邮件API)
|
||||
print(f"客户通知已发送: {notification_content}")
|
||||
|
||||
|
||||
async def recycle_to_pool(customer_id: str, inactive_days: int = None, reason: str = "inactive_days"):
|
||||
"""回收客户到公海池"""
|
||||
dbp = DBP()
|
||||
|
||||
customer = await dbp.select_one("customers", {"id": customer_id})
|
||||
if not customer:
|
||||
raise ValueError("客户不存在")
|
||||
|
||||
if customer["status"] == "in_pool":
|
||||
raise ValueError("客户已在公海池中")
|
||||
|
||||
# 创建公海记录
|
||||
pool_id = str(uuid.uuid4()).replace('-', '')
|
||||
pool_data = {
|
||||
"id": pool_id,
|
||||
"customer_id": customer_id,
|
||||
"original_owner_id": customer["owner_id"],
|
||||
"recycle_reason": reason,
|
||||
"inactive_days": inactive_days,
|
||||
"recycled_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"pool_status": "available",
|
||||
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
|
||||
await dbp.insert("customer_pool", pool_data)
|
||||
|
||||
# 更新客户状态
|
||||
await dbp.update(
|
||||
"customers",
|
||||
{
|
||||
"status": "in_pool",
|
||||
"owner_id": "", # 清空负责人
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
},
|
||||
{"id": customer_id}
|
||||
)
|
||||
|
||||
return pool_data
|
||||
|
||||
|
||||
async def claim_from_pool(pool_id: str, new_owner_id: str = None):
|
||||
"""从公海池认领客户"""
|
||||
dbp = DBP()
|
||||
new_owner_id = new_owner_id or get_current_user_id()
|
||||
|
||||
pool_record = await dbp.select_one("customer_pool", {"id": pool_id})
|
||||
if not pool_record:
|
||||
raise ValueError("公海记录不存在")
|
||||
|
||||
if pool_record["pool_status"] != "available":
|
||||
raise ValueError("该客户已被认领或分配")
|
||||
|
||||
# 更新公海记录
|
||||
await dbp.update(
|
||||
"customer_pool",
|
||||
{
|
||||
"assigned_to": new_owner_id,
|
||||
"assigned_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"pool_status": "claimed",
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
},
|
||||
{"id": pool_id}
|
||||
)
|
||||
|
||||
# 更新客户状态和负责人
|
||||
await dbp.update(
|
||||
"customers",
|
||||
{
|
||||
"status": "active",
|
||||
"owner_id": new_owner_id,
|
||||
"last_follow_up": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
},
|
||||
{"id": pool_record["customer_id"]}
|
||||
)
|
||||
|
||||
return {"customer_id": pool_record["customer_id"], "new_owner_id": new_owner_id}
|
||||
|
||||
|
||||
async def get_customer_360_view(customer_id: str) -> Dict:
|
||||
"""获取客户360度视图"""
|
||||
dbp = DBP()
|
||||
|
||||
# 客户基本信息
|
||||
customer = await dbp.select_one("customers", {"id": customer_id})
|
||||
if not customer:
|
||||
raise ValueError("客户不存在")
|
||||
|
||||
# 商机记录
|
||||
opportunities = await dbp.query(
|
||||
"SELECT id, estimated_amount, current_stage, expected_close_date, status FROM opportunities WHERE customer_name = %(customer_name)s ORDER BY created_at DESC",
|
||||
{"customer_name": customer["customer_name"]}
|
||||
)
|
||||
|
||||
# 合同历史(假设合同管理模块存在)
|
||||
contracts = await dbp.query(
|
||||
"SELECT id, contract_no, amount, start_date, end_date, status FROM contracts WHERE customer_id = %(customer_id)s ORDER BY created_at DESC",
|
||||
{"customer_id": customer_id}
|
||||
)
|
||||
|
||||
# 服务工单(假设服务模块存在)
|
||||
service_tickets = await dbp.query(
|
||||
"SELECT id, ticket_no, subject, priority, status, created_at FROM service_tickets WHERE customer_id = %(customer_id)s ORDER BY created_at DESC",
|
||||
{"customer_id": customer_id}
|
||||
)
|
||||
|
||||
# 回款情况
|
||||
payments = await dbp.query(
|
||||
"SELECT id, invoice_no, amount, paid_amount, due_date, status FROM payment_records WHERE customer_id = %(customer_id)s ORDER BY due_date DESC",
|
||||
{"customer_id": customer_id}
|
||||
)
|
||||
|
||||
return {
|
||||
"customer": customer,
|
||||
"opportunities": opportunities,
|
||||
"contracts": contracts,
|
||||
"service_tickets": service_tickets,
|
||||
"payments": payments
|
||||
}
|
||||
|
||||
|
||||
def get_current_user_id() -> str:
|
||||
"""获取当前用户ID(模拟实现)"""
|
||||
return "current_user_id"
|
||||
|
||||
|
||||
# 同步版本函数
|
||||
def sync_create_customer(*args, **kwargs):
|
||||
return create_customer(*args, **kwargs)
|
||||
|
||||
def sync_initiate_handover(*args, **kwargs):
|
||||
return initiate_handover(*args, **kwargs)
|
||||
|
||||
def sync_complete_handover_preparation(*args, **kwargs):
|
||||
return complete_handover_preparation(*args, **kwargs)
|
||||
|
||||
def sync_approve_handover(*args, **kwargs):
|
||||
return approve_handover(*args, **kwargs)
|
||||
|
||||
def sync_confirm_handover(*args, **kwargs):
|
||||
return confirm_handover(*args, **kwargs)
|
||||
|
||||
def sync_recycle_to_pool(*args, **kwargs):
|
||||
return recycle_to_pool(*args, **kwargs)
|
||||
|
||||
def sync_claim_from_pool(*args, **kwargs):
|
||||
return claim_from_pool(*args, **kwargs)
|
||||
|
||||
def sync_get_customer_360_view(*args, **kwargs):
|
||||
return get_customer_360_view(*args, **kwargs)
|
||||
24
customer_management/init.py
Normal file
24
customer_management/init.py
Normal file
@ -0,0 +1,24 @@
|
||||
from ahserver.serverenv import ServerEnv
|
||||
from appPublic.worker import awaitify
|
||||
from .core import (
|
||||
sync_create_customer,
|
||||
sync_initiate_handover,
|
||||
sync_complete_handover_preparation,
|
||||
sync_approve_handover,
|
||||
sync_confirm_handover,
|
||||
sync_recycle_to_pool,
|
||||
sync_claim_from_pool,
|
||||
sync_get_customer_360_view
|
||||
)
|
||||
|
||||
|
||||
def load_customer_management():
|
||||
env = ServerEnv()
|
||||
env.create_customer = awaitify(sync_create_customer)
|
||||
env.initiate_handover = awaitify(sync_initiate_handover)
|
||||
env.complete_handover_preparation = awaitify(sync_complete_handover_preparation)
|
||||
env.approve_handover = awaitify(sync_approve_handover)
|
||||
env.confirm_handover = awaitify(sync_confirm_handover)
|
||||
env.recycle_to_pool = awaitify(sync_recycle_to_pool)
|
||||
env.claim_from_pool = awaitify(sync_claim_from_pool)
|
||||
env.get_customer_360_view = awaitify(sync_get_customer_360_view)
|
||||
46
init/data.json
Normal file
46
init/data.json
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"appcodes": [
|
||||
{
|
||||
"id": "customer_type",
|
||||
"name": "客户类型编码",
|
||||
"hierarchy_flg": "0"
|
||||
},
|
||||
{
|
||||
"id": "customer_level",
|
||||
"name": "客户分级编码",
|
||||
"hierarchy_flg": "0"
|
||||
}
|
||||
],
|
||||
"appcodes_kv": [
|
||||
{
|
||||
"id": "customer_type_individual",
|
||||
"parentid": "customer_type",
|
||||
"k": "individual",
|
||||
"v": "个人"
|
||||
},
|
||||
{
|
||||
"id": "customer_type_enterprise",
|
||||
"parentid": "customer_type",
|
||||
"k": "enterprise",
|
||||
"v": "企业"
|
||||
},
|
||||
{
|
||||
"id": "customer_level_important",
|
||||
"parentid": "customer_level",
|
||||
"k": "important",
|
||||
"v": "重要"
|
||||
},
|
||||
{
|
||||
"id": "customer_level_normal",
|
||||
"parentid": "customer_level",
|
||||
"k": "normal",
|
||||
"v": "普通"
|
||||
},
|
||||
{
|
||||
"id": "customer_level_potential",
|
||||
"parentid": "customer_level",
|
||||
"k": "potential",
|
||||
"v": "潜在"
|
||||
}
|
||||
]
|
||||
}
|
||||
43
json/customer_pool_list.json
Normal file
43
json/customer_pool_list.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"tblname": "customer_pool",
|
||||
"alias": "customer_pool_list",
|
||||
"title": "客户公海池",
|
||||
"params": {
|
||||
"sortby": ["recycled_at desc"],
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "customer_id", "original_owner_id", "assigned_to", "created_at"],
|
||||
"alters": {
|
||||
"recycle_reason": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "inactive_days",
|
||||
"text": "未跟进天数超限"
|
||||
},
|
||||
{
|
||||
"value": "manual",
|
||||
"text": "手动回收"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pool_status": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "available",
|
||||
"text": "可领取"
|
||||
},
|
||||
{
|
||||
"value": "assigned",
|
||||
"text": "已分配"
|
||||
},
|
||||
{
|
||||
"value": "claimed",
|
||||
"text": "已认领"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
json/customers_list.json
Normal file
92
json/customers_list.json
Normal file
@ -0,0 +1,92 @@
|
||||
{
|
||||
"tblname": "customers",
|
||||
"alias": "customers_list",
|
||||
"title": "客户档案管理",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"logined_userid": "owner_id",
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "owner_id", "updated_at"],
|
||||
"alters": {
|
||||
"customer_type": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "individual",
|
||||
"text": "个人"
|
||||
},
|
||||
{
|
||||
"value": "enterprise",
|
||||
"text": "企业"
|
||||
}
|
||||
]
|
||||
},
|
||||
"customer_level": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "important",
|
||||
"text": "重要"
|
||||
},
|
||||
{
|
||||
"value": "normal",
|
||||
"text": "普通"
|
||||
},
|
||||
{
|
||||
"value": "potential",
|
||||
"text": "潜在"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "active",
|
||||
"text": "活跃"
|
||||
},
|
||||
{
|
||||
"value": "inactive",
|
||||
"text": "非活跃"
|
||||
},
|
||||
{
|
||||
"value": "in_pool",
|
||||
"text": "公海"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"binds": [
|
||||
{
|
||||
"wid": "customer_type",
|
||||
"event": "changed",
|
||||
"actiontype": "script",
|
||||
"target": "tax_id",
|
||||
"script": "// 当客户类型为个人时,隐藏税号字段\nconst customerType = this.getValue('customer_type');\nconst taxIdField = this.getWidget('tax_id');\nif (customerType === 'individual') {\n taxIdField.hide();\n} else {\n taxIdField.show();\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"subtables": [
|
||||
{
|
||||
"field": "customer_id",
|
||||
"title": "客户360度视图",
|
||||
"url": "{{entire_url(customer_360_view)}}",
|
||||
"subtable": "customers"
|
||||
},
|
||||
{
|
||||
"field": "customer_id",
|
||||
"title": "交接记录",
|
||||
"url": "{{entire_url(handover_history_list)}}",
|
||||
"subtable": "customer_handover"
|
||||
},
|
||||
{
|
||||
"field": "customer_id",
|
||||
"title": "公海记录",
|
||||
"url": "{{entire_url(pool_history_list)}}",
|
||||
"subtable": "customer_pool"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
51
json/handover_items_list.json
Normal file
51
json/handover_items_list.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"tblname": "customer_handover_items",
|
||||
"alias": "handover_items_list",
|
||||
"title": "交接项目明细",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "handover_id", "item_id", "updated_at"],
|
||||
"alters": {
|
||||
"item_type": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "basic_info",
|
||||
"text": "基本信息"
|
||||
},
|
||||
{
|
||||
"value": "opportunities",
|
||||
"text": "未结商机"
|
||||
},
|
||||
{
|
||||
"value": "contracts",
|
||||
"text": "历史合同"
|
||||
},
|
||||
{
|
||||
"value": "service_tickets",
|
||||
"text": "服务工单"
|
||||
},
|
||||
{
|
||||
"value": "payment_issues",
|
||||
"text": "回款问题"
|
||||
}
|
||||
]
|
||||
},
|
||||
"is_completed": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "1",
|
||||
"text": "已完成"
|
||||
},
|
||||
{
|
||||
"value": "0",
|
||||
"text": "未完成"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
json/handover_list.json
Normal file
55
json/handover_list.json
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"tblname": "customer_handover",
|
||||
"alias": "handover_list",
|
||||
"title": "客户交接管理",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "from_owner_id", "to_owner_id", "reviewer_id", "updated_at"],
|
||||
"alters": {
|
||||
"handover_reason": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "resignation",
|
||||
"text": "人员离职"
|
||||
},
|
||||
{
|
||||
"value": "position_change",
|
||||
"text": "岗位调整"
|
||||
}
|
||||
]
|
||||
},
|
||||
"current_stage": {
|
||||
"uitype": "code",
|
||||
"data": [
|
||||
{
|
||||
"value": "preparation",
|
||||
"text": "准备阶段"
|
||||
},
|
||||
{
|
||||
"value": "review",
|
||||
"text": "审核阶段"
|
||||
},
|
||||
{
|
||||
"value": "confirmation",
|
||||
"text": "确认阶段"
|
||||
},
|
||||
{
|
||||
"value": "completed",
|
||||
"text": "已完成"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"subtables": [
|
||||
{
|
||||
"field": "handover_id",
|
||||
"title": "交接项目明细",
|
||||
"url": "{{entire_url(handover_items_list)}}",
|
||||
"subtable": "customer_handover_items"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
145
models/customer_handover.json
Normal file
145
models/customer_handover.json
Normal file
@ -0,0 +1,145 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "customer_handover",
|
||||
"title": "客户交接表",
|
||||
"primary": "id",
|
||||
"catelog": "relation"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"title": "交接ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no",
|
||||
"comments": "主键 - UUID格式"
|
||||
},
|
||||
{
|
||||
"name": "customer_id",
|
||||
"title": "客户ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no",
|
||||
"comments": "被交接的客户ID"
|
||||
},
|
||||
{
|
||||
"name": "from_owner_id",
|
||||
"title": "原负责人ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no",
|
||||
"comments": "原客户负责人ID"
|
||||
},
|
||||
{
|
||||
"name": "to_owner_id",
|
||||
"title": "新负责人ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no",
|
||||
"comments": "新客户负责人ID"
|
||||
},
|
||||
{
|
||||
"name": "handover_reason",
|
||||
"title": "交接原因",
|
||||
"type": "str",
|
||||
"length": 100,
|
||||
"nullable": "no",
|
||||
"comments": "交接触发原因:resignation=离职, position_change=岗位调整"
|
||||
},
|
||||
{
|
||||
"name": "current_stage",
|
||||
"title": "当前阶段",
|
||||
"type": "str",
|
||||
"length": 20,
|
||||
"nullable": "no",
|
||||
"default": "preparation",
|
||||
"comments": "交接流程阶段:preparation=准备, review=审核, confirmation=确认, completed=完成"
|
||||
},
|
||||
{
|
||||
"name": "reviewer_id",
|
||||
"title": "审核人ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "yes",
|
||||
"comments": "负责审核交接清单的人员ID"
|
||||
},
|
||||
{
|
||||
"name": "prepared_at",
|
||||
"title": "准备完成时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "yes",
|
||||
"comments": "原负责人完成交接清单准备的时间"
|
||||
},
|
||||
{
|
||||
"name": "reviewed_at",
|
||||
"title": "审核完成时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "yes",
|
||||
"comments": "审核人完成审核的时间"
|
||||
},
|
||||
{
|
||||
"name": "confirmed_at",
|
||||
"title": "确认完成时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "yes",
|
||||
"comments": "接手人确认接收的时间"
|
||||
},
|
||||
{
|
||||
"name": "completed_at",
|
||||
"title": "交接完成时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "yes",
|
||||
"comments": "整个交接流程完成的时间"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no",
|
||||
"comments": "交接流程创建时间"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
"title": "更新时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no",
|
||||
"comments": "最后更新时间"
|
||||
},
|
||||
{
|
||||
"name": "notes",
|
||||
"title": "备注",
|
||||
"type": "text",
|
||||
"nullable": "yes",
|
||||
"comments": "交接过程中的备注信息"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"name": "idx_handover_customer",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["customer_id"]
|
||||
},
|
||||
{
|
||||
"name": "idx_handover_from_owner",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["from_owner_id"]
|
||||
},
|
||||
{
|
||||
"name": "idx_handover_to_owner",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["to_owner_id"]
|
||||
},
|
||||
{
|
||||
"name": "idx_handover_stage",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["current_stage"]
|
||||
},
|
||||
{
|
||||
"name": "idx_handover_created",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["created_at"]
|
||||
}
|
||||
]
|
||||
}
|
||||
91
models/customer_handover_items.json
Normal file
91
models/customer_handover_items.json
Normal file
@ -0,0 +1,91 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "customer_handover_items",
|
||||
"title": "客户交接项目明细表",
|
||||
"primary": "id",
|
||||
"catelog": "relation"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"title": "项目ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no",
|
||||
"comments": "主键 - UUID格式"
|
||||
},
|
||||
{
|
||||
"name": "handover_id",
|
||||
"title": "交接ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no",
|
||||
"comments": "关联的交接记录ID"
|
||||
},
|
||||
{
|
||||
"name": "item_type",
|
||||
"title": "项目类型",
|
||||
"type": "str",
|
||||
"length": 50,
|
||||
"nullable": "no",
|
||||
"comments": "交接项目类型:basic_info=基本信息, opportunities=未结商机, contracts=历史合同, service_tickets=服务工单, payment_issues=回款问题"
|
||||
},
|
||||
{
|
||||
"name": "item_id",
|
||||
"title": "关联ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "yes",
|
||||
"comments": "关联的具体记录ID(如商机ID、合同ID等)"
|
||||
},
|
||||
{
|
||||
"name": "item_description",
|
||||
"title": "项目描述",
|
||||
"type": "text",
|
||||
"nullable": "no",
|
||||
"comments": "项目详细描述或补充说明"
|
||||
},
|
||||
{
|
||||
"name": "is_completed",
|
||||
"title": "是否完成",
|
||||
"type": "str",
|
||||
"length": 1,
|
||||
"nullable": "no",
|
||||
"default": "0",
|
||||
"comments": "是否已完成交接:1=是, 0=否"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no",
|
||||
"comments": "项目创建时间"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
"title": "更新时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no",
|
||||
"comments": "最后更新时间"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"name": "idx_handover_items_handover",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["handover_id"]
|
||||
},
|
||||
{
|
||||
"name": "idx_handover_items_type",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["item_type"]
|
||||
},
|
||||
{
|
||||
"name": "idx_handover_items_item_id",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["item_id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
116
models/customer_pool.json
Normal file
116
models/customer_pool.json
Normal file
@ -0,0 +1,116 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "customer_pool",
|
||||
"title": "客户公海池表",
|
||||
"primary": "id",
|
||||
"catelog": "relation"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"title": "公海记录ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no",
|
||||
"comments": "主键 - UUID格式"
|
||||
},
|
||||
{
|
||||
"name": "customer_id",
|
||||
"title": "客户ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no",
|
||||
"comments": "回收到公海的客户ID"
|
||||
},
|
||||
{
|
||||
"name": "original_owner_id",
|
||||
"title": "原负责人ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no",
|
||||
"comments": "客户原来的负责人ID"
|
||||
},
|
||||
{
|
||||
"name": "recycle_reason",
|
||||
"title": "回收原因",
|
||||
"type": "str",
|
||||
"length": 100,
|
||||
"nullable": "no",
|
||||
"comments": "回收原因:inactive_days=未跟进天数超限, manual=手动回收"
|
||||
},
|
||||
{
|
||||
"name": "inactive_days",
|
||||
"title": "未跟进天数",
|
||||
"type": "long",
|
||||
"nullable": "yes",
|
||||
"comments": "触发回收的未跟进天数"
|
||||
},
|
||||
{
|
||||
"name": "recycled_at",
|
||||
"title": "回收时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no",
|
||||
"comments": "客户被回收到公海的时间"
|
||||
},
|
||||
{
|
||||
"name": "assigned_to",
|
||||
"title": "分配给",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "yes",
|
||||
"comments": "分配给的新负责人ID(如果已分配)"
|
||||
},
|
||||
{
|
||||
"name": "assigned_at",
|
||||
"title": "分配时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "yes",
|
||||
"comments": "客户被分配的时间"
|
||||
},
|
||||
{
|
||||
"name": "pool_status",
|
||||
"title": "公海状态",
|
||||
"type": "str",
|
||||
"length": 20,
|
||||
"nullable": "no",
|
||||
"default": "available",
|
||||
"comments": "公海状态:available=可领取, assigned=已分配, claimed=已认领"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no",
|
||||
"comments": "记录创建时间"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"name": "idx_pool_customer",
|
||||
"idxtype": "unique",
|
||||
"idxfields": ["customer_id"]
|
||||
},
|
||||
{
|
||||
"name": "idx_pool_original_owner",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["original_owner_id"]
|
||||
},
|
||||
{
|
||||
"name": "idx_pool_assigned_to",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["assigned_to"]
|
||||
},
|
||||
{
|
||||
"name": "idx_pool_status",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["pool_status"]
|
||||
},
|
||||
{
|
||||
"name": "idx_pool_recycled",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["recycled_at"]
|
||||
}
|
||||
]
|
||||
}
|
||||
167
models/customers.json
Normal file
167
models/customers.json
Normal file
@ -0,0 +1,167 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "customers",
|
||||
"title": "客户档案表",
|
||||
"primary": "id",
|
||||
"catelog": "entity"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"title": "客户ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no",
|
||||
"comments": "主键 - UUID格式"
|
||||
},
|
||||
{
|
||||
"name": "customer_name",
|
||||
"title": "客户名称",
|
||||
"type": "str",
|
||||
"length": 255,
|
||||
"nullable": "no",
|
||||
"comments": "客户公司名称或个人姓名"
|
||||
},
|
||||
{
|
||||
"name": "customer_type",
|
||||
"title": "客户类型",
|
||||
"type": "str",
|
||||
"length": 20,
|
||||
"nullable": "no",
|
||||
"comments": "客户类型:individual=个人, enterprise=企业"
|
||||
},
|
||||
{
|
||||
"name": "phone",
|
||||
"title": "手机号",
|
||||
"type": "str",
|
||||
"length": 20,
|
||||
"nullable": "yes",
|
||||
"comments": "客户手机号码"
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"title": "邮箱",
|
||||
"type": "str",
|
||||
"length": 255,
|
||||
"nullable": "yes",
|
||||
"comments": "客户邮箱地址"
|
||||
},
|
||||
{
|
||||
"name": "tax_id",
|
||||
"title": "企业税号",
|
||||
"type": "str",
|
||||
"length": 50,
|
||||
"nullable": "yes",
|
||||
"comments": "企业统一社会信用代码或税号"
|
||||
},
|
||||
{
|
||||
"name": "industry",
|
||||
"title": "所属行业",
|
||||
"type": "str",
|
||||
"length": 100,
|
||||
"nullable": "yes",
|
||||
"comments": "客户所属行业"
|
||||
},
|
||||
{
|
||||
"name": "customer_level",
|
||||
"title": "分级标签",
|
||||
"type": "str",
|
||||
"length": 20,
|
||||
"nullable": "no",
|
||||
"default": "potential",
|
||||
"comments": "客户分级:important=重要, normal=普通, potential=潜在"
|
||||
},
|
||||
{
|
||||
"name": "address",
|
||||
"title": "地址",
|
||||
"type": "text",
|
||||
"nullable": "yes",
|
||||
"comments": "客户详细地址"
|
||||
},
|
||||
{
|
||||
"name": "owner_id",
|
||||
"title": "负责人ID",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no",
|
||||
"comments": "当前负责该客户的销售人员ID"
|
||||
},
|
||||
{
|
||||
"name": "region",
|
||||
"title": "区域",
|
||||
"type": "str",
|
||||
"length": 100,
|
||||
"nullable": "yes",
|
||||
"comments": "客户所在区域"
|
||||
},
|
||||
{
|
||||
"name": "last_follow_up",
|
||||
"title": "最后跟进时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "yes",
|
||||
"comments": "最后一次跟进时间"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no",
|
||||
"comments": "客户档案创建时间"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
"title": "更新时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no",
|
||||
"comments": "最后更新时间"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"title": "状态",
|
||||
"type": "str",
|
||||
"length": 20,
|
||||
"nullable": "no",
|
||||
"default": "active",
|
||||
"comments": "客户状态:active=活跃, inactive=非活跃, in_pool=公海"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"name": "idx_customers_phone",
|
||||
"idxtype": "unique",
|
||||
"idxfields": ["phone"]
|
||||
},
|
||||
{
|
||||
"name": "idx_customers_tax_id",
|
||||
"idxtype": "unique",
|
||||
"idxfields": ["tax_id"]
|
||||
},
|
||||
{
|
||||
"name": "idx_customers_owner",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["owner_id"]
|
||||
},
|
||||
{
|
||||
"name": "idx_customers_name",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["customer_name"]
|
||||
},
|
||||
{
|
||||
"name": "idx_customers_level",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["customer_level"]
|
||||
},
|
||||
{
|
||||
"name": "idx_customers_status",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["status"]
|
||||
},
|
||||
{
|
||||
"name": "idx_customers_last_follow",
|
||||
"idxtype": "index",
|
||||
"idxfields": ["last_follow_up"]
|
||||
}
|
||||
]
|
||||
}
|
||||
20
pyproject.toml
Normal file
20
pyproject.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=45", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "customer_management"
|
||||
version = "1.0.0"
|
||||
description = "客户管理模块 - 提供客户档案管理、交接管理和公海池功能"
|
||||
authors = [{name = "Hermes Agent", email = "hermes@example.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = [
|
||||
"ahserver",
|
||||
"sqlor-database-module",
|
||||
"bricks-framework"
|
||||
]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["."]
|
||||
include = ["customer_management*"]
|
||||
51
wwwroot/base.ui
Normal file
51
wwwroot/base.ui
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"widgettype": "TabPanel",
|
||||
"options": {
|
||||
"title": "客户管理"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "CRUD",
|
||||
"options": {
|
||||
"title": "客户档案",
|
||||
"url": "{{entire_url(customers_list)}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "CRUD",
|
||||
"options": {
|
||||
"title": "客户交接",
|
||||
"url": "{{entire_url(handover_list)}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "CRUD",
|
||||
"options": {
|
||||
"title": "客户公海池",
|
||||
"url": "{{entire_url(customer_pool_list)}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Panel",
|
||||
"options": {
|
||||
"title": "客户360度视图"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Form",
|
||||
"options": {
|
||||
"title": "客户查询",
|
||||
"fields": [
|
||||
{
|
||||
"name": "customer_id",
|
||||
"label": "客户ID",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"onSubmit": "get_customer_360_view"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user