修改过滤和字段

This commit is contained in:
jlj 2025-05-23 12:11:03 +08:00
parent 024847575c
commit 38a1a0a808
9 changed files with 515 additions and 411 deletions

View File

@ -227,7 +227,7 @@ class CampaignStatusConsumer(WebsocketConsumer):
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
"gmv_generated": f"${creator.gmv}k" if creator.gmv else "$0",
"views_generated": f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0",
"pricing": f"${creator.pricing_min}" if creator.pricing_min else "$0",
"pricing": f"${creator.pricing}" if creator.pricing else "$0",
"status": status
}
creator_list.append(creator_data)

View File

@ -151,7 +151,7 @@ class OfferStatusService:
"followers": followers_formatted,
"gmv_generated": f"${creator.gmv}k" if creator.gmv else "$0",
"views_generated": avg_views_formatted,
"pricing": f"${creator.pricing_min}" if creator.pricing_min else "$0",
"pricing": f"${creator.pricing}" if creator.pricing else "$0",
"status": status
}
creator_list.append(creator_data)

View File

@ -365,7 +365,7 @@ class CampaignViewSet(viewsets.ModelViewSet):
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
"GMV Generated": f"${creator.gmv}k" if creator.gmv else "$0",
"Views Generated": f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0",
"Pricing": f"${creator.pricing_min}" if creator.pricing_min else "$0",
"Pricing": f"${creator.pricing}" if creator.pricing else "$0",
"Status": cc.status
}
all_creator_list.append(creator_data)
@ -521,7 +521,7 @@ class CampaignViewSet(viewsets.ModelViewSet):
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
"gmv_generated": f"${creator.gmv}k" if creator.gmv else "$0",
"views_generated": f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0",
"pricing": f"${creator.pricing_min}" if creator.pricing_min else "$0",
"pricing": f"${creator.pricing}" if creator.pricing else "$0",
"status": status
}
creator_list.append(creator_data)
@ -589,7 +589,7 @@ class CampaignViewSet(viewsets.ModelViewSet):
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
"gmv_generated": f"${creator.gmv}k" if creator.gmv else "$0",
"views_generated": f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0",
"pricing": f"${creator.pricing_min}" if creator.pricing_min else "$0",
"pricing": f"${creator.pricing}" if creator.pricing else "$0",
"status": status
}
creator_list.append(creator_data)
@ -648,7 +648,7 @@ class CampaignViewSet(viewsets.ModelViewSet):
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
"gmv_generated": f"${creator.gmv}k" if creator.gmv else "$0",
"views_generated": f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0",
"pricing": f"${creator.pricing_min}" if creator.pricing_min else "$0",
"pricing": f"${creator.pricing}" if creator.pricing else "$0",
"status": status
}
creator_list.append(creator_data)

View File

