user.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. from typing import Optional, Any, List
  2. from pydantic import BaseModel, Field, field_validator, model_validator
  3. from datetime import datetime
  4. # 中国大陆手机号(与 M2M 同步等接口一致)
  5. _CN_MOBILE_PATTERN = r"^1[3-9]\d{9}$"
  6. class UserBase(BaseModel):
  7. mobile: str
  8. name: Optional[str] = None
  9. english_name: Optional[str] = None
  10. organization_id: Optional[int] = None
  11. status: str = "PENDING"
  12. role: Optional[str] = "ORDINARY_USER"
  13. is_deleted: int = 0
  14. class UserCreate(UserBase):
  15. password: str
  16. admin_password: Optional[str] = None
  17. class UserRegister(BaseModel):
  18. mobile: str
  19. password: str
  20. sms_code: str # Added field
  21. name: str # Chinese Name
  22. class PromoteUserRequest(BaseModel):
  23. password: str
  24. captcha_id: str
  25. captcha_code: str
  26. class DeleteUserRequest(BaseModel):
  27. """删除普通用户(软删除)时校验操作者密码与短信验证码。"""
  28. password: str
  29. sms_code: str
  30. class BatchResetEnglishNameRequest(BaseModel):
  31. user_ids: List[int]
  32. admin_password: str
  33. class BatchSetOrganizationRequest(BaseModel):
  34. """批量设置用户所属组织;organization_id 为 null 表示取消归属。"""
  35. user_ids: List[int] = Field(..., min_length=1, max_length=500)
  36. organization_id: Optional[int] = None
  37. admin_password: str
  38. class UserUpdate(BaseModel):
  39. password: Optional[str] = None
  40. mobile: Optional[str] = None
  41. name: Optional[str] = None
  42. english_name: Optional[str] = None
  43. organization_id: Optional[int] = None
  44. status: Optional[str] = None
  45. role: Optional[str] = None
  46. is_deleted: Optional[int] = None
  47. admin_password: Optional[str] = None
  48. class UserSyncRequest(BaseModel):
  49. mobile: str = Field(..., pattern=_CN_MOBILE_PATTERN, description="用户手机号(平台唯一标识)")
  50. name: Optional[str] = Field(
  51. None,
  52. description="姓名;UPSERT 时必填。新建用户时英文名由平台根据姓名自动生成并去重",
  53. )
  54. english_name: Optional[str] = Field(
  55. None,
  56. description="已废弃:M2M 同步不再使用此字段,英文名由平台根据姓名生成",
  57. )
  58. password: Optional[str] = None
  59. status: Optional[str] = None
  60. mapped_key: str = Field(..., min_length=1, max_length=100, description="外部系统用户 ID(映射账号)")
  61. mapped_email: Optional[str] = None # External User Email
  62. is_active: Optional[bool] = None # True=Active, False=Disabled. None=No Change
  63. sync_action: Optional[str] = "UPSERT"
  64. @field_validator("mobile", "mapped_key", mode="before")
  65. @classmethod
  66. def strip_required_str(cls, v):
  67. if isinstance(v, str):
  68. return v.strip()
  69. return v
  70. class UserInDBBase(UserBase):
  71. id: int
  72. organization_name: Optional[str] = None
  73. created_at: datetime
  74. updated_at: datetime
  75. @model_validator(mode="before")
  76. @classmethod
  77. def flatten_organization(cls, data: Any) -> Any:
  78. from app.models.user import User as UserORM
  79. if isinstance(data, UserORM):
  80. d = {c.name: getattr(data, c.name) for c in data.__table__.columns}
  81. org = getattr(data, "organization", None)
  82. d["organization_name"] = org.name if org is not None else None
  83. return d
  84. return data
  85. class Config:
  86. from_attributes = True
  87. class User(UserInDBBase):
  88. pass
  89. class UserInDB(UserInDBBase):
  90. password_hash: str
  91. class UserList(BaseModel):
  92. total: int
  93. items: List[User]
  94. class UserSyncSimple(BaseModel):
  95. mobile: str
  96. name: Optional[str] = None
  97. english_name: Optional[str] = None
  98. class Config:
  99. from_attributes = True
  100. class UserSyncList(BaseModel):
  101. total: int
  102. items: List[UserSyncSimple]