소스 검색

将报告fileid持久化到数据库,并完善流程

yangzeyu 8 달 전
부모
커밋
32e30f98c8
15개의 변경된 파일454개의 추가작업 그리고 274개의 파일을 삭제
  1. 0 203
      api.py
  2. 9 0
      api/__init__.py
  3. 88 0
      api/eval_report.py
  4. 77 0
      api/recommend.py
  5. 68 0
      api/report.py
  6. 19 0
      api/request_body.py
  7. 26 4
      api_test.py
  8. 6 0
      config/service_config.yaml
  9. 31 9
      database/dao/mysql_dao.py
  10. 1 0
      database/db/mysql.py
  11. 35 0
      run_api.py
  12. 2 2
      utils/__init__.py
  13. 90 0
      utils/file_stream.py
  14. 0 53
      utils/file_ultils.py
  15. 2 3
      utils/report_utils.py

+ 0 - 203
api.py

@@ -1,203 +0,0 @@
-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, FileUploadUtils
-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         # 投放的品规数量
-    cultivacation_id: str       # 投放策略id
-    limit_cycle_name: str       # 投放周期名称
-    
-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_and_upload_report,
-        request
-    )
-    
-    return {"code": 200, "msg": "success", "data": {"recommendationInfo": request_data}}
-
-def generate_and_upload_report(request: RecommendRequest):
-    """生成并上传报告到阿里云文件数据库"""
-    # 生成相关报告
-    report_util = ReportUtils(request.city_uuid, request.product_code)
-    report_util.generate_all_data(request.recall_cust_count, request.delivery_count)
-    
-    # 上传报告
-    reports_dir = os.path.join('./data/reports', request.city_uuid, request.product_code)
-    report_files = [
-        '卷烟信息表',
-        '品规商户特征关系表',
-        '相似卷烟表',
-        '商户售卖推荐表'
-    ]
-    file_id_map = FileUploadUtils.upload_files(reports_dir, report_files)
-    
-    # 将返回的file_id保存到数据库中
-    data_dict = {
-        'cultivacation_id': request.cultivacation_id,
-        'city_uuid': request.city_uuid,
-        'limit_cycle_name': request.limit_cycle_name,
-        'product_code': request.product_code,
-        'product_info_table': file_id_map.get('卷烟信息表'),
-        'relation_table': file_id_map.get('品规商户特征关系表'),
-        'similarity_product_table': file_id_map.get('相似卷烟表'),
-        'recommend_table': file_id_map.get('商户售卖推荐表'),
-    }
-    dao.insert_report(data_dict)
-
-@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)

+ 9 - 0
api/__init__.py

@@ -0,0 +1,9 @@
+from .recommend import router as recommend_router
+from .report import router as report_router
+from .eval_report import router as eval_report_router
+
+__all__ = [
+    'recommend_router',
+    'report_router',
+    'eval_report_router'
+]

+ 88 - 0
api/eval_report.py

@@ -0,0 +1,88 @@
+from config import load_service_config
+from database import MySqlDao
+from fastapi import APIRouter, status, HTTPException
+import os
+from .request_body import EvalReportRequest
+import requests
+from utils import ReportUtils, FileStreamUtils
+
+cfgs = load_service_config()
+dao = MySqlDao()
+router = APIRouter()
+
+@router.post('/eval_report')
+async def 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)
+    
+    # 获取推荐列表
+    file_id = dao.get_report_file_id(request.cultivacation_id)['recommend_table'].item()
+    if file_id is None:
+        return {"code": 405, "msg": "推荐表丢失,生成验证报告失败!", "data": {"reportInfo": "推荐表丢失,生成验证报告失败!"}}
+    
+    recommend_data = FileStreamUtils.download_file(file_id)
+    if recommend_data is None:
+        return {"code": 405, "msg": "下载推荐数据出错,生成验证报告失败!", "data": {"reportInfo": "下载推荐数据出错,生成验证报告失败!"}}
+    
+    # 生成验证报告
+    report_util.generate_eval_data(request.limit_cycle_name, recommend_data)
+    
+    # 上传报告
+    eval_report = ['投放验证报告']
+    file_id_map = FileStreamUtils.upload_files(reports_dir, eval_report)
+    
+    dao.update_eval_report_data(request.cultivacation_id, file_id_map.get('投放验证报告'))
+    
+
+@router.post('/eval_report')
+async def 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返回给前端

