Compare commits

...

2 Commits

Author SHA1 Message Date
panda 82c6c5f94a 接车宝派发更新 2025-12-29 15:48:07 +08:00
panda 3462c8df55 .gitignore&修复mdwule引用 2025-12-26 10:17:30 +08:00
7 changed files with 9891 additions and 20 deletions
+106 -1
View File
@@ -4,6 +4,9 @@ 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()
@@ -24,6 +27,7 @@ class JCBEfficientCarPickup:
def __init__(self):
# 使用 pymysql 连接数据库
self.daily_revisit_list = None
self.field_mapping = {}
self.staff_id_list = None
self.customer_service_list = None
@@ -108,7 +112,8 @@ class JCBEfficientCarPickup:
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 = {'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)
@@ -137,6 +142,14 @@ class JCBEfficientCarPickup:
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:
@@ -145,6 +158,7 @@ class JCBEfficientCarPickup:
if data_JCB is None:
logger.error("获取接车宝数据失败,返回None")
raise ValueError("获取接车宝数据失败,返回None")
self.load_all_data()
logger.info(f"数据加载完成")
@@ -181,6 +195,97 @@ class JCBEfficientCarPickup:
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[:20]
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)
+1 -1
View File
@@ -274,7 +274,7 @@ class CommonModule:
else: # 如果没有数据,返回空 DataFrame
data_NGV = pd.DataFrame()
headers = [
"门店编码", "类型", "占位", "价格"
"门店编码", "类型", "订单商品名称", "价格"
]
data_NGV.columns = headers
+9252
View File
File diff suppressed because one or more lines are too long
@@ -112,7 +112,9 @@ class JCBEfficientCarPickup:
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 = {'api_key': Config.EFFICIENT_CAR_PICKUP_APP_ID,
# 'entry_id': "69522f61d0195d3bf42ed251",
'entry_id': Config.EFFICIENT_CAR_PICKUP_ENTRY_ID,
"data_list": new_sign_abnormal_data}
result = api_instance.entry_data_batch_create(data)
@@ -189,7 +191,7 @@ class JCBEfficientCarPickup:
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) # 发送请求
self.send_request(new_sign_abnormal) # 发送请求
logger.info(f"新签异常待办回访完成")
else:
logger.info(f"新签异常待办回访无数据,跳过")
@@ -279,8 +281,8 @@ class JCBEfficientCarPickup:
# 发送或跳过
if not abnormal_data.empty:
abnormal_data = abnormal_data[:40]
# self.send_request(abnormal_data)
abnormal_data = abnormal_data[:20]
self.send_request(abnormal_data)
logger.info(f"异常待办完成,共 {len(abnormal_data)}")
else:
logger.info("异常待办无数据,跳过")
@@ -317,7 +319,7 @@ class JCBEfficientCarPickup:
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)
self.send_request(customer_to_opportunity)
logger.info(f"优质客户转商机完成")
else:
logger.info(f"优质客户转商机无数据,跳过")
@@ -344,7 +346,7 @@ class JCBEfficientCarPickup:
if outdated_30 is not None and not outdated_30.empty:
outdated_30["表单类型"] = "过期7天回访"
outdated_30["派发日期"] = current_date_str
# self.send_request(outdated_30)
self.send_request(outdated_30)
logger.info(f"过期7天客服回访完成")
else:
logger.info(f"过期7天客服回访无数据,跳过")
+105
View File
@@ -0,0 +1,105 @@
{
"cells": [
{
"cell_type": "code",
"id": "initial_id",
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2025-12-17T08:57:05.953495Z",
"start_time": "2025-12-17T08:57:05.580269Z"
}
},
"source": [
"import pandas as pd\n",
"import mysql.connector\n",
"from mysql.connector import Error\n",
"\n",
"# 连接信息\n",
"HS_DB_Config = {\n",
" 'host': \"f6-public.rwlb.rds.aliyuncs.com\",\n",
" 'user': \"rw_operation_data_relay\",\n",
" 'password': \"m+q5Z4%IVuF9bf\",\n",
" 'database': \"f6operation_data_relay\"\n",
" } # 衡时数据库链接配置-mysql\n",
"table_name = \"saas_period_product_fenmu\" # 请替换为实际的表名\n",
"# table_name = \"yida_process_time_statistics\"\n",
"\n",
"# 连接\n",
"connection = mysql.connector.connect(\n",
" host=HS_DB_Config[\"host\"],\n",
" user=HS_DB_Config[\"user\"],\n",
" password=HS_DB_Config[\"password\"],\n",
" database=HS_DB_Config[\"database\"]\n",
")\n",
"\n",
"print(f\"成功连接 {HS_DB_Config['database']}\")\n",
"cursor = connection.cursor()\n",
"\n",
"# 读取Excel文件\n",
"df = pd.read_excel(\n",
" r\"C:\\Users\\zy187\\Desktop\\应续约信息-商户与商品-数据表格.xlsx\",\n",
" sheet_name=\"Sheet1\")\n",
"\n",
"# 处理空值 - 将NaN/NaT/空字符串统一转为None\n",
"df = df.map(lambda x: None if pd.isna(x) or str(x).strip() == '' else x)\n",
"\n",
"# 生成插入语句\n",
"columns = ', '.join(df.columns)\n",
"placeholders = ', '.join(['%s'] * len(df.columns))\n",
"insert_query = f\"INSERT INTO {table_name} ({columns}) VALUES ({placeholders})\"\n",
"\n",
"# 批量插入数据,每次1000条\n",
"records = [tuple(row) for row in df.values]\n",
"batch_size = 1000\n",
"total_records = len(records)\n",
"inserted_count = 0\n",
"\n",
"for i in range(0, total_records, batch_size):\n",
" batch = records[i:i+batch_size]\n",
" cursor.executemany(insert_query, batch)\n",
" connection.commit()\n",
" inserted_count += len(batch)\n",
" print(f\"已成功导入 {inserted_count}/{total_records} 条记录\")\n",
"\n",
"print(f\"总共成功导入 {inserted_count} 条记录到 {table_name} 表\")\n",
"\n",
"cursor.close()\n",
"connection.close()"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"成功连接 f6operation_data_relay\n",
"已成功导入 6/6 条记录\n",
"总共成功导入 6 条记录到 saas_period_product_fenmu 表\n"
]
}
],
"execution_count": 3
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+391
View File
@@ -0,0 +1,391 @@
{
"cells": [
{
"cell_type": "code",
"id": "initial_id",
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2025-12-25T06:14:43.010877600Z",
"start_time": "2025-12-25T06:14:42.705835200Z"
}
},
"source": [
"import json\n",
"import requests\n",
"\n",
"triggerConf = {\n",
"\"_widget_17666295456371\": \"675b900991ad2491c69389ca\",\n",
"\"_widget_17666295463651\": \"6931063d64187eaf6b927557\",\n",
"\"_widget_17666295471001\": \"694caec6d3f22e2e1723b776\",\n",
"\"_widget_17666325849471\": \"11240984669918342117\",\n",
"\"_widget_17666340965771\": \"新起点养车维修\"\n",
"}\n",
"\n",
"\n",
"# 获取输入参数(带默认空字符串)\n",
"api_key = str(triggerConf.get('_widget_17666295456371', '')).strip()\n",
"entry_id = str(triggerConf.get('_widget_17666295463651', '')).strip()\n",
"data_id = str(triggerConf.get('_widget_17666295471001', '')).strip()\n",
"company_id = str(triggerConf.get('_widget_17666325849471', '')).strip()\n",
"org_str = triggerConf.get('_widget_17666340965771', '')\n",
"\n",
"# 安全校验:必要参数不能为空\n",
"if not all([api_key, entry_id, data_id, company_id]):\n",
" raise ValueError(\"缺少必要参数:应用ID、表单ID、数据ID 或 公司ID 不能为空\")\n",
"\n",
"# 解析连锁店列表\n",
"org_list = []\n",
"if org_str:\n",
" if isinstance(org_str, str):\n",
" org_list = [item.strip() for item in org_str.split(',') if item.strip()]\n",
" elif isinstance(org_str, list):\n",
" org_list = [str(item).strip() for item in org_str if str(item).strip()]\n",
"\n",
"# 请求头\n",
"BEARER_TOKEN = \"qygHulymo1fekJk4CIZyNKjyQAzG8CFN\"\n",
"headers = {\n",
" 'Authorization': f'Bearer {BEARER_TOKEN}',\n",
" 'Content-Type': 'application/json'\n",
"}\n",
"\n",
"# 第一步:获取当前触发数据的内容\n",
"data_payload = {\n",
" \"app_id\": api_key,\n",
" \"entry_id\": entry_id,\n",
" \"data_id\": data_id\n",
"}\n",
"trigger_data_url = \"https://api.jiandaoyun.com/api/v5/app/entry/data/get\"\n",
"try:\n",
" data_response = requests.post(trigger_data_url, headers=headers, data=json.dumps(data_payload), timeout=10)\n",
" data_response.raise_for_status()\n",
" data_resp_json = data_response.json()\n",
" trigger_data = data_resp_json.get(\"data\", {})\n",
" if not trigger_data:\n",
" raise ValueError(\"未能获取到当前数据内容\")\n",
"except Exception as e:\n",
" raise RuntimeError(f\"获取当前数据失败: {str(e)}\")\n",
"\n",
"# 第二步:查询该公司下所有进行中的数据\n",
"url = \"https://api.jiandaoyun.com/api/v5/app/entry/data/list\"\n",
"payload = {\n",
" \"app_id\": api_key,\n",
" \"entry_id\": entry_id,\n",
" \"filter\": {\n",
" \"rel\": \"and\",\n",
" \"cond\": [\n",
" {\"field\": \"_widget_1766631811839\", \"type\": \"text\", \"method\": \"eq\", \"value\": company_id},\n",
" {\"field\": \"flowState\", \"type\": \"flowstate\", \"method\": \"eq\", \"value\": [0]}\n",
" ]\n",
" }\n",
"}\n",
"try:\n",
" response = requests.post(url, headers=headers, json=payload, timeout=10)\n",
" resp_json = response.json()\n",
" data_list = resp_json.get(\"data\", [])\n",
"except Exception as e:\n",
" raise RuntimeError(f\"查询进行中数据失败: {str(e)}\")\n",
"\n",
"# # 要同步的字段映射(目标字段ID -> 触发数据中的源字段ID\n",
"# field_mapping = {\n",
"# \"_widget_1764820541638\": \"_widget_1764820541638\", # 是否联系上\n",
"# \"_widget_1764820541641\": \"_widget_1764820541641\", # 现阶段问题\n",
"# \"_widget_1765330820509\": \"_widget_1765330820509\", # 未联系原因\n",
"# \"_widget_1764820541653\": \"_widget_1764820541653\", # 联系情况及问题说明\n",
"# \"_widget_1764820541697\": \"_widget_1764820541697\", # 回访方式\n",
"# \"_widget_1764820541657\": \"_widget_1764820541657\", # 潜在商机\n",
"# \"_widget_1764820541659\": \"_widget_1764820541659\", # 商机详情\n",
"# \"_widget_1764820541654\": \"_widget_1764820541654\", # 续约意愿\n",
"# \"_widget_1764820541700\": \"_widget_1764820541700\", # 不续约原因\n",
"# \"_widget_1764820541707\": \"_widget_1764820541707\", # 产品原因\n",
"# \"_widget_1764820541709\": \"_widget_1764820541709\", # 服务问题\n",
"# \"_widget_1764820541711\": \"_widget_1764820541711\", # 门店原因\n",
"# \"_widget_1764820541713\": \"_widget_1764820541713\", # 价格原因\n",
"# \"_widget_1764820541702\": \"_widget_1764820541702\", # 不续约具体情况说明\n",
"# }\n",
"#\n",
"# # 遍历匹配的连锁店数据\n",
"# for data in data_list:\n",
"# store_code = data.get(\"_widget_1764820541617\")\n",
"# if not store_code or store_code not in org_list:\n",
"# continue\n",
"#\n",
"# new_data_id = data.get(\"_id\")\n",
"# if not new_data_id:\n",
"# continue\n",
"#\n",
"# # 构建更新 payload(仅包含非 None 值)\n",
"# update_data = {}\n",
"# for target_field, source_field in field_mapping.items():\n",
"# value = trigger_data.get(source_field)\n",
"# # 如果值为 None 或空字符串(根据业务决定是否跳过)\n",
"# if value is not None:\n",
"# update_data[target_field] = {\"value\": value}\n",
"#\n",
"# if not update_data:\n",
"# continue # 无有效字段可更新\n",
"#\n",
"# modify_payload = {\n",
"# \"app_id\": api_key,\n",
"# \"entry_id\": entry_id,\n",
"# \"data_id\": new_data_id,\n",
"# \"data\": update_data\n",
"# }\n",
"#\n",
"# # 同步字段\n",
"# try:\n",
"# modif_url = \"https://api.jiandaoyun.com/api/v5/app/entry/data/update\"\n",
"# res = requests.post(modif_url, headers=headers, data=json.dumps(modify_payload), timeout=10)\n",
"# res.raise_for_status()\n",
"# except Exception as e:\n",
"# print(f\"更新数据 {new_data_id} 失败: {e}\")\n",
"# continue # 继续处理下一个,不中断\n",
"#\n",
"# # 第三步:尝试自动审批(仅当处于指定节点)\n",
"# try:\n",
"# instance_url = \"https://api.jiandaoyun.com/api/v6/workflow/instance/get\"\n",
"# instance_payload = {\n",
"# \"instance_id\": new_data_id,\n",
"# \"tasks_type\": 1\n",
"# }\n",
"# instance_res = requests.post(instance_url, headers=headers, data=json.dumps(instance_payload), timeout=10)\n",
"# instance_res.raise_for_status()\n",
"# instance_data = instance_res.json()\n",
"# tasks = instance_data.get(\"tasks\", [])\n",
"#\n",
"# if not tasks:\n",
"# continue\n",
"#\n",
"# last_task = tasks[-1]\n",
"# flow_id = last_task.get(\"flow_id\")\n",
"# task_id = last_task.get(\"task_id\")\n",
"# assignee_info = last_task.get(\"assignee\") or {}\n",
"# username = assignee_info.get(\"username\")\n",
"#\n",
"# if flow_id in [1, 2, 3, 9] and task_id and username:\n",
"# allow_url = \"https://api.jiandaoyun.com/api/v1/workflow/task/approve\"\n",
"# allow_payload = {\n",
"# \"username\": username,\n",
"# \"instance_id\": new_data_id,\n",
"# \"task_id\": task_id\n",
"# }\n",
"# allo_result = requests.post(allow_url, headers=headers, data=json.dumps(allow_payload), timeout=10)\n",
"# allo_result.raise_for_status()\n",
"# print(f\"自动审批成功: data_id={new_data_id}, task_id={task_id}\")\n",
"# else:\n",
"# print(f\"跳过自动审批: flow_id={flow_id}, task_id={task_id}, username={username}\")\n",
"#\n",
"# except Exception as e:\n",
"# print(f\"自动审批失败 (data_id={new_data_id}): {e}\")\n"
],
"outputs": [],
"execution_count": 8
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-12-25T06:14:45.807079300Z",
"start_time": "2025-12-25T06:14:45.761404700Z"
}
},
"cell_type": "code",
"source": "resp_json",
"id": "d4299d8a0596e5f8",
"outputs": [
{
"data": {
"text/plain": [
"{'data': [{'_id': '694caec6d3f22e2e1723b775',\n",
" 'creator': {'name': 'F6汽车科技', 'username': '#admin', 'status': 1, 'type': 0},\n",
" 'updater': {'name': 'F6汽车科技', 'username': '#admin', 'status': 1, 'type': 0},\n",
" 'deleter': None,\n",
" 'createTime': '2025-12-25T03:25:58.151Z',\n",
" 'updateTime': '2025-12-25T05:57:01.097Z',\n",
" 'deleteTime': None,\n",
" 'flowState': 0,\n",
" '_widget_1764820541663': '680f49b134bee97fbb857ef1',\n",
" '_widget_1764820541616': '新起点养车',\n",
" '_widget_1764820541617': '新起点养车维修',\n",
" '_widget_1764820541661': 'CHS202411050285054',\n",
" '_widget_1764820541618': '',\n",
" '_widget_1764820541672': '2026-04-23T16:00:00.000Z',\n",
" '_widget_1764820541623': '基础版',\n",
" '_widget_1764820541624': 'SaaS新签:999',\n",
" '_widget_1764820541621': '王旭',\n",
" '_widget_1764820541622': '19819386119',\n",
" '_widget_1764820541625': {'name': '邵宝振',\n",
" 'username': '032861373036352679',\n",
" 'status': 1,\n",
" 'type': 0,\n",
" 'departments': [122333676],\n",
" 'integrate_id': '032861373036352679'},\n",
" '_widget_1766631811839': '11240984669918342117',\n",
" '_widget_1764820541628': '',\n",
" '_widget_1764820541634': None,\n",
" '_widget_1765352838631': None,\n",
" '_widget_1764820541630': '',\n",
" '_widget_1764820541635': None,\n",
" '_widget_1765352838632': None,\n",
" '_widget_1764820541632': '',\n",
" '_widget_1764820541636': None,\n",
" '_widget_1765352838633': None,\n",
" '_widget_1764820541638': '',\n",
" '_widget_1764820541641': '',\n",
" '_widget_1765330820509': '',\n",
" '_widget_1764820541653': '',\n",
" '_widget_1764820541697': '',\n",
" '_widget_1764820541657': '',\n",
" '_widget_1764820541659': '',\n",
" '_widget_1764820541654': '',\n",
" '_widget_1764820541700': '',\n",
" '_widget_1764820541707': '',\n",
" '_widget_1764820541709': '',\n",
" '_widget_1764820541711': '',\n",
" '_widget_1764820541713': '',\n",
" '_widget_1764820541702': '',\n",
" '_widget_1764820541717': [],\n",
" '_widget_1764820541681': '',\n",
" '_widget_1765330820391': [],\n",
" '_widget_1764820541674': '',\n",
" '_widget_1764820541679': None,\n",
" '_widget_1764820541676': '',\n",
" '_widget_1764820541680': '',\n",
" '_widget_1764820541865': '2026-02-22T16:00:00.000Z',\n",
" '_widget_1765964381895': '2026-03-24T16:00:00.000Z',\n",
" '_widget_1765964381896': '2026-04-23T16:00:00.000Z',\n",
" '_widget_1765964381897': '2026-07-22T16:00:00.000Z',\n",
" '_widget_1765352838609': '',\n",
" '_widget_1765352838610': '',\n",
" '_widget_1765964381952': '单店',\n",
" '_widget_1765866283543': '',\n",
" '_widget_1764820541715': {'name': '申晓勇',\n",
" 'username': '171201105229666119',\n",
" 'status': 1,\n",
" 'type': 0,\n",
" 'departments': [177751223],\n",
" 'integrate_id': '171201105229666119'},\n",
" '_widget_1766130435561': '普通客户(VIP',\n",
" '_widget_1764820541678': {'name': '王斌',\n",
" 'username': '1253235059942945',\n",
" 'status': 1,\n",
" 'type': 0,\n",
" 'departments': [122333676],\n",
" 'integrate_id': '1253235059942945'},\n",
" '_widget_1766376046563': '00059',\n",
" '_widget_1766469131897': '否',\n",
" '_widget_1766633812134': '',\n",
" 'appId': '675b900991ad2491c69389ca',\n",
" 'entryId': '6931063d64187eaf6b927557'},\n",
" {'_id': '694caec6d3f22e2e1723b776',\n",
" 'creator': {'name': 'F6汽车科技', 'username': '#admin', 'status': 1, 'type': 0},\n",
" 'updater': {'name': 'F6汽车科技', 'username': '#admin', 'status': 1, 'type': 0},\n",
" 'deleter': None,\n",
" 'createTime': '2025-12-25T03:25:58.151Z',\n",
" 'updateTime': '2025-12-25T05:58:40.218Z',\n",
" 'deleteTime': None,\n",
" 'flowState': 0,\n",
" '_widget_1764820541663': '680f48b90a8a958979d7a2d3',\n",
" '_widget_1764820541616': '武陵区隆铖改汽车俱乐部',\n",
" '_widget_1764820541617': '武陵区隆铖改汽车俱乐部',\n",
" '_widget_1764820541661': 'CHS202504240297172',\n",
" '_widget_1764820541618': '',\n",
" '_widget_1764820541672': '2026-04-23T16:00:00.000Z',\n",
" '_widget_1764820541623': '进阶版',\n",
" '_widget_1764820541624': 'SaaS新签:1199',\n",
" '_widget_1764820541621': '朱松松',\n",
" '_widget_1764820541622': '18932158817',\n",
" '_widget_1764820541625': {'name': '胡冰',\n",
" 'username': '19212436541043695',\n",
" 'status': 1,\n",
" 'type': 0,\n",
" 'departments': [122503482],\n",
" 'integrate_id': '19212436541043695'},\n",
" '_widget_1766631811839': '11240984669918342117',\n",
" '_widget_1764820541628': '主动',\n",
" '_widget_1764820541634': None,\n",
" '_widget_1765352838631': None,\n",
" '_widget_1764820541630': '主动',\n",
" '_widget_1764820541635': None,\n",
" '_widget_1765352838632': None,\n",
" '_widget_1764820541632': '主动',\n",
" '_widget_1764820541636': None,\n",
" '_widget_1765352838633': None,\n",
" '_widget_1764820541638': '否',\n",
" '_widget_1764820541641': '',\n",
" '_widget_1765330820509': '空号/停机/关机',\n",
" '_widget_1764820541653': '234',\n",
" '_widget_1764820541697': 'udesk外呼',\n",
" '_widget_1764820541657': '',\n",
" '_widget_1764820541659': '',\n",
" '_widget_1764820541654': '否',\n",
" '_widget_1764820541700': '服务问题',\n",
" '_widget_1764820541707': '',\n",
" '_widget_1764820541709': '联系不上运营顾问',\n",
" '_widget_1764820541711': '',\n",
" '_widget_1764820541713': '',\n",
" '_widget_1764820541702': '',\n",
" '_widget_1764820541717': [],\n",
" '_widget_1764820541681': '是',\n",
" '_widget_1765330820391': [],\n",
" '_widget_1764820541674': '',\n",
" '_widget_1764820541679': None,\n",
" '_widget_1764820541676': '',\n",
" '_widget_1764820541680': '',\n",
" '_widget_1764820541865': '2026-02-22T16:00:00.000Z',\n",
" '_widget_1765964381895': '2026-03-24T16:00:00.000Z',\n",
" '_widget_1765964381896': '2026-04-23T16:00:00.000Z',\n",
" '_widget_1765964381897': '2026-07-22T16:00:00.000Z',\n",
" '_widget_1765352838609': '',\n",
" '_widget_1765352838610': '',\n",
" '_widget_1765964381952': '单店',\n",
" '_widget_1765866283543': '',\n",
" '_widget_1764820541715': {'name': '孙海程',\n",
" 'username': '084119070623372941',\n",
" 'status': 1,\n",
" 'type': 0,\n",
" 'departments': [177751223],\n",
" 'integrate_id': '084119070623372941'},\n",
" '_widget_1766130435561': '普通客户(VIP',\n",
" '_widget_1764820541678': {'name': '金华斌',\n",
" 'username': '3704680936560271',\n",
" 'status': 1,\n",
" 'type': 0,\n",
" 'departments': [122503482],\n",
" 'integrate_id': '3704680936560271'},\n",
" '_widget_1766376046563': '00060',\n",
" '_widget_1766469131897': '否',\n",
" '_widget_1766633812134': '',\n",
" 'appId': '675b900991ad2491c69389ca',\n",
" 'entryId': '6931063d64187eaf6b927557'}]}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 9
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+28 -12
View File
@@ -85,6 +85,7 @@ class RenewalToDo:
"经营模式": "_widget_1765964381952",
"公司等级": "_widget_1766130435561",
"公司id": "_widget_1766631811839",
"订单商品名称":"_widget_1766730385209",
"提交人": "creator",
"提交时间": "createTime",
"更新时间": "updateTime"
@@ -346,9 +347,9 @@ class RenewalToDo:
data_NGV['30天自动流转时间'] = data_NGV['过期日'] - pd.Timedelta(days=0)
data_NGV['0天自动流转时间'] = data_NGV['过期日'] + pd.Timedelta(days=90)
data_NGV['120天是否跟进'] = ""
data_NGV['60天是否跟进']= ""
data_NGV['30天是否跟进']= ""
data_NGV['120天是否跟进'] = ""
data_NGV['60天是否跟进']= ""
data_NGV['30天是否跟进']= ""
# 格式化为字符串(去掉时区)
for col in ['过期日', '120天自动流转时间', '60天自动流转时间', '30天自动流转时间', '0天自动流转时间']:
data_NGV[col] = data_NGV[col].dt.strftime('%Y-%m-%d %H:%M:%S')
@@ -361,24 +362,39 @@ class RenewalToDo:
)
# 新增上次购买价格列
# 1. 清洗并拼接类型+价格
df_lp = self.last_price[['门店编码', '类型', '价格']].copy()
# 1. 清洗数据
df_lp = self.last_price[['门店编码', '类型', '订单商品名称', '价格']].copy()
# 处理“类型”和“订单商品名称”的缺失值
df_lp['类型'] = df_lp['类型'].fillna('').astype(str)
df_lp['订单商品名称'] = df_lp['订单商品名称'].fillna('').astype(str)
# 处理价格:转数字、四舍五入、填0、转字符串
df_lp['价格'] = (
pd.to_numeric(df_lp['价格'], errors='coerce')
.round().fillna(0).astype(int).astype(str)
)
# 2. 拼接“类型:价格”
df_lp['类型_价格'] = df_lp['类型'] + ':' + df_lp['价格']
# 2. 按门店聚合,分号连接
agg_df = df_lp.groupby('门店编码', as_index=False)['类型_价格'].apply(';'.join)
# 3. 按门店聚合两列
agg_df = df_lp.groupby('门店编码', as_index=False).agg({
'类型_价格': lambda x: ';'.join(x),
'订单商品名称': lambda x: ';'.join(x)
})
# 3. 合并回主表
data_NGV = data_NGV.merge(agg_df, on='门店编码', how='left').fillna({'类型_价格': ''})
data_NGV.rename(columns={'类型_价格': '上次购买价格'}, inplace=True)
# 4. 合并回主表
data_NGV = data_NGV.merge(agg_df, on='门店编码', how='left')
# 4. 处理没有匹配记录的门店(填空或默认值)
data_NGV['上次购买价格'] = data_NGV['上次购买价格'].fillna('')
# 5. 填充缺失值为空字符串,并重命名列
data_NGV['类型_价格'] = data_NGV['类型_价格'].fillna('')
data_NGV['订单商品名称'] = data_NGV['订单商品名称'].fillna('')
data_NGV.rename(columns={
'类型_价格': '上次购买价格',
'订单商品名称': '订单商品名称'
}, inplace=True)
# 成员字段替换(现在列名是中文)
staff_name_cols = [