from config import load_service_config from fastapi import FastAPI, Request, status, BackgroundTasks, HTTPException from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse from database.dao.mysql_dao import MySqlDao from models import Recommend import os from pydantic import BaseModel import uvicorn from utils import ReportUtils import requests from typing import List, Dict app = FastAPI() dao = MySqlDao() cfgs = load_service_config() # 添加全局异常处理器 @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, content={ "code": 400, "msg": "请求参数错误", "data": { "detail": exc.errors(), "body": exc.body } }, ) # 定义请求体 class RecommendRequest(BaseModel): city_uuid: str # 城市id product_code: str # 卷烟编码 recall_cust_count: int # 推荐的商户数量 delivery_count: int # 投放的品规数量 class ReportRequest(BaseModel): city_uuid: str # 城市id product_code: str # 卷烟编码 class EvalReportRequest(BaseModel): city_uuid: str # 城市id product_code: str # 卷烟编码 start_time: str # 开始投放时间 end_time: str # 结束投放时间 @app.post("/brandcultivation/api/v1/recommend") async def recommend(request: RecommendRequest, backgroundTasks: BackgroundTasks): gbdtlr_model_path = os.path.join("./models/rank/weights", request.city_uuid, "gbdtlr_model.pkl") if not os.path.exists(gbdtlr_model_path): return {"code": 200, "msg": "model not defined", "data": {"recommendationInfo": "该城市的模型未训练,请先进行训练"}} # 初始化模型 recommend_model = Recommend(request.city_uuid) # 判断该品规是否是新品规 products_in_oreder = dao.get_product_from_order(request.city_uuid)["product_code"].unique().tolist() if request.product_code in products_in_oreder: recommend_list = recommend_model.get_recommend_list_by_gbdtlr(request.product_code, recall_count=request.recall_cust_count) else: recommend_list = recommend_model.get_recommend_list_by_item2vec(request.product_code, recall_count=request.recall_cust_count) recommend_data = recommend_model.get_recommend_and_delivery(recommend_list, delivery_count=request.delivery_count) request_data = [] for index, data in enumerate(recommend_data): id = index + 1 request_data.append( { "id": id, "cust_code": data["cust_code"], "recommend_score": data["recommend_score"], "delivery_count": data["delivery_count"] } ) # 异步执行报告生成任务 backgroundTasks.add_task( generate_report, request.city_uuid, request.product_code, request.recall_cust_count, request.delivery_count ) return {"code": 200, "msg": "success", "data": {"recommendationInfo": request_data}} def generate_report(city_uuid, product_id, recall_count, delivery_count): """生成报告""" report_util = ReportUtils(city_uuid, product_id) report_util.generate_all_data(recall_count, delivery_count) repots_dir = os.path.join('./data/reports', city_uuid, product_id) upload_file(repots_dir) def upload_file(reports_dir): """上传报告文件""" base_url = cfgs["aliyun"]["upload_url"] files = [ "卷烟信息表.xlsx", "品规商户特征关系表.xlsx", "相似卷烟表.xlsx" ] # 设置请求头 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", "Accept": "*/*", } # 设置Cookie cookies = { "expires_in": "10800000", "ecp_token": "ZXlKamIyUmxJam9pTVRKbU16bGxNMlV6TkdVeE1USmpNVFkwTVdVd09HUmpOVEV4TTJSaE5tRWlMQ0p6WTI5d1pTSTZJbkJ5YjNSbFkzUmxaQ0lzSW1Oc2FXVnVkQ0k2SW1Oc2FXVnVkRjlzYjI1bmFta2lmUT09", "acw_tc": "0a031e1717502370539856352e2b3bd4c277ffe39d26a06bff018dff60ea99", "isg": "BDAwNyXyI9cQD__-0PkIodjTAfiCeRTD-vUtYyqmIw3d5bGP0Y0nUayWOe2F3syb", "dd-ztna-token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIwNTAwMzQzMDM4MjYxNDMwOTQiLCJjb3JwSWQiOiJkaW5nYzc2YTJhMzdiMjRhODYwYTRhYzVkNjk4MDg2NGQzMzUiLCJkZXB0SWRzIjoiW1s4Mzk0NzIxNjAsODM2MjQ1MTUzLDFdXSIsInJvbGVJZHMiOiJbMjEwNDk3MDY1OF0iLCJ1bmlvbklkIjoiVXFyYTB4U0VQcWNxdFNhQTFqSzRzZ2lFaUUiLCJleHAiOjE3NTA4Mjg3ODksImlhdCI6MTc1MDIyMzk4OX0.ZR8MMaqiIszgBN5-evfpRXdy2Gmcn6Wk_U_1SQirGCek4m82cM1x8NzUqyBnIT9ApB5MJk_-aD5Q1e1LM74-3GkHNRDH9GxX4mN9flLdrjL0YzJDdOWAktZBatWPW7sW4rmq_yNC0fDJZ4j8N6ZObzo4Hvj08f7wCxqWrawrttgGoEYIBcKbMJBWyj0RBSOQdCfK3NS_-6AuTdc6reAircpd9l7SgJ-ln1lcOFImjr1x4NQz7yOy3GcvY3DGtkG6ZRz5kwsajLlxOr0At_xWHFxqdV8V_WIJNV1y725cd8WGoqyZPA9xTQnPvRtyCbud8DDZ3oojohNeV9sNW5BFtA" } files_id = {} for file in files: file_path = os.path.join(reports_dir, file) try: with open(file_path, 'rb') as f: files = {'file': (os.path.basename(file_path), f)} response = requests.post( base_url, headers=headers, files=files, cookies=cookies, verify=True ) if response.json()["success"]: file_id = response.json()["data"]["file_info"]["fileid"] files_id[os.path.basename(file_path).split('.')[0]] = file_id except requests.exceptions.RequestException as e: print("请求出错:", e) except Exception as e: print("发生错误:", e) files_id_str = "" if files_id: for filename, file_id in files_id.items(): files_id_str += f"{filename},{file_id}\n" else: files_id_str = "failed" with open(os.path.join(reports_dir, "files_id.txt"), 'w', encoding="utf-8") as file: file.write(files_id_str) @app.post("/brandcultivation/api/v1/report") async def get_file_id(request: ReportRequest): files_id_path = os.path.join("./data/reports", request.city_uuid, request.product_code, "files_id.txt") if not os.path.exists(files_id_path): raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Reports not found" ) with open(files_id_path, 'r', encoding="utf-8") as file: lines = file.readlines() if lines[0].strip() == "failed": raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="reports upload failed" ) request_data = [] for index, line in enumerate(lines): filename, file_id = line.strip().split(",") request_data.append( { "id": index+1, "filename": filename, "file_id": file_id } ) return {"code": 200, "msg": "success", "data": {"reportInfo": request_data}} @app.post("/brandcultivation/api/v1/eval_report") async def get_eval_report(request: EvalReportRequest): """获取验证报告""" reports_dir = os.path.join('./data/reports', request.city_uuid, request.product_code) # 首先生成验证报告 report_util = ReportUtils(request.city_uuid, request.product_code) report_util.generate_eval_data(request.start_time, request.end_time) # 其次上传验证报告到阿里云 base_url = cfgs["aliyun"]["upload_url"] headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", "Accept": "*/*", } cookies = { "expires_in": "10800000", "ecp_token": "ZXlKamIyUmxJam9pT1dZNFltRTNOVGhrTW1WbVpUYzRaR05oWkRZME5ERTRPREU1TkRObU9EY2lMQ0p6WTI5d1pTSTZJbkJ5YjNSbFkzUmxaQ0lzSW1Oc2FXVnVkQ0k2SW1Oc2FXVnVkRjlzYjI1bmFta2lmUT09", "acw_tc": "0a067c4317466919842786603e2653311180a2a4fa0fd05acb99cf0458b890", "isg": "BDAwNyXyI9cQD__-0PkIodjTAfiCeRTD-vUtYyqmIw3d5bGP0Y0nUayWOe2F3syb", "dd-ztna-token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIwNTAwMzQzMDM4MjYxNDMwOTQiLCJjb3JwSWQiOiJkaW5nYzc2YTJhMzdiMjRhODYwYTRhYzVkNjk4MDg2NGQzMzUiLCJkZXB0SWRzIjoiW1s4Mzk0NzIxNjAsODM2MjQ1MTUzLDFdXSIsInJvbGVJZHMiOiJbMjEwNDk3MDY1OF0iLCJ1bmlvbklkIjoiVXFyYTB4U0VQcWNxdFNhQTFqSzRzZ2lFaUUiLCJleHAiOjE3NDcxODQ3MjksImlhdCI6MTc0NjU3OTkyOX0.dfv612-LwnIdoKL2G73gg7LYy8SBmvr3Zaan97Q5wGUbEFdWw0JUqOQQ1jdeom_Nd9FNCHlkZM32DvwyUrNnvbg1QQy2JeYEpAgysG4h0MT_OghE6-xGVQBIkg72GPTo_cvdMYG9SMfaCo5H-73zFfwMFASFoXCDoIPha6NioIskOJMmvQVsDkHtRXYh_gv0XaJxSWirDWhKC9vxPGaIwDff8doHwPdi9uO-tO9LFy9RXdyIsBXWem31rBSD3D6FmqZLZjOOZhCKMym1VenfIKC10Oa1zm8-Y8bGyMHG0LO_68AJstKYT4alJoBVDHXpMp3zvSXXQB6da_fIthQD4A" } eval_file_path = os.path.join(reports_dir, "投放验证报告.xlsx") try: with open(eval_file_path, 'rb') as f: files = {'file': (os.path.basename(eval_file_path), f)} response = requests.post( base_url, headers=headers, files=files, verify=True ) if response.json()["success"]: file_id = response.json()["data"]["file_info"]["fileid"] return {"code": 200, "msg": "success", "data": {"reportInfo": {"id": 1, "filename": "投放验证报告", "file_id": file_id}}} else: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="reports upload failed" ) except requests.exceptions.RequestException as e: print("请求出错:", e) except Exception as e: print("发生错误:", e) # 最后将file_id返回给前端 if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=7960) # report_dir = "./data/reports/00000000000000000000000011445301/440298" # upload_file(report_dir)