from django.conf import settings from django.db import models from core.enums import StaffGender, StaffIdType, StaffRole, StaffStatus from core.models.base import SoftDeleteModel class Staff(SoftDeleteModel): org_unit = models.ForeignKey( "org.OrgUnit", on_delete=models.RESTRICT, related_name="staff_members", db_index=True, verbose_name="所属组织节点", help_text="当前所属组织节点(门店或店组)", ) user = models.OneToOneField( settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name="staff_profile", verbose_name="登录账号", help_text="Django auth 登录账号", ) name = models.CharField( max_length=50, verbose_name="真实姓名", ) nickname = models.CharField( max_length=50, blank=True, default="", verbose_name="昵称", help_text="通讯录/显示名", ) employee_no = models.CharField( max_length=30, null=True, blank=True, unique=True, verbose_name="员工工号", help_text="系统自动生成或手动录入", ) role = models.CharField( max_length=30, choices=StaffRole.choices, verbose_name="系统角色", help_text="agent=经纪人 / store_manager=店长 / area_manager=区域经理 / admin=管理员 / operator=运营 / system=系统账号", ) job_title = models.CharField( max_length=100, blank=True, default="", verbose_name="职务名称", help_text='如「高级业务员」', ) job_category = models.CharField( max_length=50, blank=True, default="", verbose_name="职务类别", help_text='如「置业顾问」(经纪人判定字段)', ) job_level = models.SmallIntegerField( null=True, blank=True, verbose_name="职级", ) supervisor = models.ForeignKey( "self", null=True, blank=True, on_delete=models.SET_NULL, related_name="subordinates", verbose_name="直属上级", ) status = models.CharField( max_length=20, choices=StaffStatus.choices, default=StaffStatus.ACTIVE, verbose_name="员工状态", help_text="active=在职 / probation=试用期 / resigned=已离职 / frozen=账号冻结", ) phone_enc = models.BinaryField( null=True, blank=True, verbose_name="手机号(加密)", help_text="AES-256-GCM 加密手机号", ) phone_hash = models.CharField( max_length=64, null=True, blank=True, db_index=True, verbose_name="手机号哈希", help_text="SHA-256 哈希,用于唯一性索引", ) phone_hide = models.BooleanField( default=False, verbose_name="通讯录隐藏手机号", ) email = models.EmailField( max_length=255, blank=True, default="", verbose_name="邮箱", ) extension = models.CharField( max_length=20, blank=True, default="", verbose_name="分机号", ) avatar_key = models.TextField( blank=True, default="", verbose_name="头像存储路径", help_text="R2/S3 头像路径", ) is_active = models.BooleanField( default=True, verbose_name="是否启用", help_text="FALSE 时账号不可登录(联动 auth_user.is_active)", ) is_system_admin = models.BooleanField( default=False, verbose_name="是否系统管理员", help_text="影响权限上限", ) first_joined_at = models.DateField( null=True, blank=True, verbose_name="首次入职日期", help_text="计算工龄起点", ) rejoined_at = models.DateField( null=True, blank=True, verbose_name="最近复职日期", ) resigned_at = models.DateField( null=True, blank=True, verbose_name="最近离职日期", ) joined_count = models.SmallIntegerField( default=1, verbose_name="累计入职次数", ) industry_exp_years = models.SmallIntegerField( null=True, blank=True, verbose_name="行业经验", help_text="单位:年", ) mentor = models.ForeignKey( "self", null=True, blank=True, on_delete=models.SET_NULL, related_name="mentees", verbose_name="师傅", help_text="带教员工", ) business_type = models.CharField( max_length=50, blank=True, default="", verbose_name="业务类型", ) bank_name = models.CharField( max_length=100, blank=True, default="", verbose_name="银行名称", ) bank_account = models.CharField( max_length=50, blank=True, default="", verbose_name="银行卡号", help_text="内部财务用", ) partner_no = models.CharField( max_length=50, blank=True, default="", verbose_name="联号", ) recruit_by = models.ForeignKey( "self", null=True, blank=True, on_delete=models.SET_NULL, related_name="recruited_staff", verbose_name="招聘人", ) recruit_source = models.CharField( max_length=50, blank=True, default="", verbose_name="招聘来源", ) referrer = models.ForeignKey( "self", null=True, blank=True, on_delete=models.SET_NULL, related_name="referred_staff", verbose_name="转介人", ) class Meta: db_table = "staff" verbose_name = "员工" verbose_name_plural = "员工" indexes = [ models.Index(fields=["org_unit"], name="idx_staff_org_unit"), models.Index(fields=["supervisor"], name="idx_staff_supervisor"), models.Index(fields=["status"], name="idx_staff_status"), ] def __str__(self) -> str: return self.name class StaffPersonalInfo(models.Model): staff = models.OneToOneField( "org.Staff", on_delete=models.CASCADE, related_name="personal_info", primary_key=True, verbose_name="所属员工", ) gender = models.CharField( max_length=10, choices=StaffGender.choices, blank=True, default="", verbose_name="性别", help_text="male=男 / female=女 / unknown=未知", ) id_type = models.CharField( max_length=20, choices=StaffIdType.choices, blank=True, default="", verbose_name="证件类型", help_text="id_card=身份证 / passport=护照 / other=其他", ) id_number_enc = models.BinaryField( null=True, blank=True, verbose_name="证件号码(加密)", help_text="AES 加密", ) id_number_hash = models.CharField( max_length=64, null=True, blank=True, db_index=True, verbose_name="证件号码哈希", help_text="SHA-256 哈希,实名认证比对用", ) id_verified = models.BooleanField( default=False, verbose_name="是否实名认证", ) id_verified_at = models.DateTimeField( null=True, blank=True, verbose_name="认证时间", ) birthdate = models.DateField( null=True, blank=True, verbose_name="出生日期", ) native_place = models.CharField( max_length=100, blank=True, default="", verbose_name="籍贯", ) domicile_type = models.CharField( max_length=20, blank=True, default="", verbose_name="户籍性质", ) marital_status = models.CharField( max_length=20, blank=True, default="", verbose_name="婚姻状况", ) political_status = models.CharField( max_length=20, blank=True, default="", verbose_name="政治面貌", ) has_children = models.BooleanField( null=True, blank=True, verbose_name="有无子女", ) education_level = models.CharField( max_length=20, blank=True, default="", verbose_name="最高学历", ) ethnicity = models.CharField( max_length=20, blank=True, default="", verbose_name="民族", ) domicile_address = models.CharField( max_length=200, blank=True, default="", verbose_name="户口所在地", ) residence_address = models.CharField( max_length=200, blank=True, default="", verbose_name="居住地址", ) work_start_date = models.DateField( null=True, blank=True, verbose_name="参加工作时间", ) emergency_contact = models.CharField( max_length=50, blank=True, default="", verbose_name="紧急联系人", ) emergency_phone_enc = models.BinaryField( null=True, blank=True, verbose_name="紧急联系人电话(加密)", ) updated_at = models.DateTimeField( auto_now=True, verbose_name="最后更新时间", ) updated_by = models.ForeignKey( "org.Staff", null=True, blank=True, on_delete=models.SET_NULL, related_name="updated_personal_info", verbose_name="最后修改人", ) class Meta: db_table = "staff_personal_info" verbose_name = "员工个人信息" verbose_name_plural = "员工个人信息"