262 lines
12 KiB
Python
262 lines
12 KiB
Python
"""
|
|
客户相关后台任务模块
|
|
包含修改客户信息等功能
|
|
"""
|
|
import logging
|
|
import requests
|
|
import pandas as pd
|
|
import time
|
|
import re
|
|
from typing import Dict, Any, Optional
|
|
from app.tasks.common import update_jiandaoyun, approve_workflow
|
|
|
|
logger = logging.getLogger('app')
|
|
|
|
|
|
def modify_customer_info_background(data: Dict[str, Any], cookies: Dict[str, str], df: pd.DataFrame, save_path: str):
|
|
"""
|
|
修改客户信息后台任务。
|
|
|
|
此函数用于后台任务,用于修改会员信息。
|
|
|
|
Args:
|
|
data (Dict[str, Any]): 前端请求发送过来的数据,包含文件信息和其他必要参数。
|
|
cookies (Dict[str, str]): 登录用户的Cookies。
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
df = df.where(pd.notnull(df), None)
|
|
params = {
|
|
'pageSize': 100,
|
|
'pageNo': '1',
|
|
}
|
|
|
|
res = requests.get(
|
|
'https://yunxiu.f6car.cn/member/customer/listForPermission',
|
|
params=params,
|
|
cookies=cookies,
|
|
)
|
|
|
|
total = int(res.json().get("data").get("total"))
|
|
total_pages = (total // params["pageSize"]) + (1 if total % params["pageSize"] > 0 else 0)
|
|
print(f"总计{total_pages}页")
|
|
|
|
all_customers = []
|
|
max_retries = 10
|
|
retry_count = 0
|
|
for page in range(1, total_pages + 1):
|
|
print(f"正在请求第 {page} 页...")
|
|
params["pageNo"] = page
|
|
|
|
while retry_count < max_retries:
|
|
response = requests.get(
|
|
'https://yunxiu.f6car.cn/member/customer/listForPermission',
|
|
params=params,
|
|
cookies=cookies,
|
|
timeout=10
|
|
)
|
|
time.sleep(1)
|
|
if response.status_code == 200:
|
|
suppliers = response.json().get("data", {}).get("data", [])
|
|
all_customers.extend(suppliers)
|
|
break
|
|
else:
|
|
retry_count += 1
|
|
print(f"请求第 {page} 页失败,正在重试(第 {retry_count} 次)...")
|
|
time.sleep(3)
|
|
|
|
# 获取专属运营顾问列表
|
|
json_data = {
|
|
'includeStopedEmployee': False,
|
|
'pageSize': 1000,
|
|
'filterNullUser': False,
|
|
'keyword': '',
|
|
'idOwnOrgList': [],
|
|
}
|
|
|
|
response = requests.post(
|
|
'https://yunxiu.f6car.cn/hive/employee/searchStaffInGroup',
|
|
cookies=cookies,
|
|
json=json_data,
|
|
)
|
|
|
|
staff_list = response.json().get("data").get("list")
|
|
name_to_userid = {
|
|
emp['name']: emp['userId']
|
|
for emp in staff_list
|
|
if emp['userId'] is not None
|
|
}
|
|
df['userId'] = df['专属运营顾问'].map(name_to_userid)
|
|
|
|
def extract_province_city_district(address: Optional[str]) -> Dict[str, Optional[str]]:
|
|
"""安全解析省市区信息,所有返回值都可能为None"""
|
|
if not address:
|
|
return {'省': None, '市': None, '区': None}
|
|
|
|
try:
|
|
pattern = r'(?P<省>(?:[\u4e00-\u9fa5]+(?:省|自治区|特别行政区))?)' \
|
|
r'(?P<市>(?:[\u4e00-\u9fa5]+(?:市|自治州|地区|盟))?)' \
|
|
r'(?P<区>(?:[\u4e00-\u9fa5]+区|[\u4e00-\u9fa5]+县|[\u4e00-\u9fa5]+旗)?)'
|
|
match = re.match(pattern, address.strip())
|
|
return {k: v if v else None for k, v in match.groupdict().items()} if match else {'省': None, '市': None,
|
|
'区': None}
|
|
except Exception:
|
|
return {'省': None, '市': None, '区': None}
|
|
|
|
def safe_get(d: Optional[Dict], *keys, default=None):
|
|
"""多层字典安全获取值,始终返回可能为None的值"""
|
|
if not isinstance(d, dict):
|
|
return default
|
|
|
|
for key in keys:
|
|
d = d.get(key, {})
|
|
if not isinstance(d, dict):
|
|
break
|
|
return d if d != {} else default
|
|
|
|
def convert_to_request_data(original_data: Optional[Dict[str, Any]], df: pd.DataFrame) -> Dict[str, Any]:
|
|
"""
|
|
完全安全的字典转换函数
|
|
特点:
|
|
1. 每个字段的值都可能为None
|
|
2. 不会因为任何字段为空而报错
|
|
3. 不使用任何默认值,完全保留原始数据的空值状态
|
|
"""
|
|
customer_info = safe_get(original_data, 'data', 'customerInfo') if original_data else None
|
|
|
|
address_parts = extract_province_city_district(
|
|
safe_get(customer_info, 'provinceCityAreaName') if customer_info else None
|
|
)
|
|
|
|
cell_phone = safe_get(customer_info, 'cellPhone')
|
|
|
|
exclusive_info = None
|
|
df_row = None
|
|
if cell_phone and not df.empty:
|
|
matched_rows = df[df['客户手机号'] == cell_phone]
|
|
if not matched_rows.empty:
|
|
df_row = matched_rows.iloc[0]
|
|
exclusive_info = {
|
|
'userId': df_row.get('userId'),
|
|
'name': df_row.get('专属运营顾问')
|
|
}
|
|
|
|
request_data = {
|
|
"pkId": safe_get(customer_info, 'idCustomer'),
|
|
"idCustomer": safe_get(customer_info, 'idCustomer'),
|
|
"name": df_row.get('客户姓名') if df_row is not None and pd.notna(df_row.get('客户姓名')) else safe_get(
|
|
customer_info, 'name'),
|
|
"sex": safe_get(customer_info, 'sex'),
|
|
"customerType": df_row.get('客户类型') if df_row is not None and pd.notna(
|
|
df_row.get('客户类型')) else safe_get(
|
|
customer_info, 'customerType'),
|
|
"customerSource": safe_get(customer_info, 'customerSource'),
|
|
"customerSourceName": df_row.get('客户来源') if df_row is not None and pd.notna(
|
|
df_row.get('客户来源')) else safe_get(customer_info, 'customerSourceName'),
|
|
"companyName": df_row.get('单位名称') if df_row is not None and pd.notna(
|
|
df_row.get('单位名称')) else safe_get(
|
|
customer_info, 'companyName'),
|
|
"cellPhone": cell_phone,
|
|
"wechart": safe_get(customer_info, 'wechart'),
|
|
"qq": safe_get(customer_info, 'qq'),
|
|
"contacts": safe_get(customer_info, 'contacts'),
|
|
"contactTelephone": safe_get(customer_info, 'contactTelephone'),
|
|
"province": safe_get(customer_info, 'province'),
|
|
"city": safe_get(customer_info, 'city'),
|
|
"area": safe_get(customer_info, 'area'),
|
|
"street": safe_get(customer_info, 'street'),
|
|
"address": safe_get(customer_info, 'address'),
|
|
"detailAddress": safe_get(customer_info, 'detailAddress'),
|
|
"pId": safe_get(customer_info, 'province'),
|
|
"cId": safe_get(customer_info, 'city'),
|
|
"aId": safe_get(customer_info, 'area'),
|
|
"provinceName": address_parts.get('省'),
|
|
"cityName": address_parts.get('市'),
|
|
"areaName": address_parts.get('区'),
|
|
"provinceCityAreaName": safe_get(customer_info, 'provinceCityAreaName'),
|
|
"birthday": safe_get(customer_info, 'birthday'),
|
|
"creationtime": safe_get(customer_info, 'creationtime'),
|
|
"modifiedtime": safe_get(customer_info, 'modifiedtime'),
|
|
"creator": safe_get(customer_info, 'creator'),
|
|
"creatorName": safe_get(customer_info, 'creatorName'),
|
|
"modifier": safe_get(customer_info, 'modifier'),
|
|
"idOwnOrg": safe_get(customer_info, 'idOwnOrg'),
|
|
"idOwnGroup": safe_get(customer_info, 'idOwnGroup'),
|
|
"insuranceCompany": safe_get(customer_info, 'insuranceCompany'),
|
|
"maritalStatus": safe_get(customer_info, 'maritalStatus'),
|
|
"monthlyIncome": safe_get(customer_info, 'monthlyIncome'),
|
|
"idNumber": safe_get(customer_info, 'idNumber'),
|
|
"personHobby": safe_get(customer_info, 'personHobby'),
|
|
"credentialsType": safe_get(customer_info, 'credentialsType'),
|
|
"points": safe_get(customer_info, 'points'),
|
|
"maxAccountAmount": safe_get(customer_info, 'maxAccountAmount'),
|
|
"pointsEnable": safe_get(customer_info, 'pointsEnable'),
|
|
"level": safe_get(customer_info, 'level'),
|
|
"memberCardNo": safe_get(customer_info, 'memberCardNo'),
|
|
"customerLevel": safe_get(customer_info, 'customerLevel'),
|
|
"exclusiveConsultantId": exclusive_info['userId'] if exclusive_info else safe_get(customer_info,
|
|
'exclusiveConsultantId'),
|
|
"exclusiveConsultantName": exclusive_info['name'] if exclusive_info else safe_get(customer_info,
|
|
'exclusiveConsultantName'),
|
|
"exclusiveOrgId": safe_get(customer_info, 'exclusiveOrgId'),
|
|
"exclusiveOrgName": safe_get(customer_info, 'exclusiveOrgName'),
|
|
"customerMemo": df_row.get('客户备注') if df_row is not None and pd.notna(
|
|
df_row.get('客户备注')) else safe_get(
|
|
customer_info, 'customerMemo'),
|
|
"isDel": safe_get(customer_info, 'isDel'),
|
|
"idFrom": safe_get(customer_info, 'idFrom'),
|
|
"mnemonic": safe_get(customer_info, 'mnemonic'),
|
|
"idOrgSource": safe_get(customer_info, 'idOrgSource'),
|
|
"firstArrivalIdSourceBill": safe_get(customer_info, 'firstArrivalIdSourceBill'),
|
|
"lastArrivalIdSourceBill": safe_get(customer_info, 'lastArrivalIdSourceBill'),
|
|
"customerInfoType": safe_get(customer_info, 'customerInfoType'),
|
|
"customerInfoCompleteDate": safe_get(customer_info, 'customerInfoCompleteDate'),
|
|
"customerInfoCompleteType": safe_get(customer_info, 'customerInfoCompleteType'),
|
|
"xczUserId": safe_get(customer_info, 'xczUserId'),
|
|
"xczUuid": safe_get(customer_info, 'xczUuid'),
|
|
"idWxbCustomer": safe_get(customer_info, 'idWxbCustomer'),
|
|
"promoteEmployeeId": safe_get(customer_info, 'promoteEmployeeId'),
|
|
"promoteEmployeeName": safe_get(customer_info, 'promoteEmployeeName'),
|
|
"promoteMemberId": safe_get(customer_info, 'promoteMemberId'),
|
|
"promoteMemberName": safe_get(customer_info, 'promoteMemberName'),
|
|
"driverExpiryDate": safe_get(customer_info, 'driverExpiryDate'),
|
|
"crmDeleteExclusiveFlag": safe_get(customer_info, 'crmDeleteExclusiveFlag'),
|
|
"totalObtainPoints": safe_get(customer_info, 'totalObtainPoints'),
|
|
"totalUsedPoints": safe_get(customer_info, 'totalUsedPoints'),
|
|
"orgName": safe_get(customer_info, 'orgName'),
|
|
"weChatFollower": safe_get(customer_info, 'weChatFollower'),
|
|
"pointsEnableConfig": safe_get(customer_info, 'pointsEnableConfig'),
|
|
"personalPointsEnableConfig": safe_get(customer_info, 'personalPointsEnableConfig'),
|
|
"pointsButtonStatus": safe_get(customer_info, 'pointsButtonStatus'),
|
|
"tmallInstallMember": safe_get(customer_info, 'tmallInstallMember'),
|
|
"corpId": safe_get(customer_info, 'corpId'),
|
|
"thirdCorpId": safe_get(customer_info, 'thirdCorpId'),
|
|
}
|
|
|
|
return request_data
|
|
|
|
for customer in all_customers:
|
|
phone = customer.get("cellPhone")
|
|
if phone in df["客户手机号"].tolist():
|
|
print("开始修改")
|
|
cus_id = customer.get("idCustomer", {})
|
|
cus_response = requests.get(f'https://yunxiu.f6car.cn/member/customer/{cus_id}', cookies=cookies)
|
|
original_data = cus_response.json()
|
|
final_json_data = convert_to_request_data(original_data, df)
|
|
response = requests.post(
|
|
'https://yunxiu.f6car.cn/member/customer/modifyCustomer',
|
|
cookies=cookies,
|
|
json=final_json_data,
|
|
)
|
|
print("修改完成")
|
|
|
|
time.sleep(1)
|
|
|
|
msg = update_jiandaoyun(data, f'修改完成')
|
|
|
|
if msg.get('msg'):
|
|
approve_workflow(data)
|
|
print('表单已自动提交至下一步')
|
|
|