接车宝异常派发逻辑添加

This commit is contained in:
z66
2025-12-22 09:54:45 +08:00
parent 7d23df0b43
commit a3541ab5e1
6 changed files with 669 additions and 120 deletions
+7
View File
@@ -24,6 +24,13 @@
</Attribute>
</value>
</entry>
<entry key="\test\output\last_price.csv">
<value>
<Attribute>
<option name="separator" value="," />
</Attribute>
</value>
</entry>
</map>
</option>
</component>
+3 -3
View File
@@ -262,7 +262,7 @@ class CommonModule:
)
cursor = conn.cursor()
sql = f"""SELECT * FROM saas_renewal_rate_org_code_version_amount_saas;"""
sql = f"""SELECT * FROM org_code_total_paid_amount;"""
# 执行语句并获取结果集
cursor.execute(sql)
rows = cursor.fetchall()
@@ -274,7 +274,7 @@ class CommonModule:
else: # 如果没有数据,返回空 DataFrame
data_NGV = pd.DataFrame()
headers = [
"门店编码", "上次购买价格",
"门店编码", "类型", "占位", "价格"
]
data_NGV.columns = headers
@@ -319,7 +319,7 @@ class CommonModule:
"应续约月份", "商户中心id", "门店id", "门店编码", "门店名称", "是否主店",
"商品名称", "应续约日", "公司id", "公司名称", "公司等级", "加盟商名称",
"开户时间", "开户渠道来源", "门店状态", "大区", "小区", "省份", "城市",
"区域经理", "运营负责人", "技术专家", "商品数量", "分母金额", "是否续约", "elt时间","月分区",
"区域经理", "运营负责人", "技术专家", "上次购买数量", "分母金额", "是否续约", "elt时间", "月分区",
]
data_NGV.columns = headers
+219 -93
View File
@@ -44,9 +44,123 @@
'_widget_1764820541678': {
'value': '195159084238961158'
},
'_widget_1764820541717': {
'value': "[{'_widget_1764820541719': {'value': '员工账号'}, '_widget_1764820541720': {'value': Decimal('0.551200000000000000')}, '_widget_1764820541721': {'value': '2025-02-17 00:00:00'}, '_widget_1764820541722': {'value': None}, '_widget_1764820541723': {'value': None}, '_widget_1764820541724': {'value': None}, '_widget_1764820541725': {'value': None}}, {'_widget_1764820541719': {'value': '商用车车型解析'}, '_widget_1764820541720': {'value': Decimal('0.322800000000000000')}, '_widget_1764820541721': {'value': '2025-02-17 00:00:00'}, '_widget_1764820541722': {'value': None}, '_widget_1764820541723': {'value': None}, '_widget_1764820541724': {'value': None}, '_widget_1764820541725': {'value': None}}, {'_widget_1764820541719': {'value': '企微助手'}, '_widget_1764820541720': {'value': Decimal('589.000000000000000000')}, '_widget_1764820541721': {'value': '2025-08-28 00:00:00'}, '_widget_1764820541722': {'value': None}, '_widget_1764820541723': {'value': None}, '_widget_1764820541724': {'value': None}, '_widget_1764820541725': {'value': None}}, {'_widget_1764820541719': {'value': '保险机器人'}, '_widget_1764820541720': {'value': Decimal('137.802600000000000000')}, '_widget_1764820541721': {'value': '2025-08-28 00:00:00'}, '_widget_1764820541722': {'value': None}, '_widget_1764820541723': {'value': None}, '_widget_1764820541724': {'value': None}, '_widget_1764820541725': {'value': None}}, {'_widget_1764820541719': {'value': '员工账号'}, '_widget_1764820541720': {'value': Decimal('21.623800000000000000')}, '_widget_1764820541721': {'value': '2025-04-06 00:00:00'}, '_widget_1764820541722': {'value': None}, '_widget_1764820541723': {'value': None}, '_widget_1764820541724': {'value': None}, '_widget_1764820541725': {'value': None}}]"
},
'_widget_1764820541717': [
{
'_widget_1764820541719': {
'value': ''
},
'_widget_1764820541720': {
'value': 0.5512
},
'_widget_1764820541721': {
'value': '2025-02-17 00:00:00'
},
'_widget_1764820541722': {
'value': None
},
'_widget_1764820541723': {
'value': None
},
'_widget_1764820541724': {
'value': None
},
'_widget_1764820541725': {
'value': None
}
},
{
'_widget_1764820541719': {
'value': ''
},
'_widget_1764820541720': {
'value': 0.3228
},
'_widget_1764820541721': {
'value': '2025-02-17 00:00:00'
},
'_widget_1764820541722': {
'value': None
},
'_widget_1764820541723': {
'value': None
},
'_widget_1764820541724': {
'value': None
},
'_widget_1764820541725': {
'value': None
}
},
{
'_widget_1764820541719': {
'value': ''
},
'_widget_1764820541720': {
'value': 589.0
},
'_widget_1764820541721': {
'value': '2025-08-28 00:00:00'
},
'_widget_1764820541722': {
'value': None
},
'_widget_1764820541723': {
'value': None
},
'_widget_1764820541724': {
'value': None
},
'_widget_1764820541725': {
'value': None
}
},
{
'_widget_1764820541719': {
'value': ''
},
'_widget_1764820541720': {
'value': 137.8026
},
'_widget_1764820541721': {
'value': '2025-08-28 00:00:00'
},
'_widget_1764820541722': {
'value': None
},
'_widget_1764820541723': {
'value': None
},
'_widget_1764820541724': {
'value': None
},
'_widget_1764820541725': {
'value': None
}
},
{
'_widget_1764820541719': {
'value': ''
},
'_widget_1764820541720': {
'value': 21.6238
},
'_widget_1764820541721': {
'value': '2025-04-06 00:00:00'
},
'_widget_1764820541722': {
'value': None
},
'_widget_1764820541723': {
'value': None
},
'_widget_1764820541724': {
'value': None
},
'_widget_1764820541725': {
'value': None
}
}
],
'_widget_1764820541865': {
'value': '2026-07-15 01:59:32'
}
@@ -93,9 +207,31 @@
'_widget_1764820541678': {
'value': '0103433535667605'
},
'_widget_1764820541717': {
'value': "[{'_widget_1764820541719': {'value': '员工账号'}, '_widget_1764820541720': {'value': Decimal('300.000000000000000000')}, '_widget_1764820541721': {'value': '2025-10-08 00:00:00'}, '_widget_1764820541722': {'value': None}, '_widget_1764820541723': {'value': None}, '_widget_1764820541724': {'value': None}, '_widget_1764820541725': {'value': None}}]"
},
'_widget_1764820541717': [
{
'_widget_1764820541719': {
'value': ''
},
'_widget_1764820541720': {
'value': 300.0
},
'_widget_1764820541721': {
'value': '2025-10-08 00:00:00'
},
'_widget_1764820541722': {
'value': None
},
'_widget_1764820541723': {
'value': None
},
'_widget_1764820541724': {
'value': None
},
'_widget_1764820541725': {
'value': None
}
}
],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -142,9 +278,7 @@
'_widget_1764820541678': {
'value': '054169455736370109'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-15 03:03:11'
}
@@ -191,9 +325,7 @@
'_widget_1764820541678': {
'value': '180720602439816818'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -240,9 +372,7 @@
'_widget_1764820541678': {
'value': '171128505620867604'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-15 07:42:56'
}
@@ -289,9 +419,7 @@
'_widget_1764820541678': {
'value': '0237196424846034'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-15 08:16:39'
}
@@ -338,9 +466,31 @@
'_widget_1764820541678': {
'value': '052909055823302010'
},
'_widget_1764820541717': {
'value': "[{'_widget_1764820541719': {'value': '轮胎寄存'}, '_widget_1764820541720': {'value': Decimal('64.468500000000000000')}, '_widget_1764820541721': {'value': '2025-04-16 00:00:00'}, '_widget_1764820541722': {'value': None}, '_widget_1764820541723': {'value': None}, '_widget_1764820541724': {'value': None}, '_widget_1764820541725': {'value': None}}]"
},
'_widget_1764820541717': [
{
'_widget_1764820541719': {
'value': ''
},
'_widget_1764820541720': {
'value': 64.4685
},
'_widget_1764820541721': {
'value': '2025-04-16 00:00:00'
},
'_widget_1764820541722': {
'value': None
},
'_widget_1764820541723': {
'value': None
},
'_widget_1764820541724': {
'value': None
},
'_widget_1764820541725': {
'value': None
}
}
],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -387,9 +537,7 @@
'_widget_1764820541678': {
'value': '0103433535667605'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-15 00:37:33'
}
@@ -436,9 +584,7 @@
'_widget_1764820541678': {
'value': '283227523421943504'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-15 06:39:40'
}
@@ -485,9 +631,7 @@
'_widget_1764820541678': {
'value': '283227523421943504'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-15 09:13:04'
}
@@ -532,9 +676,7 @@
'_widget_1764820541678': {
'value': '1253235059942945'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -581,9 +723,7 @@
'_widget_1764820541678': {
'value': '054169455736370109'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-15 07:44:19'
}
@@ -630,9 +770,7 @@
'_widget_1764820541678': {
'value': '195159084238961158'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-15 03:36:05'
}
@@ -679,9 +817,7 @@
'_widget_1764820541678': {
'value': '293429301023738442'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -728,9 +864,7 @@
'_widget_1764820541678': {
'value': '3704680936560271'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-15 00:28:43'
}
@@ -777,9 +911,7 @@
'_widget_1764820541678': {
'value': '0103433535667605'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-15 02:19:27'
}
@@ -826,9 +958,7 @@
'_widget_1764820541678': {
'value': '054169455736370109'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -875,9 +1005,7 @@
'_widget_1764820541678': {
'value': '3704680936560271'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -924,9 +1052,7 @@
'_widget_1764820541678': {
'value': '054169455736370109'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -973,9 +1099,7 @@
'_widget_1764820541678': {
'value': '293429301023738442'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -1022,9 +1146,7 @@
'_widget_1764820541678': {
'value': '054169455736370109'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -1071,9 +1193,7 @@
'_widget_1764820541678': {
'value': '055512041727184572'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -1120,9 +1240,7 @@
'_widget_1764820541678': {
'value': '0237196424846034'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -1169,9 +1287,7 @@
'_widget_1764820541678': {
'value': '3048162260682931'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-15 01:32:58'
}
@@ -1218,9 +1334,31 @@
'_widget_1764820541678': {
'value': '180720602439816818'
},
'_widget_1764820541717': {
'value': "[{'_widget_1764820541719': {'value': '员工账号'}, '_widget_1764820541720': {'value': Decimal('208.000000000000000000')}, '_widget_1764820541721': {'value': '2025-04-15 00:00:00'}, '_widget_1764820541722': {'value': None}, '_widget_1764820541723': {'value': None}, '_widget_1764820541724': {'value': None}, '_widget_1764820541725': {'value': None}}]"
},
'_widget_1764820541717': [
{
'_widget_1764820541719': {
'value': ''
},
'_widget_1764820541720': {
'value': 208.0
},
'_widget_1764820541721': {
'value': '2025-04-15 00:00:00'
},
'_widget_1764820541722': {
'value': None
},
'_widget_1764820541723': {
'value': None
},
'_widget_1764820541724': {
'value': None
},
'_widget_1764820541725': {
'value': None
}
}
],
'_widget_1764820541865': {
'value': '2026-07-15 05:06:51'
}
@@ -1265,9 +1403,7 @@
'_widget_1764820541678': {
'value': '171128505620867604'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -1312,9 +1448,7 @@
'_widget_1764820541678': {
'value': '171128505620867604'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -1359,9 +1493,7 @@
'_widget_1764820541678': {
'value': '195159084238961158'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -1406,9 +1538,7 @@
'_widget_1764820541678': {
'value': '054169455736370109'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -1453,9 +1583,7 @@
'_widget_1764820541678': {
'value': '171128505620867604'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -1502,9 +1630,7 @@
'_widget_1764820541678': {
'value': '180720602439816818'
},
'_widget_1764820541717': {
'value': '[]'
},
'_widget_1764820541717': [],
'_widget_1764820541865': {
'value': '2026-07-14 16:00:00'
}
@@ -0,0 +1,388 @@
from datetime import timedelta, datetime
from config import Config
import pandas as pd
from back_ground_module import CommonModule
from api import API
from log_config import configure_task_logger, configure_error_task_logger
from datetime import datetime, timedelta, timezone
import pandas as pd
import os
# 获取已经配置好的常规日志记录器
logger = configure_task_logger()
# 获取已经配置好的错误任务日志记录器
error_task_logger = configure_error_task_logger()
# 保存为CSV文件
output_dir = "output" # 设置输出目录
# 创建输出目录(如果不存在)
import os
os.makedirs(output_dir, exist_ok=True)
common_module = CommonModule()
api_instance = API()
class JCBEfficientCarPickup:
"""接车宝日常回访"""
def __init__(self):
# 使用 pymysql 连接数据库
self.daily_revisit_list = None
self.field_mapping = {}
self.staff_id_list = None
self.customer_service_list = None
def load_cus_data(self):
# 获取接车宝客服表单
payload = {"api_key": "6717470a0b3975ef583c6df1",
"entry_id": "67b6f2462f9ac03b783d409a",
}
customer_service = api_instance.entry_data_list(payload)
customer_service_list = customer_service.get("data") # api请求格式,将数据封装在data字典里
return customer_service_list
def today_customer_service_list(self):
# 获取今日接车宝派发客服顺序
global is_customer_service_data_id
today_customer_service_list = []
all_customer_service_list = []
today_customer_service_start_list = []
for row_items in self.load_cus_data():
# print(row_items)
customer_service_name_id = row_items.get("_widget_1740042824214", {}).get("username", {})
customer_service_name = row_items.get("_widget_1740042824214", {}).get("name", {})
customer_service_state = row_items.get("_widget_1740117343937", {})
is_last_day_end = row_items.get("_widget_1740042824216", {})
customer_service_data_id = row_items.get("_id", {})
print(customer_service_name, customer_service_name_id, customer_service_state, is_last_day_end)
all_customer_service_list.append(
[customer_service_name, customer_service_name_id, customer_service_state, is_last_day_end,
customer_service_data_id])
if is_last_day_end == "": # 判断是否是下次开始位置
last_day_end_customer_service = customer_service_name_id
is_customer_service_data_id = row_items.get("_id", {})
split_index = None
for index, row in enumerate(all_customer_service_list):
print(row[3])
if row[3] == "":
split_index = index
print(f"找到索引 {index}")
break
if split_index is not None:
# 根据索引切割列表
first_part = all_customer_service_list[split_index:] # 索引位置及之后的行
second_part = all_customer_service_list[:split_index] # 索引位置之前的行
# 调换两个子列表的位置并重新组合
today_customer_service_start_list = first_part + second_part
else:
# 如果没有找到"是",保持原列表不变
today_customer_service_start_list = all_customer_service_list
pass
for index, row in enumerate(today_customer_service_start_list):
if row[2] == "":
today_customer_service_list.append(row[1])
return today_customer_service_list, is_customer_service_data_id, all_customer_service_list
def send_request(self, df):
if df is None or df.empty: # 检查DataFrame是否为None或空
logger.info("当前派发数据为空或None,跳过此派发")
return
today_customer_service_list, is_customer_service_data_id, all_customer_service_list = self.today_customer_service_list()
# 初始化派发索引
next_dispatcher_index = 0
# 显式循环分配跟进人
follow_up_persons = []
for _ in range(len(df)):
follow_up_person = today_customer_service_list[next_dispatcher_index]
follow_up_persons.append(follow_up_person)
next_dispatcher_index = (next_dispatcher_index + 1) % len(today_customer_service_list)
# 添加跟进人到 DataFrame
df["跟进人"] = follow_up_persons
# 获取下一个派发人
next_dispatcher = today_customer_service_list[next_dispatcher_index]
new_sign_abnormal_data = [self.row_to_dict(row, self.field_mapping) for index, row in
df.iterrows()]
data = {'api_key': Config.EFFICIENT_CAR_PICKUP_APP_ID, 'entry_id': Config.EFFICIENT_CAR_PICKUP_ENTRY_ID,
"data_list": new_sign_abnormal_data}
result = api_instance.entry_data_batch_create(data)
logger.info(f"数据发送成功:{result}")
data1 = {"api_key": Config.EFFICIENT_CAR_PICKUP_APP_ID,
"entry_id": Config.EFFICIENT_CAR_PICKUP_CUSTOMER_SERVICE_ID,
"data_id": is_customer_service_data_id,
"data":
{"_widget_1740042824216": {"value": ""}, }
} # 原来的是"_widget_1740042824216": {"value": "是"},修改昨日截至人员
next_customer_service_data_id = None
for index, row in enumerate(all_customer_service_list):
print(row[3])
if row[1] == next_dispatcher:
next_customer_service_data_id = row[4]
break
data2 = {"api_key": Config.EFFICIENT_CAR_PICKUP_APP_ID,
"entry_id": Config.EFFICIENT_CAR_PICKUP_CUSTOMER_SERVICE_ID,
"data_id": next_customer_service_data_id,
"data":
{"_widget_1740042824216": {"value": ""}, }}
api_instance.entry_data_update(data1)
result2 = api_instance.entry_data_update(data2)
logger.info(f"明日派发人员信息已修改:{result2}")
def load_all_data(self):
# 获取接车宝日常回访单
payload = {"api_key": "6717470a0b3975ef583c6df1",
"entry_id": "67174710da507490d8ac12c1",
}
daily_revisit = api_instance.entry_data_list(payload)
self.daily_revisit_list = daily_revisit.get("data") # api请求格式,将数据封装在data字典里
def main(self):
task_start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
try:
logger.info(f"接车宝日常回访开始执行")
data_JCB = common_module.get_jcb_details()
if data_JCB is None:
logger.error("获取接车宝数据失败,返回None")
raise ValueError("获取接车宝数据失败,返回None")
self.load_all_data()
logger.info(f"数据加载完成")
# data_JCB.to_csv(os.path.join(output_dir, 'JCB_all_data.csv'), index=False)
self.fields()
# 新签异常待办回访。
# 当前日期
current_date = datetime.now()
current_date = current_date + timedelta(days=0)
current_date_str = current_date.strftime("%Y-%m-%d")
seven_days_ago = current_date - timedelta(days=7)
seven_days_ago = seven_days_ago.date()
# print(three_days_ago)
new_sign_abnormal = []
for index, row in data_JCB.iterrows():
new_row = row.copy()
# 先转成字符串,再解析回 date 对象
new_row['开户日'] = datetime.strptime(str(row['开户日']), "%Y-%m-%d").date()
if new_row['开户日'] == seven_days_ago and row['当月开单天数'] == 0:
# print(row['账号'], row['开户日'], row['当月开单天数'])
row["日期"] = datetime.strptime(str(row['开户日']), "%Y-%m-%d").date()
row['日期'] = row["日期"].strftime("%Y-%m-%d")
new_sign_abnormal.append(row)
new_sign_abnormal = pd.DataFrame(new_sign_abnormal) if new_sign_abnormal else None
if new_sign_abnormal is not None and not new_sign_abnormal.empty:
new_sign_abnormal["表单类型"] = "新签异常待办"
new_sign_abnormal["派发日期"] = current_date_str
# self.send_request(new_sign_abnormal) # 发送请求
logger.info(f"新签异常待办回访完成")
else:
logger.info(f"新签异常待办回访无数据,跳过")
# 异常待办
current_local = datetime.now() + timedelta(days=-1) # tz-naive,代表本地时间
current_date_str = current_local.strftime("%Y-%m-%d")
# 计算30天前的本地日期(用于开户日判断)
thirty_days_ago_local = (current_local - timedelta(days=30)).date()
abnormal_data = []
for index, row in data_JCB.iterrows():
try:
# 开户日是本地日期字符串,解析为 date 对象
open_date = datetime.strptime(str(row['开户日']), "%Y-%m-%d").date()
except (ValueError, TypeError):
continue # 跳过无效日期
if (
open_date < thirty_days_ago_local
and row['近30天开单天数'] == 0
and row['客户状态'] == "留存"
):
new_row = row.copy()
new_row["日期"] = open_date.strftime("%Y-%m-%d")
abnormal_data.append(new_row)
abnormal_data = pd.DataFrame(abnormal_data) if abnormal_data else pd.DataFrame()
if not abnormal_data.empty:
abnormal_data["表单类型"] = "异常待办"
abnormal_data["派发日期"] = current_date_str
# 清洗手机号(仅去除浮点型 .0
def clean_phone(x):
if pd.isna(x) or x == "" or x == "None":
return ""
s = str(x)
if s.endswith('.0') and s[:-2].isdigit():
return s[:-2]
return s
abnormal_data['联系手机号'] = abnormal_data['联系手机号'].apply(clean_phone)
# 构建云端已派发记录 DataFrame
df_cloud = pd.DataFrame([
{
"数据id": item.get("_id", ""),
"账号": item.get("_widget_1739258942667", ""),
"提交时间": item.get("createTime", ""),
"表单类型": item.get("_widget_1739951204545", "")
}
for item in self.daily_revisit_list
])
recent_accounts = set()
if not df_cloud.empty and not abnormal_data.empty:
# 将 createTime 转为 UTC 时间(强制统一时区)
df_cloud["提交时间"] = pd.to_datetime(df_cloud["提交时间"], utc=True, errors="coerce")
df_cloud = df_cloud.dropna(subset=["提交时间"])
# 筛选“异常待办”
df_abnormal_cloud = df_cloud[df_cloud["表单类型"] == "异常待办"]
if not df_abnormal_cloud.empty:
# 每个账号保留最新一条
df_recent = df_abnormal_cloud.sort_values("提交时间").groupby("账号", as_index=False).tail(1)
current_utc = datetime.now(timezone.utc)
cutoff_utc = pd.Timestamp(current_utc) - pd.Timedelta(days=30)
# 安全比较:两边都是 UTC
recent_accounts = set(df_recent[df_recent["提交时间"] > cutoff_utc]["账号"])
# 剔除已派发账号 + 过滤有效手机号
if not abnormal_data.empty:
abnormal_data = abnormal_data[
(~abnormal_data["账号"].isin(recent_accounts)) &
(abnormal_data["联系手机号"].notna()) &
(abnormal_data["联系手机号"] != "") &
(abnormal_data["联系手机号"] != "None")
]
# # 保存结果
output_path = os.path.join(output_dir, "异常待办1.csv")
abnormal_data.to_csv(output_path, index=False)
# 发送或跳过
if not abnormal_data.empty:
abnormal_data = abnormal_data[:40]
# self.send_request(abnormal_data)
logger.info(f"异常待办完成,共 {len(abnormal_data)}")
else:
logger.info("异常待办无数据,跳过")
# 优质客户转商机
# current_date = datetime.now()
thirty_days_ago = current_date - timedelta(days=30)
sixty_days_ago = current_date - timedelta(days=60)
thirty_days_ago = thirty_days_ago.date()
sixty_days_ago = sixty_days_ago.date()
customer_to_opportunity = []
for index, row in data_JCB.iterrows():
new_row = row.copy()
# 先转成字符串,再解析回 date 对象
new_row['到期日'] = datetime.strptime(str(row['到期日']), "%Y-%m-%d").date()
if new_row['到期日'] == thirty_days_ago and row['近一周开单量'] >= 3 and row[
'G状态:近30天开单大于等于10天'] == 1:
print(row['账号'], row['到期日'], row['当月开单天数'], row['当月G天数'])
row["日期"] = datetime.strptime(str(row['开户日']), "%Y-%m-%d").date()
row['日期'] = row["日期"].strftime("%Y-%m-%d")
customer_to_opportunity.append(row)
# 推送给客服
pass
if new_row['到期日'] == sixty_days_ago and row['近一周开单量'] >= 3 and row[
'G状态:近30天开单大于等于10天'] == 1:
print(row['账号'], row['到期日'], row['当月开单天数'], row['当月G天数'])
row["日期"] = datetime.strptime(str(row['开户日']), "%Y-%m-%d").date()
row['日期'] = row["日期"].strftime("%Y-%m-%d")
customer_to_opportunity.append(row)
# 推送给客服
pass
customer_to_opportunity = pd.DataFrame(customer_to_opportunity) if customer_to_opportunity else None
if customer_to_opportunity is not None and not customer_to_opportunity.empty:
customer_to_opportunity["表单类型"] = "续约优质客户转商机"
customer_to_opportunity["派发日期"] = current_date_str
# self.send_request(customer_to_opportunity)
logger.info(f"优质客户转商机完成")
else:
logger.info(f"优质客户转商机无数据,跳过")
# 过期7天客服回访
# current_date = datetime.now()
seven_days_ago = current_date - timedelta(days=7)
seven_days_ago = seven_days_ago.date()
outdated_30 = []
for index, row in data_JCB.iterrows():
new_row = row.copy()
new_row['到期日'] = datetime.strptime(str(row['到期日']), "%Y-%m-%d").date()
# seven_days_ago = seven_days_ago.date()
# print(row['到期日'], seven_days_ago)
if new_row['到期日'] == seven_days_ago and row['客户状态'] == "过期":
print(row['账号'], row['到期日'], row['当月开单天数'], row['当月G天数'])
row["日期"] = datetime.strptime(str(row['开户日']), "%Y-%m-%d").date()
row['日期'] = row["日期"].strftime("%Y-%m-%d")
outdated_30.append(row)
# 推送给客服
pass
outdated_30 = pd.DataFrame(outdated_30) if outdated_30 else None
if outdated_30 is not None and not outdated_30.empty:
outdated_30["表单类型"] = "过期7天回访"
outdated_30["派发日期"] = current_date_str
# self.send_request(outdated_30)
logger.info(f"过期7天客服回访完成")
else:
logger.info(f"过期7天客服回访无数据,跳过")
common_module.send_task_status(task_start_time, "接车宝日常派发")
logger.info(f"接车宝日常派发执行完成")
except Exception as e:
common_module.send_task_error(task_start_time, "接车宝日常派发", str(e))
error_task_logger.error(f"接车宝日常派发执行出错:{e}")
@staticmethod
def row_to_dict(row, field_mapping):
"""将一行数据转换为指定格式的字典"""
result = {}
# print(field_mapping)
for col_name, widget_id in field_mapping.items():
# print(col_name, widget_id)
if col_name in row:
value = row[col_name]
clean_value = None if pd.isna(value) else value
result[widget_id] = {"value": clean_value}
return result
def fields(self):
self.field_mapping = {"日期": "_widget_1739252804406", "产品名称": "_widget_1739252804397",
"账号": "_widget_1739258942667", "联系手机号": "_widget_1739252804407",
"使用时长": "_widget_1739252804409", "开户日": "_widget_1739252804396",
"到期日": "_widget_1739252804408", "续约日": "_widget_1739252804410",
"客户状态": "_widget_1739252804400", "近一周开单量": "_widget_1739252804413",
"近一周是否活跃": "_widget_1739252804414",
"G状态:近30天开单大于等于10天": "_widget_1739252804415",
"当月开单天数": "_widget_1739252804416", "近30天开单天数": "_widget_1739252804417",
"当月G天数": "_widget_1739252804418", "日分区": "_widget_1739252804419",
"表单类型": "_widget_1739951204545", "派发日期": "_widget_1740036367181",
"跟进人": "_widget_1740043340255",
}
if __name__ == "__main__":
start = JCBEfficientCarPickup()
start.main()
+52 -24
View File
@@ -16,6 +16,7 @@ os.makedirs(output_dir, exist_ok=True)
class RenewalToDo:
def __init__(self):
self.renewal_data_list = None
self.cyclic_increasing = None
self.franchisee = None
self.last_price = None
@@ -75,9 +76,13 @@ class RenewalToDo:
"业务类型(续约、升级)": "_widget_1764820541680",
"连锁门店待办同步处理": "_widget_1764820541681",
"选择需要同步的门店名称": "_widget_1765330820391",
"过期日后90天日期": "_widget_1764820541865",
"120天自动流转时间": "_widget_1764820541865",
"60天自动流转时间": "_widget_1765964381895",
"30天自动流转时间": "_widget_1765964381896",
"0天自动流转时间": "_widget_1765964381897",
"当前所处节点": "_widget_1765352838609",
"流程状态": "_widget_1765352838610",
"经营模式": "_widget_1765964381952",
"提交人": "creator",
"提交时间": "createTime",
"更新时间": "updateTime"
@@ -93,7 +98,7 @@ class RenewalToDo:
"contact_mobile": "联系手机号",
"service_impl_principal": "专属运营顾问",
"technician": "运营专家",
"expiry_time_plus_90d": "过期日后90天日期",
"manage_model": "经营模式",
}
self.subform_field_map = {
"商品名称": "_widget_1764820541719",
@@ -105,6 +110,9 @@ class RenewalToDo:
"续约后订单编码": "_widget_1764820541725",
# 根据实际需要添加更多字段
}
self.renewal_list_map ={
}
def load_all_data(self):
"""
@@ -144,6 +152,15 @@ class RenewalToDo:
print("加载省市区人员关系表失败")
self.province_staff_id_list = []
# 获取已派发续约待办(进行中)
payload = {"api_key": "6694d3c4fcb69ca9a111a6c4",
"entry_id": "6769204a1902c9341340a1bc",
"filter": {"rel": "and",
"cond": [{"field": "flowState", "type": "flowstate", "method": "eq", "value": [0]}]},
}
renewal = api_instance.entry_data_list(payload)
self.renewal_data_list = renewal.get("data")
@staticmethod
def replace_names_with_staff_ids(df, name_columns, staff_id_list):
"""
@@ -178,11 +195,8 @@ class RenewalToDo:
return df
@staticmethod
def row_to_dict(row, field_mapping, subform_fields=None):
def row_to_dict(row, field_mapping):
"""将一行数据转换为指定格式的字典"""
if subform_fields is None:
subform_fields = set()
result = {}
for col_name, widget_id in field_mapping.items():
if col_name not in row:
@@ -201,11 +215,8 @@ class RenewalToDo:
else:
clean_value = value
# 子表单字段直接赋值,其他包 {"value": ...}
if widget_id in subform_fields:
result[widget_id] = clean_value
else:
result[widget_id] = {"value": clean_value}
# 所有字段统一包 {"value": ...},包括子表单
result[widget_id] = {"value": clean_value}
return result
@staticmethod
@@ -325,10 +336,13 @@ class RenewalToDo:
.dt.tz_convert('UTC')
)
# 新增列:过期日后90天
data_NGV['过期日后90天日期'] = data_NGV['过期日'] + pd.Timedelta(days=90)
# 新增4列:辅助时间字段
data_NGV['120天自动流转时间'] = data_NGV['过期日'] - pd.Timedelta(days=60)
data_NGV['60天自动流转时间'] = data_NGV['过期日'] - pd.Timedelta(days=30)
data_NGV['30天自动流转时间'] = data_NGV['过期日'] - pd.Timedelta(days=0)
data_NGV['0天自动流转时间'] = data_NGV['过期日'] + pd.Timedelta(days=90)
# 格式化为字符串(去掉时区)
for col in ['过期日', '过期日后90天日期']:
for col in ['过期日', '120天自动流转时间', '60天自动流转时间', '30天自动流转时间', '0天自动流转时间']:
data_NGV[col] = data_NGV[col].dt.strftime('%Y-%m-%d %H:%M:%S')
# 新增加盟商列
@@ -339,11 +353,24 @@ class RenewalToDo:
)
# 新增上次购买价格列
data_NGV = data_NGV.merge(
self.last_price[['门店编码', '上次购买价格']],
on='门店编码',
how='left'
# 1. 清洗并拼接类型+价格
df_lp = self.last_price[['门店编码', '类型', '价格']].copy()
df_lp['类型'] = df_lp['类型'].fillna('').astype(str)
df_lp['价格'] = (
pd.to_numeric(df_lp['价格'], errors='coerce')
.round().fillna(0).astype(int).astype(str)
)
df_lp['类型_价格'] = df_lp['类型'] + df_lp['价格']
# 2. 按门店聚合,分号连接
agg_df = df_lp.groupby('门店编码', as_index=False)['类型_价格'].apply(';'.join)
# 3. 合并回主表
data_NGV = data_NGV.merge(agg_df, on='门店编码', how='left').fillna({'类型_价格': ''})
data_NGV.rename(columns={'类型_价格': '上次购买价格'}, inplace=True)
# 4. 处理没有匹配记录的门店(填空或默认值)
data_NGV['上次购买价格'] = data_NGV['上次购买价格'].fillna('')
# 成员字段替换(现在列名是中文)
staff_name_cols = [
@@ -422,9 +449,6 @@ class RenewalToDo:
return None
# === Step 3: 遍历主表每一行,构建最终提交记录 ===
# 定义哪些字段是子表单(widget ID 集合)
subform_widget_ids = {self.field_map["周期性增购"]} # 即 {"_widget_1764820541717"}
for _, row in data_NGV.iterrows():
row_dict = row.to_dict()
@@ -441,11 +465,11 @@ class RenewalToDo:
if not customer_service:
no_customer_service_data.append(row_dict)
# 3.3 注入周期性增购子表单(直接赋 list,不转字符串!)
# 3.3 注入周期性增购子表单
row_dict["周期性增购"] = cyclic_subforms.get(org_code, [])
# 3.4 转换为 widget 格式(注意传入 subform_widget_ids
widget_record = self.row_to_dict(row_dict, self.field_map, subform_fields=subform_widget_ids)
# 3.4 转换为 widget 格式
widget_record = self.row_to_dict(row_dict, self.field_map)
records.append(widget_record)
# === Step 4: 批量提交 ===
@@ -464,6 +488,7 @@ class RenewalToDo:
logger.info(f"已提交 {len(records)} 条数据进行派发")
def main(self):
task_start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
try:
logger.info("任务开始")
@@ -474,6 +499,9 @@ class RenewalToDo:
data_NGV = self.process_data()
# step3:数据派发
self.dispatch_task(data_NGV)
# step4:过期日发生变化更新已有表单
# step5:自动同意原表单
common_module.send_task_status(task_start_time, "续约回访待办")
except Exception as e: