添加token

This commit is contained in:
jlj 2025-05-21 15:25:38 +08:00
parent f882c25093
commit 1d5b3eb237
4 changed files with 117 additions and 104 deletions

View File

@ -19,7 +19,7 @@ class OfferStatusService:
:return: 状态字符串 :return: 状态字符串
""" """
try: try:
url = "http://127.0.0.1:8000/api/operation/negotiations/offer_status/" url = "http://81.69.223.133:58099/api/operation/negotiations/offer_status/"
payload = { payload = {
'creator_id': str(creator_id), 'creator_id': str(creator_id),

View File

@ -11,6 +11,7 @@ from rest_framework import viewsets, status
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from apps.user.authentication import CustomTokenAuthentication
from django.db.models import Q from django.db.models import Q
import os import os
import subprocess import subprocess
@ -30,6 +31,8 @@ from apps.brands.models import Product
client = Client(host="http://localhost:11434") client = Client(host="http://localhost:11434")
class ContentAnalysisAPI(APIView): class ContentAnalysisAPI(APIView):
authentication_classes = [CustomTokenAuthentication]
permission_classes = [IsAuthenticated]
parser_classes = (MultiPartParser, FormParser) parser_classes = (MultiPartParser, FormParser)
def post(self, request): def post(self, request):
@ -167,79 +170,71 @@ class ContentAnalysisAPI(APIView):
class NegotiationViewSet(viewsets.ModelViewSet): class NegotiationViewSet(viewsets.ModelViewSet):
queryset = Negotiation.objects.all() queryset = Negotiation.objects.all()
serializer_class = NegotiationSerializer serializer_class = NegotiationSerializer
authentication_classes = [CustomTokenAuthentication]
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""创建谈判(事务保护版)""" """创建谈判并返回包含初始消息的响应"""
try: serializer = self.get_serializer(data=request.data)
# 开启事务:所有数据库操作要么全部成功,要么全部回滚 serializer.is_valid(raise_exception=True)
with transaction.atomic():
# 1. 验证请求数据
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) # 自动返回400错误
creator = serializer.validated_data['creator'] # Extract creator and product from validated data
product = serializer.validated_data['product'] creator = serializer.validated_data['creator']
product = serializer.validated_data['product']
# 2. 检查达人是否存在 # 检查该用户是否存在
if not CreatorProfile.objects.filter(id=creator.id).exists(): if not CreatorProfile.objects.filter(id=creator.id).exists():
return Response( return Response({
{"code": 404, "message": "未找到指定的达人", "data": None}, "code": 404,
status=status.HTTP_404_NOT_FOUND "message": "未找到指定的达人",
) "data": None
})
# 3. 检查商品是否存在 # Check if the product exists
if not Product.objects.filter(id=product.id).exists(): if not Product.objects.filter(id=product.id).exists():
return Response( return Response({
{"code": 404, "message": "未找到指定的商品", "data": None}, "code": 404,
status=status.HTTP_404_NOT_FOUND "message": "未找到指定的商品",
) "data": None
})
# 4. 检查是否已存在相同谈判 # Check if a negotiation already exists for the same creator and product
if Negotiation.objects.filter(creator=creator, product=product).exists(): existing_negotiation = Negotiation.objects.filter(creator=creator, product=product).first()
return Response( if existing_negotiation:
{ return Response({
"code": 400, "code": 400,
"message": "谈判已存在", "message": "谈判已存在",
"data": {} "data": {
}, "negotiation_id": existing_negotiation.id
status=status.HTTP_400_BAD_REQUEST }
) })
# 5. 创建谈判记录 # 1. 创建谈判记录
negotiation = serializer.save() negotiation = serializer.save()
# 6. 生成初始消息 # 2. 生成并保存初始消息
initial_message = self._generate_welcome_message(negotiation) initial_message = self._generate_welcome_message(negotiation)
message = Message.objects.create( message = Message.objects.create(
negotiation=negotiation, negotiation=negotiation,
role='assistant', role='assistant',
content=initial_message, content=initial_message,
stage=negotiation.status stage=negotiation.status
) )
# 7. 返回成功响应 # 4. 构建响应数据
return Response( response_data = {
{ "code": 200,
"code": 200, "message": "谈判已创建",
"message": "谈判已创建", "data": {
"data": { "id": message.id,
"id": message.id, **serializer.data,
**serializer.data, "content": initial_message,
"content": initial_message, "created_at": message.created_at
"created_at": message.created_at }
} }
},
status=status.HTTP_201_CREATED,
headers=self.get_success_headers(serializer.data)
)
except Exception as e: headers = self.get_success_headers(serializer.data)
# 记录异常细节(实际生产环境应接入监控系统) return Response(response_data, status=status.HTTP_201_CREATED, headers=headers)
# logger.error(f"谈判创建失败: {str(e)}", exc_info=True)
return Response(
{"code": 500, "message": "服务器内部错误", "data": None},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@action(detail=True, methods=['post']) @action(detail=True, methods=['post'])
def chat(self, request, pk=None): def chat(self, request, pk=None):
@ -441,6 +436,7 @@ class NegotiationViewSet(viewsets.ModelViewSet):
🔍 商品详情 🔍 商品详情
- 名称{product.name} - 名称{product.name}
- 类目{product.category}
- 核心卖点{product.description} - 核心卖点{product.description}
🤝 合作优势 🤝 合作优势
@ -479,8 +475,8 @@ class NegotiationViewSet(viewsets.ModelViewSet):
你是作为一个专业的商务谈判专家, 现在正在与达人 {negotiation.creator.name} 进行价格谈判 你是作为一个专业的商务谈判专家, 现在正在与达人 {negotiation.creator.name} 进行价格谈判
当前谈判轮次{negotiation.current_round}/4 当前谈判轮次{negotiation.current_round}/4
商品参考价格{negotiation.product.sales_price_min} 商品参考价格{negotiation.product.max_price}
商品最低价格{negotiation.product.sales_price_max} 商品最低价格{negotiation.product.min_price}
达人最新回复 达人最新回复
{history[-1]['content']} {history[-1]['content']}
@ -537,8 +533,7 @@ class NegotiationViewSet(viewsets.ModelViewSet):
# history = self._get_stage_messages(negotiation) # history = self._get_stage_messages(negotiation)
content = f""" content = f"""
当前谈判已进入正式合同的准备阶段 当前谈判已进入正式合同的准备阶段
如果用户继续谈跟价格有关的信息的话我们就表示已经谈好价格了无法修改
如果用户表示没有收到合同邮件的话我们就再次向用户发送合同邮件 如果用户表示没有收到合同邮件的话我们就再次向用户发送合同邮件
如果用户表示接受的话我们引导用户去查看邮件并签署合同 如果用户表示接受的话我们引导用户去查看邮件并签署合同
如果用户表示拒绝的话我们引导用户去查看合同邮件然后在邮件中拒绝签署此合同 如果用户表示拒绝的话我们引导用户去查看合同邮件然后在邮件中拒绝签署此合同
@ -605,7 +600,7 @@ class NegotiationViewSet(viewsets.ModelViewSet):
subject = f"合同文件 - {negotiation.product.name}" subject = f"合同文件 - {negotiation.product.name}"
recipient = '3299361176@qq.com' recipient = '3299361176@qq.com'
if not recipient: if not recipient:
# logger.error(f"未找到达人 {negotiation.creator.name} 的邮箱,无法发送合同。") logger.error(f"未找到达人 {negotiation.creator.name} 的邮箱,无法发送合同。")
return False return False
try: try:
email = EmailMessage( email = EmailMessage(
@ -620,15 +615,17 @@ class NegotiationViewSet(viewsets.ModelViewSet):
email.attach('商品合同.docx', f.read(), email.attach('商品合同.docx', f.read(),
'application/vnd.openxmlformats-officedocument.wordprocessingml.document') 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')
email.send(fail_silently=False) email.send(fail_silently=False)
# logger.info(f"合同已发送到 {recipient}") logger.info(f"合同已发送到 {recipient}")
return True return True
except Exception as e: except Exception as e:
# logger.error(f"发送合同邮件失败: {e}") logger.error(f"发送合同邮件失败: {e}")
return False return False
## 根据接收到的达人列表来查找达人(已弃用) ## 根据接收到的达人列表来查找达人(已弃用)
class TopCreatorsAPI(APIView): class TopCreatorsAPI(APIView):
authentication_classes = [CustomTokenAuthentication]
permission_classes = [IsAuthenticated]
def post(self, request): def post(self, request):
# Extract filtering criteria and creator list from the request # Extract filtering criteria and creator list from the request
criteria = request.data.get('criteria', "") criteria = request.data.get('criteria', "")
@ -715,6 +712,8 @@ class TopCreatorsAPI(APIView):
## 生成sql直接查询达人库 ## 生成sql直接查询达人库
class CreatorSQLSearchAPI(APIView): class CreatorSQLSearchAPI(APIView):
authentication_classes = [CustomTokenAuthentication]
permission_classes = [IsAuthenticated]
def post(self, request): def post(self, request):
criteria = request.data.get('criteria', '') criteria = request.data.get('criteria', '')
top_n = int(request.data.get('top_n', 10)) # 默认为10 top_n = int(request.data.get('top_n', 10)) # 默认为10
@ -722,48 +721,54 @@ class CreatorSQLSearchAPI(APIView):
return Response({"error": "缺少筛选条件"}, status=400) return Response({"error": "缺少筛选条件"}, status=400)
table_schema = ''' table_schema = '''
creator_profiles( feishu_creators(
id bigint 主键, id char(32) 主键,
name varchar(255) 达人名称, record_id varchar(100) 记录ID,
avatar_url text 头像URL, contact_person varchar(50) 联系人姓名,
email varchar(255) 电子邮箱, handle longtext 账号/达人昵称,
instagram varchar(255) Instagram账号, tiktok_url longtext 抖音主页链接,
tiktok_link varchar(255) TikTok链接, fans_count varchar(50) 粉丝数,
location varchar(100) 位置, gmv varchar(100) GMV,
live_schedule varchar(255) 直播时间表, email varchar(254) 邮箱,
category varchar(100) 类别, phone varchar(50) 电话,
e_commerce_level int 电商能力等级, account_type varchar(50) 账号类型,
exposure_level varchar(10) 曝光等级, price_quote longtext 报价,
followers int 粉丝数, response_speed varchar(50) 响应速度,
gmv decimal(12,2) GMV(千美元), cooperation_intention varchar(50) 合作意向,
items_sold decimal(12,2) 售出商品数量, payment_method varchar(50) 支付方式,
avg_video_views int 平均视频浏览量, payment_account varchar(100) 支付账号,
pricing_min decimal(10,2) 最低个人定价, address longtext 地址,
pricing_max decimal(10,2) 最高个人定价, has_ooin varchar(10) 是否有OOIN,
pricing_package varchar(100) 套餐定价, source varchar(100) 数据来源,
collab_count int 合作次数, contact_status varchar(50) 联系状态,
latest_collab varchar(100) 最新合作, cooperation_brands json 合作品牌,
e_commerce_platforms json 电商平台, system_categories varchar(100) 系统分类,
gmv_by_channel json GMV按渠道分布, actual_categories varchar(100) 实际分类,
gmv_by_category json GMV按类别分布, human_categories varchar(100) 人工分类,
mcn varchar(255) MCN机构, creator_base varchar(100) 达人基地,
create_time datetime 创建时间, notes longtext 备注,
update_time datetime 更新时间 created_at datetime 创建时间,
updated_at datetime 更新时间
) )
''' '''
prompt = f""" prompt = f"""
你是一个SQL专家下面是MySQL表creator_profiles的结构: 你是一个SQL专家下面是MySQL表feishu_creators的结构:
{table_schema} {table_schema}
以下是我对表中每个字段的解释: 方便你写出正确的sql查询语句 以下是我对表中每个字段的解释: 方便你写出正确的sql查询语句
请根据以下自然语言筛选条件, 生成一条MySQL的SELECT语句, 查询daren_detail.creator_profiles表(注意一定要写数据库名称.表名称), 返回所有字段不要加任何解释说明, 只输出SQL语句本身 注意: fans_count 字段为字符串可能为纯数字 '1234'也可能带有 K M百万后缀 '9K', '56.5K', '13.2M'请在SQL中将其统一转换为数字后再进行比较, K=1000, M=1000000例如查找粉丝数量大于10000的博主: SELECT * FROM your_table WHERE (CASE WHEN fans_count LIKE '%K' THEN CAST(REPLACE(fans_count, 'K', '') AS DECIMAL(10,2)) * 1000 WHEN fans_count LIKE '%M' THEN CAST(REPLACE(fans_count, 'M', '') AS DECIMAL(10,2)) * 1000000 ELSE CAST(fans_count AS DECIMAL(10,2)) END) > 100000;
注意: response_speed 字段有以下几个取值: 1无回复 2一般 3正常 4积极请在sql中直接使用上述的某个值就好, 不要进行任何转换例如 SELECT * FROM daren.feishu_creators WHERE response_speed = '积极'
注意: source 字段有以下几个取值: 1. 线下活动 2TAP后台请在sql中直接使用上述的某个值就好, 不要进行任何转换例如: SELECT * FROM daren.feishu_creators WHERE source = 'TAP后台'
注意: system_categories 字段是一个varchar类型的值, 是一个列表形式表示的例如 ['日用百货']['美妆个护,保健'] 这种例如查找'美妆个护'的博主 SELECT * FROM daren.feishu_creators WHERE system_categories LIKE '%美妆个护%';
注意: gmv 字段是一个varchar类型的值, 例如 $86.89$8761.98$15.5K$12.5M 这种例如我要查询gmv大于10000美金的达人: SELECT * FROM daren.feishu_creators WHERE gmv NOT LIKE '$0-%' AND (CASE WHEN gmv LIKE '%K' THEN CAST(SUBSTRING(gmv, 2, LENGTH(gmv) - 2) AS DECIMAL(10,2)) * 1000 WHEN gmv LIKE '%M' THEN CAST(SUBSTRING(gmv, 2, LENGTH(gmv) - 2) AS DECIMAL(10,2)) * 1000000 ELSE CAST(SUBSTRING(gmv, 2) AS DECIMAL(10,2)) END) > 10000;
请根据以下自然语言筛选条件, 生成一条MySQL的SELECT语句, 查询daren.feishu_creators表(注意一定要写数据库名称.表名称), 返回所有字段不要加任何解释说明, 只输出SQL语句本身
筛选条件{criteria} 筛选条件{criteria}
""" """
# 2. 让大模型生成SQL # 2. 让大模型生成SQL
response = client.chat( response = client.chat(
model="qwen2.5:32b", model="deepseek-r1:70b",
messages=[{'role': 'user', 'content': prompt}], messages=[{'role': 'user', 'content': prompt}],
) )
sql = self._extract_sql(response['message']['content']) sql = self._extract_sql(response['message']['content'])

View File

@ -223,6 +223,3 @@ SIMPLE_JWT = {
'JTI_CLAIM': None, # 不在 token 中包含 JWT ID 'JTI_CLAIM': None, # 不在 token 中包含 JWT ID
} }
# JWT配置
JWT_SECRET_KEY = 'your-secret-key-here' # 建议使用更安全的密钥
JWT_EXPIRATION_DELTA = 24 * 60 * 60 # token有效期24小时

View File

@ -29,3 +29,14 @@ ERROR 2025-05-20 17:40:33,375 offer_status_service
ERROR 2025-05-20 17:41:03,425 offer_status_service 获取谈判状态失败: 不存在与用户id为14和商品id为241a67e0-1c99-44de-a5dd-40622ffa23b6的谈判 ERROR 2025-05-20 17:41:03,425 offer_status_service 获取谈判状态失败: 不存在与用户id为14和商品id为241a67e0-1c99-44de-a5dd-40622ffa23b6的谈判
ERROR 2025-05-20 17:41:33,477 offer_status_service 获取谈判状态失败: 不存在与用户id为14和商品id为241a67e0-1c99-44de-a5dd-40622ffa23b6的谈判 ERROR 2025-05-20 17:41:33,477 offer_status_service 获取谈判状态失败: 不存在与用户id为14和商品id为241a67e0-1c99-44de-a5dd-40622ffa23b6的谈判
ERROR 2025-05-20 17:42:03,529 offer_status_service 获取谈判状态失败: 不存在与用户id为14和商品id为241a67e0-1c99-44de-a5dd-40622ffa23b6的谈判 ERROR 2025-05-20 17:42:03,529 offer_status_service 获取谈判状态失败: 不存在与用户id为14和商品id为241a67e0-1c99-44de-a5dd-40622ffa23b6的谈判
INFO 2025-05-21 15:11:55,810 status_polling_service 已启动活动 1 的状态轮询,间隔 30 秒
INFO 2025-05-21 15:11:55,812 consumers 已启动活动 1 的状态轮询
INFO 2025-05-21 15:11:55,813 consumers WebSocket连接已建立: campaign_1
ERROR 2025-05-21 15:11:55,860 offer_status_service 请求谈判状态接口失败: 401
ERROR 2025-05-21 15:12:25,874 offer_status_service 请求谈判状态接口失败: 401
ERROR 2025-05-21 15:12:55,889 offer_status_service 请求谈判状态接口失败: 401
ERROR 2025-05-21 15:13:25,904 offer_status_service 请求谈判状态接口失败: 401
ERROR 2025-05-21 15:13:55,924 offer_status_service 请求谈判状态接口失败: 401
ERROR 2025-05-21 15:14:25,949 offer_status_service 请求谈判状态接口失败: 401
ERROR 2025-05-21 15:14:55,967 offer_status_service 请求谈判状态接口失败: 401
ERROR 2025-05-21 15:15:25,985 offer_status_service 请求谈判状态接口失败: 401