{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "initial_id", "metadata": { "collapsed": true }, "outputs": [], "source": [ "import datetime\n", "import os\n", "import time\n", "import requests\n", "from api import API\n", "from back_ground_module import CommonModule\n", "import pandas as pd\n", "from log_config import configure_task_logger, configure_error_task_logger\n", "\n", "api_instance = API()\n", "common_module = CommonModule()\n", "# start_time = datetime.datetime.now()\n", "\n", "# 获取已经配置好的常规日志记录器\n", "logger = configure_task_logger()\n", "\n", "# 获取已经配置好的错误任务日志记录器\n", "error_task_logger = configure_error_task_logger()\n", "output_dir = \"output\" # 设置输出目录\n", "os.makedirs(output_dir, exist_ok=True)\n", "\n", "class NewExceptionTask:\n", " \"\"\"\n", " SaaS异常回访\n", " \"\"\"\n", "\n", " def __init__(self):\n", " self.exception_service_todo = None\n", " self.get_feature_usage = None\n", " self.saas_create_time = None\n", " self.index = None\n", " self.date_one = None\n", " self.data_yichang_S = None\n", " self.date_list = None\n", " self.Smart_detection = None\n", " self.service_remind = None\n", " self.NGV_data_list = None\n", " self.permissions_table = None\n", " self.staff_id_list = None\n", " self.json_list = []\n", " self.policy_recognition = None\n", " self.widget_list = None\n", " self.private_domain = None\n", " self.public_domain = None\n", " self.public_domain_list = None\n", " self.different_industries = None\n", " self.different_industries_list = None\n", " self.groupnotification = None\n", " self.fields_mapping = {\n", " \"门店名称\": \"_widget_1748241895830\",\n", " \"联系人\": \"_widget_1748241895831\",\n", " \"开户时间\": \"_widget_1748241895839\",\n", " \"门店编码\": \"_widget_1748241895842\",\n", " \"联系方式\": \"_widget_1748241895832\",\n", " \"系统版本\": \"_widget_1748241895850\",\n", " \"公司名称\": \"_widget_1748241895844\",\n", " \"运营顾问\": \"_widget_1748246808679\",\n", " \"区域经理\": \"_widget_1748246808682\",\n", " \"公司等级\": \"_widget_1748241895846\",\n", " \"运营专家\": \"_widget_1748246808681\",\n", " \"操作模式E.L/E.S\": \"_widget_1748241895853\",\n", " \"活跃健康状态变化\": \"_widget_1748241895829\",\n", " \"初始日\": \"_widget_1748241895833\",\n", " \"推进日\": \"_widget_1748241895834\",\n", " \"异常跟进情况描述\": \"_widget_1748512176640\",\n", " \"异常变化原因\": \"_widget_1748512176641\",\n", " \"正常使用\": \"_widget_1748512176643\",\n", " \"门店原因\": \"_widget_1748512176645\",\n", " \"服务原因\": \"_widget_1748512176647\",\n", " \"产品原因\": \"_widget_1748512176649\",\n", " \"未正式切换\": \"_widget_1748512176651\",\n", " \"跟进状态\": \"_widget_1748512176655\",\n", " \"是否可激活\": \"_widget_1758615839701\",\n", " \"是否有续约风险\": \"_widget_1758615839703\",\n", " \"当前跟进人\": \"_widget_1748246808678\",\n", " \"激活策略\": \"_widget_1758615839717\",\n", " \"跟进时间\": \"_widget_1748512176654\",\n", " \"是否跟进完成\": \"_widget_1751273412737\",\n", " \"区域客服\": \"_widget_1748246808680\",\n", " \"大区\": \"_widget_1748241895847\",\n", " \"省\": \"_widget_1748241895848\",\n", " \"城市\": \"_widget_1748241895855\",\n", " \"门店类型\": \"_widget_1748241895849\",\n", " \"saas客户类型\": \"_widget_1748241895851\",\n", " \"门店阶段\": \"_widget_1748241895852\",\n", " \"提交人\": \"creator\",\n", " \"提交时间\": \"createTime\",\n", " \"更新时间\": \"updateTime\"\n", " }\n", "\n", " def calculate_date_one(self, start_offset=0):\n", " \"\"\"\n", " 计算从当前日期(或指定偏移量的日期)开始,往前遍历遇到date_list中日期的次数。\n", "\n", " 参数:\n", " - start_offset: 从当前日期起始的天数偏移量,默认为0(即今天)。负数表示过去,正数表示未来。\n", "\n", " 返回:\n", " - date_one: 遍历到date_list中日期的次数。\n", " \"\"\"\n", " jdy_date = datetime.datetime.now().strftime(\"%Y-%m-%d\")\n", " jdy_start_time = datetime.datetime.now().strftime(\"%Y-%m-%d \")\n", " # 设置起始日期\n", " now_time = datetime.datetime.now() + datetime.timedelta(days=start_offset)\n", "\n", " # 初始化计数器\n", " date_one = 1\n", " print(\"当前日期:\", now_time.strftime(\"%Y-%m-%d\"))\n", " # 检查起始日期是否在date_list中\n", " if now_time.strftime(\"%Y-%m-%d\") in self.date_list:\n", " date_one = 0\n", " print(\"开始次数:\", date_one)\n", "\n", " else:\n", " # 遍历日期\n", " for i in range(1, 10):\n", " new_date = now_time + datetime.timedelta(days=-i)\n", " new_date_str = new_date.strftime(\"%Y-%m-%d\")\n", " print(\"遍历日期:\", new_date_str)\n", " if new_date_str in self.date_list:\n", " date_one += 1\n", " print(\"节假日期:\", new_date_str)\n", " else:\n", " break\n", "\n", " print(\"遍历次数:\", date_one)\n", " return date_one\n", "\n", " @staticmethod\n", " def download_url_content(url, save_path):\n", " \"\"\"\n", " 下载指定 URL 的内容并保存到本地文件。\n", "\n", " :param url: 要下载内容的 URL\n", " :param save_path: 保存文件的路径\n", " \"\"\"\n", " try:\n", " # 发送 GET 请求以获取内容\n", " response = requests.get(url, stream=True)\n", " response.raise_for_status() # 如果响应状态码不是 200,抛出异常\n", "\n", " # 确保保存目录存在\n", " os.makedirs(os.path.dirname(save_path), exist_ok=True)\n", "\n", " # 将内容写入文件\n", " with open(save_path, 'wb') as file:\n", " for chunk in response.iter_content(chunk_size=8192): # 分块写入,避免占用过多内存\n", " if chunk: # 过滤掉空块\n", " file.write(chunk)\n", "\n", " print(f\"文件已成功保存到 {save_path}\")\n", "\n", " except requests.exceptions.RequestException as e:\n", " print(f\"下载失败: {e}\")\n", " except Exception as e:\n", " print(f\"发生错误: {e}\")\n", "\n", " def load_all_data(self):\n", " \"\"\"加载所有必要的数据表\"\"\"\n", " # 省市区人员关系表\n", " payload = {\"api_key\": \"675b900991ad2491c69389ca\", \"entry_id\": \"676512ac3e54dc3159460c0a\"}\n", " json_dict = api_instance.entry_data_list(payload)\n", " self.json_list = json_dict.get(\"data\")\n", "\n", " # 获取简道云员工id\n", " payload = {\"api_key\": \"6694d3c4fcb69ca9a111a6c4\",\n", " \"entry_id\": \"6769204a1902c9341340a1bc\",\n", " }\n", " staff_id = api_instance.entry_data_list(payload)\n", " self.staff_id_list = staff_id.get(\"data\") # api请求格式,将数据封装在data字典里\n", "\n", " # 获取NGV数据\n", " payload = {\"api_key\": \"675b900991ad2491c69389ca\", \"entry_id\": \"675bb02bd2d53c2034c665e4\"}\n", " self.NGV_data_list = api_instance.entry_data_list(payload).get(\"data\", [])\n", " # print(\"NGV获取后的类型:\", type(self.NGV_data_list))\n", "\n", " # 获取异常服务待办\n", " payload = {\"api_key\": \"675b900991ad2491c69389ca\", \"entry_id\": \"68340de79f116c0b66b6b0cc\"}\n", " self.exception_service_todo = api_instance.entry_data_list(payload).get(\"data\", [])\n", " print(self.exception_service_todo)\n", "\n", " @staticmethod\n", " def build_index(json_list):\n", " index = {}\n", " for json_item in json_list:\n", " try:\n", " key = (json_item['_widget_1734677164861'], json_item['_widget_1734677164862'],\n", " json_item['_widget_1734677164863']) # 省市区\n", " if '_widget_1734677164870' not in json_item: # 异常回访客服\n", " raise KeyError(\"缺少 '异常回访客服' 键\")\n", " index[key] = json_item\n", " except KeyError as e:\n", " print(f\"警告:{e},跳过该条记录: {json_item}\")\n", " continue\n", " print('index', index)\n", " return index\n", "\n", " @staticmethod\n", " def find_customer_service(province_name, city_name, area_name, index):\n", " key = (province_name, city_name, area_name)\n", " # print(index)\n", " if key not in index:\n", " return \"数据缺失: 未找到对应的异常回访客服\"\n", "\n", " return index[key]\n", "\n", " @staticmethod\n", " def get_staff_id(row_item, name):\n", " \"\"\"辅助函数,用于获取员工ID\"\"\"\n", " if str(row_item[\"_widget_1734942794144\"]) == str(name): # 检查姓名是否匹配\n", " return row_item[\"_widget_1734942794145\"] # 返回员工ID\n", " return None\n", "\n", " def assign_customer_service(self, province_name, city_name, area_name, index):\n", " \"\"\"根据省市区派发给异常回访客服\"\"\"\n", " # try:\n", " customer_service_info = self.find_customer_service(province_name, city_name, area_name, index)\n", " customer_service = customer_service_info.get('_widget_1734677164870', {}).get('username') # 异常回访客服\n", " return customer_service\n", " # except Exception as e:\n", " # print(f\"Error finding customer service: {e}\")\n", " # return \"分配失败,请检查\", \"分配失败,请检查\", \"分配失败,请检查\"\n", "\n", " def main(self):\n", "\n", " task_start_time = datetime.datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")\n", " try:\n", " self.load_all_data()\n", "\n", " data = common_module.get_yichang_details(days_back=1)\n", " self.data_yichang_S = pd.DataFrame() if data is None or data.empty else data.astype(str)\n", " self.index = self.build_index(self.json_list)\n", "\n", " logger.info(\"开始运行SaaS异常回访\")\n", " if self.data_yichang_S.empty:\n", " logger.info(\"未获取到数据或数据为空\")\n", " common_module.send_task_status(task_start_time, \"异常服务待办派发\")\n", " return\n", "\n", " data_yichang = self.data_yichang_S.copy()\n", " # data_yichang.to_csv(os.path.join(output_dir,\"data_yichang.csv\"), index=False)\n", "\n", " def replace_values(series):\n", " # 使用条件判断来进行替换\n", " return series.apply(lambda x: '' if pd.isna(x) or x in ['NA', 'None', ''] else x)\n", "\n", " # 对整个DataFrame的所有列应用替换函数\n", " data_yichang = data_yichang.apply(replace_values)\n", "\n", " for index_num, row in data_yichang.iterrows(): # 对过滤后的每一条进行派发\n", " try:\n", " # 每次循环前清空省市区变量\n", " province_name = None\n", " city_name = None\n", " area_name = None\n", "\n", " is_pass = False\n", " for exception_service in self.exception_service_todo :\n", " if exception_service['_widget_1748241895842'] == row['org_code'] and exception_service['_widget_1748512176655'] in ['未处理', '处理中']:\n", " is_pass = True\n", " break\n", " if is_pass:\n", " logger.info(f\"已存在待办,跳过该条记录: {row}\")\n", " continue\n", "\n", " payload_dict = {}\n", "\n", " distribution_date = datetime.datetime.now(datetime.timezone.utc)\n", " distribution_date = distribution_date.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'\n", "\n", " date_obj1 = datetime.datetime.strptime(row[\"init_day\"], \"%Y%m%d\").strftime(\"%Y-%m-%d\")\n", " date_obj2 = datetime.datetime.strptime(row[\"push_day\"], \"%Y%m%d\").strftime(\"%Y-%m-%d\")\n", "\n", " NGV_roles = {\n", " 'service_impl_principal': row['service_impl_principal'], # 运营负责人\n", " 'area_manager': row['area_manager'], # 区域经理\n", " 'technician': row['technician'], # 运营专家\n", " }\n", " for role, name in NGV_roles.items(): # 寻找对应的员工ID\n", " for row_item in self.staff_id_list:\n", " staff_id = self.get_staff_id(row_item, name)\n", " if staff_id:\n", " NGV_roles[role] = staff_id\n", " break # 找到后退出循环\n", " else:\n", " NGV_roles[role] = None # 如果没有找到对应的员工ID\n", " relationship_manager, area_manager, technician = [NGV_roles[role] for role in\n", " ['service_impl_principal',\n", " 'area_manager',\n", " 'technician']]\n", "\n", " UUid = time.strftime(\"%Y%m%d%H%M%S\", time.localtime())\n", "\n", " NGV_data_id = None\n", " reason = None\n", " create_exception =None\n", " create_date = None\n", "\n", " # 优先从 data_yichang_S 获取省市区信息\n", " province_name = row.get('province_name')\n", " city_name = row.get('city_name')\n", " area_name = row.get('area_name') if 'area_name' in row else row.get('district_name')\n", "\n", " # 检查省市区是否完整(省市区是一体的,任意一个缺失就需要从NGV获取)\n", " use_ngv_location = False\n", " if (not province_name or province_name in ['', 'None', 'NA'] or\n", " not city_name or city_name in ['', 'None', 'NA'] or\n", " not area_name or area_name in ['', 'None', 'NA']):\n", " use_ngv_location = True\n", " logger.info(f\"门店 {row['org_code']} 的省市区信息不完整,将从NGV_data_list获取\")\n", "\n", " # 获取关联数据\n", " for NGV_Data in self.NGV_data_list:\n", " # NGV_Data = NGV_Data.get(\"data\")\n", " if row[\"org_code\"] == NGV_Data.get(\"_widget_1734062123071\"): # 门店编码\n", " NGV_data_id = NGV_Data.get(\"_id\")\n", "\n", " # 如果需要从 NGV_data_list 获取省市区信息\n", " if use_ngv_location:\n", " province_name = NGV_Data.get(\"_widget_1734062123090\")\n", " city_name = NGV_Data.get(\"_widget_1734062123092\")\n", " area_name = NGV_Data.get(\"_widget_1734062123094\")\n", " logger.info(f\"【从NGV获取省市区】门店 {row['org_code']}: {province_name}, {city_name}, {area_name}\")\n", "\n", " # 门店原因\n", " reason = NGV_Data.get(\"_widget_1758617393828\")\n", " logger.info(f\"获取关联数据成功:{NGV_data_id}, {province_name}, {city_name}, {area_name}\")\n", " # 是否生成异常待办\n", " create_exception = NGV_Data.get(\"_widget_1758769279995\")\n", " # 获取上线日期(文本)\n", " create_date = NGV_Data.get(\"_widget_1734062123176\")\n", " break # 找到匹配的数据后退出循环\n", "\n", " # 判断门店原因\n", " # if reason in [\"门店倒闭\", \"门店转让\", \"加盟其他连锁\",\"切换竞品\",\"虚拟门店\",\"重新开户\",\"已退款\",\"二套系统\"]:\n", " # continue\n", "\n", " # 判断是否继续生成异常待办\n", " if create_exception == \"否\":\n", " continue\n", " # 新增:检查 create_date_str 是否存在且有效\n", " if not create_date:\n", " logger.warning(\"上线日期为空,跳过该记录\")\n", " continue\n", "\n", " # 定义可能的日期格式(灵活应对不同格式)\n", " date_formats = [\n", " \"%Y-%m-%d %H:%M:%S\", # 含时间\n", " \"%Y-%m-%d\", # 仅日期\n", " \"%Y/%m/%d\",\n", " \"%Y/%m/%d %H:%M:%S\"\n", " ]\n", "\n", " parsed_date = None\n", " for fmt in date_formats:\n", " try:\n", " parsed_date = datetime.datetime.strptime(create_date.strip(), fmt).date()\n", " logger.debug(f\"使用格式 {fmt} 成功解析日期: {parsed_date}\")\n", " break\n", " except ValueError:\n", " continue\n", "\n", " if parsed_date is None:\n", " logger.error(f\"无法解析上线日期: '{create_date}',支持的格式: %Y-%m-%d, %Y-%m-%d %H:%M:%S 等\")\n", " continue # 解析失败,跳过\n", "\n", " # 使用解析后的日期进行判断\n", " now_date = datetime.date.today()\n", " delta = now_date - parsed_date\n", " days_diff = delta.days\n", "\n", " if days_diff > 30:\n", " logger.info(f\"上线日期 {parsed_date} 超过30天({days_diff}天),生成待办\")\n", " # ✅ 继续后续待办创建逻辑\n", " else:\n", " logger.info(f\"上线日期 {parsed_date} 在30天内,跳过处理\")\n", " continue\n", "\n", "\n", " if not NGV_data_id:\n", " logger.warning(f\"未找到关联数据,请检查门店编码: {row['org_code']}\")\n", "\n", " # 根据省市区派发给异常回访客服\n", " # 检查省市区是否都有值,如果有任何一个为空,则客服为空\n", " if (not province_name or province_name in ['', 'None', 'NA'] or\n", " not city_name or city_name in ['', 'None', 'NA'] or\n", " not area_name or area_name in ['', 'None', 'NA']):\n", " customer_service = None\n", " logger.warning(f\"【省市区信息缺失】门店 {row['org_code']} 省市区信息不完整,异常回访客服设置为空\")\n", " logger.warning(f\"省: {province_name}, 市: {city_name}, 区: {area_name}\")\n", " else:\n", " customer_service = self.assign_customer_service(province_name, city_name, area_name, self.index)\n", " logger.info(f\"【派发客服】门店 {row['org_code']} 派发给客服: {customer_service}\")\n", "\n", " payload_dict.update({\n", " \"_widget_1748241895829\": {\"value\": row[\"health_warning_info\"]}, # 活跃健康状态变化\n", "\n", " \"_widget_1748241895830\": {\"value\": row[\"org_name\"]}, # 门店名称\n", "\n", " \"_widget_1748241895831\": {\"value\": row[\"contacts\"]}, # 联系人\n", "\n", " \"_widget_1748241895832\": {\"value\": row['contact_mobile']}, # 联系方式\n", "\n", " \"_widget_1748241895833\": {\n", " \"value\": int(time.mktime(time.strptime(date_obj1, \"%Y-%m-%d\")) * 1000) if row[\n", " \"init_day\"] != '' else ''},\n", " # 初始日\n", "\n", " \"_widget_1748241895834\": {\n", " \"value\": int(time.mktime(time.strptime(date_obj2, \"%Y-%m-%d\")) * 1000) if row[\n", " \"push_day\"] != '' else ''},\n", " # 推进日\n", "\n", " \"_widget_1748246808678\": {\"value\": customer_service}, # 当前跟进人\n", " # \"_widget_1748246808678\": {\"value\": \"083726094935447433\"}, # 当前跟进人\n", "\n", " \"_widget_1748246808679\": {\"value\": relationship_manager}, # 运营负责人\n", "\n", " \"_widget_1748246808680\": {\"value\": customer_service}, # 区域客服\n", "\n", " \"_widget_1748241895839\": {\n", " \"value\": int(time.mktime(time.strptime(row[\"saas_create_time\"], \"%Y-%m-%d\")) * 1000) if row[\n", " \"saas_create_time\"] != '' else ''},\n", " # 开户时间\n", "\n", " \"_widget_1748246808681\": {\"value\": technician}, # 技术专家\n", "\n", " \"_widget_1748246808682\": {\"value\": area_manager}, # 区域经理\n", "\n", " \"_widget_1748241895842\": {\"value\": row['org_code']}, # 门店编码\n", "\n", " \"_widget_1748241895844\": {\"value\": row['group_name']}, # 公司名称\n", "\n", " \"_widget_1748241895846\": {\"value\": row['group_grade']}, # 公司等级\n", "\n", " \"_widget_1748241895847\": {\"value\": row['region_name']}, # 大区\n", "\n", " \"_widget_1748241895848\": {\"value\": row['province_name']}, # 省\n", "\n", " \"_widget_1748241895849\": {\"value\": row['org_type']}, # 门店类型\n", "\n", " \"_widget_1748241895850\": {\"value\": row['saas_edition_fmt']}, # 系统版本\n", "\n", " \"_widget_1748241895851\": {\"value\": row['saas_customer_type']}, # saas客户类型\n", "\n", " \"_widget_1748241895852\": {\"value\": row['org_stage']}, # 门店阶段\n", "\n", " \"_widget_1748241895853\": {\"value\": row['contact_mobile']}, # 操作模式E.L/E.S\n", "\n", " \"_widget_1748241895855\": {\"value\": row['city_name']}, # 城市\n", "\n", " \"_widget_1748247754304\": {\"value\": NGV_data_id}, # 数据id\n", "\n", " \"_widget_1748512176655\": {\"value\": \"未处理\"}, # 跟进状态\n", "\n", " })\n", "\n", " routine_follow_up_payload = {\n", " \"api_key\": \"675b900991ad2491c69389ca\",\n", " \"entry_id\": \"68340de79f116c0b66b6b0cc\", # 异常服务跟进待办\n", " \"is_start_workflow\": \"true\",\n", " \"data\": payload_dict,\n", " \"transaction_id\": UUid\n", " }\n", "\n", " res = api_instance.data_batch_create(routine_follow_up_payload)\n", " logger.info(f\"创建结果:{res}\")\n", " except:\n", " pass\n", " common_module.send_task_status(task_start_time, \"异常服务待办派发\")\n", " except Exception as e:\n", " error_task_logger.error(f\"异常服务待办派发执行时发生异常: {e}\")\n", " common_module.send_task_error(task_start_time, \"异常服务待办派发\", str(e))\n", "\n", "\n", "if __name__ == '__main__':\n", " start = NewExceptionTask()\n", " start.main()\n" ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-11-17T02:17:23.550591Z", "start_time": "2025-11-17T02:16:33.774564Z" } }, "cell_type": "code", "source": [ "import datetime\n", "import os\n", "import time\n", "import requests\n", "from api import API\n", "from back_ground_module import CommonModule\n", "import pandas as pd\n", "from log_config import configure_task_logger, configure_error_task_logger\n", "\n", "api_instance = API()\n", "common_module = CommonModule()\n", "# start_time = datetime.datetime.now()\n", "\n", "# 获取已经配置好的常规日志记录器\n", "logger = configure_task_logger()\n", "\n", "# 获取已经配置好的错误任务日志记录器\n", "error_task_logger = configure_error_task_logger()\n", "output_dir = \"output\" # 设置输出目录\n", "os.makedirs(output_dir, exist_ok=True)\n", "\n", "from datetime import datetime,timedelta\n", "for i in range(1,27):\n", " data = common_module.get_yichang_details(days_back=i)\n", " time = (datetime.now() - timedelta(days=i)).strftime(\"%Y-%m-%d\")\n", " data.to_excel(os.path.join(output_dir,f\"异常数据{time}天.xlsx\"),index = False)\n" ], "id": "3fbb80f75435d56b", "outputs": [], "execution_count": 5 } ], "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 }