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")