Files
boss_dp/server/serializers.py
2026-03-06 10:05:49 +08:00

201 lines
8.1 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, RecruitFilterSnapshot, 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_internal_value(self, data):
"""请求中兼容 minAmount/maxAmount 等别名;支持 multipart/form-dataQueryDict/多值列表)。"""
if data is None:
data = {}
else:
data = dict(data)
# multipart 解析后字段值可能是 list如 ["测试配置"]),需展平为标量
for key in list(data.keys()):
val = data[key]
if isinstance(val, (list, tuple)):
data[key] = val[0] if len(val) > 0 else ""
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" 或单元素列表
raw = data.get("is_active")
if raw is not None:
if isinstance(raw, (list, tuple)):
raw = raw[0] if raw else ""
if isinstance(raw, str):
data["is_active"] = raw.lower() in ("true", "1", "yes", "")
return super().to_internal_value(data)
# ────────────────────────── 话术 ──────────────────────────
class RecruitFilterSnapshotSerializer(serializers.ModelSerializer):
class Meta:
model = RecruitFilterSnapshot
fields = "__all__"
read_only_fields = ["id", "synced_at", "created_at"]
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"]