@ -1,365 +0,0 @@
from django.core.management.base import BaseCommand
from django.utils import timezone
from apps.daren_detail.models import (
CollaborationMetrics, VideoMetrics, LiveMetrics, CreatorProfile,
CreatorCampaign, BrandCampaign, CreatorVideo, FollowerMetrics,
TrendMetrics, PublicCreatorPool, PrivateCreatorPool, PrivateCreatorRelation
)
from apps.expertproducts.models import Product as ExpertProduct, Creator as ExpertCreator, Negotiation, Message
from apps.discovery.models import SearchSession, Creator as DiscoveryCreator
from apps.brands.models import Brand, Product as BrandProduct, Campaign, BrandChatSession
from apps.template.models import TemplateCategory, Template
from apps.user.models import User
import random
from datetime import datetime, timedelta
import uuid
class Command(BaseCommand):
help = '填充测试数据到所有相关模型'
def handle(self, *args, **kwargs):
self.stdout.write('开始填充数据...')
# 创建用户(避免重复)
user, created = User.objects.get_or_create(
email='test@example.com',
defaults={
'password': 'testpassword',
'company': '测试公司',
'name': '测试用户',
'is_first_login': False,
'last_login': timezone.now()
}
)
# 创建品牌(避免重复)
brands = []
brand_names = ['U品牌', 'R品牌', 'X品牌', 'Q品牌', 'A品牌', 'M品牌']
for name in brand_names:
brand, _ = Brand.objects.get_or_create(
name=name,
defaults={
'description': f'{name}的描述信息',
'logo_url': f'https://example.com/logos/{name}.png',
'category': '电子产品',
'source': '内部',
'collab_count': random.randint(5, 20),
'creators_count': random.randint(10, 50),
'campaign_id': str(uuid.uuid4()),
'total_gmv_achieved': random.randint(10000, 100000),
'total_views_achieved': random.randint(100000, 1000000),
'shop_overall_rating': round(random.uniform(3.5, 5.0), 1),
'dataset_id_list': [str(uuid.uuid4()) for _ in range(3)]
}
)
brands.append(brand)
# 创建品牌产品(避免重复)
products = []
for brand in brands:
for i in range(3):
product, _ = BrandProduct.objects.get_or_create(
brand=brand,
name=f'{brand.name}产品{i+1}',
defaults={
'description': f'{brand.name}产品{i+1}的详细描述',
'image_url': f'https://example.com/products/{brand.name}_{i+1}.jpg',
'pid': f'PID_{uuid.uuid4().hex[:8]}',
'commission_rate': random.uniform(5, 20),
'open_collab': random.uniform(10, 30),
'available_samples': random.randint(10, 100),
'sales_price_min': random.uniform(100, 500),
'sales_price_max': random.uniform(500, 2000),
'stock': random.randint(100, 1000),
'items_sold': random.randint(50, 500),
'product_rating': round(random.uniform(3.5, 5.0), 1),
'reviews_count': random.randint(10, 100),
'collab_creators': random.randint(5, 20),
'tiktok_shop': random.choice([True, False]),
'dataset_id': str(uuid.uuid4())
}
)
products.append(product)
# 创建活动(避免重复)
campaigns = []
for brand in brands:
campaign, _ = Campaign.objects.get_or_create(
brand=brand,
name=f'{brand.name}活动',
defaults={
'description': f'{brand.name}的活动描述',
'image_url': f'https://example.com/campaigns/{brand.name}.jpg',
'service': '直播带货',
'creator_type': 'KOL',
'creator_level': 'L3',
'creator_category': '电子产品',
'creators_count': random.randint(5, 20),
'gmv': '10000-50000',
'followers': '10000-100000',
'views': '100000-1000000',
'budget': '5000-20000',
'start_date': timezone.now(),
'end_date': timezone.now() + timedelta(days=30),
'dataset_id': str(uuid.uuid4()),
'status': 'in_progress',
'gmv_achieved': '25000',
'views_achieved': '500000',
'video_link': 'https://example.com/videos/campaign.mp4'
}
)
campaign.link_product.set(random.sample(products, 2))
campaigns.append(campaign)
# 创建创作者档案
creators = []
for i in range(10):
creator = CreatorProfile.objects.create(
name=f'创作者{i+1}',
avatar_url=f'https://example.com/avatars/creator_{i+1}.jpg',
email=f'creator{i+1}@example.com',
instagram=f'creator{i+1}',
tiktok_link=f'https://tiktok.com/@{i+1}',
location='上海',
live_schedule='每周一、三、五 20:00-22:00',
category=random.choice([c[0] for c in CreatorProfile.CATEGORY_CHOICES]),
e_commerce_level=random.randint(1, 7),
exposure_level=random.choice([e[0] for e in CreatorProfile.EXPOSURE_LEVEL_CHOICES]),
followers=random.randint(10000, 1000000),
gmv=random.uniform(10000, 1000000),
items_sold=random.uniform(1000, 10000),
avg_video_views=random.randint(10000, 100000),
pricing_min=random.uniform(1000, 5000),
pricing_max=random.uniform(5000, 20000),
pricing_package='基础套餐',
collab_count=random.randint(5, 50),
latest_collab='最新合作项目',
e_commerce_platforms=['TikTok', 'Instagram'],
gmv_by_channel={'TikTok': 60, 'Instagram': 40},
gmv_by_category={'电子产品': 70, '服装': 30},
mcn='知名MCN机构'
)
creators.append(creator)
# 创建协作指标
for creator in creators:
CollaborationMetrics.objects.create(
avg_commission_rate=random.uniform(5, 20),
products_count=random.randint(10, 50),
brand_collaborations=random.randint(5, 20),
min_product_price=random.uniform(100, 500),
max_product_price=random.uniform(500, 2000),
start_date=timezone.now().date(),
end_date=timezone.now().date() + timedelta(days=30),
creator=creator
)
# 创建视频指标
for creator in creators:
for video_type in ['regular', 'shoppable']:
VideoMetrics.objects.create(
video_type=video_type,
gpm=random.uniform(100, 1000),
videos_count=random.randint(10, 50),
avg_views=random.uniform(10000, 100000),
avg_engagement=random.uniform(1, 10),
avg_likes=random.randint(1000, 10000),
start_date=timezone.now().date(),
end_date=timezone.now().date() + timedelta(days=30),
creator=creator
)
# 创建直播指标
for creator in creators:
for live_type in ['regular', 'shoppable']:
LiveMetrics.objects.create(
live_type=live_type,
gpm=random.uniform(100, 1000),
lives_count=random.randint(5, 20),
avg_views=random.uniform(10000, 100000),
avg_engagement=random.uniform(1, 10),
avg_likes=random.randint(1000, 10000),
start_date=timezone.now().date(),
end_date=timezone.now().date() + timedelta(days=30),
creator=creator
)
# 创建创作者视频
for creator in creators:
for i in range(3):
CreatorVideo.objects.create(
creator=creator,
title=f'视频标题{i+1}',
description=f'视频描述{i+1}',
thumbnail_url=f'https://example.com/thumbnails/video_{i+1}.jpg',
video_url=f'https://example.com/videos/video_{i+1}.mp4',
video_id=f'VID_{uuid.uuid4().hex[:8]}',
video_type=random.choice(['regular', 'product']),
badge=random.choice(['red', 'gold']),
view_count=random.randint(10000, 100000),
like_count=random.randint(1000, 10000),
comment_count=random.randint(100, 1000),
has_product=random.choice([True, False]),
product_name=f'产品{i+1}' if random.choice([True, False]) else None,
product_url=f'https://example.com/products/{i+1}' if random.choice([True, False]) else None,
release_date=timezone.now().date()
)
# 创建粉丝指标
for creator in creators:
FollowerMetrics.objects.create(
creator=creator,
start_date=timezone.now().date(),
end_date=timezone.now().date() + timedelta(days=30),
female_percentage=random.uniform(40, 60),
male_percentage=random.uniform(40, 60),
age_18_24_percentage=random.uniform(20, 40),
age_25_34_percentage=random.uniform(30, 50),
age_35_44_percentage=random.uniform(10, 30),
age_45_54_percentage=random.uniform(5, 15),
age_55_plus_percentage=random.uniform(1, 10),
location_data={
'上海': random.uniform(10, 30),
'北京': random.uniform(10, 30),
'广州': random.uniform(5, 20),
'深圳': random.uniform(5, 20),
'杭州': random.uniform(5, 15)
}
)
# 创建趋势指标
for creator in creators:
for i in range(10):
TrendMetrics.objects.create(
creator=creator,
date=timezone.now().date() - timedelta(days=i),
gmv=random.uniform(1000, 10000),
items_sold=random.randint(100, 1000),
followers_count=random.randint(10000, 100000),
video_views=random.randint(10000, 100000),
engagement_rate=random.uniform(1, 10)
)
# 创建公有达人库
for creator in creators:
PublicCreatorPool.objects.create(
creator=creator,
category=creator.category,
remark=f'备注信息:{creator.name}的详细信息'
)
# 创建私有达人库(避免重复)
private_pool, _ = PrivateCreatorPool.objects.get_or_create(
user=user,
name='我的收藏',
defaults={
'description': '收藏的达人列表',
'is_default': True
}
)
# 创建私有达人关联
for creator in random.sample(creators, 5):
PrivateCreatorRelation.objects.create(
private_pool=private_pool,
creator=creator,
added_from_public=True,
notes=f'关于{creator.name}的笔记',
status=random.choice(['active', 'archived', 'favorite'])
)
# 创建专家产品
expert_products = []
for i in range(10):
p = ExpertProduct.objects.create(
name=f'专家产品{i+1}',
category='电子产品',
max_price=random.uniform(1000, 5000),
min_price=random.uniform(500, 1000),
description=f'专家产品{i+1}的详细描述'
)
expert_products.append(p)
# 创建专家创作者
expert_creators = []
for i in range(10):
c = ExpertCreator.objects.create(
name=f'专家创作者{i+1}',
sex=random.choice(['', '']),
age=random.randint(18, 45),
category='带货类',
followers=random.randint(10000, 1000000)
)
expert_creators.append(c)
# 创建谈判记录
for i in range(10):
negotiation = Negotiation.objects.create(
creator=expert_creators[i],
product=expert_products[i],
status=random.choice(['brand_review', 'price_negotiation', 'contract_review']),
current_round=random.randint(1, 5),
context={'current_price': random.uniform(500, 2000)}
)
# 为每个谈判创建消息
for j in range(3):
Message.objects.create(
negotiation=negotiation,
role=random.choice(['user', 'assistant']),
content=f'谈判消息{j+1}',
stage=negotiation.status
)
# 创建搜索会话
for i in range(10):
session = SearchSession.objects.create(
session_number=i+1,
creator_count=random.randint(5, 20),
shoppable_creators=random.randint(2, 10),
avg_followers=random.uniform(10000, 100000),
avg_gmv=random.uniform(10000, 100000),
avg_video_views=random.uniform(10000, 100000)
)
# 为每个会话创建创作者
for j in range(5):
DiscoveryCreator.objects.create(
session=session,
name=f'发现创作者{j+1}',
avatar=f'https://example.com/avatars/discovery_{j+1}.jpg',
category=random.choice([c[0] for c in DiscoveryCreator.CATEGORIES]),
ecommerce_level=random.choice([l[0] for l in DiscoveryCreator.ECOMMERCE_LEVELS]),
exposure_level=random.choice([l[0] for l in DiscoveryCreator.EXPOSURE_LEVELS]),
followers=random.uniform(10000, 1000000),
gmv=random.uniform(10000, 100000),
items_sold=random.uniform(1000, 10000),
avg_video_views=random.uniform(10000, 100000),
has_ecommerce=random.choice([True, False]),
tiktok_url=f'https://tiktok.com/@{j+1}'
)
# 创建模板分类
categories = []
for i in range(5):
category = TemplateCategory.objects.create(
name=f'模板分类{i+1}',
description=f'模板分类{i+1}的描述'
)
categories.append(category)
# 创建模板
for category in categories:
for i in range(2):
Template.objects.create(
title=f'模板{i+1}',
content=f'这是模板{i+1}的详细内容,包含了很多有用的信息。',
category=category,
mission=random.choice([m[0] for m in Template.MISSION_CHOICES]),
platform=random.choice([p[0] for p in Template.PLATFORM_CHOICES]),
collaboration_type=random.choice([c[0] for c in Template.COLLABORATION_CHOICES]),
service=random.choice([s[0] for s in Template.SERVICE_CHOICES]),
is_public=random.choice([True, False])
)
self.stdout.write(self.style.SUCCESS('数据填充完成!'))

