简道云fastapi

This commit is contained in:
z66
2025-11-07 17:48:49 +08:00
commit 073f0646a1
30 changed files with 5933 additions and 0 deletions
+298
View File
@@ -0,0 +1,298 @@
import requests
from urllib.parse import quote
import pandas as pd
import os
import urllib.parse
from datetime import datetime
from app.api import API
from typing import Optional, Dict, Any, Tuple
from app.config import Config
from app.module import F6Module
import threading
from app import back_ground_tasks
api_instance = API()
class F6PluginModule:
@staticmethod
def accept_file(data: Dict[str, Any]) -> Tuple[Optional[str], Dict[str, Any]]: # 接收文件
"""
接收文件。
此方法用于处理前端上传的文件,下载文件并保存到指定目录。主要步骤包括:
1. 处理前端传递的数据,获取文件的URL。
2. 解析URL以获取文件名。
3. 根据当前时间生成新的文件名,以避免文件名冲突。
4. 下载文件并保存到指定目录。
5. 返回文件保存路径和处理后的数据。
Args:
data (dict): 包含文件URL和其他必要信息的字典。
Returns:
tuple: 包含文件保存路径和处理后的数据的元组。如果文件保存成功,返回保存路径和数据;如果失败,返回 None 和数据。
"""
data = api_instance.entry_data_get(data=data)
print(data)
try:
# 安全地访问附件信息
data_dict = data.get('data', {})
attachments = data_dict.get('附件', [])
if not attachments or len(attachments) == 0:
print('上传url未读取到,或无上传文件: 附件列表为空')
save_path = ''
return save_path, data
first_attachment = attachments[0]
url = first_attachment.get('url')
if not url:
print('上传url未读取到,或无上传文件: URL为空')
save_path = ''
return save_path, data
print(url)
except (KeyError, IndexError, TypeError) as e:
print(f'上传url未读取到,或无上传文件:{e}')
save_path = ''
return save_path, data
parsed_url = urllib.parse.urlparse(url)
query_params = urllib.parse.parse_qs(parsed_url.query)
attname = query_params.get('attname', [''])[0]
filename = urllib.parse.unquote(attname)
# 获取当前时间并格式化为指定格式的字符串
timestamp = datetime.now().strftime("%Y-%m-%d %H-%M-%S")
# 分离文件名和扩展名
name_part, ext_part = filename.rsplit('.', 1) if '.' in filename else (filename, '')
# 构建新文件名
new_filename = f"{name_part}{timestamp}.{ext_part}" if ext_part else f"{name_part}{timestamp}"
save_path = os.path.join(Config.SAVE_DIRECTORY, new_filename)
print(save_path)
response = requests.get(url, stream=True)
if response.status_code == 200:
with open(save_path, 'wb') as file:
for chunk in response.iter_content(chunk_size=1024):
if chunk:
file.write(chunk)
return save_path, data
else:
return None, data
def check_file(self, data: Dict[str, Any]) -> Dict[str, str]: # 校验上传文件
"""
校验上传文件。
此方法负责接收前端上传的文件,并根据文件类型和操作指令进行相应的校验。主要步骤包括:
1. 调用 `accept_file` 方法处理前端传递的数据,获取文件保存路径和处理后的数据。
2. 根据数据中的 `Action` 字段判断需要执行的操作类型。
3. 如果文件保存路径有效,继续执行以下步骤:
- 如果操作类型为 `create_brand`,则读取文件和模板文件,校验文件格式是否正确。
- 如果文件格式正确,返回成功消息;否则返回错误消息。
4. 如果文件保存路径无效,返回相应的错误消息。
5. 如果读取文件过程中发生异常,捕获异常并返回错误消息。
Args:
data (dict): 前端请求发送过来的数据,包含文件信息和其他必要参数。
Returns:
dict: 包含文件校验结果的消息字典。如果校验成功,则返回文件路径和校验标志;如果失败,则返回错误消息。
"""
save_path, data1 = self.accept_file(data)
# 安全地获取 Action 字段
data_dict = data1.get('data', {})
action = data_dict.get('Action(隐藏)')
if not action:
return {'msg': '缺少Action字段,无法校验文件'}
if save_path:
try:
if action == 'create_brand':
df1 = pd.read_excel(save_path, sheet_name=0)
if "品牌" in df1.columns[0]: # 校验表头名字
print('文件校验成功')
return {'msg': f'{save_path}', 'check': ''}
else:
print("'msg':'文件上传格式错误'")
return {'msg': '文件上传格式错误'}
elif action == 'modify_customer_info':
df = pd.read_excel(save_path, sheet_name=0)
if "客户手机号" in df.columns[0]: # 校验表头名字
print('文件校验成功')
return {'msg': f'{save_path}', 'check': ''}
else:
print("'msg':'文件上传格式错误'")
return {'msg': '文件上传格式错误'}
elif action == 'delete_cars':
pass
else:
pass
except Exception as e:
return {'msg': f'读取Excel文件失败: {str(e)},文件路径:{save_path}'}
else:
return {'msg': '当前节点无附件上传', 'check': ''}
@staticmethod
def create_brand(data: Dict[str, Any]) -> Dict[str, str]: # 创建品牌
entry_data = api_instance.entry_data_get(data=data)
print('执行 品牌批量新建')
username = entry_data['data']['账号']
password = entry_data['data']['密码']
company_name = entry_data['data']['公司名称']
save_path = entry_data['data']['文件保存地址']
login_response = F6Module.login_in(username, password, company_name)
if login_response is None:
return {'msg': '登录失败'}
try:
df = pd.read_excel(save_path, sheet_name=0, dtype='string')
except Exception as e:
return {'msg': f'读取Excel文件失败: {str(e)},文件路径:{save_path}'}
cookies = requests.utils.dict_from_cookiejar(login_response.cookies)
try:
thread = threading.Thread(target=back_ground_tasks.create_brand_background,
args=(data, cookies, df, save_path))
thread.start()
except Exception as e:
print(f'创建线程失败: {str(e)}')
return {'msg': '正在执行', 'msg_details': '正在执行,请稍后看结果'}
@staticmethod
def delete_history(data: Dict[str, Any]) -> Dict[str, str]:
entry_data = api_instance.entry_data_get(data=data)
username = entry_data['data']['账号']
password = entry_data['data']['密码']
company_name = entry_data['data']['公司名称']
org_name = entry_data['data']['门店名称']
login_response = F6Module.login_in(username, password, company_name)
if login_response is None:
return {'msg': '未执行', 'msg_details': '登录失败'}
cookies = requests.utils.dict_from_cookiejar(login_response.cookies)
url = 'https://yunxiu.f6car.cn/hive/org/getPageOrgGroupMembers?currentPage=1&pageSize=1000&name='
res = requests.get(url=url, cookies=cookies)
store_data = res.json()
org_lists = store_data['data']['list']
org_id = ''
org_name1 = ''
for org in org_lists:
org_name1 = org['orgName']
if org_name == org['orgName']:
org_id = org['orgId']
if org_id:
thread = threading.Thread(target=back_ground_tasks.delete_history_background,
args=(data, cookies, org_id, org_name1))
thread.start()
return {'msg': '正在执行中', 'msg_details': '请稍后查看结果'}
else:
return {'msg': '未执行', 'msg_details': '门店信息错误'}
@staticmethod
def delete_customer(data):
entry_data = api_instance.entry_data_get(data=data)
username = entry_data['data']['账号']
password = entry_data['data']['密码']
company_name = entry_data['data']['公司名称']
res = F6Module.login_in(username, password, company_name)
if res is not None:
cookies = requests.utils.dict_from_cookiejar(res.cookies)
url = "https://yunxiu.f6car.cn/member/customer/listForPermission?pageSize=50000&pageNo=1"
res = requests.get(url, cookies=cookies)
json = res.json()
if json:
thread = threading.Thread(target=back_ground_tasks.delete_customer_background,
args=(data, cookies, json['data']['data'],))
thread.start()
return {'msg': '正在执行中', 'msg_details': '8-20点3.5s一条数据,其余时间1.5s一条数据'}
else:
return {'msg': '未执行', 'msg_details': '无客户信息'}
else:
return {'msg': '未执行', 'msg_details': '登录失败'}
@staticmethod
def delete_cars(data):
entry_data = api_instance.entry_data_get(data=data)
username = entry_data['data']['账号']
password = entry_data['data']['密码']
company_name = entry_data['data']['公司名称']
res = F6Module.login_in(username, password, company_name)
if res is not None:
cookies = requests.utils.dict_from_cookiejar(res.cookies)
url = "https://yunxiu.f6car.cn/member/car/carListForPermission"
header = {
'Referer': 'https://yunxiu.f6car.cn/erp/view/index.html',
'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'
}
json_data = {"pageSize": 100, "pageNo": 1}
res = requests.post(url=url, cookies=cookies, json=json_data, headers=header)
res_data = res.json()
total_items = int(res_data["data"]['total'])
all_page = total_items // 100 + (total_items % 100 > 0)
if res_data:
thread = threading.Thread(target=back_ground_tasks.delete_car_background,
args=(data, url, cookies, header, all_page))
thread.start()
return {'msg': '正在执行中', 'msg_details': '8-20点3.5s一条数据,其余时间1.5s一条数据'}
else:
return {'msg': '未执行', 'msg_details': '无客户车辆信息'}
else:
return {'msg': '未执行', 'msg_details': '登录失败'}
def modify_customer_info(self, data: Dict[str, str]):
entry_data = api_instance.entry_data_get(data=data)
username = entry_data['data']['账号']
password = entry_data['data']['密码']
company_name = entry_data['data']['公司名称']
save_path = entry_data['data']['文件保存地址']
login_response = F6Module.login_in(username, password, company_name)
if login_response is None:
return {'msg': '未执行', 'msg_details': '登录失败'}
cookies = requests.utils.dict_from_cookiejar(login_response.cookies)
try:
df = pd.read_excel(save_path, sheet_name=0, dtype='string')
except Exception as e:
return {'msg': f'读取Excel文件失败: {str(e)},文件路径:{save_path}'}
if cookies:
thread = threading.Thread(target=back_ground_tasks.modify_customer_info_background,
args=(data, cookies, df, save_path))
thread.start()
return {'msg': '正在执行中', 'msg_details': '请稍后查看结果'}
else:
return {'msg': '未执行', 'msg_details': 'cookies获取失败'}
+2
View File
@@ -0,0 +1,2 @@
__all__ = []
+226
View File
@@ -0,0 +1,226 @@
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
from datetime import datetime
api_instance = API()
logger = logging.getLogger('app')
class F6Module:
@staticmethod
def get_captcha() -> AnyStr:
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]:
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]:
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]:
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]]:
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]:
return data
+22
View File
@@ -0,0 +1,22 @@
import requests
from urllib.parse import quote
import pandas as pd
import os
import urllib.parse
from datetime import datetime
from app.api import API
from typing import Optional, Dict, Any, Tuple
from app.config import Config
from app.module import F6Module
import threading
from app import back_ground_tasks
api_instance = API()
class OtherPluginModule:
def sms_signature_status(self):
pass