Files
2026-06-25 17:41:06 +08:00

260 lines
12 KiB
Python

from tortoise import fields
from .base import BaseModel, TimestampMixin
from .enums import RoleType
class WeixinUser(BaseModel, TimestampMixin):
# 自动成为订单关联方
auto_relate = fields.BooleanField(default=False, description="是否自动成为订单关联方")
userid = fields.CharField(max_length=64, description="用户微信id", index=True) # acctid
corp_id = fields.CharField(max_length=64, description="企业微信id", default="1970325009092879", index=True)
vid = fields.CharField(max_length=64, description="用户微信id", default='', index=True)
wx_id_hash = fields.CharField(max_length=64, null=True, description="微信id hash")
is_quit = fields.BooleanField(default=False, description="是否离职")
has_external_user_permit = fields.BooleanField(default=False, description="是否具有对外联系权限")
wx_nick_name = fields.CharField(max_length=64, null=True, description="微信昵称")
account = fields.CharField(max_length=64, null=True, description="账号")
position = fields.CharField(max_length=64, null=True, description="职位")
role = fields.CharEnumField(RoleType, null=True, description="用户角色", index=True)
depart_ids = fields.JSONField(default=[], description="部门ID列表")
username = fields.CharField(max_length=64, null=True, description="用户微信名称", index=True)
english_name = fields.CharField(max_length=64, null=True, description="英文名")
name = fields.CharField(max_length=64, null=True, description="用户名称", index=True)
realname = fields.CharField(max_length=64, null=True, description="用户真实名字", index=True)
alias = fields.CharField(max_length=30, null=True, description="别名")
avatar = fields.CharField(max_length=512, null=True, description="头像")
mobile = fields.CharField(max_length=11, null=True, description="手机号")
email = fields.CharField(max_length=64, null=True, description="邮箱")
gender = fields.IntField(null=True, description="性别")
erp_id = fields.IntField(null=True, description="ERP中的用户ID", index=True)
erp_name = fields.CharField(max_length=64, null=True, description="ERP中的用户名称")
crm_id = fields.IntField(null=True, description="星云有客中的用户ID", index=True)
crm_name = fields.CharField(max_length=64, null=True, description="星云有客中的用户名称")
def to_dict(self, exclude_fields=None, include_sensitive=False):
"""
自定义字典转换行为
Args:
exclude_fields: 要排除的字段列表
include_sensitive: 是否包含敏感信息(如手机号、邮箱等)
Returns:
dict: 转换后的字典
"""
exclude_fields = exclude_fields or ['staff_memberships']
data = {}
# 获取所有字段名
model_fields = self._meta.fields_map.keys()
for field_name in model_fields:
if field_name in exclude_fields:
continue
# 如果不包含敏感信息,则跳过敏感字段
if not include_sensitive and field_name in ['mobile', 'email']:
continue
value = getattr(self, field_name)
# 处理枚举字段
if field_name == 'role' and value is not None:
data[field_name] = value.value if hasattr(value, 'value') else value
# 处理布尔字段的默认值显示
elif field_name == 'is_quit':
data[field_name] = bool(value) if value is not None else False
elif field_name == 'has_external_user_permit':
data[field_name] = bool(value) if value is not None else False
else:
data[field_name] = value
return data
def to_public_dict(self):
"""
返回公开信息的字典(不包含敏感信息)
"""
sensitive_fields = ['mobile', 'email']
exclude_fields = ['wx_id_hash'] # 可能还有其他不想暴露的字段
all_exclude = sensitive_fields + exclude_fields
return self.to_dict(exclude_fields=all_exclude, include_sensitive=False)
def to_detail_dict(self):
"""
返回详细信息的字典(包含所有信息)
"""
return self.to_dict(include_sensitive=True)
def to_safe_dict(self, visible_fields=None):
"""
返回指定字段的安全字典
Args:
visible_fields: 指定要包含的字段列表,如果为None则使用默认安全字段
"""
if visible_fields is None:
# 默认的安全字段(不包含敏感信息)
visible_fields = [
'userid', 'vid', 'wx_nick_name', 'username', 'name',
'realname', 'alias', 'avatar', 'position', 'role',
'depart_ids', 'erpid', 'crmid', 'created_at', 'updated_at'
]
data = {}
for field_name in visible_fields:
if hasattr(self, field_name):
value = getattr(self, field_name)
if field_name == 'role' and value is not None:
data[field_name] = value.value if hasattr(value, 'value') else value
else:
data[field_name] = value
return data
class Meta:
table = "weixin_user"
class WeixinGroupChat(BaseModel, TimestampMixin):
chat_id = fields.CharField(max_length=64, description="群聊ID", index=True)
xingyun_chat_id = fields.IntField(null=True, description="星云中的群聊ID", index=True)
name = fields.CharField(max_length=64, null=True, description="群聊名称")
create_time = fields.IntField(description="创建时间")
admin_list = fields.JSONField(default=[], description="所有管理员")
owner = fields.CharField(max_length=64, null=True, description="群主")
member_version = fields.CharField(max_length=64, null=True, description="群成员版本")
external_user_count = fields.IntField(null=True, description="外部群成员数量")
external_member_list = fields.JSONField(default=[], description="外部群成员列表")
internal_member_count = fields.IntField(null=True, description="内部群成员数量")
internal_member_list = fields.JSONField(default=[], description="内部群成员列表")
avatars = fields.JSONField(default=[], description="群成员头像")
class Meta:
table = "weixin_group_chat"
class WeixinCustomer(BaseModel, TimestampMixin):
order_id = fields.CharField(max_length=64, null=True, description="订单ID", index=True)
shop_name = fields.CharField(max_length=64, null=True, description="店铺名称")
weixin_id = fields.CharField(max_length=64, null=True, description="微信体系中的id", index=True)
weixin_name = fields.CharField(max_length=64, null=True, description="微信体系中的用户名")
weixin_unionid = fields.CharField(max_length=64, null=True, description="微信体系中的unionid", index=True)
weixin_avatar = fields.CharField(max_length=512, null=True, description="微信头像")
xingyun_id = fields.IntField(null=True, description="星云有客中的用户ID", index=True)
xingyun_sex = fields.IntField(null=True, description="星云有客中的用户性别", index=True)
xingyun_name = fields.CharField(max_length=64, null=True, description="星云有客中的用户名")
xingyun_avatar = fields.CharField(max_length=512, null=True, description="星云有客头像")
xingyun_tags = fields.JSONField(default=[], null=True, description="星云用户标签")
xingyun_external_userid = fields.CharField(max_length=64, null=True, description="星云有客中的外部用户ID")
xingyun_sync = fields.BooleanField(default=False, description="是否已同步到星云", index=True)
# erp_id = fields.IntField(null=True, description="ERP中的客户ID", index=True)
# erp_name = fields.CharField(max_length=64, null=True, description="ERP中的客户名称")
# erp_avatar = fields.CharField(max_length=512, null=True, description="ERP头像")
taobao_id = fields.CharField(max_length=64, null=True, description="淘宝中的用户ID", index=True)
taobao_name = fields.CharField(max_length=64, null=True, description="淘宝中的用户名")
taobao_avatar = fields.CharField(max_length=512, null=True, description="淘宝头像")
need_confirm = fields.BooleanField(null=True, description="是否需要确认")
extra = fields.JSONField(default={}, description="额外信息")
# 使用 through 指向自定义中间模型
groups = fields.ManyToManyField(
"models.WeixinGroupChat",
through="customer_group", # 必须是中间模型的 table 名(或模型名)
related_name="customers"
)
@classmethod
def orm_format(cls, data: dict):
# 将不在字段中的字段保存到extra字段中
all_fields = set(cls._meta.fields_map.keys())
extra = {}
data_in = {"extra": extra}
for k, v in data.copy().items():
if k not in all_fields:
extra[k] = v
else:
data_in[k] = v
id = data_in.pop('id', None)
if id: data_in['platform_id'] = id
return data_in
def to_dict(self, *args, **kwargs):
# 类似于WeixinUser参数
return {
"id": self.id,
"userid": self.weixin_id,
"name": self.taobao_name,
"avatar": self.xingyun_avatar,
"weixin_name": self.weixin_name,
"role": 'buyer',
"is_customer": True,
}
# data = await super().to_dict(*args, **kwargs)
# data['id'] = data.pop('platform_id', None)
# data.update(data.pop('extra', {}))
# return data
def dump_dict(self):
all_fields = set(self._meta.fields_map.keys())
data = {}
for k, v in self.__dict__.items():
if k in all_fields:
data[k] = v
return data
@classmethod
def create_bind(cls, data: dict):
# 将不在字段中的字段保存到extra字段中
data_in = cls.orm_format(data)
return cls(**data_in)
class Meta:
table = "weixin_customer"
# === 中间模型:CustomerGroup ===
class CustomerGroup(BaseModel, TimestampMixin):
"""顾客与群聊的关联关系(带额外信息)"""
# 外键指向顾客
customer = fields.ForeignKeyField(
"models.WeixinCustomer",
related_name="group_memberships" # 从 Customer 反向查关系
)
# 外键指向群聊
group = fields.ForeignKeyField(
"models.WeixinGroupChat",
related_name="customer_memberships" # 从 Group 反向查关系
)
# 外键指向群聊
staff = fields.ForeignKeyField(
"models.WeixinUser",
related_name="staff_memberships" # 从 User 反向查关系
)
# 额外字段
join_time = fields.DatetimeField(auto_now_add=True, description="入群时间", index=True)
role = fields.CharField(
max_length=32,
default="member",
description="群内角色:member / admin / owner",
index=True
)
staff_userid = fields.CharField(max_length=64, null=True, description="客服ID", index=True)
customer_userid = fields.CharField(max_length=64, null=True, description="顾客的ID", index=True)
group_chatid = fields.CharField(max_length=64, null=True, description="群聊ID", index=True)
order_id = fields.CharField(max_length=64, null=True, description="订单ID", index=True)
shop_name = fields.CharField(max_length=64, null=True, description="店铺名称", index=True)
remark = fields.CharField(max_length=255, null=True, description="备注")
class Meta:
table = "customer_group"
# 确保同一个顾客不能重复加入同一个群
unique_together = ("customer", "group")