rambo il y a 3 mois
Parent
commit
4e7f6cb16c
6 fichiers modifiés avec 332 ajouts et 36 suppressions
  1. 113 22
      python/api.py
  2. 1 1
      python/config.ini
  3. 1 0
      python/error.txt
  4. 26 7
      python/models.py
  5. 185 2
      python/service/online_request/module_online_data.py
  6. 6 4
      python/temp.py

+ 113 - 22
python/api.py

@@ -28,6 +28,12 @@ from win32gui import EnumWindows, GetWindowText
 from service.online_request.module_online_data import OnlineDataRequest
 from concurrent.futures import ThreadPoolExecutor
 from functools import partial
+from service.online_request.module_online_data import AIGCDataRequest
+def parserGoodsDict2Aigc(return_data_check_before_detail):
+    '''获取商品组装数据'''
+    goods_no_dict = return_data_check_before_detail.get("data",{}).get("goods_no_dict", {})
+    return goods_no_dict
+
 
 @app.get("/")
 async def index():
@@ -154,13 +160,18 @@ async def fromExcelHandler(params: HandlerDetail):
             shutil.rmtree(image_dir)
             # path = os.path.dirname(path)
     cutOutMode = (
-        '0'
-        if settings.getSysConfigs("other_configs", "cutout_mode", "普通抠图") == "普通抠图"
-        else '1'
+        "0"
+        if settings.getSysConfigs("other_configs", "cutout_mode", "普通抠图")
+        == "普通抠图"
+        else "1"
     )
     config_data = {
         "image_dir": limit_path,  # 这个目录作为本次生成的图片目录非常重要 例:./output/当前日期
-        "image_order": params.template_image_order,
+        "image_order": (
+            "俯视,侧视,后跟,鞋底,内里,组合,组合2,组合3,组合4,组合5"
+            if params.template_image_order == None or params.template_image_order == ""
+            else params.template_image_order
+        ),
         "goods_art_no": goods_art_no,
         "goods_art_nos": goods_art_no_arrays,
         "is_check_number": False,
@@ -289,11 +300,17 @@ async def handle_detail(request: Request, params: HandlerDetail):
     obj = None
     token = "Bearer " + params.token
     uuid = params.uuid
+    aigc_clazz = AIGCDataRequest(token)
     run_main = RunMain(obj, token, uuid)
     onlineData = OnlineDataRequest(token)
     goods_art_no_arrays = params.goods_art_no
-    is_only_cutout = params.is_only_cutout
-    online_stores = params.online_stores
+    is_only_cutout = params.is_only_cutout  # 是否仅抠图
+    online_stores = params.online_stores  # 上传第三方的店铺名称数组
+    is_detail = params.is_detail  # 上传第三方的店铺名称数组
+    is_product_scene = params.is_product_scene  # 上传第三方的店铺名称数组
+    is_upper_footer = params.is_upper_footer  # 上传第三方的店铺名称数组
+    upper_footer_params = params.upper_footer_params  # 上传第三方的店铺名称数组
+    product_scene_prompt = params.product_scene_prompt  # 上传第三方的店铺名称数组
     handler_result = []
     handler_result_folder = ""
     if is_only_cutout == 1:
@@ -351,15 +368,15 @@ async def handle_detail(request: Request, params: HandlerDetail):
         temp_class[tempItem.template_id] = tempItem.template_local_classes
         temp_name_list.append(tempItem.template_id)
     cutOutMode = (
-        '1'
+        "1"
         if settings.getSysConfigs("other_configs", "cutout_mode", "普通抠图")
         == "普通抠图"
-        else '2'
+        else "2"
     )
     config_data = {
         "image_dir": limit_path,
         "image_order": (
-            "俯视,侧视,后跟,鞋底,内里"
+            "俯视,侧视,后跟,鞋底,内里,组合,组合2,组合3,组合4,组合5"
             if params.template_image_order == None or params.template_image_order == ""
             else params.template_image_order
         ),
@@ -415,9 +432,80 @@ async def handle_detail(request: Request, params: HandlerDetail):
             "msg": "",
             "data": {"output_folder": handler_result_folder, "list": handler_result},
         }
+    if is_product_scene == 1:
+        if product_scene_prompt == "" or product_scene_prompt == None:
+            raise UnicornException("请填写场景描述")
+    if is_upper_footer == 1:
+        if upper_footer_params == {} or upper_footer_params == None:
+            raise UnicornException("请选择模特")
+        man_id = upper_footer_params.get("man_id")
+        if not man_id:
+            raise UnicornException("请选择男模特")
+        women_id = upper_footer_params.get("women_id")
+        if not women_id:
+            raise UnicornException("请选择女模特")
     handler_result = []
     try:
         return_data_check_before_detail = run_main.check_before_detail(config_data)
+        if is_product_scene == 1:
+            goods_dict = parserGoodsDict2Aigc(return_data_check_before_detail)
+            new_goods_dict = {}
+            for goods_art_no  in goods_dict.keys():
+                goods_art_dict_info  = goods_dict[goods_art_no]
+                first_goods_art_no_info = goods_art_dict_info.get("货号资料",[])[0]
+                first_pics = first_goods_art_no_info.get("pics")
+                ceshi_image_path = first_pics.get("侧视-抠图")
+                save_root_path = ceshi_image_path.split("阴影图处理")[0]
+                save_image_path = f"{save_root_path}场景图.jpg"
+                aigc_clazz.center_paste_image(ceshi_image_path, save_image_path)
+                image_path = aigc_clazz.generateProductScene(
+                    save_image_path, product_scene_prompt, save_image_path
+                )
+                goods_art_dict_info["场景图"] = image_path
+                new_goods_dict[goods_art_no] = goods_art_dict_info
+                handler_result.append(
+                    {
+                        "goods_art_no": goods_art_no,
+                        "success": True,
+                        "info": "处理成功",
+                    }
+                )
+            return_data_check_before_detail["data"]["goods_no_dict"] = new_goods_dict
+        if is_upper_footer == 1:
+            goods_dict = parserGoodsDict2Aigc(return_data_check_before_detail)
+            new_goods_dict = {}
+            for goods_art_no in goods_dict.keys():
+                goods_art_dict_info = goods_dict[goods_art_no]
+                first_goods_art_no_info = goods_art_dict_info.get("货号资料", [])[0]
+                first_pics = first_goods_art_no_info.get("pics")
+                gender = goods_art_dict_info.get("性别")
+                model_id = man_id if "男" in gender else women_id
+                ceshi_image_path = first_pics.get("侧视-抠图")
+                save_root_path = ceshi_image_path.split("阴影图处理")[0]
+                save_image_path = f"{save_root_path}模特图.jpg"
+                shutil.copy(ceshi_image_path, save_image_path)
+                image_path = aigc_clazz.generateUpperShoes(
+                    save_image_path, model_id, save_image_path
+                )
+                goods_art_dict_info["模特图"] = image_path
+                new_goods_dict[goods_art_no] = goods_art_dict_info
+                handler_result.append(
+                    {
+                        "goods_art_no": goods_art_no,
+                        "success": True,
+                        "info": "处理成功",
+                    }
+                )
+            return_data_check_before_detail["data"]["goods_no_dict"] = new_goods_dict
+        if is_detail == 0:
+            return {
+                "code": 0,
+                "msg": "",
+                "data": {
+                    "output_folder": handler_result_folder,
+                    "list": handler_result,
+                },
+            }
         check_for_detail_first_res = await run_main.check_for_detail_first_call_back(
             return_data_check_before_detail
         )
@@ -459,17 +547,17 @@ async def handle_detail(request: Request, params: HandlerDetail):
                             }
                         )
                 else:
-                    if len(online_stores) >0:
+                    if len(online_stores) > 0:
                         result_goods_no_dict = return_data_check_before_detail["data"][
                             "goods_no_dict"
                         ]
-                        for goods_idx,goods_no_dict in enumerate(result_goods_no_dict.keys()):
+                        for goods_idx, goods_no_dict in enumerate(
+                            result_goods_no_dict.keys()
+                        ):
                             all_detail_path_list = config_data["all_detail_path_list"]
                             for detail_path in all_detail_path_list:
                                 if goods_no_dict in detail_path:
-                                    detail_path_replace = detail_path.replace(
-                                        "\\", "/"
-                                    )
+                                    detail_path_replace = detail_path.replace("\\", "/")
                                     result_goods_no_dict[goods_no_dict][
                                         "detail_path"
                                     ] = f"{detail_path_replace}/详情页.jpg"
@@ -724,16 +812,18 @@ def delect_goods_arts(params: PhotoRecordDelete):
         "msg": "操作成功",
         "data": None,
     }
+
+
 def query_config_by_key(key_name):
     """
-        在sys_configs.json格式的数据中查询指定的key,如果匹配则返回整个对象
-        
-        Args:
-            key_name (str): 要查询的key名称
-            
-        Returns:
-            dict or None: 匹配的对象或None(如果没有找到)
-        """
+    在sys_configs.json格式的数据中查询指定的key,如果匹配则返回整个对象
+
+    Args:
+        key_name (str): 要查询的key名称
+
+    Returns:
+        dict or None: 匹配的对象或None(如果没有找到)
+    """
     try:
         # 获取所有配置数据
         configs = json.load(open("sys_configs.json", encoding="utf-8"))
@@ -748,6 +838,7 @@ def query_config_by_key(key_name):
         print(f"查询配置时出错: {e}")
         return None
 
+
 @app.get("/get_sys_config", description="查询系统配置")
 def get_sys_config(key: str = None):
     if key == None:

+ 1 - 1
python/config.ini

@@ -10,7 +10,7 @@ app_run=api:app
 # 端口号
 port=7074
 debug=false
-env=prod
+env=dev
 # 线程数
 works=5
 project=惠利玛

+ 1 - 0
python/error.txt

@@ -0,0 +1 @@
+{"code":999,"message":"\u751f\u6210\u8bb0\u5f55id\u4e0d\u80fd\u4e3a\u7a7a","debug":{"database":{"total":2,"items":[{"connection":"mysql","query":"select * from `accounts` where `accounts`.`id` = '1296' and `accounts`.`deleted_at` is null limit 1;","time":1.12},{"connection":"mysql","query":"select * from `aigc_userpoints_logs` where (`partner_account_id` = '1296' and `action_name` = '\u65b0\u7528\u6237\u6ce8\u518c') limit 1;","time":0.73}]},"cache":{"hit":{"keys":["18323b96c68234597b1fa8d10fecb6bbe45cadc3","18323b96c68234597b1fa8d10fecb6bbe45cadc3","18323b96c68234597b1fa8d10fecb6bbe45cadc3","18323b96c68234597b1fa8d10fecb6bbe45cadc3","18323b96c68234597b1fa8d10fecb6bbe45cadc3","ai_gc_day_sign_in2025-08-291296","18323b96c68234597b1fa8d10fecb6bbe45cadc3","18323b96c68234597b1fa8d10fecb6bbe45cadc3","64c84e952453cd25d3097c7cfb8ba8178a0d109f"],"total":9},"miss":{"keys":["64c84e952453cd25d3097c7cfb8ba8178a0d109f"],"total":1},"write":{"keys":[],"total":0},"forget":{"keys":[],"total":0}},"profiling":[{"event":"request-time","time":0.01586294174194336}],"memory":{"usage":4364888,"peak":4389336}}}

+ 26 - 7
python/models.py

@@ -1,16 +1,18 @@
 from middleware import *
 import datetime
 
+
 class HlmForwardRequest(BaseModel):
     method: str = Field(default="GET", description="请求方法")
     headers: dict = Field(default={}, description="请求头")
     target_url: str = Field(default="", description="目标地址")
-    query_params:str = Field(default="", description="请求参数")
+    query_params: str = Field(default="", description="请求参数")
 
 
 class ModelGetDeviceConfig(BaseModel):
     """获取可执行程序命令列表"""
-    tab_id: int =Field(
+
+    tab_id: int = Field(
         default=1, description="类型,【执行左脚程序】或者【执行右脚程序】"
     )
 
@@ -18,9 +20,7 @@ class ModelGetDeviceConfig(BaseModel):
 class ModelGetDeviceConfigDetail(BaseModel):
     """获取可执行程序命令列表"""
 
-    id: int = Field(
-        default=None, description="可执行程序得id"
-    )
+    id: int = Field(default=None, description="可执行程序得id")
 
 
 class ModelGetDeviceConfigDetailQuery(BaseModel):
@@ -61,6 +61,7 @@ class PhotoRecordDelete(BaseModel):
 
 class SysConfigParams(BaseModel):
     """系统配置"""
+
     key: str = Field(default=None, description="类型")
     value: str = Field(default=None, description="json数据")
 
@@ -91,21 +92,39 @@ class HandlerDetail(BaseModel):
     goods_art_no: list[str] = Field(default=None, description="货号")
     uuid: str = Field(default=None, description="uuid")
     token: str = Field(default="", description="惠利玛请求token")
-    template_image_order:str = Field(default="", description="模板图片排序")
+    template_image_order: str = Field(default="", description="模板图片排序")
     excel_path: Optional[str] = Field(default="", description="excel路径")
     temp_name: str = Field(default="", description="选中的模板名称")
     temp_list: list[TemplateItem] = Field(default=[], description="所有模板列表")
     logo_path: Optional[str] = Field(default="", description="logo地址路径")
     is_only_cutout: Optional[int] = Field(default=0, description="是否仅抠图;0否;1是")
-    online_stores: Optional[list[str]] = Field(default=[], description="是否仅抠图;0否;1是")
+    online_stores: Optional[list[str]] = Field(
+        default=[], description="上传的店铺,数组形式"
+    )
+    is_product_scene: Optional[int] = Field(
+        default=0, description="是否生成场景图;0否;1是"
+    )
+    is_detail: Optional[int] = Field(default=0, description="是否生成详情图;0否;1是")
+    is_upper_footer: Optional[int] = Field(
+        default=0, description="是否生成上脚图;0否;1是"
+    )
+    product_scene_prompt: Optional[str] = Field(
+        default=[], description="上传的店铺,数组形式"
+    )
+    upper_footer_params: Optional[dict] = Field(
+        default=None, description="上脚图参数配置"
+    )
+
 
 class LogoParams(BaseModel):
     """logo参数"""
 
     logo_path: str = Field(default="", description="logo地址路径")
 
+
 class LogoParamsupdate(BaseModel):
     """系统配置"""
+
     path: str = Field(default=None, description="要删除得文件路径")
 
 

+ 185 - 2
python/service/online_request/module_online_data.py

@@ -4,6 +4,10 @@ import settings
 import json, asyncio
 import numpy as np
 from utils.common import message_queue
+from middleware import UnicornException
+from PIL import Image
+import io, time
+
 
 class JsonEncoder(json.JSONEncoder):
     """Convert numpy classes to JSON serializable objects."""
@@ -17,6 +21,183 @@ class JsonEncoder(json.JSONEncoder):
             return super(JsonEncoder, self).default(obj)
 
 
+def download_image_with_pil(url, save_path):
+    """通过url保存图片"""
+    try:
+        # 发送请求获取图片数据
+        response = requests.get(url)
+        image_data = response.content
+        # 使用PIL处理下载的图片
+        image = Image.open(io.BytesIO(image_data))
+        image = image.convert("RGB")
+        # 根据当前时间生成文件名
+        # 保存图片
+        image.save(save_path)
+        return save_path
+
+    except Exception as e:
+        print("保存错误",e)
+        return "error"
+
+
+class AIGCDataRequest(object):
+    def __init__(self, token):
+        self.s = requests.session()
+        self.token = token
+        self.post_headers = {
+            "Authorization": token,
+        }
+
+    def uploadImage(self, local_path: str) -> str:
+        post_headers = {"Authorization": self.token}
+        url = settings.DOMAIN + "/api/upload"
+        resultData = self.s.post(
+            url, files={"file": open(local_path, "rb")}, headers=post_headers
+        ).json()
+        return resultData["data"]["url"]
+
+    def center_paste_image(
+        self, source_image_path, output_path, width=900, ratio=(3, 4)
+    ):
+        """
+        将一张PNG透明图片居中粘贴到指定尺寸和比例的背景上,保持透明度
+
+        Args:
+            source_image_path: 源PNG图片路径
+            output_path: 输出图片路径
+            width: 目标图片宽度
+            ratio: 目标图片比例 (宽:高)
+        """
+        # 计算目标尺寸 (900 x 1200)
+        target_width = width
+        target_height = int(width * ratio[1] / ratio[0])
+
+        # 创建透明背景图片(RGBA模式)
+        background = Image.new(
+            "RGBA", (target_width, target_height), (255, 255, 255, 0)
+        )
+
+        # 打开源图片
+        source_img = Image.open(source_image_path)
+
+        # 确保源图片是RGBA模式以保持透明度
+        if source_img.mode != "RGBA":
+            source_img = source_img.convert("RGBA")
+
+        # 调整源图片大小以适应目标尺寸,保持原图比例
+        source_width, source_height = source_img.size
+        scale = min(target_width / source_width, target_height / source_height)
+        new_width = int(source_width * scale)
+        new_height = int(source_height * scale)
+
+        # 调整源图片尺寸
+        source_img_resized = source_img.resize(
+            (new_width, new_height), Image.Resampling.LANCZOS
+        )
+
+        # 计算居中粘贴位置
+        paste_x = (target_width - new_width) // 2
+        paste_y = (target_height - new_height) // 2
+
+        # 将调整后的源图片居中粘贴到背景上,保留透明度
+        background.paste(source_img_resized, (paste_x, paste_y), source_img_resized)
+
+        # 如果需要保存为PNG格式以保持透明度
+        if output_path.lower().endswith(".png"):
+            background.save(output_path, format="PNG")
+        else:
+            # 如果保存为JPG等不支持透明度的格式,转换为RGB并使用白色背景
+            background = background.convert("RGB")
+            background.save(output_path)
+
+        return background
+
+    def generateProductScene(self, local_path, prompt, save_path):
+        imageUrl = self.uploadImage(local_path)
+        print("imageUrl", imageUrl)
+        data = {
+            "site": 1,
+            "base_image": imageUrl,
+            "keyword": prompt,
+            "model_type": 1,
+            "gemini_model": "gemini-2.5-flash-image-preview",
+        }
+        """生成场景图"""
+        url = settings.DOMAIN + "/api/ai_image/inspired/command_to_image"
+        resultData = self.s.post(url, data=data, headers=self.post_headers).json()
+        code = resultData.get("code", 0)
+        message = resultData.get("message", "")
+        if code != 0:
+            raise UnicornException(message)
+        image_arr = resultData.get("data", None).get("image", [])
+        if len(image_arr) == 0:
+            raise UnicornException("场景图生成失败")
+        image_url = image_arr[0]
+        save_image_path = download_image_with_pil(image_url, save_path)
+        return save_image_path
+
+    def searchProgress(self, id):
+        """查询进度"""
+        url = settings.DOMAIN + "/api/ai_image/main/search_bacth_progress"
+        data = {"site": 1, "generate_ids": [id], "type": "aigc_pro"}
+        resultData = self.s.post(url, json=data, headers=self.post_headers)
+        resultData = resultData.json()
+        code = resultData.get("code", 0)
+        message = resultData.get("message", "")
+        if code != 0:
+            raise UnicornException(message)
+        data_result = resultData.get("data", [])
+        if len(data_result) == 0:
+            return -1, None
+        data_item = data_result[0]
+        status = data_item.get("status", -1)
+        if status in [0,1]:
+            return status, None
+        result_image_urls = data_item.get("result_image_urls", [])
+        result_image = result_image_urls[0] if len(result_image_urls) > 0 else None
+        return status, result_image
+
+    def generateUpperShoes(self, local_path, model_id, save_path):
+        """生成上脚图"""
+        print("生成上脚图", local_path, model_id, save_path)
+        imageUrl = self.uploadImage(local_path)
+        data = {
+            "site": 1,
+            "model_template_id": model_id,
+            "base_image": imageUrl,
+            "creatClass": "鞋子上脚图-鞋子上脚图",
+            "creatTimer": 40,
+            "pname": "OnFeetImage",
+        }
+        """生成上脚图"""
+        url = settings.DOMAIN + "/api/ai_image/main/upper_footer"
+        resultData = self.s.post(url, data=data, headers=self.post_headers).json()
+        code = resultData.get("code", 0)
+        message = resultData.get("message", "")
+        if code != 0:
+            raise UnicornException(message)
+        generate_ids = resultData.get("data", None).get("generate_ids", [])
+        if len(generate_ids) == 0:
+            raise UnicornException("模特图生成失败")
+        generate_id = generate_ids[0]
+        search_times = 60
+        status = 0
+        result_image = None
+        print("generate_id", generate_id)
+        while search_times > 0:
+            print(f"查询第{search_times}次")
+            status, result_image = self.searchProgress(generate_id)
+            if status in [-1, 2]:
+                break
+            time.sleep(1)
+            search_times -= 1
+        if not result_image:
+            raise UnicornException("模特图生成失败")
+        save_image_path = download_image_with_pil(result_image, save_path)
+        print("上脚图save_image_path",result_image, save_image_path)
+        return save_image_path
+
+
 class OnlineDataRequest(object):
     def __init__(self, token):
         self.s = requests.session()
@@ -318,12 +499,12 @@ class OnlineDataRequest(object):
         return resultData["data"]["url"]
 
     def upload_goods_api(self, params):
-        '''上传商品api'''
+        """上传商品api"""
         post_headers = {
             "Authorization": self.token,
             "Content-Type": "application/json",
         }
-        url = settings.DOMAIN+"/api/ai_image/camera_machine/publish_goods"
+        url = settings.DOMAIN + "/api/ai_image/camera_machine/publish_goods"
         postData = json.dumps(params)
         print("上传商品api==>url", url)
         print("上传第三方数据打印", params)
@@ -551,6 +732,8 @@ class GetOnlineDataHLM(OnlineDataRequest):
             goods_number_data[data["goods_art_no"]]["颜色名称"] = data["color"]
             goods_number_data[data["goods_art_no"]]["商品标题"] = data["goods_title"]
             goods_number_data[data["goods_art_no"]]["商品价格"] = data["retail_price"]
+            goods_number_data[data["goods_art_no"]]["性别"] = data["gender"]
+            goods_number_data[data["goods_art_no"]]["token"] = self.token
 
         return goods_number_data
 

+ 6 - 4
python/temp.py

@@ -1,6 +1,6 @@
 # from PIL import Image
 # from settings import recordDataPoint
-from service.online_request.module_online_data import OnlineDataRequest
+from service.online_request.module_online_data import OnlineDataRequest,AIGCDataRequest
 
 tempData = {
     "code": 0,
@@ -120,6 +120,8 @@ tempData = {
 }
 goods_no_dict = tempData["data"]["goods_no_dict"]
 params = []
-token = "71c53bf53045d6bc5cb65a85be9e6064b6a5e2ba"
-onlineData = OnlineDataRequest(token)
-onlineData.uploadGoods2ThirdParty(goods_no_dict, params)
+token = "Bearer 18323b96c68234597b1fa8d10fecb6bbe45cadc3"
+# onlineData = OnlineDataRequest(token)
+# aigc_clazz = AIGCDataRequest(token)
+# res = aigc_clazz.searchProgress(1120457)
+# print("res", res)