+ 77 - 0
api/recommend.py

@@ -0,0 +1,77 @@
+from database import MySqlDao
+from fastapi import APIRouter, BackgroundTasks
+from .request_body import RecommendRequest
+
+from models import Recommend
+import os
+from utils import FileStreamUtils, ReportUtils
+
+dao = MySqlDao()
+
+router = APIRouter()
+
+@router.post("/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_and_upload_report,
+        request
+    )
+    
+    return {"code": 200, "msg": "success", "data": {"recommendationInfo": request_data}}
+
+def generate_and_upload_report(request: RecommendRequest):
+    """生成并上传报告到阿里云文件数据库"""
+    # 生成相关报告
+    report_util = ReportUtils(request.city_uuid, request.product_code)
+    report_util.generate_all_data(request.recall_cust_count, request.delivery_count)
+    
+    # 上传报告
+    reports_dir = os.path.join('./data/reports', request.city_uuid, request.product_code)
+    report_files = [
+        '卷烟信息表',
+        '品规商户特征关系表',
+        '相似卷烟表',
+        '商户售卖推荐表'
+    ]
+    file_id_map = FileStreamUtils.upload_files(reports_dir, report_files)
+    
+    # 将返回的file_id保存到数据库中
+    data_dict = {
+        'cultivacation_id': request.cultivacation_id,
+        'city_uuid': request.city_uuid,
+        'limit_cycle_name': request.limit_cycle_name,
+        'product_code': request.product_code,
+        'product_info_table': file_id_map.get('卷烟信息表'),
+        'relation_table': file_id_map.get('品规商户特征关系表'),
+        'similarity_product_table': file_id_map.get('相似卷烟表'),
+        'recommend_table': file_id_map.get('商户售卖推荐表'),
+    }
+    dao.insert_report(data_dict)

+ 68 - 0
api/report.py

@@ -0,0 +1,68 @@
+from database import MySqlDao
+from fastapi import APIRouter, status, HTTPException
+import os
+from .request_body import ReportRequest
+
+dao =MySqlDao()
+router = APIRouter()
+
+@router.post("/report")
+async def report(request: ReportRequest):
+    """获取推荐相关报告接口"""
+    file_id_record = dao.get_report_file_id(request.cultivacation_id)
+    if file_id_record.empty:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Reports not found"
+        )
+    
+    file_id_map = {
+        '卷烟信息表': file_id_record['product_info_table'].item(),
+        '品规商户特征关系表': file_id_record['relation_table'].item(),
+        '相似卷烟表': file_id_record['similarity_product_table'].item()
+    }
+    
+    request_data = []
+    for index, filename in enumerate(file_id_map):
+        request_data.append(
+            {
+                "id": index+1,
+                "filename": filename,
+                "file_id": file_id_map.get(filename)
+            }
+        )
+        
+    return {"code": 200, "msg": "success", "data": {"reportInfo": request_data}}
+
+# @router.post("/report")
+# async def report_api(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}}
+

+ 19 - 0
api/request_body.py

@@ -0,0 +1,19 @@
+from pydantic import BaseModel
+
+# 定义请求体
+class RecommendRequest(BaseModel):
+    city_uuid: str              # 城市id
+    product_code: str           # 卷烟编码
+    recall_cust_count: int      # 推荐的商户数量
+    delivery_count: int         # 投放的品规数量
+    cultivacation_id: str       # 投放策略id
+    limit_cycle_name: str       # 投放周期名称
+    
+class ReportRequest(BaseModel):
+    cultivacation_id: str       # 投放策略id
+    
+class EvalReportRequest(BaseModel):
+    city_uuid: str              # 城市id
+    product_code: str           # 卷烟编码
+    cultivacation_id: str       # 投放策略id
+    limit_cycle_name: str       # 投放周期名称

