day14-Trae之一键换脸APP开发04

今日内容

1 Python 对接Coze工作流

# 1 之前使用coze做了一个工作流
	-换脸
    -当时只能发布到,集成到智能体中---》发布到coze商店---》链接地址---》把链接地址发送给其他人使用
    -我想对这个功能收费?
    -我想做一款app/微信小程序---》在app,小程序上使用功能
    	-拍照,相册中照片直接用
    -还想在app如收费功能。。。
    
# 2 现在目标:使用Python代码,能调用这个工作流完成操作
	-后续任意工作流,都是这个流程
    
############ python可以调用,其他语言也可以调用(我们以python为例)###############

# 3 Python调用coze工作流流程
	1 工作流发布
    2 根据api,使用不同语言写代码:
        参照这个地址:https://www.coze.cn/open/docs/developer_guides/workflow_run
   		-1 https://api.coze.cn/v1/files/upload  上传两张图片
            	-返回两个文件id号
    	-2 https://api.coze.cn/v1/workflow/run  执行工作流
            	-携带两个文件的id号
                -返回工作流执行的id号
        -3 https://api.coze.cn/v1/workflows/:workflow_id/run_histories/:execute_id 
				-带着工作流执行的id号--查询结果
        -4 自己写代码把图片保存到本地
    3 获取api_key
    	-https://www.coze.cn/open/oauth/pats
        -自己设定一个长一点的时间
        	
    4 编写代码测试
    	-api_key:我的:pat_2mYHUdcRixq90o4Nd2Jp0nPhpBcIZg4wFZkkmvxe3UHcztZdXi0glSX1rPqxHK9T
          	-你们需要获取自己的
        -workflow_id:工作流id
        	-我的是:7536960050292998154
        -两张图片:自己换
        
     5 测试完成图片放在
    	-coze_results 文件夹下
        
        
        
        
        
# 我们app后端使用Python写的---》这个代码可以直接放在我们后端项目中使用


# 直接用Trae对接coze工作流,可能会有错--》所以我提前写好了一个测试通过的
# 使用提示词,让Trae它参照我的代码,再去写就不会有错了
参照: test_coze.py ,这个测试脚本我已经测试通过,按照这个修改后端coze换脸工作流代码,使其能顺利调用coze工作流

import os
import requests

import json
import time

