""" F6 系统模块 本模块提供 F6 系统相关的功能,包括: - 登录和认证 - 验证码识别 - 公司信息获取 - 门店信息获取 - 保持连接 依赖: - requests: HTTP 请求 - PIL: 图像处理 - pytesseract: OCR 识别 """ import requests import hashlib from urllib.parse import quote from datetime import datetime from app.api import API from typing import Optional, Dict, AnyStr from PIL import Image, ImageEnhance import pytesseract import logging # 简道云 API 实例,用于调用简道云 API api_instance = API() # 日志记录器 logger = logging.getLogger('app') class F6Module: """ F6 系统模块类 提供 F6 系统相关的所有功能,包括登录、信息获取等。 """ @staticmethod def get_captcha() -> AnyStr: """ 获取并识别验证码 从 F6 系统获取验证码图片,使用 OCR 识别验证码文本。 Returns: AnyStr: 识别出的验证码文本 注意: 需要系统安装 Tesseract OCR 才能正常工作 """ captcha_url = 'https://yunxiu.f6car.cn/kzf6/login/captcha-image' response = requests.get(captcha_url) with open('captcha.png', 'wb') as f: f.write(response.content) image = Image.open('captcha.png') enhancer = ImageEnhance.Contrast(image) image = enhancer.enhance(2.0) enhancer = ImageEnhance.Brightness(image) image = enhancer.enhance(1.5) image = image.convert('L') image = image.point(lambda x: 0 if x < 128 else 255, '1') image.save('preprocessed_captcha.png') captcha_text = pytesseract.image_to_string(image) print(f"识别的验证码为: {captcha_text}") return captcha_text @staticmethod def login_in(username: str, password: str, company_name: str = '默认门店',) -> Optional[requests.Response]: """ F6 系统登录 使用用户名和密码登录 F6 系统,并选择指定的公司。 如果触发验证码,会自动识别并重试登录。 Args: username: 用户名 password: 密码(明文,方法内部会进行 MD5 加密) company_name: 公司名称,默认为'默认门店' Returns: Optional[requests.Response]: 登录响应对象,登录失败返回 None 注意: 密码会在方法内部进行 MD5 加密处理 """ url = "https://yunxiu.f6car.com/kzf6/login/confirm" session = requests.Session() header = { 'Referer': url, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/130.0.0.0 Safari/537.36 Edg/129.0.0.0' } data = { 'username': username, 'password': hashlib.md5(password.encode('utf-8')).hexdigest(), } try: res = session.post(url=url, headers=header, data=data) res_json = res.json() if res_json.get('message') == '请输入图形验证码': logger.warning("触发图形验证码") captcha_text = F6Module.get_captcha() data.update({'imageCode': captcha_text}) res = session.post(url=url, headers=header, data=data) res_json = res.json() if res_json.get("data") is None: return res else: group_id = '' for group in res_json.get('data', []): if group.get("groupName") == company_name: group_id = group.get("groupId") break if not group_id: logger.error(f"未找到公司名称: {company_name}") return None token = quote(res_json['token']) # URL 编码 url = (f'https://yunxiu.f6car.cn/kzf6/user/loginAfterChooseGroup?' f'token={token}&groupId={group_id}&macAddress=') res1 = session.get(url, cookies=res.cookies) return res1 except Exception as e: print(f"Error during login: {e}") return None def accept_login_message(self, data: Dict[str, str]) -> Dict[str, str]: """ 接受登录消息并处理 处理简道云插件发送的登录请求,执行登录并返回结果。 Args: data: 包含用户名、密码、公司名称的字典 Returns: Dict[str, str]: 登录结果,包含状态信息 """ username = data['username'] password = data['password'] company_name = data['company_name'] res = self.login_in(username, password, company_name) if res is not None: cookies = requests.utils.dict_from_cookiejar(res.cookies) json = res.json() url = 'https://yunxiu.f6car.cn/hive/company/getGroupName' res1 = requests.get(url=url, cookies=cookies) data1 = res1.json() if data1['code'] == 200: if data1['data'] == company_name: if json['status'] == 'success': json['status'] = '登录成功' elif json['status'] == 'Error': json['status'] = '登录失败,请检查账号密码' else: json['status'] = '公司名称不正确或未选择公司名称,请重试' else: json['status'] = '请输入正确的账号密码并选择公司名称' return json else: return {"status": "登录失败,请检查公司名称"} def get_company_information(self, data: Dict[str, str]) -> Dict[str, str]: """ 获取公司信息 根据用户名和密码获取 F6 系统中的公司信息,并将结果保存到简道云。 Args: data: 包含用户名、密码的字典 Returns: Dict[str, str]: 包含时间戳的消息,用于后续查询 """ username = data['username'] password = data['password'] timestamp = datetime.now().strftime("%Y-%m-%d %H-%M-%S") print(username) url = "https://yunxiu.f6car.com/kzf6/login/confirm" session = requests.Session() header = { 'Referer': url, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0' } data = { 'username': username, 'password': hashlib.md5(password.encode('utf-8')).hexdigest(), } try: res = session.post(url=url, headers=header, data=data) res_json = res.json() if res_json.get('message') == '请输入图形验证码': pass jiandaoyun_data = {'api_key': '6694d3c4fcb69ca9a111a6c4', 'entry_id': '6736e2112ad50045f041a827'} if res_json.get("data") is None: print('单店') res = self.login_in(username, password) if res is not None: cookies = requests.utils.dict_from_cookiejar(res.cookies) url = 'https://yunxiu.f6car.cn/hive/company/getGroupName' res = requests.get(url=url, cookies=cookies) data = res.json() store_name = data['data'] jiandaoyun_data['data_list'] = [ {"_widget_1731650067055": {"value": f'{username}{password}{timestamp}'}, "_widget_1731650067056": {"value": f"{store_name}"}}] api_instance.entry_data_batch_create(jiandaoyun_data) res = {'msg': f'{username}{password}{timestamp}'} else: jiandaoyun_data_list = [] for group in res_json.get('data', []): append_data = {"_widget_1731650067055": {"value": f'{username}{password}{timestamp}'}, "_widget_1731650067056": {"value": f"{group['groupName']}"}} jiandaoyun_data_list.append(append_data) jiandaoyun_data['data_list'] = jiandaoyun_data_list res = api_instance.entry_data_batch_create(jiandaoyun_data) print(res) res = {'msg': f'{username}{password}{timestamp}'} return res except Exception as e: print(f"获取公司名称失败: {e}") res = {'msg': '获取公司名称失败,请重新获取'} return res def get_store_information(self, data: Dict[str, str]) -> Dict[str, dict[str, str]]: """ 获取门店信息 根据用户名、密码和公司名称获取 F6 系统中的门店信息, 包括门店列表、客户车辆数量、客户数量等。 Args: data: 包含用户名、密码、公司名称的字典 Returns: Dict[str, dict[str, str]]: 包含时间戳、门店信息、统计数据的结果 """ username = data['username'] password = data['password'] company_name = data['company_name'] timestamp = datetime.now().strftime("%Y-%m-%d %H-%M-%S") login_response = self.login_in(username, password, company_name) if login_response is None: return {"msg": {'msg': '未执行', 'msg_details': '登录失败'}} cookies = requests.utils.dict_from_cookiejar(login_response.cookies) url = 'https://yunxiu.f6car.cn/hive/org/getPageOrgGroupMembers?currentPage=1&pageSize=100&name=' res = requests.get(url=url, cookies=cookies) data = res.json() org_lists = data['data']['list'] url = 'https://yunxiu.f6car.cn/member/car/carListForPermission' json = {"pageSize": 10, "pageNo": 1} car_res = requests.post(url=url, json=json, cookies=cookies) total_cars_data = car_res.json() total_cars = total_cars_data['data']['total'] url = 'https://yunxiu.f6car.cn/member/customer/listForPermission?pageSize=10&pageNo=1' customer_res = requests.get(url=url, cookies=cookies) total_customer_data = customer_res.json() total_customer = total_customer_data['data']['total'] jiandaoyun_data = {'api_key': '6694d3c4fcb69ca9a111a6c4', 'entry_id': '673c38ccca57a5cf266eb18c'} jiandaoyun_data_list = [] for org in org_lists: append_data = {"_widget_1731999948708": {"value": f'{username}{password}{company_name}{timestamp}'}, "_widget_1731999948709": {"value": f"{org['orgName']}"}} jiandaoyun_data_list.append(append_data) jiandaoyun_data['data_list'] = jiandaoyun_data_list api_instance.entry_data_batch_create(jiandaoyun_data) res = {'msg': {"msg": f'{username}{password}{company_name}{timestamp}', "total_cars": f"{total_cars}条客户车辆", "total_customer": f"{total_customer}条客户"}} return res @staticmethod def get_keep_heart(data: Dict[str, str]) -> Dict[str, str]: """ 保持连接 用于保持连接的心跳检测,直接返回接收到的数据。 Args: data: 接收到的数据字典 Returns: Dict[str, str]: 原样返回接收到的数据 """ return data