Files
boss_dp/server/serializers.py
2026-03-05 23:04:32 +08:00

189 lines
7.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
DRF 序列化器。
"""
from rest_framework import serializers
from server.models import (
BossAccount, TaskLog, FilterConfig, ChatScript, ContactRecord, SystemConfig,
FollowUpConfig, FollowUpScript, FollowUpRecord
)
# ────────────────────────── 账号 ──────────────────────────
class BossAccountSerializer(serializers.ModelSerializer):
"""账号序列化器(读取用)。"""
# 运行时补充的字段(非数据库字段)
worker_name = serializers.CharField(read_only=True, default="")
worker_online = serializers.BooleanField(read_only=True, default=False)
class Meta:
model = BossAccount
fields = [
"id", "worker_id", "browser_id", "browser_name",
"boss_username", "boss_id", "is_logged_in",
"current_task_id", "current_task_status",
"checked_at", "created_at", "updated_at",
"worker_name", "worker_online",
]
class AccountBindSerializer(serializers.Serializer):
"""添加账号请求。"""
browser_name = serializers.CharField(max_length=128)
worker_id = serializers.CharField(max_length=64)
# ────────────────────────── 任务 ──────────────────────────
class TaskCreateSerializer(serializers.Serializer):
"""提交任务请求。"""
task_type = serializers.CharField(max_length=64)
id = serializers.IntegerField(required=False, allow_null=True, default=None) # 账号 ID
account_id = serializers.IntegerField(required=False, allow_null=True, default=None) # 同上,别名
boss_id = serializers.CharField(max_length=64, required=False, allow_blank=True, default="") # 即 /api/accounts 返回的 id账号主键
worker_id = serializers.CharField(max_length=64, required=False, allow_blank=True, default="")
account_name = serializers.CharField(max_length=128, required=False, allow_blank=True, default="")
params = serializers.JSONField(required=False, default=dict)
class TaskOutSerializer(serializers.Serializer):
"""任务信息响应。"""
task_id = serializers.CharField()
task_type = serializers.CharField()
status = serializers.CharField()
worker_id = serializers.CharField(allow_null=True)
account_name = serializers.CharField(allow_null=True)
params = serializers.DictField()
progress = serializers.CharField(allow_null=True)
result = serializers.JSONField(allow_null=True)
error = serializers.CharField(allow_null=True)
created_at = serializers.CharField()
updated_at = serializers.CharField()
# ────────────────────────── Worker ──────────────────────────
class BrowserProfileSerializer(serializers.Serializer):
"""浏览器窗口信息。"""
id = serializers.CharField()
name = serializers.CharField()
remark = serializers.CharField()
class WorkerOutSerializer(serializers.Serializer):
"""Worker 信息响应。"""
worker_id = serializers.CharField()
worker_name = serializers.CharField()
browsers = BrowserProfileSerializer(many=True)
online = serializers.BooleanField()
current_task_id = serializers.CharField(allow_null=True)
# ────────────────────────── 认证 ──────────────────────────
class LoginSerializer(serializers.Serializer):
"""登录请求。"""
username = serializers.CharField(max_length=64)
password = serializers.CharField(max_length=128)
# ────────────────────────── 筛选配置 ──────────────────────────
# 请求中可能出现的“金额”字段别名,统一映射为 min_amount / max_amount保证请求与响应字段名一致
FILTER_AMOUNT_ALIASES = {
"minAmount": "min_amount",
"maxAmount": "max_amount",
"最小金额": "min_amount",
"最大金额": "max_amount",
}
class FilterConfigSerializer(serializers.ModelSerializer):
"""筛选配置:列表/详情返回 name, position_keywords, city, salary_min, salary_max, experience, education, is_active 等。"""
class Meta:
model = FilterConfig
fields = "__all__"
read_only_fields = ["id", "created_at", "updated_at"]
def to_representation(self, instance):
"""响应中 is_active 统一为字符串 "true" / "false""""
data = super().to_representation(instance)
data["is_active"] = "true" if instance.is_active else "false"
return data
def to_internal_value(self, data):
"""请求中兼容 minAmount/maxAmount、最小金额/最大金额 等别名,统一为 min_amount/max_amount。"""
if isinstance(data, dict):
data = dict(data)
for alias, canonical in FILTER_AMOUNT_ALIASES.items():
if alias in data and canonical not in data:
data[canonical] = data.pop(alias)
# 请求里 is_active 可能是字符串 "true"/"false"
if "is_active" in data and isinstance(data["is_active"], str):
data["is_active"] = data["is_active"].lower() in ("true", "1", "yes", "")
return super().to_internal_value(data)
# ────────────────────────── 话术 ──────────────────────────
class ChatScriptSerializer(serializers.ModelSerializer):
script_type_display = serializers.CharField(source="get_script_type_display", read_only=True)
class Meta:
model = ChatScript
fields = "__all__"
read_only_fields = ["id", "created_at", "updated_at"]
# ────────────────────────── 联系记录 ──────────────────────────
class ContactRecordSerializer(serializers.ModelSerializer):
class Meta:
model = ContactRecord
fields = "__all__"
read_only_fields = ["id", "created_at"]
# ────────────────────────── 系统配置 ──────────────────────────
class SystemConfigSerializer(serializers.ModelSerializer):
class Meta:
model = SystemConfig
fields = "__all__"
read_only_fields = ["updated_at"]
# ────────────────────────── 复聊配置 ──────────────────────────
class FollowUpScriptSerializer(serializers.ModelSerializer):
"""复聊话术序列化器。"""
class Meta:
model = FollowUpScript
fields = "__all__"
read_only_fields = ["id", "created_at"]
class FollowUpConfigSerializer(serializers.ModelSerializer):
"""复聊配置序列化器(包含关联的话术列表)。"""
scripts = serializers.SerializerMethodField()
class Meta:
model = FollowUpConfig
fields = "__all__"
read_only_fields = ["id", "created_at", "updated_at"]
def get_scripts(self, obj):
"""获取该配置下的所有话术。"""
scripts = FollowUpScript.objects.filter(config_id=obj.id, is_active=True).order_by('day_number', 'order')
return FollowUpScriptSerializer(scripts, many=True).data
class FollowUpRecordSerializer(serializers.ModelSerializer):
"""复聊记录序列化器。"""
class Meta:
model = FollowUpRecord
fields = "__all__"
read_only_fields = ["id", "sent_at"]