class CozeWorkflowTester:
    def __init__(self, api_key, workflow_id, base_url='https://api.coze.cn'):
        self.api_key = api_key
        self.workflow_id = workflow_id
        self.base_url = base_url
        self.headers = {
            'Authorization': f'Bearer {self.api_key}',
            'Content-Type': 'application/json'
        }
        self.file_upload_endpoint = f"{self.base_url}/v1/files/upload"
        self.workflow_endpoint = f"{self.base_url}/v1/workflow/run"
        # 根据用户提供的正确接口,使用参数化URL格式
        self.task_status_endpoint_template = "{base_url}/v1/workflows/{workflow_id}/run_histories/{execute_id}"

    def upload_file(self, file_path):
        """上传文件到Coze并获取文件ID"""
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")

        try:

            # 构建请求头,不包含Content-Type,让requests自动处理
            upload_headers = {
                'Authorization': f'Bearer {self.api_key}'
            }

            # 读取文件并准备上传
            with open(file_path, 'rb') as f:
                files = {'file': (os.path.basename(file_path), f)}

                # 发送请求
                response = requests.post(
                    url=self.file_upload_endpoint,
                    headers=upload_headers,
                    files=files
                )

            # 解析响应
            result = response.json()

            # 检查是否成功
            if response.status_code == 200 and 'data' in result and result.get('code') == 0:
                file_id = result['data'].get('id')
                if file_id:
                    return file_id
                else:
                    raise Exception(f"无法从响应中提取文件ID: {result}")
            else:
                raise Exception(f"文件上传失败: {result}")
        except Exception as e:
            raise Exception(f"文件上传失败")

    def run_workflow(self, source_image_path, target_image_path):
        """运行Coze工作流"""
        try:
            # 先上传图片获取文件ID
            source_file_id = self.upload_file(source_image_path)
            target_file_id = self.upload_file(target_image_path)
            payload = {
                "workflow_id": self.workflow_id,
                "parameters": {
                    "face": json.dumps({"file_id": source_file_id}),
                    "backed": json.dumps({"file_id": target_file_id}),
                    "text": "执行换脸操作"
                },
                "app_id": "",
                "is_async": True
            }

            # 发送请求
            response = requests.post(
                self.workflow_endpoint,
                headers=self.headers,
                json=payload
            )



            # 检查响应
            if response.status_code != 200:
                error_msg = f"工作流调用失败,状态码: {response.status_code}, 响应: {response.text}"
                raise Exception(error_msg)

            # 解析响应
            try:
                result = response.json()
            except json.JSONDecodeError:
                error_msg = f"无法解析响应JSON: {response.text}"
                raise Exception(error_msg)

            # 根据用户提示,正确的任务ID提取方式应该是检查msg为Success和提取execute_id
            # 检查响应消息是否成功
            if result.get('code') == 0 or result.get('msg') == '':
                # 提取execute_id作为任务ID
                task_id = result.get('execute_id')
                if task_id:
                    return task_id
                else:
                    error_msg = f"msg为Success但未找到execute_id: {result}"
                    raise Exception(error_msg)
            else:
                error_msg = f"工作流调用失败,返回消息: {result.get('msg')},详情: {result}"
                raise Exception(error_msg)
        except Exception as e:
            raise Exception(f"工作流调用异常: {str(e)}")

    def get_workflow_result(self, task_id):
        """获取工作流执行结果"""
        try:

            # 使用用户提供的正确接口URL格式: https://api.coze.cn/v1/workflows/:workflow_id/run_histories/:execute_id
            # 构建完整的任务状态查询URL
            task_status_endpoint = self.task_status_endpoint_template.format(
                base_url=self.base_url,
                workflow_id=self.workflow_id,
                execute_id=task_id
            )

            # 发送GET请求获取任务状态,新接口使用GET而不是POST
            response = requests.get(
                task_status_endpoint,
                headers=self.headers
            )

            # 检查响应
            if response.status_code != 200:
                raise Exception(f"查询结果失败: {response.status_code} - {response.text}")

            # 解析响应
            result = response.json()

            # 根据用户要求,只要code为0表示成功,返回的数据在data中
            # 检查任务状态
            code = result.get('code')

            if code == 0:
                # 任务成功完成

                # 获取data中的结果数据
                data = result.get('data', {})[0]

                execute_status = data.get('execute_status', '')
                if execute_status == 'Success':
                    result_image_url = json.loads(json.loads(data.get('output')).get('Output')).get('output')
                    if result_image_url:
                        # 下载结果图片到本地
                        local_path = self.download_image(result_image_url, task_id)

                        return {
                            'status': 'success',
                            'result_image_url': result_image_url,
                            'local_image_path': local_path,
                            'processing_time': result.get('processing_time', 0),
                            'task_id': task_id
                        }
                    else:
                        return {
                            'status': 'success',
                            'message': '任务成功但未返回图片',
                            'task_id': task_id
                        }
                elif execute_status == 'Fail':
                    return {
                        'status': 'failed',
                        'error_message': '任务执行失败',
                        'task_id': task_id
                    }
                else:
                    return {
                        'status': 'processing',
                        'task_id': task_id
                    }
            else:
                error_msg = f"查询结果失败,返回消息: {result.get('msg')},详情: {result}"
                raise Exception(error_msg)

        except Exception as e:
            raise Exception(f"查询结果异常: {str(e)}")

    def download_image(self, image_url, task_id):
        """下载图片到本地"""
        try:
            # 创建保存目录
            result_dir = os.path.join(os.getcwd(), 'coze_results')
            os.makedirs(result_dir, exist_ok=True)

            # 生成保存路径
            image_filename = f"result_{task_id}_{int(time.time())}.jpg"
            save_path = os.path.join(result_dir, image_filename)

            response = requests.get(image_url, stream=True)
            response.raise_for_status()

            # 保存图片
            with open(save_path, 'wb') as f:
                for chunk in response.iter_content(chunk_size=8192):
                    f.write(chunk)
            return save_path
        except Exception as e:
            return None