+ 26 - 4
api_test.py

@@ -1,14 +1,36 @@
 import requests
 import json
 
-url = "http://127.0.0.1:7960/brandcultivation/api/v1/recommend"
+# url = "http://127.0.0.1:7960/brandcultivation/api/v1/recommend"
+# payload = {
+#     "city_uuid": "00000000000000000000000011445301",
+#     "product_code": "440298",
+#     "recall_cust_count": 1000,
+#     "delivery_count": 2000,
+#     "cultivacation_id": "10000002",
+#     "limit_cycle_name": "202505W2(05.012-05.18)"
+# }
+# headers = {'Content-Type': 'application/json'}
+
+# response = requests.post(url, data=json.dumps(payload), headers=headers)
+# print(response.json())
+
+
+# url = "http://127.0.0.1:7960/brandcultivation/api/v1/report"
+# payload = {
+#     "cultivacation_id": "10000001",
+# }
+# headers = {'Content-Type': 'application/json'}
+
+# response = requests.post(url, data=json.dumps(payload), headers=headers)
+# print(response.json())
+
+url = "http://127.0.0.1:7960/brandcultivation/api/v1/eval_report"
 payload = {
     "city_uuid": "00000000000000000000000011445301",
     "product_code": "440298",
-    "recall_cust_count": 500,
-    "delivery_count": 1100,
     "cultivacation_id": "10000001",
-    "limit_cycle_name": "202505W1(05.05-05.11)"
+    "limit_cycle_name": "202502W2(02.10-02.16)"
 }
 headers = {'Content-Type': 'application/json'}
 

+ 6 - 0
config/service_config.yaml

@@ -1,6 +1,12 @@
 aliyun:
+  # 开发环境
   # upload_url: "http://file-center.jcpt:8080/file/fileUpload"
+  # download_url: "http://10.79.117.86/screen/mapi/file/fileDownload/11C1AC088863421C9BC32A5E722F5147"
+
   upload_url: "https://10-79-117-86-8p1kxyomtjwgt3.ztna-dingtalk.com/screen/mapi/file/fileUpload"
+  download_url: "https://10-79-117-86-8p1kxyomtjwgt3.ztna-dingtalk.com/screen/mapi/file/fileDownload/11C1AC088863421C9BC32A5E722F5147"
+
+
 
   cookies:
     "expires_in": "10800000"

+ 31 - 9
database/dao/mysql_dao.py

@@ -203,20 +203,19 @@ class MySqlDao:
         
         return data
     
-    def get_delivery_data_by_product(self, city_uuid, product_id, start_time, end_time):
+    def get_delivery_data_by_product(self, city_uuid, product_id, limit_cycle_name):
+        """通过品规获取验证数据"""
         query = f"""
             SELECT *
             FROM {self._eval_order_name}
             WHERE city_uuid = :city_uuid
             AND goods_code = :product_id
-            AND cycle_begin_date = :start_time
-            AND cycle_end_date = :end_time
+            AND limit_cycle_name = :limit_cycle_name
         """
         params = {
             "city_uuid": city_uuid,
             "product_id": product_id,
-            "start_time": start_time,
-            "end_time": end_time
+            "limit_cycle_name": limit_cycle_name,
         }
         data = self.db_helper.load_data_with_page(query, params)
         
@@ -291,12 +290,35 @@ class MySqlDao:
             data[col] = data[col].fillna(0).infer_objects(copy=False)
         
     def insert_report(self, data_dict):
+        """向report中插入数据"""
         return self.db_helper.insert_data(self._report_tablename, data_dict)
