1.客户信息修改,将硬编码改为动态取值
2.新增项目批量停用、材料批量修改功能
This commit is contained in:
@@ -0,0 +1,281 @@
|
|||||||
|
"""
|
||||||
|
项目材料相关后台任务模块
|
||||||
|
|
||||||
|
本模块包含项目材料相关的后台任务,包括:
|
||||||
|
- 项目信息批量停用
|
||||||
|
- 材料信息批量修改
|
||||||
|
|
||||||
|
这些任务在后台线程中执行,不会阻塞主请求。
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
from typing import Dict, Any, List, Optional
|
||||||
|
from datetime import datetime
|
||||||
|
from tqdm import tqdm
|
||||||
|
from app.tasks.common import update_jiandaoyun, approve_workflow, get_operate_org_id, get_card_list, \
|
||||||
|
execute_failure_handler
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
|
||||||
|
logger = logging.getLogger('app')
|
||||||
|
|
||||||
|
|
||||||
|
def batch_disable_projects(data: Dict[str, Any], cookies: Dict[str, str], df: pd.DataFrame, save_path: str,
|
||||||
|
option) -> None:
|
||||||
|
"""
|
||||||
|
项目批量停用后台任务
|
||||||
|
|
||||||
|
在后台线程中批量停用项目,从 Excel 文件中读取项目编码。
|
||||||
|
执行完成后会更新简道云表单并自动提交工作流。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: 包含表单ID(api_key)、表单ID(entry_id)、数据ID(data_id)的字典
|
||||||
|
cookies: 用户登录 F6 系统的 cookies 信息
|
||||||
|
df: Excel 文件读取的内容,DataFrame 格式,第一列为项目编码
|
||||||
|
save_path: Excel 文件保存的地址,执行完成后会删除此文件
|
||||||
|
option: 批量启用、批量停用
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
|
||||||
|
注意:
|
||||||
|
- 无效的项目(None、空字符串)会被跳过
|
||||||
|
- 执行完成后会自动删除上传的文件
|
||||||
|
- 执行结果会更新到简道云表单
|
||||||
|
"""
|
||||||
|
if option == "批量启用":
|
||||||
|
type_ = 0 # 1 停用,0启用
|
||||||
|
else:
|
||||||
|
type_ = 1
|
||||||
|
df = df.where(pd.notnull(df), None)
|
||||||
|
|
||||||
|
# 获取门店id
|
||||||
|
org_id = get_operate_org_id(data)
|
||||||
|
# 获取项目信息
|
||||||
|
json_data = {
|
||||||
|
'param': '',
|
||||||
|
'name': '',
|
||||||
|
'customCode': '',
|
||||||
|
'currentPage': 1,
|
||||||
|
'pageSize': 100,
|
||||||
|
'isDel': -type_,
|
||||||
|
'customInvoiceCategory': 0,
|
||||||
|
'idOwnOrg': org_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
'https://ids-goods.f6car.cn/f6-ids-goods/service/getServiceList',
|
||||||
|
cookies=cookies,
|
||||||
|
json=json_data,
|
||||||
|
)
|
||||||
|
all_project_list = []
|
||||||
|
total_pages = response.json().get("data", {}).get("totalPages", "")
|
||||||
|
for page in tqdm(range(1, total_pages + 1)):
|
||||||
|
json_data['currentPage'] = str(page)
|
||||||
|
response = requests.post(
|
||||||
|
'https://ids-goods.f6car.cn/f6-ids-goods/service/getServiceList',
|
||||||
|
cookies=cookies,
|
||||||
|
json=json_data,
|
||||||
|
)
|
||||||
|
project_list = response.json().get("data", {}).get("records", [])
|
||||||
|
all_project_list.extend(project_list)
|
||||||
|
|
||||||
|
# 遍历获取到的项目信息停用文件中的项目
|
||||||
|
code_list = df.iloc[:, 0].dropna().astype(str).tolist()
|
||||||
|
res_data_list = []
|
||||||
|
results = []
|
||||||
|
for item in tqdm(all_project_list):
|
||||||
|
custom_code = item.get("customCode")
|
||||||
|
if not custom_code or str(custom_code) not in code_list or not code_list:
|
||||||
|
continue
|
||||||
|
info_id = item.get("infoId")
|
||||||
|
pk_id = item.get("pkId")
|
||||||
|
json_data = {
|
||||||
|
"orgIdList": [
|
||||||
|
org_id,
|
||||||
|
],
|
||||||
|
"isDel": type_, # 1 停用 0启用
|
||||||
|
"infoId": info_id,
|
||||||
|
"pkId": pk_id,
|
||||||
|
"type": 1,
|
||||||
|
"idOwnOrg": org_id
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
'https://ids-goods.f6car.cn/f6-ids-goods/service/editAttributeByType',
|
||||||
|
cookies=cookies,
|
||||||
|
json=json_data,
|
||||||
|
)
|
||||||
|
res_data_list.append(response.json())
|
||||||
|
response.raise_for_status() # 抛出HTTP错误
|
||||||
|
results.append({'材料编码': custom_code, '状态': '停用/启用成功'})
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
results.append({'材料编码': custom_code, '状态': f'停用/启用失败: {str(e)}'})
|
||||||
|
pass
|
||||||
|
|
||||||
|
print({'msg': '已执行', 'msg_details': f'{results}'})
|
||||||
|
logger.info(f"停用/启用结果: {results}")
|
||||||
|
os.remove(save_path)
|
||||||
|
print(f'{save_path}已删除')
|
||||||
|
# 调用api回写改掉 执行明细与执行状态
|
||||||
|
msg = update_jiandaoyun(data, f'{results}')
|
||||||
|
|
||||||
|
if msg.get('msg'):
|
||||||
|
approve_workflow(data)
|
||||||
|
print('表单已自动提交至下一步')
|
||||||
|
|
||||||
|
|
||||||
|
def batch_modify_materials(data: Dict[str, Any], cookies: Dict[str, str], df: pd.DataFrame, save_path: str,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
材料批量修改后台任务
|
||||||
|
|
||||||
|
在后台线程中批量修改材料,从 Excel 文件中读取材料编码。
|
||||||
|
执行完成后会更新简道云表单并自动提交工作流。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: 包含表单ID(api_key)、表单ID(entry_id)、数据ID(data_id)的字典
|
||||||
|
cookies: 用户登录 F6 系统的 cookies 信息
|
||||||
|
df: Excel 文件读取的内容,DataFrame 格式,第一列为材料编码
|
||||||
|
save_path: Excel 文件保存的地址,执行完成后会删除此文件
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
|
||||||
|
注意:
|
||||||
|
- 无效的材料编码(None、空字符串)会被跳过
|
||||||
|
- 执行完成后会自动删除上传的文件
|
||||||
|
- 执行结果会更新到简道云表单
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 获取门店id
|
||||||
|
org_id = get_operate_org_id(data)
|
||||||
|
# 获取材料信息
|
||||||
|
json_data = {
|
||||||
|
'keyWord': '',
|
||||||
|
'idOwnOrg': org_id,
|
||||||
|
'currentPage': 1,
|
||||||
|
'pageSize': 100,
|
||||||
|
'name': '',
|
||||||
|
'brand': '',
|
||||||
|
'supplierCode': '',
|
||||||
|
'customCode': '',
|
||||||
|
'categoryName': '',
|
||||||
|
'categoryId': '',
|
||||||
|
'labelId': '',
|
||||||
|
'labelName': '',
|
||||||
|
'spec': '',
|
||||||
|
'applyModel': '',
|
||||||
|
'sellPurchaseStatuses': [
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
],
|
||||||
|
'customInvoiceCategory': 0,
|
||||||
|
'getThirdPlatformCode': 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
'https://ids-goods.f6car.com/f6-ids-goods/part/getExactPartStockInfo',
|
||||||
|
cookies=cookies,
|
||||||
|
json=json_data,
|
||||||
|
)
|
||||||
|
all_materials_list = []
|
||||||
|
total_pages = response.json().get("data", {}).get("totalPages", "")
|
||||||
|
for page in tqdm(range(1, total_pages + 1)):
|
||||||
|
json_data['currentPage'] = str(page)
|
||||||
|
response = requests.post(
|
||||||
|
'https://ids-goods.f6car.com/f6-ids-goods/part/getExactPartStockInfo',
|
||||||
|
cookies=cookies,
|
||||||
|
json=json_data,
|
||||||
|
)
|
||||||
|
materials_list = response.json().get("data", {}).get("records", [])
|
||||||
|
all_materials_list.extend(materials_list)
|
||||||
|
|
||||||
|
# 构建更新映射:{原customCode: {new_customCode, brand, name, spec}}
|
||||||
|
update_map = {}
|
||||||
|
for _, row in df.iterrows():
|
||||||
|
orig_code = str(row.iloc[0]).strip() if pd.notna(row.iloc[0]) else None
|
||||||
|
if not orig_code:
|
||||||
|
continue
|
||||||
|
update_map[orig_code] = {
|
||||||
|
"customCode": str(row.iloc[1]).strip() if pd.notna(row.iloc[1]) else "",
|
||||||
|
"brand": str(row.iloc[2]).strip() if pd.notna(row.iloc[2]) else "",
|
||||||
|
"name": str(row.iloc[3]).strip() if pd.notna(row.iloc[3]) else "",
|
||||||
|
"spec": str(row.iloc[4]).strip() if pd.notna(row.iloc[4]) else "",
|
||||||
|
}
|
||||||
|
|
||||||
|
# 遍历获取到的材料信息修改材料属性
|
||||||
|
code_list = df.iloc[:, 0].dropna().astype(str).tolist()
|
||||||
|
res_data_list = []
|
||||||
|
results = []
|
||||||
|
for item in tqdm(all_materials_list):
|
||||||
|
custom_code = item.get("customCode")
|
||||||
|
# 判断当前材料编码是否在文件中
|
||||||
|
if not custom_code or str(custom_code) not in update_map:
|
||||||
|
continue
|
||||||
|
part_id = item.get("partId")
|
||||||
|
try:
|
||||||
|
# 获取材料明细
|
||||||
|
params = {
|
||||||
|
'partId': part_id,
|
||||||
|
'customInvoiceCategory': '0',
|
||||||
|
'getThirdPlatformCode': '0',
|
||||||
|
}
|
||||||
|
|
||||||
|
materials_response = requests.get(
|
||||||
|
'https://ids-goods.f6car.com/f6-ids-goods/part/getPartInfo',
|
||||||
|
params=params,
|
||||||
|
cookies=cookies,
|
||||||
|
)
|
||||||
|
if materials_response.status_code != 200:
|
||||||
|
results.append({'材料编码': custom_code, '状态': '获取明细失败'})
|
||||||
|
continue
|
||||||
|
|
||||||
|
detail = materials_response.json().get("data")
|
||||||
|
if not detail:
|
||||||
|
results.append({'材料编码': custom_code, '状态': '明细为空'})
|
||||||
|
continue
|
||||||
|
|
||||||
|
updates = update_map[custom_code]
|
||||||
|
# === 关键:强制覆盖,空值也写入 ===
|
||||||
|
detail["customCode"] = updates["customCode"] # 可能是 ""
|
||||||
|
detail["brand"] = updates["brand"] # 可能是 ""
|
||||||
|
detail["name"] = updates["name"] # 可能是 ""
|
||||||
|
detail["showName"] = updates["name"] # 同步 showName(重要!)
|
||||||
|
detail["spec"] = updates["spec"] # 可能是 ""
|
||||||
|
|
||||||
|
# 修复价格和数组(必须!)
|
||||||
|
for f in ["purchasePrice", "sellPrice"]:
|
||||||
|
if isinstance(detail.get(f), (int, float)):
|
||||||
|
detail[f] = f"{detail[f]:.2f}"
|
||||||
|
if detail.get("partBarCodeVos") is None:
|
||||||
|
detail["partBarCodeVos"] = []
|
||||||
|
|
||||||
|
update_resp = requests.post(
|
||||||
|
'https://ids-goods.f6car.com/f6-ids-goods/part/updateAreaPartBasicInfo',
|
||||||
|
cookies=cookies,
|
||||||
|
json=detail
|
||||||
|
)
|
||||||
|
if update_resp.status_code == 200 and update_resp.json().get("code") == 200:
|
||||||
|
results.append({'材料编码': custom_code, '状态': '修改成功'})
|
||||||
|
else:
|
||||||
|
msg = update_resp.json().get("message", "未知错误")
|
||||||
|
results.append({'材料编码': custom_code, '状态': f'修改失败: {msg}'})
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
results.append({'材料编码': custom_code, '状态': f'修改属性失败: {str(e)}'})
|
||||||
|
pass
|
||||||
|
|
||||||
|
print({'msg': '已执行', 'msg_details': f'{results}'})
|
||||||
|
logger.info(f"批量修改结果: {results}")
|
||||||
|
os.remove(save_path)
|
||||||
|
print(f'{save_path}已删除')
|
||||||
|
# 调用api回写改掉 执行明细与执行状态
|
||||||
|
msg = update_jiandaoyun(data, f'{results}')
|
||||||
|
|
||||||
|
if msg.get('msg'):
|
||||||
|
approve_workflow(data)
|
||||||
|
print('表单已自动提交至下一步')
|
||||||
Reference in New Issue
Block a user