if __name__ == '__main__':
    coze = CozeWorkflowTester(
        api_key='pat_2mYHUdcRixq90o4Nd2Jp0nPhpBcIZg4wFZkkmvxe3UHcztZdXi0glSX1rPqxHK9T',
        workflow_id='7536960050292998154'
    )
    try:
        # 运行工作流
        task_id = coze.run_workflow('./source.jpg', './target.jpg')

        # 轮询结果,最多等待120秒
        max_wait_time = 120
        wait_interval = 5
        elapsed_time = 0

        while elapsed_time < max_wait_time:
            result = coze.get_workflow_result(task_id)

            if result['status'] == 'success':
                print(f"\n测试成功!")
                print(f"任务ID: {result['task_id']}")
                print(f"处理时间: {result.get('processing_time', '未知')}秒")
                print(f"结果图片URL: {result.get('result_image_url', '无')}")
                print(f"本地保存路径: {result.get('local_image_path', '无')}")
                break
            elif result['status'] == 'failed':
                print(f"\n测试失败!")
                print(f"任务ID: {result['task_id']}")
                print(f"错误信息: {result.get('error_message', '未知错误')}")
                break

            # 等待一段时间后重试
            print(f"任务处理中,{wait_interval}秒后再次查询...")
            time.sleep(wait_interval)
            elapsed_time += wait_interval

        print(f"\n测试超时!任务仍在处理中,已等待{max_wait_time}秒")
        print(f"任务ID: {task_id}")
        print("请稍后手动查询结果")
    except Exception as e:
        print(f"\n测试异常: {str(e)}")

image-20250921202149334

image-20250921202248502

image-20250921202421802

重要

# 我们水平其实目前不太具备---》前后端都开发完成,然后一点错误都没有的能力--》即便借助了Trae
	-需要我们有点水平
    
# 基础不是特别好的---》老师建议--》是在老师这个项目的基础上改
	-一会我讲如何在我基础上改
# 基础特别好---》可以按照我上课讲的,从零生成--》最终完成
	-这里面错误特别多:Trae这个ai编辑器,写android不是特别好
    -这里面还涉及了很多前后端联调的东西,所以有些难度
    
# 大家如果从零生成有问题,按照一会讲的---》是在老师这个项目的基础上改

# 我们学Trae的目的---》想让大家做一些小型项目---》后续慢慢成长



项目在本地运行

# 1 运行后端:方式一
	cd change_face_api
    python manage.py runserver 0.0.0.0:8000
# 2 运行后端:方式二
	Trae的对话中提示词:
    这个文件夹:change_face_api是一个后端django项目,请帮我运行在 0.0.0.0:8000 这个地址

image-20250921204100918

# 方式一:运行app端

# 1 前端app运行---》使用Androidstudio打开项目
# 2 插上手机--》打开手的usb调试---》点绿色监听运行
	-vivo手机--》不允许安装未知来源app--》搜一下--》解开--》可以解开--》就可以装了
# 方式二:运行app端
    -在trae中使用提示词:
    这个文件夹:是一个app项目,请帮我运行在手机上
  


    
    
# 本地跑的项目,手机无法访问
	必须保证:手机[无线]和电脑[有线]连同一个路由器
    
# 老师我前两天在调试的时候,trae能打包安装成功,但是android里就有报错
	-原因:Androidstudio默认给使用了高版本的java导致的

image-20250921204530242

2 云服务器

# 1 如果大家想上线项目,互联网用户都能使用【你的朋友都能用这个app,而不是只有我们自己玩】,必须有公网ip---》购买云服务器---》dify的云服务器部署--》讲过
	-如果大家只是做练习---》本地玩就够了--》互联网用户无法使用
    -你朋友真的要用:本地运行后端,你朋友连你家路由器,安装app,他就可以用了
    