View File

@ -0,0 +1,26 @@
# Generated by Django 5.1.5 on 2025-05-23 03:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('daren_detail', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='creatorprofile',
name='pricing_max',
),
migrations.RemoveField(
model_name='creatorprofile',
name='pricing_min',
),
migrations.AddField(
model_name='creatorprofile',
name='pricing',
field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='个人定价'),
),
]

View File

@ -190,8 +190,7 @@ class CreatorProfile(models.Model):
avg_video_views = models.IntegerField(default=0, blank=True, null=True, verbose_name="平均视频浏览量")
# 价格信息 - Pricing
pricing_min = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, verbose_name="最低个人定价")
pricing_max = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, verbose_name="最高个人定价")
pricing = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, verbose_name="个人定价")
pricing_package = models.CharField(max_length=100, blank=True, null=True, verbose_name="套餐定价")
# 合作信息 - Collaboration

View File

@ -59,6 +59,7 @@ urlpatterns = [
# 公有达人和私有达人API
path('public/creators/', views.get_public_creators, name='get_public_creators'),
path('public/creators/filter/', views.filter_public_creators, name='filter_public_creators'),
path('public/creators/add/', views.add_to_public_pool, name='add_to_public_pool'),
# 私有达人库
@ -69,4 +70,5 @@ urlpatterns = [
path('private/pools/creators/add/', views.add_creator_to_private_pool, name='add_creator_to_private_pool'),
path('private/pools/creators/update/', views.update_creator_in_private_pool, name='update_creator_in_private_pool'),
path('private/pools/creators/remove/', views.remove_creator_from_private_pool, name='remove_creator_from_private_pool'),
path('private/pools/creators/filter/', views.filter_private_pool_creators, name='filter_private_pool_creators'),
]

View File

@ -135,7 +135,8 @@ def filter_creators(request):
min_price, max_price = pricing_val.split('-')
min_price = float(min_price)
max_price = float(max_price)
query = query.filter(pricing_min__gte=min_price, pricing_max__lte=max_price)
# 修改:根据单一定价字段判断是否在区间内
query = query.filter(pricing__gte=min_price, pricing__lte=max_price)
# 获取总数据量
total_count = query.count()
@ -159,11 +160,8 @@ def filter_creators(request):
followers_formatted = f"{int(creator.followers / 1000)}k" if creator.followers else "0"
avg_views_formatted = f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0"
# 格式化价格区间
if creator.pricing_min is not None and creator.pricing_max is not None:
pricing_range = f"${creator.pricing_min}-{creator.pricing_max}"
else:
pricing_range = None
# 格式化价格
pricing_formatted = f"${creator.pricing}" if creator.pricing else None
# 格式化结果
formatted_creator = {
@ -178,10 +176,8 @@ def filter_creators(request):
"GMV": gmv_formatted,
"Items Sold": f"{creator.items_sold}k" if creator.items_sold else None,
"Avg. Video Views": avg_views_formatted,
"Pricing": {
"Range": pricing_range,
"Pack. P": creator.pricing_package
},
"Pricing": pricing_formatted,
"Pricing Package": creator.pricing_package,
"# Collab": creator.collab_count,
"Latest Collab.": creator.latest_collab,
"E-commerce": creator.e_commerce_platforms,
@ -285,10 +281,17 @@ def add_creator(request):
creator.avg_video_views = avg_video_views
# 更新价格信息
pricing = data.get('pricing', {})
if pricing:
creator.pricing_individual = pricing.get('individual', creator.pricing_individual)
creator.pricing_package = pricing.get('package', creator.pricing_package)
pricing_data = data.get('pricing', {})
if pricing_data:
# 处理individual价格或直接的pricing值
if 'individual' in pricing_data:
creator.pricing = pricing_data.get('individual')
elif 'pricing' in pricing_data:
creator.pricing = pricing_data.get('pricing')
elif isinstance(pricing_data, (int, float)):
creator.pricing = pricing_data
creator.pricing_package = pricing_data.get('package', creator.pricing_package) if isinstance(pricing_data, dict) else creator.pricing_package
creator.collab_count = data.get('collab_count', creator.collab_count)
creator.latest_collab = data.get('latest_collab', creator.latest_collab)
@ -508,9 +511,9 @@ def get_creator_detail(request, creator_id):
gmv_per_customer = 0
if creator.avg_video_views and creator.avg_video_views > 0:
if creator.pricing_min:
if creator.pricing:
try:
price = float(creator.pricing_min)
price = float(creator.pricing)
gpm = (price * 1000) / creator.avg_video_views
gpm = round(gpm, 2)
except (ValueError, AttributeError):
@ -554,7 +557,7 @@ def get_creator_detail(request, creator_id):
creator.category] if creator.category else [],
"mcn": creator.mcn or "",
"pricing": {
"range": f"${creator.pricing_min}-{creator.pricing_max}" if creator.pricing_min is not None and creator.pricing_max is not None else None,
"price": f"${creator.pricing}" if creator.pricing else None,
"package": creator.pricing_package
},
"collab_count": creator.collab_count,
@ -2017,7 +2020,7 @@ def get_public_creators(request):
category = request.GET.get('category')
keyword = request.GET.get('keyword')
# 基础查询
# 基础查询 - 从公有达人池开始
public_creators = PublicCreatorPool.objects.all()
# 应用过滤条件
@ -2055,11 +2058,8 @@ def get_public_creators(request):
followers_formatted = f"{int(creator.followers / 1000)}k" if creator.followers else "0"
avg_views_formatted = f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0"
# 格式化价格区间
if creator.pricing_min is not None and creator.pricing_max is not None:
pricing_range = f"${creator.pricing_min}-{creator.pricing_max}"
else:
pricing_range = None
# 格式化价格
pricing_formatted = f"${creator.pricing}" if creator.pricing else None
# 格式化结果
formatted_creator = {
@ -2073,7 +2073,8 @@ def get_public_creators(request):
"followers": followers_formatted,
"gmv": gmv_formatted,
"avg_video_views": avg_views_formatted,
"pricing": pricing_range,
"pricing": pricing_formatted,
"pricing_package": creator.pricing_package,
"collab_count": creator.collab_count,
"remark": public_creator.remark,
"category_public": public_creator.category
@ -2370,12 +2371,12 @@ def get_private_pool_creators(request, pool_id=None):
# 检查pool_id是否提供
if not pool_id:
pool_id = request.GET.get('pool_id')
if not pool_id:
return JsonResponse({
'code': 400,
'message': '缺少必要参数: pool_id',
'data': None
}, json_dumps_params={'ensure_ascii': False})
if not pool_id:
return JsonResponse({
'code': 400,
'message': '缺少必要参数: pool_id',
'data': None
}, json_dumps_params={'ensure_ascii': False})
# 获取分页参数
page = int(request.GET.get('page', 1))
@ -2438,8 +2439,8 @@ def get_private_pool_creators(request, pool_id=None):
avg_views_formatted = f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0"
# 格式化价格区间
if creator.pricing_min is not None and creator.pricing_max is not None:
pricing_range = f"${creator.pricing_min}-{creator.pricing_max}"
if creator.pricing is not None:
pricing_range = f"${creator.pricing}"
else:
pricing_range = None
@ -2766,6 +2767,439 @@ def remove_creator_from_private_pool(request):
}, json_dumps_params={'ensure_ascii': False})
@api_view(['POST'])
@authentication_classes([CustomTokenAuthentication])
@csrf_exempt
@require_http_methods(["POST"])
def filter_public_creators(request):
"""根据过滤条件筛选公有达人库列表"""
try:
from .models import PublicCreatorPool, CreatorProfile
import json
from django.db.models import Q
# 从URL获取分页参数
page = int(request.GET.get('page', 1))
page_size = int(request.GET.get('page_size', 10))
# 解析POST请求体
data = json.loads(request.body)
filter_data = data.get('filter', {})
# 基础查询 - 从公有达人池开始
public_creators = PublicCreatorPool.objects.all()
# 达人分类过滤Category 多选过滤)
category = filter_data.get('category')
if category and len(category) > 0:
public_creators = public_creators.filter(creator__category__in=category)
# 电商能力等级过滤 (L1-L7),多选
e_commerce_level = filter_data.get('e_commerce_level')
if e_commerce_level and len(e_commerce_level) > 0:
level_nums = []
for level_str in e_commerce_level:
if level_str.startswith('L'):
level_nums.append(int(level_str[1:]))
if level_nums:
public_creators = public_creators.filter(creator__e_commerce_level__in=level_nums)
# 曝光等级过滤 (KOL-1, KOL-2, KOC-1等),多选
exposure_level = filter_data.get('exposure_level')
if exposure_level and len(exposure_level) > 0:
public_creators = public_creators.filter(creator__exposure_level__in=exposure_level)
# GMV范围过滤 ($0-$5k, $5k-$25k, $25k-$50k等),多选
gmv_range = filter_data.get('gmv_range')
if gmv_range and len(gmv_range) > 0:
gmv_q = Q()
for gmv_val in gmv_range:
gmv_min, gmv_max = 0, float('inf')
if gmv_val == "$0-$5k":
gmv_min, gmv_max = 0, 5
elif gmv_val == "$5k-$25k":
gmv_min, gmv_max = 5, 25
elif gmv_val == "$25k-$50k":
gmv_min, gmv_max = 25, 50
elif gmv_val == "$50k-$150k":
gmv_min, gmv_max = 50, 150
elif gmv_val == "$150k-$400k":
gmv_min, gmv_max = 150, 400
elif gmv_val == "$400k-$1500k":
gmv_min, gmv_max = 400, 1500
elif gmv_val == "$1500k+":
gmv_min, gmv_max = 1500, float('inf')
range_q = Q()
if gmv_min > 0:
range_q &= Q(creator__gmv__gte=gmv_min)
if gmv_max < float('inf'):
range_q &= Q(creator__gmv__lte=gmv_max)
gmv_q |= range_q
public_creators = public_creators.filter(gmv_q)
# 观看量范围过滤,单选
views_range = filter_data.get('views_range')
if views_range and len(views_range) > 0:
views_min, views_max = 0, float('inf')
views_val = views_range[0]
if views_val == "0-100":
views_min, views_max = 0, 100
elif views_val == "1k-10k":
views_min, views_max = 1000, 10000
elif views_val == "10k-100k":
views_min, views_max = 10000, 100000
elif views_val == "100k-250k":
views_min, views_max = 100000, 250000
elif views_val == "250k-500k":
views_min, views_max = 250000, 500000
elif views_val == "500k+":
views_min = 500000
if views_min > 0:
public_creators = public_creators.filter(creator__avg_video_views__gte=views_min)
if views_max < float('inf'):
public_creators = public_creators.filter(creator__avg_video_views__lte=views_max)
# 价格区间过滤逻辑,单选
pricing = filter_data.get('pricing')
if pricing and len(pricing) > 0:
pricing_val = pricing[0]
if '-' in pricing_val:
min_price, max_price = pricing_val.split('-')
min_price = float(min_price)
max_price = float(max_price)
# 修改:根据单一定价字段判断是否在区间内
public_creators = public_creators.filter(
creator__pricing__gte=min_price,
creator__pricing__lte=max_price
)
# 获取总数据量
total_count = public_creators.count()
# 计算分页
start = (page - 1) * page_size
end = start + page_size
# 执行查询并分页
creators = public_creators[start:end]
creator_list = []
for public_creator in creators:
creator = public_creator.creator
# 格式化电商等级
e_commerce_level_formatted = f"L{creator.e_commerce_level}" if creator.e_commerce_level else None
# 格式化GMV
gmv_formatted = f"${creator.gmv}k" if creator.gmv else "$0"
# 格式化粉丝数和观看量
followers_formatted = f"{int(creator.followers / 1000)}k" if creator.followers else "0"
avg_views_formatted = f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0"
# 格式化价格区间
if creator.pricing is not None:
pricing_range = f"${creator.pricing}"
else:
pricing_range = None
# 格式化结果
formatted_creator = {
"public_id": public_creator.id,
"creator_id": creator.id,
"name": creator.name,
"avatar": creator.avatar_url,
"category": creator.category,
"e_commerce_level": e_commerce_level_formatted,
"exposure_level": creator.exposure_level,
"followers": followers_formatted,
"gmv": gmv_formatted,
"avg_video_views": avg_views_formatted,
"pricing": pricing_range,
"pricing_package": creator.pricing_package,
"collab_count": creator.collab_count,
"remark": public_creator.remark,
"category_public": public_creator.category
}
creator_list.append(formatted_creator)
# 计算总页数
total_pages = (total_count + page_size - 1) // page_size
# 构造分页信息
pagination = {
"current_page": page,
"total_pages": total_pages,
"total_count": total_count,
"has_next": page < total_pages,
"has_prev": page > 1
}
return JsonResponse({
'code': 200,
'message': '获取成功',
'data': creator_list,
'pagination': pagination
}, json_dumps_params={'ensure_ascii': False})
except Exception as e:
logger.error(f"筛选公有达人库列表失败: {e}")
import traceback
logger.error(f"详细错误: {traceback.format_exc()}")
return JsonResponse({
'code': 500,
'message': f'筛选公有达人库列表失败: {str(e)}',
'data': None
}, json_dumps_params={'ensure_ascii': False})
@api_view(['POST'])
@authentication_classes([CustomTokenAuthentication])
@csrf_exempt
@require_http_methods(["POST"])
def filter_private_pool_creators(request):
"""根据过滤条件筛选私有达人库中的达人"""
try:
from .models import PrivateCreatorPool, PrivateCreatorRelation
import json
from django.db.models import Q
# 解析POST请求体
data = json.loads(request.body)
# 获取私有库ID
pool_id = data.get('pool_id')
if not pool_id:
return JsonResponse({
'code': 400,
'message': '缺少必要参数: pool_id',
'data': None
}, json_dumps_params={'ensure_ascii': False})
# 获取过滤条件
filter_data = data.get('filter', {})
# 获取分页参数
page = int(request.GET.get('page', 1))
page_size = int(request.GET.get('page_size', 10))
# 获取状态过滤参数,如果提供了才使用
status = filter_data.get('status')
# 查询私有库信息
try:
private_pool = PrivateCreatorPool.objects.get(id=pool_id)
# 检查私有库是否属于当前登录用户
if private_pool.user_id != request.user.id:
return JsonResponse({
'code': 403,
'message': '没有权限访问此私有达人库',
'data': None
}, json_dumps_params={'ensure_ascii': False})
except PrivateCreatorPool.DoesNotExist:
return JsonResponse({
'code': 404,
'message': f'找不到ID为 {pool_id} 的私有库',
'data': None
}, json_dumps_params={'ensure_ascii': False})
# 查询私有库中的达人关联
creator_relations = PrivateCreatorRelation.objects.filter(
private_pool=private_pool
).select_related('creator')
# 应用状态过滤条件仅当提供了status参数时
if status:
creator_relations = creator_relations.filter(status=status)
# 应用复杂过滤条件
# --------- 从filter_creators借鉴的过滤逻辑 ---------
# Category 多选过滤
category = filter_data.get('category')
if category and len(category) > 0:
creator_relations = creator_relations.filter(creator__category__in=category)
# 电商能力等级过滤 (L1-L7),多选
e_commerce_level = filter_data.get('e_commerce_level')
if e_commerce_level and len(e_commerce_level) > 0:
level_nums = []
for level_str in e_commerce_level:
if level_str.startswith('L'):
level_nums.append(int(level_str[1:]))
if level_nums:
creator_relations = creator_relations.filter(creator__e_commerce_level__in=level_nums)
# 曝光等级过滤 (KOL-1, KOL-2, KOC-1等),多选
exposure_level = filter_data.get('exposure_level')
if exposure_level and len(exposure_level) > 0:
creator_relations = creator_relations.filter(creator__exposure_level__in=exposure_level)
# GMV范围过滤 ($0-$5k, $5k-$25k, $25k-$50k等),多选
gmv_range = filter_data.get('gmv_range')
if gmv_range and len(gmv_range) > 0:
gmv_q = Q()
for gmv_val in gmv_range:
gmv_min, gmv_max = 0, float('inf')
if gmv_val == "$0-$5k":
gmv_min, gmv_max = 0, 5
elif gmv_val == "$5k-$25k":
gmv_min, gmv_max = 5, 25
elif gmv_val == "$25k-$50k":
gmv_min, gmv_max = 25, 50
elif gmv_val == "$50k-$150k":
gmv_min, gmv_max = 50, 150
elif gmv_val == "$150k-$400k":
gmv_min, gmv_max = 150, 400
elif gmv_val == "$400k-$1500k":
gmv_min, gmv_max = 400, 1500
elif gmv_val == "$1500k+":
gmv_min, gmv_max = 1500, float('inf')
range_q = Q()
if gmv_min > 0:
range_q &= Q(creator__gmv__gte=gmv_min)
if gmv_max < float('inf'):
range_q &= Q(creator__gmv__lte=gmv_max)
gmv_q |= range_q
creator_relations = creator_relations.filter(gmv_q)
# 观看量范围过滤,单选
views_range = filter_data.get('views_range')
if views_range and len(views_range) > 0:
views_min, views_max = 0, float('inf')
views_val = views_range[0]
if views_val == "0-100":
views_min, views_max = 0, 100
elif views_val == "1k-10k":
views_min, views_max = 1000, 10000
elif views_val == "10k-100k":
views_min, views_max = 10000, 100000
elif views_val == "100k-250k":
views_min, views_max = 100000, 250000
elif views_val == "250k-500k":
views_min, views_max = 250000, 500000
elif views_val == "500k+":
views_min = 500000
if views_min > 0:
creator_relations = creator_relations.filter(creator__avg_video_views__gte=views_min)
if views_max < float('inf'):
creator_relations = creator_relations.filter(creator__avg_video_views__lte=views_max)
# 价格区间过滤逻辑,单选
pricing = filter_data.get('pricing')
if pricing and len(pricing) > 0:
pricing_val = pricing[0]
if '-' in pricing_val:
min_price, max_price = pricing_val.split('-')
min_price = float(min_price)
max_price = float(max_price)
# 修改:根据单一定价字段判断是否在区间内
creator_relations = creator_relations.filter(
creator__pricing__gte=min_price,
creator__pricing__lte=max_price
)
# 获取总数据量
total_count = creator_relations.count()
# 计算分页
start = (page - 1) * page_size
end = start + page_size
# 执行查询并分页
paged_relations = creator_relations[start:end]
creator_list = []
for relation in paged_relations:
creator = relation.creator
# 格式化电商等级
e_commerce_level_formatted = f"L{creator.e_commerce_level}" if creator.e_commerce_level else None
# 格式化GMV
gmv_formatted = f"${creator.gmv}k" if creator.gmv else "$0"
# 格式化粉丝数和观看量
followers_formatted = f"{int(creator.followers / 1000)}k" if creator.followers else "0"
avg_views_formatted = f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0"
# 格式化价格区间
if creator.pricing is not None:
pricing_range = f"${creator.pricing}"
else:
pricing_range = None
# 格式化结果
formatted_creator = {
"relation_id": relation.id,
"creator_id": creator.id,
"name": creator.name,
"avatar": creator.avatar_url,
"category": creator.category,
"e_commerce_level": e_commerce_level_formatted,
"exposure_level": creator.exposure_level,
"followers": followers_formatted,
"gmv": gmv_formatted,
"avg_video_views": avg_views_formatted,
"pricing": pricing_range, # 使用格式化后的价格区间
"pricing_package": creator.pricing_package,
"collab_count": creator.collab_count,
"notes": relation.notes,
"status": relation.status,
"added_from_public": relation.added_from_public,
"added_at": relation.created_at.strftime('%Y-%m-%d')
}
creator_list.append(formatted_creator)
# 计算总页数
total_pages = (total_count + page_size - 1) // page_size
# 构造分页信息
pagination = {
"current_page": page,
"total_pages": total_pages,
"total_count": total_count,
"has_next": page < total_pages,
"has_prev": page > 1
}
# 构造私有库信息
pool_info = {
"id": private_pool.id,
"name": private_pool.name,
"description": private_pool.description,
"is_default": private_pool.is_default,
"user_id": private_pool.user_id,
"created_at": private_pool.created_at.strftime('%Y-%m-%d')
}
return JsonResponse({
'code': 200,
'message': '获取成功',
'data': creator_list,
'pagination': pagination,
'pool_info': pool_info
}, json_dumps_params={'ensure_ascii': False})
except Exception as e:
logger.error(f"筛选私有达人库列表失败: {e}")
import traceback
logger.error(f"详细错误: {traceback.format_exc()}")
return JsonResponse({
'code': 500,
'message': f'筛选私有达人库列表失败: {str(e)}',
'data': None
}, json_dumps_params={'ensure_ascii': False})

