diff --git a/.idea/csv-editor.xml b/.idea/csv-editor.xml index 632bb9b..dc000ef 100644 --- a/.idea/csv-editor.xml +++ b/.idea/csv-editor.xml @@ -24,6 +24,13 @@ + + + + + + diff --git a/back_ground_module/common_module.py b/back_ground_module/common_module.py index d7828ae..54960c5 100644 --- a/back_ground_module/common_module.py +++ b/back_ground_module/common_module.py @@ -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 diff --git a/test/result.json b/test/result.json index 1b1d0c8..420ba75 100644 --- a/test/result.json +++ b/test/result.json @@ -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' } diff --git a/test/借车宝异常待办(已改完待上线).py b/test/借车宝异常待办(已改完待上线).py new file mode 100644 index 0000000..8b3f23b --- /dev/null +++ b/test/借车宝异常待办(已改完待上线).py @@ -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() diff --git a/test/续约待办派发.py b/test/续约待办派发.py index cecb317..c42e1e3 100644 --- a/test/续约待办派发.py +++ b/test/续约待办派发.py @@ -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: diff --git a/test/续约待办派发测试数据.py b/test/续约待办派发测试数据.py new file mode 100644 index 0000000..e69de29