修改过滤和字段
This commit is contained in:
parent
024847575c
commit
38a1a0a808
@ -227,7 +227,7 @@ class CampaignStatusConsumer(WebsocketConsumer):
|
|||||||
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
|
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
|
||||||
"gmv_generated": f"${creator.gmv}k" if creator.gmv 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",
|
"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
|
"status": status
|
||||||
}
|
}
|
||||||
creator_list.append(creator_data)
|
creator_list.append(creator_data)
|
||||||
|
@ -151,7 +151,7 @@ class OfferStatusService:
|
|||||||
"followers": followers_formatted,
|
"followers": followers_formatted,
|
||||||
"gmv_generated": f"${creator.gmv}k" if creator.gmv else "$0",
|
"gmv_generated": f"${creator.gmv}k" if creator.gmv else "$0",
|
||||||
"views_generated": avg_views_formatted,
|
"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
|
"status": status
|
||||||
}
|
}
|
||||||
creator_list.append(creator_data)
|
creator_list.append(creator_data)
|
||||||
|
@ -365,7 +365,7 @@ class CampaignViewSet(viewsets.ModelViewSet):
|
|||||||
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
|
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
|
||||||
"GMV Generated": f"${creator.gmv}k" if creator.gmv 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",
|
"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
|
"Status": cc.status
|
||||||
}
|
}
|
||||||
all_creator_list.append(creator_data)
|
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",
|
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
|
||||||
"gmv_generated": f"${creator.gmv}k" if creator.gmv 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",
|
"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
|
"status": status
|
||||||
}
|
}
|
||||||
creator_list.append(creator_data)
|
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",
|
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
|
||||||
"gmv_generated": f"${creator.gmv}k" if creator.gmv 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",
|
"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
|
"status": status
|
||||||
}
|
}
|
||||||
creator_list.append(creator_data)
|
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",
|
"followers": f"{int(creator.followers / 1000)}k" if creator.followers else "0",
|
||||||
"gmv_generated": f"${creator.gmv}k" if creator.gmv 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",
|
"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
|
"status": status
|
||||||
}
|
}
|
||||||
creator_list.append(creator_data)
|
creator_list.append(creator_data)
|
||||||
|
@ -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('数据填充完成!'))
|
|
@ -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='个人定价'),
|
||||||
|
),
|
||||||
|
]
|
@ -190,8 +190,7 @@ class CreatorProfile(models.Model):
|
|||||||
avg_video_views = models.IntegerField(default=0, blank=True, null=True, verbose_name="平均视频浏览量")
|
avg_video_views = models.IntegerField(default=0, blank=True, null=True, verbose_name="平均视频浏览量")
|
||||||
|
|
||||||
# 价格信息 - Pricing
|
# 价格信息 - Pricing
|
||||||
pricing_min = 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_max = 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="套餐定价")
|
pricing_package = models.CharField(max_length=100, blank=True, null=True, verbose_name="套餐定价")
|
||||||
|
|
||||||
# 合作信息 - Collaboration
|
# 合作信息 - Collaboration
|
||||||
|
@ -59,6 +59,7 @@ urlpatterns = [
|
|||||||
|
|
||||||
# 公有达人和私有达人API
|
# 公有达人和私有达人API
|
||||||
path('public/creators/', views.get_public_creators, name='get_public_creators'),
|
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'),
|
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/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/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/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'),
|
||||||
]
|
]
|
||||||
|
@ -135,7 +135,8 @@ def filter_creators(request):
|
|||||||
min_price, max_price = pricing_val.split('-')
|
min_price, max_price = pricing_val.split('-')
|
||||||
min_price = float(min_price)
|
min_price = float(min_price)
|
||||||
max_price = float(max_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()
|
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"
|
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"
|
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_formatted = f"${creator.pricing}" if creator.pricing else None
|
||||||
pricing_range = f"${creator.pricing_min}-{creator.pricing_max}"
|
|
||||||
else:
|
|
||||||
pricing_range = None
|
|
||||||
|
|
||||||
# 格式化结果
|
# 格式化结果
|
||||||
formatted_creator = {
|
formatted_creator = {
|
||||||
@ -178,10 +176,8 @@ def filter_creators(request):
|
|||||||
"GMV": gmv_formatted,
|
"GMV": gmv_formatted,
|
||||||
"Items Sold": f"{creator.items_sold}k" if creator.items_sold else None,
|
"Items Sold": f"{creator.items_sold}k" if creator.items_sold else None,
|
||||||
"Avg. Video Views": avg_views_formatted,
|
"Avg. Video Views": avg_views_formatted,
|
||||||
"Pricing": {
|
"Pricing": pricing_formatted,
|
||||||
"Range": pricing_range,
|
"Pricing Package": creator.pricing_package,
|
||||||
"Pack. P": creator.pricing_package
|
|
||||||
},
|
|
||||||
"# Collab": creator.collab_count,
|
"# Collab": creator.collab_count,
|
||||||
"Latest Collab.": creator.latest_collab,
|
"Latest Collab.": creator.latest_collab,
|
||||||
"E-commerce": creator.e_commerce_platforms,
|
"E-commerce": creator.e_commerce_platforms,
|
||||||
@ -285,10 +281,17 @@ def add_creator(request):
|
|||||||
creator.avg_video_views = avg_video_views
|
creator.avg_video_views = avg_video_views
|
||||||
|
|
||||||
# 更新价格信息
|
# 更新价格信息
|
||||||
pricing = data.get('pricing', {})
|
pricing_data = data.get('pricing', {})
|
||||||
if pricing:
|
if pricing_data:
|
||||||
creator.pricing_individual = pricing.get('individual', creator.pricing_individual)
|
# 处理individual价格或直接的pricing值
|
||||||
creator.pricing_package = pricing.get('package', creator.pricing_package)
|
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.collab_count = data.get('collab_count', creator.collab_count)
|
||||||
creator.latest_collab = data.get('latest_collab', creator.latest_collab)
|
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
|
gmv_per_customer = 0
|
||||||
|
|
||||||
if creator.avg_video_views and creator.avg_video_views > 0:
|
if creator.avg_video_views and creator.avg_video_views > 0:
|
||||||
if creator.pricing_min:
|
if creator.pricing:
|
||||||
try:
|
try:
|
||||||
price = float(creator.pricing_min)
|
price = float(creator.pricing)
|
||||||
gpm = (price * 1000) / creator.avg_video_views
|
gpm = (price * 1000) / creator.avg_video_views
|
||||||
gpm = round(gpm, 2)
|
gpm = round(gpm, 2)
|
||||||
except (ValueError, AttributeError):
|
except (ValueError, AttributeError):
|
||||||
@ -554,7 +557,7 @@ def get_creator_detail(request, creator_id):
|
|||||||
creator.category] if creator.category else [],
|
creator.category] if creator.category else [],
|
||||||
"mcn": creator.mcn or "",
|
"mcn": creator.mcn or "",
|
||||||
"pricing": {
|
"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
|
"package": creator.pricing_package
|
||||||
},
|
},
|
||||||
"collab_count": creator.collab_count,
|
"collab_count": creator.collab_count,
|
||||||
@ -2017,7 +2020,7 @@ def get_public_creators(request):
|
|||||||
category = request.GET.get('category')
|
category = request.GET.get('category')
|
||||||
keyword = request.GET.get('keyword')
|
keyword = request.GET.get('keyword')
|
||||||
|
|
||||||
# 基础查询
|
# 基础查询 - 从公有达人池开始
|
||||||
public_creators = PublicCreatorPool.objects.all()
|
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"
|
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"
|
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_formatted = f"${creator.pricing}" if creator.pricing else None
|
||||||
pricing_range = f"${creator.pricing_min}-{creator.pricing_max}"
|
|
||||||
else:
|
|
||||||
pricing_range = None
|
|
||||||
|
|
||||||
# 格式化结果
|
# 格式化结果
|
||||||
formatted_creator = {
|
formatted_creator = {
|
||||||
@ -2073,7 +2073,8 @@ def get_public_creators(request):
|
|||||||
"followers": followers_formatted,
|
"followers": followers_formatted,
|
||||||
"gmv": gmv_formatted,
|
"gmv": gmv_formatted,
|
||||||
"avg_video_views": avg_views_formatted,
|
"avg_video_views": avg_views_formatted,
|
||||||
"pricing": pricing_range,
|
"pricing": pricing_formatted,
|
||||||
|
"pricing_package": creator.pricing_package,
|
||||||
"collab_count": creator.collab_count,
|
"collab_count": creator.collab_count,
|
||||||
"remark": public_creator.remark,
|
"remark": public_creator.remark,
|
||||||
"category_public": public_creator.category
|
"category_public": public_creator.category
|
||||||
@ -2370,12 +2371,12 @@ def get_private_pool_creators(request, pool_id=None):
|
|||||||
# 检查pool_id是否提供
|
# 检查pool_id是否提供
|
||||||
if not pool_id:
|
if not pool_id:
|
||||||
pool_id = request.GET.get('pool_id')
|
pool_id = request.GET.get('pool_id')
|
||||||
if not pool_id:
|
if not pool_id:
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
'code': 400,
|
'code': 400,
|
||||||
'message': '缺少必要参数: pool_id',
|
'message': '缺少必要参数: pool_id',
|
||||||
'data': None
|
'data': None
|
||||||
}, json_dumps_params={'ensure_ascii': False})
|
}, json_dumps_params={'ensure_ascii': False})
|
||||||
|
|
||||||
# 获取分页参数
|
# 获取分页参数
|
||||||
page = int(request.GET.get('page', 1))
|
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"
|
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:
|
if creator.pricing is not None:
|
||||||
pricing_range = f"${creator.pricing_min}-{creator.pricing_max}"
|
pricing_range = f"${creator.pricing}"
|
||||||
else:
|
else:
|
||||||
pricing_range = None
|
pricing_range = None
|
||||||
|
|
||||||
@ -2766,6 +2767,439 @@ def remove_creator_from_private_pool(request):
|
|||||||
}, json_dumps_params={'ensure_ascii': False})
|
}, 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})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,6 +3,14 @@ from rest_framework import exceptions
|
|||||||
from django.contrib.auth.models import AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
from .models import User, UserToken
|
from .models import User, UserToken
|
||||||
from django.utils import timezone
|
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):
|
class CustomTokenAuthentication(authentication.BaseAuthentication):
|
||||||
keyword = 'Token' # 设置认证头关键字
|
keyword = 'Token' # 设置认证头关键字
|
||||||
@ -12,14 +20,14 @@ class CustomTokenAuthentication(authentication.BaseAuthentication):
|
|||||||
auth_header = request.META.get('HTTP_AUTHORIZATION')
|
auth_header = request.META.get('HTTP_AUTHORIZATION')
|
||||||
|
|
||||||
if not auth_header:
|
if not auth_header:
|
||||||
raise exceptions.AuthenticationFailed('未提供认证头')
|
raise CustomAuthenticationFailed(401, '未提供认证头')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 提取token
|
# 提取token
|
||||||
parts = auth_header.split()
|
parts = auth_header.split()
|
||||||
|
|
||||||
if len(parts) != 2 or parts[0] != self.keyword:
|
if len(parts) != 2 or parts[0] != self.keyword:
|
||||||
raise exceptions.AuthenticationFailed('认证头格式不正确')
|
raise CustomAuthenticationFailed(401, '认证头格式不正确')
|
||||||
|
|
||||||
token = parts[1]
|
token = parts[1]
|
||||||
|
|
||||||
@ -30,18 +38,18 @@ class CustomTokenAuthentication(authentication.BaseAuthentication):
|
|||||||
expired_at__gt=timezone.now() # 确保token未过期
|
expired_at__gt=timezone.now() # 确保token未过期
|
||||||
)
|
)
|
||||||
except UserToken.DoesNotExist:
|
except UserToken.DoesNotExist:
|
||||||
raise exceptions.AuthenticationFailed('无效或过期的token')
|
raise CustomAuthenticationFailed(401, '无效或过期的token')
|
||||||
|
|
||||||
# 检查用户是否激活
|
# 检查用户是否激活
|
||||||
if not token_obj.user.is_active:
|
if not token_obj.user.is_active:
|
||||||
raise exceptions.AuthenticationFailed('用户未激活')
|
raise CustomAuthenticationFailed(401, '用户未激活')
|
||||||
|
|
||||||
return (token_obj.user, None)
|
return (token_obj.user, None)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if isinstance(e, exceptions.AuthenticationFailed):
|
if isinstance(e, CustomAuthenticationFailed):
|
||||||
raise e
|
raise e
|
||||||
raise exceptions.AuthenticationFailed('认证失败')
|
raise CustomAuthenticationFailed(401, '认证失败')
|
||||||
|
|
||||||
def authenticate_header(self, request):
|
def authenticate_header(self, request):
|
||||||
return self.keyword
|
return self.keyword
|
Loading…
Reference in New Issue
Block a user