View File

@ -3,6 +3,14 @@ from rest_framework import exceptions
from django.contrib.auth.models import AnonymousUser
from .models import User, UserToken
from django.utils import timezone
from rest_framework.exceptions import APIException
from rest_framework import status
class CustomAuthenticationFailed(APIException):
status_code = status.HTTP_401_UNAUTHORIZED
def __init__(self, code, message):
self.detail = {"code": code, "message": message}
class CustomTokenAuthentication(authentication.BaseAuthentication):
keyword = 'Token' # 设置认证头关键字
@ -12,14 +20,14 @@ class CustomTokenAuthentication(authentication.BaseAuthentication):
auth_header = request.META.get('HTTP_AUTHORIZATION')
if not auth_header:
raise exceptions.AuthenticationFailed('未提供认证头')
raise CustomAuthenticationFailed(401, '未提供认证头')
try:
# 提取token
parts = auth_header.split()
if len(parts) != 2 or parts[0] != self.keyword:
raise exceptions.AuthenticationFailed('认证头格式不正确')
raise CustomAuthenticationFailed(401, '认证头格式不正确')
token = parts[1]
@ -30,18 +38,18 @@ class CustomTokenAuthentication(authentication.BaseAuthentication):
expired_at__gt=timezone.now() # 确保token未过期
)
except UserToken.DoesNotExist:
raise exceptions.AuthenticationFailed('无效或过期的token')
raise CustomAuthenticationFailed(401, '无效或过期的token')
# 检查用户是否激活
if not token_obj.user.is_active:
raise exceptions.AuthenticationFailed('用户未激活')
raise CustomAuthenticationFailed(401, '用户未激活')
return (token_obj.user, None)
except Exception as e:
if isinstance(e, exceptions.AuthenticationFailed):
if isinstance(e, CustomAuthenticationFailed):
raise e
raise exceptions.AuthenticationFailed('认证失败')
raise CustomAuthenticationFailed(401, '认证失败')
def authenticate_header(self, request):
return self.keyword