# 2 购买云服务
	-地址:106.15.79.178
    -使用finalshell链接

2.1 上线架构图

image-20250921210506250

3 安装python3.11

# 1 阿里云的centos上有python环境:linux,mac 的很多服务使用了python开发的
	-我们开发项目,使用python11,这个解释器版本,我们不用,这个有点老了
	- python3.9.23     pip-->python/python3  pip/pip3 都被占了
  
 	-咱们项目开发,在3.11上开发的,需要使用3.11的解释器来运行

	-我们目标【多版本共存在同一个机器上,不要用乱了】:有多个python解释器
    	python  python3---》代指 python3.9    pip  pip3--》代指 python3.9的pip
        python3.11---》是我们自己装的           pip3.11 --》代指 python3.11的pip
        
    - 以后:这个云服务器上有两个python解释器
    	python/python3/pip/pip3----->默认的 python3.9.23 ---》我们不用他
        python3.11/pip3.11------------>我们自己装的,运行我们的后端项目
   

pip不是linux的一种命令嘛?为啥pip还有版本呢?
python解释器装完后会释放两个命令
	python pip  成对出现
	python执行代码  pip 下载第三方模块的
    
# 2 我们使用源码编译安装


######### 安装步骤 ######
#1  源码安装python,依赖一些第三方zlib* libffi-devel
dnf install openssl-devel bzip2-devel expat-devel readline-devel sqlite-devel psmisc libffi-devel zlib* libffi-devel  -y

# 2 前往用户根目录
cd 

#2 下载  3.11.9 源码 服务器终端
# https://registry.npmmirror.com/binary.html?path=python/
wget https://registry.npmmirror.com/-/binary/python/3.11.9/Python-3.11.9.tgz


#3  解压安装包
tar -xf Python-3.11.9.tgz

#4 进入目标文件
cd Python-3.11.9

#5  配置安装路径:/usr/local/python3
# 把3.11.9 编译安装到/usr/local/python311路径下
./configure --prefix=/usr/local/python311

#6  编译并安装,如果报错,说明缺依赖
# yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel psmisc libffi-devel zlib* libffi-devel  -y
# make只是编译----》可执行文件,没有安装
# 类似于在win上下载了安装包,但是没安装
# make install 安装---》类似于在win上下了安装包,一路下一步安装了,指定安装位置---》/usr/local/python39
make &&  make install

#7  建立软连接:/usr/local/python311路径不在环境变量,终端命令 python3,pip3
ln -s /usr/local/python311/bin/python3 /usr/bin/python3.11
ln -s /usr/local/python311/bin/pip3 /usr/bin/pip3.11

# 机器上有多个python和pip命令,对应关系如下
python       3.9      pip 
python3      3.9      pip3
python3.11   3.11      pip3.11

#8  删除安装包与文件: 不删留着也可以
cd 
rm -rf Python-3.11.9
rm -rf Python-3.11.9.tgz
#  python解释器、虚拟环境、conda、各种版本兼容、环境等等,这些蒙蒙的,怎么统一管理?
	python解释器是运行python代码的软件--》必须要有
    虚拟环境:多个项目,为了防止第三方模块冲突---》所有每个项目使用一个环境
    	换脸项目---》第三方模块---》单独放在一个小房子里---》用自己的
        换肾项目---》第三方模块---》单独放在一个小房子里---》用自己的
    一个操作系统内上装了多个python解释器
    	3.11   3.12 
    -conda 就是python虚拟环境、
    
    -第三模块有版本:不同项目用的版本又不一样
    
    
# 服务器上有Py安装包,为什么不卸载然后装?
	linux 服务上有很多内置服务--》内置软件
    这个内置软件使用python写的---》使用的版本是python3.9.23--》如果把它写了--》很多系统服务就运行不了了---》系统就进不来
    不要动别人的,我们再这个基础上加我们自己的