+    
+    def update_eval_report_data(self, cultivacation_id, eval_fileid):
+        """更新投放记录中的验证报告fileid"""
+        update_data = {"val_table": eval_fileid}
+        conditions = [
+            "cultivacation_id = :cultivacation_id",
+        ]
+        condition_params = {
+            'cultivacation_id': cultivacation_id,
+        }
+        
+        self.db_helper.update_data(self._report_tablename, update_data, conditions, condition_params)
+    
+    def get_report_file_id(self, cultivacation_id):
+        """从report中根据cultivacation_id获取对应文件的fileid"""
+        query = f"SELECT product_info_table, relation_table, similarity_product_table, recommend_table FROM {self._report_tablename} WHERE cultivacation_id = :cultivacation_id"
+        params = {"cultivacation_id": cultivacation_id}
+        
+        result = self.db_helper.fetch_one(text(query), params)
+        data = pd.DataFrame([dict(result._mapping)] if result else None)
+        
+        return data
         
 if __name__ == "__main__":
     dao = MySqlDao()
-    city_uuid = "00000000000000000000000011445301"
+    cultivacation_id = '10000001'
     
-    cust_list = dao.get_cust_list(city_uuid)["BB_RETAIL_CUSTOMER_CODE"].tolist()
-    for i in cust_list:
-        print(i)
+    data = dao.get_report_file_id(cultivacation_id)
+    print(data)
+    

+ 1 - 0
database/db/mysql.py

@@ -96,6 +96,7 @@ class MySqlDatabaseHelper:
         try:
             result = session.execute(query, params or {}).fetchone()
             return result
+            
         except SQLAlchemyError as e:
             session.rollback()
             print(f"error: {e}")

+ 35 - 0
run_api.py

@@ -0,0 +1,35 @@
+from api import recommend_router, report_router, eval_report_router
+from fastapi import FastAPI, Request, status
+from fastapi.exceptions import RequestValidationError
+from fastapi.responses import JSONResponse
+
+import uvicorn
+
+app = FastAPI()
+
+# 添加全局异常处理器
+@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
+            }
+        },
+    )
+
+url_prefix = '/brandcultivation/api/v1'
+  
+# 注册路由
+app.include_router(recommend_router, prefix=url_prefix)
+app.include_router(report_router, prefix=url_prefix)
+app.include_router(eval_report_router, prefix=url_prefix)
+    
+if __name__ == "__main__":
+    uvicorn.run(app, host="0.0.0.0", port=7960)
+    # report_dir = "./data/reports/00000000000000000000000011445301/440298"
+    # upload_file(report_dir)

+ 2 - 2
utils/__init__.py

@@ -1,8 +1,8 @@
 #!/usr/bin/env python3
 # -*- coding:utf-8 -*-
 from utils.report_utils import ReportUtils
-from utils.file_ultils import FileUploadUtils
+from utils.file_stream import FileStreamUtils
 __all__ = [
     "ReportUtils",
-    "FileUploadUtils"
+    "FileStreamUtils"
 ]

+ 90 - 0
utils/file_stream.py