4 安装nginx

# 软件:反向代理服务器   反向带代理服务器
  - 做请求转发    (前端来了个请求---》打在了80端口上---》转到本地8000端口,或者其他机器的某个端口)
  - 静态资源代理    前端项目直接放在服务器上某个位置----》请求来了,使用nginx拿到访问的内容,直接返回
  - 负载均衡       假设来了1000个请求--》打在nginx上,nginx性能很高,能顶住---》只转发到某个django项目,可能顶不住---》集群化的不是3台django---》均匀的打在3台机器上
    

    
    
# 前往用户根目录
cd ~

#下载nginx 1.28.0
wget https://nginx.org/download/nginx-1.28.0.tar.gz
#解压安装包
tar -xf nginx-1.28.0.tar.gz

#进入目标文件
cd nginx-1.28.0

# 配置安装路径:/usr/local/nginx
# 安装 PCRE 开发包的名称是 pcre-devel,支持https访问
#zlib-devel:用于 gzip 压缩模块。openssl-devel:用于 SSL/TLS 模块(如启用 HTTPS) gcc 和 make:编译工具链
dnf install -y pcre-devel gcc make zlib-devel openssl-devel
./configure --prefix=/usr/local/nginx --with-http_ssl_module
#编译并安装
 make &&  make install

# 建立软连接:终端命令 nginx
ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx 

#删除安装包与文件:
cd ~
rm -rf nginx-1.28.0
rm -rf nginx-1.28.0.tar.xz

# 测试Nginx环境,服务器运行nginx,本地访问服务器ip
nginx   # 启动nginx服务,监听80端口----》公网ip 80 端口就能看到页面了
服务器绑定的域名 或 ip:80
    
http://106.15.79.178/ #就能看到下图
    
    
    
# 部署一个飞机大战:http://106.15.79.178/玩飞机大战---》讲完课就释放了

	-trae生成一个js的飞机大战项目
    -压缩
    -上传到:/usr/local/nginx/html
		- cd /usr/local/nginx/html/
	-解压缩:unzip plan.zip 
    -cd plan
    -执行:mv * ../ 
    -停止:nginx -s stop 
    -启动:nginx
    -访问:http://106.15.79.178/



# 静态文件放的路径
/usr/local/nginx/html

# 查看进程
ps aux | grep nginx


# 关闭和启动
关闭:nginx -s stop 
启动: nginx


# 它有配置文件---》配置监听那些地址,配置代理那些静态文件---》还没讲

image-20250921212817782

image-20250921213246009

5 安装mysql8

### 1 官方yum源
https://dev.mysql.com/downloads/repo/yum/

### 2 下载对应版本mysql源到本地,如果系统是centos9,这里选择el9版本
# no architecture的缩写,说明这个包可以在各个不同的cpu上使用
我们选择  mysql84-community-release-el9-1.noarch.rpm

### 3 或者直接来到:https://repo.mysql.com/
找到相应版本下载,我们下载
https://dev.mysql.com/downloads/file/?id=528548
    
# 从这开始-----

### 4 下载rpm包
wget https://dev.mysql.com/get/mysql84-community-release-el9-1.noarch.rpm

### 5 安装rpm包
dnf install -y mysql84-community-release-el9-1.noarch.rpm

### 6 开始安装
dnf install -y mysql-community-server --nogpgcheck  # 会自动把客户端装上
### 7 启动,查看状态
systemctl start mysqld
systemctl status mysqld

### 8 查看默认密码并登录
grep "password" /var/log/mysqld.log  #     RDSfU%&)W9U2

### 9 修改密码
mysql -uroot -p
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Lqz12345?';

#创建用户
CREATE USER 'root'@'%' IDENTIFIED BY 'Lqz12345?';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;

### 10 查看mysql版本
mysql -V


######## 后面上线项目时应该做的## 现在直接做了



#### 使用navicat 链接###
如果链接不上,就是安全组没开

## 创建库:change_face
	- navicat 图形化解密创建,change_face





# 12 安装mysqlclient
dnf install python3-devel mysql-devel --nogpgcheck -y
pip3.11 install mysqlclient

6 uwsgi

# 测试阶段使用 python manage.py runserver 0.0.0.0:8000
	-并发量低--》测试用可以
    -两三个人用
    
# 上线阶段使用--》给互联网用户用--》需要高并发
	-成百上千
    -需要再项目中编写配置文件
#1 安装uwsgi 

dnf install -y python3-devel gcc libxml2-devel
pip3.11 install uwsgi
ln -s /usr/local/python311/bin/uwsgi /usr/bin/uwsgi  # 以后在任意路径下敲uwsgi都能找到
	-uwsgi 装好,没在环境变量中--》在其他位置敲这个命令,找不到
       
    -做了软连接--》在任意位置敲这个软件名字,都能找到
    
    
# 2 敲 uwsgi  有反应,就是装好了

7 上传后端项目

7.1 编写uwsgi配置文件--change_face.ini

[uwsgi]
socket = 127.0.0.1:8000
chdir = /root/change_face_api/     # 这个是项目名,可能改,如果是自己项目
wsgi-file = change_face_project.wsgi # 这个需要改
processes = 4
threads = 2
master = true
daemonize = uwsgi.log

7.2 修改配置文件-settings.py

DEBUG = False
ALLOWED_HOSTS = ['*']
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'change_face',
        'USER': 'root',
        'PASSWORD': 'Lqz12345?',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'OPTIONS': {
            'charset': 'utf8mb4',
        },
    }
}

image-20250921221306960

7.3 在项目目录下新建 requirements.txt 如果有就不需要创建了

Django==4.2
requests==2.31.0
djangorestframework==3.14.0
django-simpleui==2023.12.12
pillow ==11.2.1
djangorestframework-simplejwt==5.3.1

7.4 压缩上传

# 1 把后端项目压缩成zip--》放到桌面上
# 2 上传到服务器
# 3 解压
	unzip change_face_api.zip 
# 4 进入到文件中
	cd change_face_api
    
# 5 安装依赖
	pip3.11 install -r requirements.txt
    
# 6 使用manage.py 运行看看有没有错,如果每错,再使用uwsgi运行
	# 查看依赖是否装好 :   pip3.11 list
	python3.11 manage.py runserver 0.0.0.0:8000
        
# 7 使用uwsgi运行
	#7. 1 启动uwsgi:注意目录,项目目录下
    uwsgi change_face.ini  # django项目跑在 8000端口了

    # 2 查看
    ps aux |grep uwsgi

    # 3 停止
    pkill -9 uwsgi

image-20250921221704920

image-20250921223054543

8 nginx配置

8.1 请求打在nginx上--转发给uwsgi

# 1 配置nginx转发
cd /usr/local/nginx/conf
mv nginx.conf nginx.conf.bak  # 把原来的配置文件备份一下
vi nginx.conf  # 创建一个新的
# 新增的server
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
	server {
        listen 8080;
        server_name  127.0.0.1;
        charset utf-8;
        location / {
            include uwsgi_params;
            uwsgi_pass 127.0.0.1:8000;
            uwsgi_param UWSGI_SCRIPT change_face_project.wsgi; 
            uwsgi_param UWSGI_CHDIR /root/change_face_api/;
            }
        location /static {
            alias /home/static;
       		}
        }
}


# 2 重启nginx 
nginx -s reload

8.2 导入假数据

# 1 本地导出
	-在数据库上右键--》转储sql文件--》数据和文件
    	-你们可以直接用我导出的
# 2 云服务的数据库中导入
	-在数据库上右键---》运行sql文件--》选择导出的sql

8.3 浏览器访问-看到下面表示成功

http://106.15.79.178:8080/admin/login/?next=/admin/

image-20250921223615618

image-20250921224332037

9 media访问

10 app打包上线

posted @ 2025-09-22 16:32  凫弥  阅读(118)  评论(0)    收藏  举报