@@ -0,0 +1,90 @@
+from config import load_service_config
+from io import BytesIO
+import os
+import pandas as pd
+import requests
+
+
+class FileStreamUtils:
+    cfgs = load_service_config()
+    upload_url = cfgs["aliyun"]["upload_url"]
+    download_url = cfgs["aliyun"]["download_url"]
+    cookies = cfgs["aliyun"]['cookies']
+     # 设置请求头
+    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": "*/*",
+    }
+    
+    @staticmethod
+    def upload_files(reports_dir, files):
+        files_id = {}
+        for filename in files:
+            file_path = os.path.join(reports_dir, f'{filename}.xlsx')
+            try:
+                with open(file_path, 'rb') as f:
+                    files = {'file': (os.path.basename(file_path), f)}
+
+                    response = requests.post(
+                        FileStreamUtils.upload_url,
+                        headers=FileStreamUtils.headers,
+                        files=files,
+                        cookies=FileStreamUtils.cookies,
+                        verify=True
+                    )
+                    
+                    if response.json()["success"]:
+                        file_id = response.json()["data"]["file_info"]["fileid"]
+                        files_id[filename] = file_id
+            except requests.exceptions.RequestException as e:
+                print("请求出错:", e)
+                return None
+            except Exception as e:
+                return None
+                
+        return files_id
+    
+    @staticmethod
+    def download_file(file_id, file_type='xlsx'):
+        """通过file_id从阿里云文件数据库下载文件"""
+        try:
+            params = {
+                'fileid': file_id,
+                'action': 'download'
+            }
+            
+            response = requests.get(
+                FileStreamUtils.download_url,
+                headers=FileStreamUtils.headers,
+                cookies=FileStreamUtils.cookies,
+                params=params,
+                verify=True
+            )
+            
+            if response.status_code == 200:
+                file_content = BytesIO(response.content)
+                print('请求成功了')
+                if file_type == 'xlsx':
+                    data = pd.read_excel(file_content)
+                elif file_type == 'csv':
+                    data = pd.read_csv(file_content)
+                else:
+                    raise ValueError(f"不支持的文件类型:{file_type}" )
+                
+                return data
+            else:
+                print(f'请求失败了{response.status_code}')
+                return None
+        except requests.exceptions.RequestException as e:
+            print("Request Error: ", e)
+            return None
+        except Exception as e:
+            print("File download Error: ", e)
+            return None
+    
+if __name__ == '__main__':
+    # print(FileStreamUtils.cfgs["aliyun"]["cookies"])
+    file_id = '11C1AC088863421C9BC32A5E722F5147'
+    
+    data = FileStreamUtils.download_file(file_id)
+    data.to_excel('./recommend_list.xlsx', index=False)

+ 0 - 53
utils/file_ultils.py

@@ -1,53 +0,0 @@
-from config import load_service_config
-import os
-import requests
-
-
-class FileUploadUtils:
-    cfgs = load_service_config()
-    base_url = cfgs["aliyun"]["upload_url"]
-    cookies = cfgs["aliyun"]['cookies']
-     # 设置请求头
-    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": "*/*",
-    }
-    
-    @staticmethod
-    def upload_files(reports_dir, files):
-        files_id = {}
-        for filename in files:
-            file_path = os.path.join(reports_dir, f'{filename}.xlsx')
-            try:
-                with open(file_path, 'rb') as f:
-                    files = {'file': (os.path.basename(file_path), f)}
-
-                    response = requests.post(
-                        FileUploadUtils.base_url,
-                        headers=FileUploadUtils.headers,
-                        files=files,
-                        cookies=FileUploadUtils.cookies,
-                        verify=True
-                    )
-                    
-                    if response.json()["success"]:
-                        file_id = response.json()["data"]["file_info"]["fileid"]
-                        files_id[filename] = file_id
-            except requests.exceptions.RequestException as e:
-                print("请求出错:", e)
-                return None
-            except Exception as e:
-                return None
-                
-        return files_id
-    
-if __name__ == '__main__':
-    # print(FileUploadUtils.cfgs["aliyun"]["cookies"])
-    report_dir = './data/reports/00000000000000000000000011445301/440298'
-    files = [
-        '卷烟信息表',
-        '品规商户特征关系表',
-        '相似卷烟表',
-        '商户售卖推荐表'
-    ]
-    FileUploadUtils.upload_files(report_dir, files)

+ 2 - 3
utils/report_utils.py

@@ -127,17 +127,16 @@ class ReportUtils:
         
         report.to_excel(os.path.join(self._save_dir, "效果验证表.xlsx"), index=False)
         
-    def generate_eval_data(self, start_time, end_time):
+    def generate_eval_data(self, check_cycle_name, recommend_data):
         """根据推荐列表生成验证报告"""
         if self._product_id == '350139':
             eval_product_id = "350355"
         else:
             eval_product_id = self._product_id
-        delivery_data = self._dao.get_delivery_data_by_product(self._city_uuid, eval_product_id, start_time, end_time)
+        delivery_data = self._dao.get_delivery_data_by_product(self._city_uuid, eval_product_id, check_cycle_name)
         delivery_data = delivery_data[DeliveryConfig.FEATURE_COLUMNS]
         delivery_data = sample_data_clear(delivery_data, DeliveryConfig)
 
-        recommend_data = pd.read_excel(os.path.join(self._save_dir, "商户售卖推荐表.xlsx"))
         recommend_data = recommend_data.drop(columns=["建议投放量(条)"])
         
         report = eval_report_process(delivery_data, recommend_data)